Skip to content

Commit

Permalink
Merge pull request #3353 from bcgov/NDT-232-When-uploading-Template-1…
Browse files Browse the repository at this point in the history
…-2-on-RFI-page-for-analyst-show-feedback

feat: add toast after file upload RFI template 1 and 2
  • Loading branch information
RRanath authored Jun 10, 2024
2 parents 2a378d7 + ddd4c92 commit 6e3c8d1
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 11 deletions.
34 changes: 34 additions & 0 deletions app/components/Analyst/RFI/RFIAnalystUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ import styled from 'styled-components';
import { useCreateNewFormDataMutation } from 'schema/mutations/application/createNewFormData';
import useHHCountUpdateEmail from 'lib/helpers/useHHCountUpdateEmail';
import useRfiCoverageMapKmzUploadedEmail from 'lib/helpers/useRfiCoverageMapKmzUploadedEmail';
import { useToast } from 'components/AppProvider';
import Link from 'next/link';

const Flex = styled('header')`
display: flex;
justify-content: space-between;
width: 100%;
`;

const StyledLink = styled(Link)`
color: ${(props) => props.theme.color.white};
`;

const RfiAnalystUpload = ({ query }) => {
const queryFragment = useFragment(
graphql`
Expand Down Expand Up @@ -52,6 +58,7 @@ const RfiAnalystUpload = ({ query }) => {
rowId: applicationId,
ccbcNumber,
} = applicationByRowId;
const { showToast, hideToast } = useToast();

const { rfiNumber } = rfiDataByRowId;

Expand Down Expand Up @@ -94,9 +101,30 @@ const RfiAnalystUpload = ({ query }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [templateData]);

const getToastMessage = () => {
return (
<>
{' '}
`Template {templateData?.templateNumber} data changed successfully, new
values for{' '}
{templateData?.templateNumber === 1
? 'Total Households and Indigenous Households'
: 'Total eligible costs and Total project costs'}{' '}
data in the application now reflect template uploads. Please see{' '}
<StyledLink
href={`/analyst/application/${router.query.applicationId}/history`}
>
history page
</StyledLink>{' '}
for details.`
</>
);
};

const handleSubmit = () => {
const updatedExcelFields = excelImportFields.join(', ');
const reasonForChange = `Auto updated from upload of ${updatedExcelFields} for RFI: ${rfiNumber}`;
hideToast();
updateRfi({
variables: {
input: {
Expand Down Expand Up @@ -129,6 +157,12 @@ const RfiAnalystUpload = ({ query }) => {
}
);
}
if (
templateData?.templateNumber === 1 ||
templateData?.templateNumber === 2
) {
showToast(getToastMessage(), 'success', 100000000);
}
},
});
}
Expand Down
63 changes: 63 additions & 0 deletions app/components/AppProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, {
createContext,
useContext,
useState,
useCallback,
useMemo,
ReactNode,
} from 'react';
import Toast, { ToastType } from 'components/Toast';

type AppContextType = {
showToast?: (message: ReactNode, type?: ToastType, timeout?: number) => void;
hideToast?: () => void;
};

const AppContext = createContext<AppContextType>({});

export const useToast = () => {
return useContext(AppContext);
};

export const AppProvider = ({ children }) => {
/**
* handling global toast messages
*/
const [toast, setToast] = useState<{
visible: boolean;
message?: ReactNode;
type?: ToastType;
timeout?: number;
}>({ visible: false });

const showToast = useCallback(
(
message: ReactNode,
type: ToastType = 'success',
timeout: number = 10000
) => {
setToast({ visible: true, message, type, timeout });
},
[]
);

const hideToast = useCallback(() => {
setToast({ visible: false });
}, []);

const contextValue = useMemo(
() => ({ showToast, hideToast }),
[showToast, hideToast]
);

return (
<AppContext.Provider value={contextValue}>
{children}
{toast?.visible && (
<Toast type={toast?.type} onClose={hideToast} timeout={toast.timeout}>
{toast.message}
</Toast>
)}
</AppContext.Provider>
);
};
2 changes: 1 addition & 1 deletion app/components/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { theme } from 'styles/GlobalTheme';

type ToastType = 'success' | 'warning' | 'error';
export type ToastType = 'success' | 'warning' | 'error';

type ToastDirection = 'left' | 'right';

Expand Down
37 changes: 37 additions & 0 deletions app/lib/theme/widgets/FileWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import bytesToSize from 'utils/bytesToText';
import FileComponent from 'lib/theme/components/FileComponent';
import useDisposeOnRouteChange from 'lib/helpers/useDisposeOnRouteChange';
import { DateTime } from 'luxon';
import { useToast } from 'components/AppProvider';
import { ToastType } from 'components/Toast';

type File = {
id: string | number;
Expand Down Expand Up @@ -65,6 +67,7 @@ const FileWidget: React.FC<FileWidgetProps> = ({
const maxFileSizeInBytes = 104857600;
const fileId = isFiles && value[0].id;
const { setTemplateData } = formContext;
const { showToast, hideToast } = useToast();

useEffect(() => {
if (rawErrors?.length > 0) {
Expand All @@ -73,6 +76,7 @@ const FileWidget: React.FC<FileWidgetProps> = ({
}, [rawErrors, setErrors]);

const getValidatedFile = async (file: any, formId: number) => {
let isTemplateValid = true;
if (templateValidate) {
const fileFormData = new FormData();
if (file) {
Expand All @@ -92,6 +96,8 @@ const FileWidget: React.FC<FileWidgetProps> = ({
data,
});
});
} else {
isTemplateValid = false;
}
});
}
Expand All @@ -109,6 +115,7 @@ const FileWidget: React.FC<FileWidgetProps> = ({
}

return {
isTemplateValid,
input: {
attachment: {
file,
Expand Down Expand Up @@ -153,6 +160,19 @@ const FileWidget: React.FC<FileWidgetProps> = ({
});
};

const showToastMessage = (files, type: ToastType = 'success') => {
const fields =
templateNumber === 1
? 'Total Households and Indigenous Households data'
: 'Total eligible costs and Total project costs data';
const message =
type === 'success'
? `Template ${templateNumber} validation successful, new values for ${fields} data in the application will update upon 'Save'`
: `Template ${templateNumber} validation failed: ${files.join(', ')} did not validate due to formatting issues. ${fields} in the application will not update.`;

showToast(message, type, 100000000);
};

const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const transaction = Sentry.startTransaction({ name: 'ccbc.function' });
const span = transaction.startChild({
Expand All @@ -161,6 +181,7 @@ const FileWidget: React.FC<FileWidgetProps> = ({
});

if (loading) return;
hideToast();
const formId =
parseInt(router?.query?.id as string, 10) ||
parseInt(router?.query?.applicationId as string, 10);
Expand All @@ -177,6 +198,8 @@ const FileWidget: React.FC<FileWidgetProps> = ({
const validatedFiles = resp.filter((file) => file.input);
setErrors(resp.filter((file) => file.error));

const validationErrors = resp.filter((file) => !file.isTemplateValid);

const uploadResponse = await Promise.all(
validatedFiles.map(async (payload) => handleUpload(payload))
);
Expand All @@ -190,6 +213,20 @@ const FileWidget: React.FC<FileWidgetProps> = ({
} else {
span.setStatus('ok');
}

if (templateValidate) {
if (validationErrors.length > 0) {
showToastMessage(
validationErrors.map(
(error) => error.fileName || error.input?.attachment?.fileName
),
'error'
);
} else if (validationErrors.length === 0 && uploadErrors.length === 0) {
showToastMessage(fileDetails.map((file) => file.name));
}
}

span.finish();
transaction.finish();

Expand Down
7 changes: 5 additions & 2 deletions app/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import GlobalStyle from 'styles/GobalStyles';
import GlobalTheme from 'styles/GlobalTheme';
import BCGovTypography from 'components/BCGovTypography';
import { SessionExpiryHandler } from 'components';
import { AppProvider } from 'components/AppProvider';

config.autoAddCss = false;

Expand Down Expand Up @@ -94,8 +95,10 @@ const MyApp = ({ Component, pageProps }: AppProps) => {
<BCGovTypography />
<Sentry.ErrorBoundary fallback={<Error500 />}>
<RelayEnvironmentProvider environment={env}>
{typeof window !== 'undefined' && <SessionExpiryHandler />}
{component}
<AppProvider>
{typeof window !== 'undefined' && <SessionExpiryHandler />}
{component}
</AppProvider>
</RelayEnvironmentProvider>
</Sentry.ErrorBoundary>
</ThemeProvider>
Expand Down
Loading

0 comments on commit 6e3c8d1

Please sign in to comment.