Skip to content

Commit

Permalink
feat: send email notification when a file upload to internal team
Browse files Browse the repository at this point in the history
  • Loading branch information
RRanath authored and ccbc-service-account committed Dec 24, 2024
1 parent 093ba15 commit be4e25a
Show file tree
Hide file tree
Showing 25 changed files with 425 additions and 43 deletions.
6 changes: 5 additions & 1 deletion app/backend/lib/ches/sendEmail.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as Sentry from '@sentry/nextjs';
import getConfig from 'next/config';
import toTitleCase from '../../../utils/formatString';
import config from '../../../config';

const CHES_API_URL = config.get('CHES_API_URL');
const namespace = getConfig()?.publicRuntimeConfig?.OPENSHIFT_APP_NAMESPACE;

const sendEmail = async (
token: string,
Expand All @@ -11,14 +14,15 @@ const sendEmail = async (
tag: string,
emailCC: string[] = []
) => {
const environment = toTitleCase(namespace?.split('-')[1] || 'Dev');
try {
const request = {
bodyType: 'html',
body,
cc: emailCC,
delayTs: 0,
encoding: 'utf-8',
from: 'CCBC Portal <[email protected]>',
from: `CCBC Portal ${environment !== 'Prod' && environment} <[email protected]>`,
priority: 'normal',
subject,
to: emailTo,
Expand Down
6 changes: 5 additions & 1 deletion app/backend/lib/ches/sendEmailMerge.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as Sentry from '@sentry/nextjs';
import getConfig from 'next/config';
import toTitleCase from '../../../utils/formatString';
import config from '../../../config';

const CHES_API_URL = config.get('CHES_API_URL');
const namespace = getConfig()?.publicRuntimeConfig?.OPENSHIFT_APP_NAMESPACE;

export interface Context {
to: string[];
Expand All @@ -19,13 +22,14 @@ const sendEmailMerge = async (
subject: string,
contexts: Contexts
) => {
const environment = toTitleCase(namespace?.split('-')[1] || 'Dev');
try {
const request = {
bodyType: 'html',
body,
contexts,
encoding: 'utf-8',
from: 'CCBC Portal <[email protected]>',
from: `CCBC Portal ${environment !== 'Prod' && environment} <[email protected]>`,
priority: 'normal',
subject,
attachments: [],
Expand Down
8 changes: 8 additions & 0 deletions app/backend/lib/emails/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import notifyConditionallyApproved from './templates/notifyConditionallyApproved
import notifyApplicationSubmission from './templates/notifyApplicationSubmission';
import notifyFailedReadOfTemplateData from './templates/notifyFailedReadOfTemplateData';
import notifySowUpload from './templates/notifySowUpload';
import notifyDocumentUpload from './templates/notifyDocumentUpload';

const email = Router();

Expand Down Expand Up @@ -98,4 +99,11 @@ email.post('/api/email/notifySowUpload', limiter, (req, res) => {
});
});

email.post('/api/email/notifyDocumentUpload', limiter, (req, res) => {
const { params } = req.body;
return handleEmailNotification(req, res, notifyDocumentUpload, {
...params,
});
});

export default email;
38 changes: 38 additions & 0 deletions app/backend/lib/emails/templates/notifyDocumentUpload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
EmailTemplate,
EmailTemplateProvider,
} from '../handleEmailNotification';

const notifyDocumentUpload: EmailTemplateProvider = (
applicationId: string,
url: string,
initiator: any,
params: any
): EmailTemplate => {
const { ccbcNumber, documentType, timestamp, documentNames } = params;

const section = {
'Claim & Progress Report': 'project?section=claimsReport',
'Community Progress Report': 'project?section=communityProgressReport',
'Milestone Report': 'project?section=milestoneReport',
'Statement of Work': 'project?section=projectInformation',
};

const link = `<a href='${url}/analyst/application/${applicationId}/${section[documentType] ?? 'rfi'}'>${ccbcNumber}</a>`;
return {
emailTo: [112, 10, 111],
emailCC: [],
tag: 'document-upload-notification',
subject: `${documentType} uploaded in Portal`,
body: `
<h1>${documentType} uploaded in Portal</h1>
<p>Notification: A ${documentType} has been uploaded in the Portal for ${link} on ${timestamp}.</p>
<ul>
${documentNames.map((file) => `<li><em>${file}</em></li>`).join('')}
</ul>
`,
};
};

export default notifyDocumentUpload;
4 changes: 2 additions & 2 deletions app/components/Analyst/History/HistoryFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { FormBase } from 'components/Form';
import { historyFilter } from 'formSchema/analyst';
import historyFilterUiSchema from 'formSchema/uiSchema/history/historyFilterUiSchema';
import transformToTitleCase from 'utils/formatString';
import toTitleCase from 'utils/formatString';

interface HistoryFilterProps {
filterOptions: { typeOptions: string[]; userOptions: string[] };
Expand Down Expand Up @@ -53,7 +53,7 @@ const HistoryFilter: React.FC<HistoryFilterProps> = ({
.filter((type) => type !== 'attachment')
.map((type) => ({
value: type,
label: transformToTitleCase(type),
label: toTitleCase(type, '_'),
}));

const filterSchema = historyFilter(formattedTypeOptions, userOptions);
Expand Down
7 changes: 7 additions & 0 deletions app/components/Analyst/Project/Claims/ClaimsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useArchiveApplicationClaimsDataMutation as useArchiveClaims } from 'sch
import excelValidateGenerator from 'lib/helpers/excelValidate';
import Toast from 'components/Toast';
import useModal from 'lib/helpers/useModal';
import useEmailNotification from 'lib/helpers/useEmailNotification';
import ClaimsView from './ClaimsView';
import ProjectTheme from '../ProjectTheme';
import ProjectForm from '../ProjectForm';
Expand Down Expand Up @@ -105,6 +106,7 @@ const ClaimsForm: React.FC<Props> = ({ application, isExpanded }) => {
const [isFormEditMode, setIsFormEditMode] = useState(false);
const [createClaims] = useCreateClaimsMutation();
const [archiveClaims] = useArchiveClaims();
const { notifyDocumentUpload } = useEmailNotification();
const hiddenSubmitRef = useRef<HTMLButtonElement>(null);
// use this to live validate the form after the first submit attempt
const [isSubmitAttempted, setIsSubmitAttempted] = useState(false);
Expand Down Expand Up @@ -195,6 +197,11 @@ const ClaimsForm: React.FC<Props> = ({ application, isExpanded }) => {

if (res?.status === 200) {
setShowToast(true);
notifyDocumentUpload(applicationRowId, {
documentType: 'Claim & Progress Report',
ccbcNumber,
documentNames: [excelFile.name],
});
}
},
onError: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import excelValidateGenerator from 'lib/helpers/excelValidate';
import { getFiscalQuarter, getFiscalYear } from 'utils/fiscalFormat';
import Toast from 'components/Toast';
import useModal from 'lib/helpers/useModal';
import useEmailNotification from 'lib/helpers/useEmailNotification';
import CommunityProgressView from './CommunityProgressView';
import ProjectTheme from '../ProjectTheme';
import ProjectForm from '../ProjectForm';
Expand Down Expand Up @@ -108,6 +109,7 @@ const CommunityProgressReportForm: React.FC<Props> = ({
const [createCommunityProgressReport] =
useCreateCommunityProgressReportMutation();
const [archiveCommunityProgressReport] = useArchiveCpr();
const { notifyDocumentUpload } = useEmailNotification();
const hiddenSubmitRef = useRef<HTMLButtonElement>(null);
// use this to live validate the form after the first submit attempt
const [isSubmitAttempted, setIsSubmitAttempted] = useState(false);
Expand Down Expand Up @@ -207,6 +209,11 @@ const CommunityProgressReportForm: React.FC<Props> = ({

if (res?.status === 200) {
setShowToast(true);
notifyDocumentUpload(applicationRowId, {
ccbcNumber,
documentType: 'Community Progress Report',
documentNames: [excelFile.name],
});
}
},
onError: () => {
Expand Down
7 changes: 7 additions & 0 deletions app/components/Analyst/Project/Milestones/MilestonesForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useArchiveApplicationMilestoneDataMutation as useArchiveMilestone } fro
import excelValidateGenerator from 'lib/helpers/excelValidate';
import Toast from 'components/Toast';
import useModal from 'lib/helpers/useModal';
import useEmailNotification from 'lib/helpers/useEmailNotification';
import MilestonesView from './MilestonesView';
import ProjectTheme from '../ProjectTheme';
import ProjectForm from '../ProjectForm';
Expand Down Expand Up @@ -125,6 +126,7 @@ const MilestonesForm: React.FC<Props> = ({ application, isExpanded }) => {
ccbcNumber,
} = queryFragment;

const { notifyDocumentUpload } = useEmailNotification();
const [formData, setFormData] = useState({} as FormData);
const deleteConfirmationModal = useModal();
// store the current community progress data node for edit mode so we have access to row id and relay connection
Expand Down Expand Up @@ -226,6 +228,11 @@ const MilestonesForm: React.FC<Props> = ({ application, isExpanded }) => {

if (res?.status === 200) {
setShowToast(true);
notifyDocumentUpload(applicationRowId, {
documentType: 'Milestone Report',
documentNames: [excelFile.name],
ccbcNumber,
});
}
},
onError: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Ajv8Validator from '@rjsf/validator-ajv8';
import excelValidateGenerator from 'lib/helpers/excelValidate';
import ReadOnlyView from 'components/Analyst/Project/ProjectInformation/ReadOnlyView';
import * as Sentry from '@sentry/nextjs';
import useEmailNotification from 'lib/helpers/useEmailNotification';
import ChangeRequestTheme from '../ChangeRequestTheme';

const StyledProjectForm = styled(ProjectForm)`
Expand Down Expand Up @@ -96,6 +97,7 @@ const ProjectInformationForm: React.FC<Props> = ({
const [createProjectInformation] = useCreateProjectInformationMutation();
const [archiveApplicationSow] = useArchiveApplicationSowMutation();
const [createChangeRequest] = useCreateChangeRequestMutation();
const { notifyDocumentUpload } = useEmailNotification();
const [hasFormSaved, setHasFormSaved] = useState<boolean>(false);
const [formData, setFormData] = useState(projectInformation?.jsonData);
const [showToast, setShowToast] = useState(false);
Expand Down Expand Up @@ -221,6 +223,12 @@ const ProjectInformationForm: React.FC<Props> = ({
}
return response.json();
});

notifyDocumentUpload(rowId, {
ccbcNumber,
documentType: 'Statement of Work',
documentNames: [sowFile.name],
});
};

const handleSubmit = (e) => {
Expand Down
16 changes: 14 additions & 2 deletions app/components/Analyst/RFI/RFIAnalystUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import { useUpdateWithTrackingRfiMutation } from 'schema/mutations/application/u
import styled from 'styled-components';

import { useCreateNewFormDataMutation } from 'schema/mutations/application/createNewFormData';
import useHHCountUpdateEmail from 'lib/helpers/useHHCountUpdateEmail';
import useEmailNotification from 'lib/helpers/useEmailNotification';
import useRfiCoverageMapKmzUploadedEmail from 'lib/helpers/useRfiCoverageMapKmzUploadedEmail';
import { useToast } from 'components/AppProvider';
import Link from 'next/link';
import joinWithAnd from 'utils/formatArray';

const Flex = styled('header')`
display: flex;
Expand Down Expand Up @@ -68,14 +69,16 @@ const RfiAnalystUpload = ({ query }) => {
const [newFormData, setNewFormData] = useState(jsonData);
const [templateData, setTemplateData] = useState(null);
const [excelImportFields, setExcelImportFields] = useState([]);
const [excelImportFiles, setExcelImportFiles] = useState([]);
const router = useRouter();
const { notifyHHCountUpdate } = useHHCountUpdateEmail();
const { notifyHHCountUpdate, notifyDocumentUpload } = useEmailNotification();
const { notifyRfiCoverageMapKmzUploaded } =
useRfiCoverageMapKmzUploadedEmail();

useEffect(() => {
if (templateData?.templateNumber === 1 && !templateData?.error) {
setExcelImportFields([...excelImportFields, 'Template 1']);
setExcelImportFiles([...excelImportFiles, templateData?.templateName]);
const newFormDataWithTemplateOne = {
...newFormData,
benefits: {
Expand All @@ -88,6 +91,7 @@ const RfiAnalystUpload = ({ query }) => {
setNewFormData(newFormDataWithTemplateOne);
} else if (templateData?.templateNumber === 2 && !templateData?.error) {
setExcelImportFields([...excelImportFields, 'Template 2']);
setExcelImportFiles([...excelImportFiles, templateData?.templateName]);
const newFormDataWithTemplateTwo = {
...newFormData,
budgetDetails: {
Expand All @@ -97,6 +101,9 @@ const RfiAnalystUpload = ({ query }) => {
},
};
setNewFormData(newFormDataWithTemplateTwo);
} else if (templateData?.templateNumber === 9 && !templateData?.error) {
setExcelImportFields([...excelImportFields, 'Template 9']);
setExcelImportFiles([...excelImportFiles, templateData?.templateName]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [templateData]);
Expand Down Expand Up @@ -163,6 +170,11 @@ const RfiAnalystUpload = ({ query }) => {
) {
showToast(getToastMessage(), 'success', 100000000);
}
notifyDocumentUpload(applicationId, {
ccbcNumber,
documentType: joinWithAnd(excelImportFields),
documentNames: excelImportFiles,
});
},
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Sentry from '@sentry/nextjs';

const useHHCountUpdateEmail = () => {
const useEmailNotification = () => {
const notifyHHCountUpdate = async (
newData: any,
oldData: any,
Expand Down Expand Up @@ -48,7 +48,30 @@ const useHHCountUpdateEmail = () => {
});
};

return { notifyHHCountUpdate };
const notifyDocumentUpload = async (applicationId: string, params: any) => {
fetch('/api/email/notifyDocumentUpload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
applicationId,
host: window.location.origin,
params: {
...params,
timestamp: new Date().toLocaleString(),
},
}),
}).then((response) => {
if (!response.ok) {
Sentry.captureException({
name: `Error sending email to notify ${params.documentType} upload`,
message: response,
});
}
return response.json();
});
};

return { notifyHHCountUpdate, notifyDocumentUpload };
};

export default useHHCountUpdateEmail;
export default useEmailNotification;
5 changes: 5 additions & 0 deletions app/lib/theme/widgets/FileWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const FileWidget: React.FC<FileWidgetProps> = ({
setTemplateData({
templateNumber,
data,
templateName: file.name,
});
} else {
isTemplateValid = false;
Expand All @@ -118,6 +119,10 @@ const FileWidget: React.FC<FileWidgetProps> = ({
);
if (response.ok) {
await response.json();
setTemplateData({
templateNumber,
templateName: file.name,
});
} else {
isTemplateValid = false;
setTemplateData({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useCreateNewFormDataMutation } from 'schema/mutations/application/creat
import { analystProjectArea, benefits } from 'formSchema/uiSchema/pages';
import useModal from 'lib/helpers/useModal';
import { RJSFSchema } from '@rjsf/utils';
import useHHCountUpdateEmail from 'lib/helpers/useHHCountUpdateEmail';
import useEmailNotification from 'lib/helpers/useEmailNotification';

const getSectionQuery = graphql`
query SectionQuery($rowId: Int!) {
Expand Down Expand Up @@ -85,7 +85,7 @@ const EditApplication = ({
const [changeReason, setChangeReason] = useState('');
const [isFormSaved, setIsFormSaved] = useState(true);
const changeModal = useModal();
const { notifyHHCountUpdate } = useHHCountUpdateEmail();
const { notifyHHCountUpdate } = useEmailNotification();
const handleChange = (e: IChangeEvent) => {
setIsFormSaved(false);
const newFormSectionData = { ...e.formData };
Expand Down
Loading

0 comments on commit be4e25a

Please sign in to comment.