Skip to content

Commit

Permalink
Merge pull request #2772 from OneCommunityGlobal/Carlos-Anniversary-C…
Browse files Browse the repository at this point in the history
…elebrated

Carlos anniversary celebrated
  • Loading branch information
one-community authored Dec 3, 2024
2 parents 0b93d7b + 8b28e2b commit e6b0195
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 23 deletions.
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^10.4.9",
"@testing-library/user-event": "^12.0.14",
"@types/react-router-dom": "^5.3.3",
"babel-eslint": "^10.1.0",
"cross-env": "^5.2.1",
"enzyme": "^3.10.0",
Expand Down
2 changes: 1 addition & 1 deletion src/actions/sendEmails.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,4 @@ export const removeNonHgnUserEmailSubscription = async (email = '') => {

return { success: false, error: error };
}
};
};
2 changes: 1 addition & 1 deletion src/actions/totalOrgSummary.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const fetchTotalOrgSummaryReportError = error => ({
export const getTaskAndProjectStats = (startDate, endDate) => {
const url = ENDPOINTS.HOURS_TOTAL_ORG_SUMMARY(startDate, endDate);
return async dispatch => {
dispatch(fetchTotalOrgSummaryReportBegin());
await dispatch(fetchTotalOrgSummaryReportBegin());
try {
const response = await axios.get(url);
dispatch(fetchTotalOrgSummaryReportSuccess(response.data));
Expand Down
68 changes: 49 additions & 19 deletions src/components/Announcements/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { sendEmail, broadcastEmailsToAll } from '../../actions/sendEmails';
import { boxStyle, boxStyleDark } from 'styles';
import { toast } from 'react-toastify';

function Announcements() {
function Announcements({title, email}) {
const darkMode = useSelector(state => state.theme.darkMode);
const dispatch = useDispatch();
const [emailTo, setEmailTo] = useState('');
const [emailList, setEmailList] = useState([]);
const [emailContent, setEmailContent] = useState('');
const [headerContent, setHeaderContent] = useState('');
Expand Down Expand Up @@ -79,11 +80,19 @@ function Announcements() {
content_css: darkMode ? 'dark' : 'default',
}

useEffect(() => {
if (email) {
const trimmedEmail = email.trim();
setEmailTo(email);
setEmailList(trimmedEmail.split(','));
}
}, [email]);

const handleEmailListChange = e => {
const emails = e.target.value.split(',');
setEmailList(emails);
};

const handleHeaderContentChange = e => {
setHeaderContent(e.target.value);
}
Expand Down Expand Up @@ -130,22 +139,22 @@ function Announcements() {

const handleSendEmails = () => {
const htmlContent = emailContent;

if (emailList.length === 0 || emailList.every(email => !email.trim())) {
toast.error('Error: Empty Email List. Please enter AT LEAST One email.');
return;
}

const invalidEmails = emailList.filter(email => !validateEmail(email.trim()));

if (invalidEmails.length > 0) {
toast.error(`Error: Invalid email addresses: ${invalidEmails.join(', ')}`);
return;
}
dispatch(sendEmail(emailList.join(','), 'Weekly Update', htmlContent));

dispatch(sendEmail(emailList.join(','), title ? 'Anniversary congrats' : 'Weekly update', htmlContent));
};


const handleBroadcastEmails = () => {
const htmlContent = `
Expand All @@ -160,7 +169,12 @@ function Announcements() {
<div className={darkMode ? 'bg-oxford-blue text-light' : ''} style={{minHeight: "100%"}}>
<div className="email-update-container">
<div className="editor">
<h3>Weekly Progress Editor</h3>
{ title ? (
<h3> {title} </h3>
)
:( <h3>Weekly Progress Editor</h3>)
}

<br />
{showEditor && <Editor
tinymceScriptSrc="/tinymce/tinymce.min.js"
Expand All @@ -171,22 +185,38 @@ function Announcements() {
setEmailContent(content);
}}
/>}
{
title ? (
""
) : (
<button type="button" className="send-button" onClick={handleBroadcastEmails} style={darkMode ? boxStyleDark : boxStyle}>
Broadcast Weekly Update
</button>
)
}

</div>
<div className={`emails ${darkMode ? 'bg-yinmn-blue' : ''}`} style={darkMode ? boxStyleDark : boxStyle}>
<label htmlFor="email-list-input" className={darkMode ? 'text-light' : 'text-dark'}>
Email List (comma-separated):
</label>
<input
type="text"
id="email-list-input"
onChange={handleEmailListChange}
className="input-text-for-announcement"
/>
{
title ? (
<p>Email</p>
) : (

<label htmlFor="email-list-input" className={darkMode ? 'text-light' : 'text-dark'}>
Email List (comma-separated):
</label>
)
}
<input type="text" value= {emailTo} id="email-list-input" onChange={ handleEmailListChange} className='input-text-for-announcement' />

<button type="button" className="send-button" onClick={handleSendEmails} style={darkMode ? boxStyleDark : boxStyle}>
Send Email to specific user
{
title ? (
"Send Email"
) : (
"Send mail to specific users"
)
}
</button>

<hr />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { useHistory } from 'react-router-dom';
import { getTotalOrgSummary } from 'actions/totalOrgSummary';
import { useEffect, useState } from 'react';
import { IoPersonOutline } from 'react-icons/io5';
import { SiGmail } from 'react-icons/si';
import { useDispatch } from 'react-redux';

export default function AnniversaryCelebrated({
fromDate,
toDate,
fromOverDate,
toOverDate,
darkMode,
}) {
const dispatch = useDispatch();
const history = useHistory();
const [anniversaryStatsOnSetDate, setAnniversaryStatsOnSetDate] = useState([]);
const [anniversaryStatsOnLastDate, setAnniversaryStatsOnLastDate] = useState([]);
const [anniversaryStatsOnSetDateQuantity, setAnniversaryStatsOnSetDateQuantity] = useState(0);
const [anniversaryStatsOnLastDateQuantity, setAnniversaryStatsOnSLastDateQuantity] = useState(0);
const percentageChange = (
(anniversaryStatsOnSetDateQuantity / anniversaryStatsOnLastDateQuantity - 1) *
100
).toFixed(2);
const isPositive = percentageChange >= 0;
const sign = isPositive ? '+' : '';

useEffect(() => {
setAnniversaryStatsOnSetDateQuantity(anniversaryStatsOnSetDate.length);
}, [anniversaryStatsOnSetDate, anniversaryStatsOnSetDateQuantity]);

useEffect(() => {
setAnniversaryStatsOnSLastDateQuantity(anniversaryStatsOnLastDate.length);
}, [anniversaryStatsOnLastDate, anniversaryStatsOnLastDateQuantity]);

useEffect(() => {
const fectchOnSetDate = async () => {
const response = await dispatch(getTotalOrgSummary(fromDate, toDate));
setAnniversaryStatsOnSetDate(response.data.anniversaryStats);
};
fectchOnSetDate();
}, [fromDate, toDate]);

useEffect(() => {
const fectchOnLastDate = async () => {
const res = await dispatch(getTotalOrgSummary(fromOverDate, toOverDate));
setAnniversaryStatsOnLastDate(res.data.anniversaryStats);
};
fectchOnLastDate();
}, [fromOverDate, toOverDate]);

const handleEmailClick = email => {
history.push('/sendemail', { state: { email } });
};

return (
<div>
<h4 className={`${darkMode ? 'text-light' : 'text-dark'} fw-bold text-center`}>
Anniversary Celebrated
</h4>
<span
className={`text-center ${isPositive ? 'text-success' : 'text-danger'}`}
style={{ fontWeight: 'bold' }}
>
{sign}
{percentageChange}% week over week
</span>
<ul className="w-90 overflow-auto" style={{ maxHeight: '220px' }}>
{Array.isArray(anniversaryStatsOnSetDate) && anniversaryStatsOnSetDate.length > 0 ? (
anniversaryStatsOnSetDate.map(item => (
<li key={item._id} className="d-flex flex-column ">
<div className="d-flex flex-row m-2">
{item.profilePic ? (
<img
src={item.profilePic}
alt="profile"
className="rounded-circle ms-5"
style={{ width: '30px', height: '30px' }}
/>
) : (
<IoPersonOutline size={30} className="mx-2" />
)}
<SiGmail
size={30}
color="red"
className="mx-2 "
style={{ cursor: 'pointer' }}
onClick={() => handleEmailClick(item.email)}
/>
<p className="mx-2">{`${item.firstName} ${item.lastName}`}</p>
</div>
</li>
))
) : (
<p>There are no Anniversaries in this period</p>
)}
</ul>
</div>
);
}
12 changes: 10 additions & 2 deletions src/components/TotalOrgSummary/TotalOrgSummary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import HoursCompletedBarChart from './HoursCompleted/HoursCompletedBarChart';
import HoursWorkList from './HoursWorkList/HoursWorkList';
import NumbersVolunteerWorked from './NumbersVolunteerWorked/NumbersVolunteerWorked';
import Loading from '../common/Loading';
import AnniversaryCelebrated from './AnniversaryCelebrated/AnniversaryCelebrated';
import RoleDistributionPieChart from './VolunteerRolesTeamDynamics/RoleDistributionPieChart';
import WorkDistributionBarChart from './VolunteerRolesTeamDynamics/WorkDistributionBarChart';

Expand Down Expand Up @@ -314,12 +315,19 @@ function TotalOrgSummary(props) {
<Row>
<Col lg={{ size: 7 }}>
<div className="component-container component-border">
<VolunteerHoursDistribution />
<h4 className="text-center">Volunteer Trends by time</h4>
<span className="text-center"> Work in progres...</span>
</div>
</Col>
<Col lg={{ size: 5 }}>
<div className="component-container component-border">
<VolunteerHoursDistribution />
<AnniversaryCelebrated
fromDate={fromDate}
toDate={toDate}
fromOverDate={fromOverDate}
toOverDate={toOverDate}
darkMode={darkMode}
/>
</div>
</Col>
</Row>
Expand Down
13 changes: 13 additions & 0 deletions src/components/common/EmailSender/EmailSender.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'
import { useLocation } from 'react-router-dom'
import Announcements from 'components/Announcements'

export const EmailSender = () => {

const location = useLocation();
const email = location.state.state.email;

return (
<Announcements title='Email Form' email={ email } />
)
};
8 changes: 8 additions & 0 deletions src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import ForgotPassword from './components/Login/ForgotPassword';
import Inventory from './components/Inventory';
import EmailSubscribeForm from './components/EmailSubscribeForm';
import UnsubscribeForm from './components/EmailSubscribeForm/Unsubscribe';
import { EmailSender } from './components/common/EmailSender/EmailSender';
import Collaboration from './components/Collaboration';

// BM Dashboard
Expand Down Expand Up @@ -243,6 +244,13 @@ export default (
component={Announcements}
routePermissions={RoutePermissions.announcements}
/>
<ProtectedRoute
path="/sendemail"
exact
component={EmailSender}
allowedRoles={[UserRole.Administrator, UserRole.Owner]}
routePermissions={RoutePermissions.projects}
/>

<ProtectedRoute
path="/totalorgsummary"
Expand Down

0 comments on commit e6b0195

Please sign in to comment.