From 33c40d575abfa58ca7bd77d7ff7fcc324a6b960b Mon Sep 17 00:00:00 2001 From: R Ranathunga Date: Thu, 15 Aug 2024 10:36:14 -0700 Subject: [PATCH 1/5] fix: refine communities validation cbc --- app/components/Review/Components.tsx | 3 +++ app/formSchema/analyst/cbc/locationsAndCounts.ts | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/components/Review/Components.tsx b/app/components/Review/Components.tsx index ad34a1376..01af9aa93 100644 --- a/app/components/Review/Components.tsx +++ b/app/components/Review/Components.tsx @@ -35,4 +35,7 @@ interface StyledColErrorProps { export const StyledColError = styled(StyledColRight)` background-color: ${(props) => props?.errorColor ? props.errorColor : props.theme.color.errorBackground}; + .pg-select-wrapper { + background-color: white !important; + } `; diff --git a/app/formSchema/analyst/cbc/locationsAndCounts.ts b/app/formSchema/analyst/cbc/locationsAndCounts.ts index 97c94c228..e983ee8d3 100644 --- a/app/formSchema/analyst/cbc/locationsAndCounts.ts +++ b/app/formSchema/analyst/cbc/locationsAndCounts.ts @@ -7,7 +7,6 @@ const locationsAndCounts: RJSFSchema = { required: [ 'projectLocations', 'communitiesAndLocalesCount', - 'indigenousCommunities', 'householdCount', ], properties: { From d880cecf266e7881f2c37de471a2afbd5816421b Mon Sep 17 00:00:00 2001 From: R Ranathunga Date: Mon, 19 Aug 2024 09:32:58 -0700 Subject: [PATCH 2/5] feat: add indication to Template upload --- .../Analyst/RFI/ListFilesWidget.tsx | 5 + .../Analyst/RFI/TemplateDescription.tsx | 49 +++ app/components/Review/ReviewSectionField.tsx | 3 +- .../uiSchema/analyst/rfiUiSchema.ts | 7 + app/lib/theme/components/FileComponent.tsx | 316 ++++++++++-------- app/lib/theme/widgets/FileWidget.tsx | 4 + .../analyst/application/[applicationId].tsx | 3 + app/styles/GlobalTheme.tsx | 1 + .../Analyst/RFI/RFIAnalystUpload.test.ts | 17 + .../components/Form/FileComponent.test.tsx | 7 +- .../[applicationId]/rfi/index.test.tsx | 17 + 11 files changed, 279 insertions(+), 150 deletions(-) create mode 100644 app/components/Analyst/RFI/TemplateDescription.tsx diff --git a/app/components/Analyst/RFI/ListFilesWidget.tsx b/app/components/Analyst/RFI/ListFilesWidget.tsx index 221429321..5a6929921 100644 --- a/app/components/Analyst/RFI/ListFilesWidget.tsx +++ b/app/components/Analyst/RFI/ListFilesWidget.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components'; import { useRouter } from 'next/router'; import { useFeature } from '@growthbook/growthbook-react'; import { AddButton } from '../Project'; +import TemplateDescription from './TemplateDescription'; const StyledContainer = styled.div` margin-bottom: 24px; @@ -40,10 +41,13 @@ const ListFilesWidget: React.FC = ({ formContext, label, value, + uiSchema, }) => { const showRfiUpload = useFeature('show_analyst_rfi_upload').value; const router = useRouter(); const isFiles = value?.length > 0; + const templateNumber = + (uiSchema['ui:options']?.templateNumber as number) ?? null; const handleDownload = async (uuid: string, fileName: string) => { const url = `/api/s3/download/${uuid}/${fileName}`; @@ -86,6 +90,7 @@ const ListFilesWidget: React.FC = ({ }} /> )} + ); }; diff --git a/app/components/Analyst/RFI/TemplateDescription.tsx b/app/components/Analyst/RFI/TemplateDescription.tsx new file mode 100644 index 000000000..f6d772d3a --- /dev/null +++ b/app/components/Analyst/RFI/TemplateDescription.tsx @@ -0,0 +1,49 @@ +import { Link } from '@button-inc/bcgov-theme'; +import { useRouter } from 'next/router'; +import styled from 'styled-components'; + +const HintText = styled.p` + color: ${(props) => props.theme.color.darkGrey}; + font-style: italic; + font-size: 13px; + a { + color: ${(props) => props.theme.color.links}; + font-size: 13px; + &:hover { + text-decoration: underline; + } + } +`; + +interface Props { + templateNumber: number; +} + +const TemplateDescription: React.FC = ({ templateNumber }) => { + const applicationId = useRouter().query.applicationId as string; + if (!templateNumber) return null; + const link = ( + + {templateNumber === 1 ? 'Benefits' : 'Budget Details'} + + ); + + return ( + (templateNumber === 1 || templateNumber === 2) && ( + + * RFI upload for Template {templateNumber} automatically updates the + data for{' '} + {templateNumber === 1 + ? 'Final Eligible Households and Indigenous' + : 'Total Eligible Costs and Total Project Costs'}{' '} + in the {link} section. Please verify the changes on the application + page.* + + ) + ); +}; + +export default TemplateDescription; diff --git a/app/components/Review/ReviewSectionField.tsx b/app/components/Review/ReviewSectionField.tsx index 72df7deb0..131b0eb7f 100644 --- a/app/components/Review/ReviewSectionField.tsx +++ b/app/components/Review/ReviewSectionField.tsx @@ -20,13 +20,14 @@ const ReviewSectionField: React.FC = (props) => { () => Object.keys(errorSchema || {}).length > 0 || !!hasFormContextError, [errorSchema, hasFormContextError] ); + const toggledSection = formContext?.toggledSection === pageName; const allowAnalystEdit = uiOptions?.allowAnalystEdit && (formContext.isEditable ?? true); return ( ` + display: flex; + justify-content: space-between; flex-direction: ${({ wrap }) => (wrap ? 'column-reverse' : 'row')}; `; +const StyledFooterText = styled('div')` + width: 100%; + font-size: 14px; + color: #606060; +`; + const StyledInputContainer = styled.div<{ useFileDate?: boolean }>` ${({ useFileDate }) => useFileDate @@ -152,6 +164,8 @@ interface FileComponentProps { maxDate?: Date; minDate?: Date; allowDragAndDrop?: boolean; + templateNumber?: number; + showTemplateUploadIndication?: boolean; } const ErrorMessage = ({ error, fileName, fileTypes }) => { @@ -230,6 +244,8 @@ const FileComponent: React.FC = ({ maxDate, minDate, allowDragAndDrop, + templateNumber, + showTemplateUploadIndication, }) => { const hiddenFileInput = useRef() as MutableRefObject; const isFiles = value?.length > 0; @@ -318,7 +334,6 @@ const FileComponent: React.FC = ({ {...fileErrorModal} /> = ({ } {...(allowDragAndDrop && dropzoneProps)} > - - {label} - {isFiles && - !hideIfFailed && - value.map((file: File) => ( - <> - - { - e.preventDefault(); - if (handleDownload) { - handleDownload(file.uuid, file.name, onError); + + + {label} + {isFiles && + !hideIfFailed && + value.map((file: File) => ( + <> + + { + e.preventDefault(); + if (handleDownload) { + handleDownload(file.uuid, file.name, onError); + } + }} + > + {file.name} + + ) => { + e.preventDefault(); + if (handleDelete) { + setDropzoneError(null); + handleDelete(file.id); + } + }} + disabled={loading || disabled} + > + + + + {useFileDate && file?.fileDate && ( + + {DateTime.fromISO(file.fileDate).toFormat('MMM dd, yyyy')} + + )} + + ))} + {dropzoneError} + {errors?.map((fileError: any) => ( + + ))} + + {statusLabel} + + {useFileDate && ( +
+

{`${fileDateTitle}`}

+ + { + const originalDate = new Date(d); + if ( + !Number.isNaN(originalDate) && + originalDate.valueOf() >= 0 + ) { + const newDate = originalDate + .toISOString() + .split('T')[0]; + setFileDate(newDate); + } else { + setFileDate(null); } }} - > - {file.name} - - ) => { - e.preventDefault(); - if (handleDelete) { - setDropzoneError(null); - handleDelete(file.id); - } + value={fileDate ? dayjs(fileDate) : null} + defaultValue={null} + slotProps={{ + actionBar: { + actions: ['clear', 'cancel'], + }, + textField: { + inputProps: { + id, + 'data-testid': 'datepicker-widget-input', + }, + }, }} - disabled={loading || disabled} - > - - - - {useFileDate && file?.fileDate && ( - - {DateTime.fromISO(file.fileDate).toFormat('MMM dd, yyyy')} - - )} - - ))} - {dropzoneError} - {errors?.map((fileError: any) => ( - - ))} - - {statusLabel} - - {useFileDate && ( -
-

{`${fileDateTitle}`}

- + +
+ )} + + ) => { + e.preventDefault(); + handleClick(); + }} + variant={buttonVariant} + disabled={loading || disabled || (useFileDate && !fileDate)} > - { - const originalDate = new Date(d); - if ( - !Number.isNaN(originalDate) && - originalDate.valueOf() >= 0 - ) { - const newDate = originalDate.toISOString().split('T')[0]; - setFileDate(newDate); - } else { - setFileDate(null); - } - }} - value={fileDate ? dayjs(fileDate) : null} - defaultValue={null} - slotProps={{ - actionBar: { - actions: ['clear', 'cancel'], - }, - textField: { - inputProps: { - id, - 'data-testid': 'datepicker-widget-input', - }, - }, - }} - slots={{ - openPickerButton: fileDate - ? ClearableIconButton - : undefined, - }} - format="YYYY-MM-DD" - /> -
-
- )} - - ) => { - e.preventDefault(); - handleClick(); - }} - variant={buttonVariant} - disabled={loading || disabled || (useFileDate && !fileDate)} - > - {loading ? ( - - ) : ( - - {buttonLabel()} - {allowDragAndDrop && ( - - - Drop files (or click to upload) - - )} - - )} - - -
- { - onChange(e); - // set target to null to allow for reupload of file with same name - e.currentTarget.value = null; - }} - style={{ display: 'none' }} - type="file" - required={required} - accept={fileTypes?.toString()} - /> + {loading ? ( + + ) : ( + + {buttonLabel()} + {allowDragAndDrop && ( + + + Drop files (or click to upload) + + )} + + )} + + + + { + onChange(e); + // set target to null to allow for reupload of file with same name + e.currentTarget.value = null; + }} + style={{ display: 'none' }} + type="file" + required={required} + accept={fileTypes?.toString()} + /> +
+ {showTemplateUploadIndication && ( + + + + )}
); diff --git a/app/lib/theme/widgets/FileWidget.tsx b/app/lib/theme/widgets/FileWidget.tsx index 499200714..3c2a691c2 100644 --- a/app/lib/theme/widgets/FileWidget.tsx +++ b/app/lib/theme/widgets/FileWidget.tsx @@ -63,6 +63,8 @@ const FileWidget: React.FC = ({ (uiSchema['ui:options']?.showValidationMessage as boolean) ?? false; const templateNumber = (uiSchema['ui:options']?.templateNumber as number) ?? 0; + const showTemplateUploadIndication = + (uiSchema['ui:options']?.showTemplateUploadIndication as boolean) ?? false; const isFiles = value?.length > 0; const loading = isCreatingAttachment || isDeletingAttachment; // 104857600 bytes = 100mb @@ -268,6 +270,8 @@ const FileWidget: React.FC = ({ maxDate={maxDate} minDate={minDate} allowDragAndDrop={allowDragAndDrop} + templateNumber={templateNumber} + showTemplateUploadIndication={showTemplateUploadIndication} /> ); }; diff --git a/app/pages/analyst/application/[applicationId].tsx b/app/pages/analyst/application/[applicationId].tsx index 5b4c03be7..b067ae629 100644 --- a/app/pages/analyst/application/[applicationId].tsx +++ b/app/pages/analyst/application/[applicationId].tsx @@ -11,6 +11,7 @@ import { ApplicationIdQuery } from '__generated__/ApplicationIdQuery.graphql'; import ReviewTheme from 'components/Review/ReviewTheme'; import AnalystLayout from 'components/Analyst/AnalystLayout'; import styled from 'styled-components'; +import { useRouter } from 'next/router'; const StyledButton = styled('button')` color: ${(props) => props.theme.color.links}; @@ -82,6 +83,7 @@ const Application = ({ applicationRfiDataByApplicationId, } = applicationByRowId; const isEditable = status !== 'withdrawn'; + const { section: toggledSection } = useRouter().query; const rfiList = applicationRfiDataByApplicationId?.edges?.map( (edge) => edge.node.rfiDataByRfiDataId @@ -133,6 +135,7 @@ const Application = ({ errors: formErrorSchema, rfiList, toggleOverride, + toggledSection, isEditable, }} formData={jsonData} diff --git a/app/styles/GlobalTheme.tsx b/app/styles/GlobalTheme.tsx index 52395bf28..c8c123742 100644 --- a/app/styles/GlobalTheme.tsx +++ b/app/styles/GlobalTheme.tsx @@ -33,6 +33,7 @@ export const theme = { disabledGrey: '#939393', placeholder: '#cccccc', backgroundMagenta: '#b200ff', + darkGrey: '#676666', }, spacing: { small: '8px', diff --git a/app/tests/components/Analyst/RFI/RFIAnalystUpload.test.ts b/app/tests/components/Analyst/RFI/RFIAnalystUpload.test.ts index c47c6179b..18c5caa28 100644 --- a/app/tests/components/Analyst/RFI/RFIAnalystUpload.test.ts +++ b/app/tests/components/Analyst/RFI/RFIAnalystUpload.test.ts @@ -109,6 +109,23 @@ describe('The RFIAnalystUpload component', () => { ).toBeInTheDocument(); }); + it('should render upload indication for template 1 and 2', () => { + componentTestingHelper.loadQuery(); + componentTestingHelper.renderComponent(); + + expect( + screen.getByText( + /RFI upload for Template 1 automatically updates the data for Final Eligible Households and Indigenous/ + ) + ).toBeInTheDocument(); + + expect( + screen.getByText( + /FI upload for Template 2 automatically updates the data for Total Eligible Costs and Total Project Costs/ + ) + ).toBeInTheDocument(); + }); + it('should render the upload button', () => { componentTestingHelper.loadQuery(); componentTestingHelper.renderComponent(); diff --git a/app/tests/components/Form/FileComponent.test.tsx b/app/tests/components/Form/FileComponent.test.tsx index f658c1437..18b62c0f2 100644 --- a/app/tests/components/Form/FileComponent.test.tsx +++ b/app/tests/components/Form/FileComponent.test.tsx @@ -30,9 +30,10 @@ describe('FileComponent', () => { it('should handle file drop correctly', async () => { renderStaticLayout(false, true); - const dropzoneInput = screen.getAllByTestId('file-test')[0]; - const dropzone = dropzoneInput.parentElement; - + const dropzoneInput = screen.getByTestId('file-test'); + const dropzone = dropzoneInput.closest( + '[class*="FileComponent__StyledContainer"]' + ); expect(dropzone).toHaveStyle('border: 1px dashed rgba(0, 0, 0, 0.16)'); fireEvent.dragOver(dropzone); diff --git a/app/tests/pages/analyst/application/[applicationId]/rfi/index.test.tsx b/app/tests/pages/analyst/application/[applicationId]/rfi/index.test.tsx index deddd47e1..1ee4d4c4c 100644 --- a/app/tests/pages/analyst/application/[applicationId]/rfi/index.test.tsx +++ b/app/tests/pages/analyst/application/[applicationId]/rfi/index.test.tsx @@ -265,6 +265,23 @@ describe('The index page', () => { ).toBeVisible(); }); + it('should show indications for template 1 and 2', async () => { + pageTestingHelper.loadQuery(); + pageTestingHelper.renderPage(); + + expect( + screen.getByText( + /RFI upload for Template 1 automatically updates the data for Final Eligible Households and Indigenous/ + ) + ).toBeInTheDocument(); + + expect( + screen.getByText( + /FI upload for Template 2 automatically updates the data for Total Eligible Costs and Total Project Costs/ + ) + ).toBeInTheDocument(); + }); + it('shows all of the correct Requested file items', async () => { pageTestingHelper.loadQuery(); pageTestingHelper.renderPage(); From 32f78f75964b7376f0c9a8aac0e98d8471cdeadc Mon Sep 17 00:00:00 2001 From: R Ranathunga Date: Mon, 26 Aug 2024 10:47:22 -0700 Subject: [PATCH 3/5] chore: scrolling to focused section in application view --- app/components/Review/Accordion.tsx | 19 +++++++++++++++++-- app/components/Review/ReviewSectionField.tsx | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/components/Review/Accordion.tsx b/app/components/Review/Accordion.tsx index 50cb524f9..c33a5ab02 100644 --- a/app/components/Review/Accordion.tsx +++ b/app/components/Review/Accordion.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { useRouter } from 'next/router'; import { BaseAccordion } from '@button-inc/bcgov-theme/Accordion'; import styled from 'styled-components'; @@ -76,6 +76,7 @@ const Accordion = ({ toggled, title, recordLocked, + focused, ...rest }: any) => { const [isToggled, setIsToggled] = useState( @@ -83,6 +84,8 @@ const Accordion = ({ ); const router = useRouter(); const applicationId = router.query.applicationId as string; + const accordionRef = useRef(null); + const handleToggle = (event) => { setIsToggled((toggle) => !toggle); if (onToggle) onToggle(event); @@ -96,9 +99,21 @@ const Accordion = ({ setIsToggled(getToggledState(toggled, defaultToggled)); }, [toggled, defaultToggled]); + useEffect(() => { + if (focused && accordionRef.current) { + window.scrollTo({ + top: + accordionRef.current.getBoundingClientRect().top + + window.scrollY - + 100, // Adjust offset to account for header + behavior: 'smooth', + }); + } + }, [focused]); + return ( -
+

{title}

{allowAnalystEdit && diff --git a/app/components/Review/ReviewSectionField.tsx b/app/components/Review/ReviewSectionField.tsx index 131b0eb7f..6ffbd8ba7 100644 --- a/app/components/Review/ReviewSectionField.tsx +++ b/app/components/Review/ReviewSectionField.tsx @@ -28,6 +28,7 @@ const ReviewSectionField: React.FC = (props) => { id={idSchema.$id} name={name} defaultToggled={uiOptions.defaultExpanded || hasErrors || toggledSection} + focused={toggledSection} toggled={formContext.toggleOverride} cbcId={formContext.cbcId} isCBC={formContext.isCBC} From 5bbd3db59470243cc76f0e19cd17f9e2bb5678dd Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:34:37 +0000 Subject: [PATCH 4/5] chore: release v1.184.2 --- CHANGELOG.md | 6 ++++++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8379559..3a48bffe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.184.2](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.184.1...v1.184.2) (2024-08-26) + +### Bug Fixes + +- refine communities validation cbc ([33c40d5](https://github.com/bcgov/CONN-CCBC-portal/commit/33c40d575abfa58ca7bd77d7ff7fcc324a6b960b)) + ## [1.184.1](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.184.0...v1.184.1) (2024-08-26) # [1.184.0](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.183.0...v1.184.0) (2024-08-26) diff --git a/db/sqitch.plan b/db/sqitch.plan index d14c9cb62..b8b78b8be 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -644,3 +644,4 @@ tables/cbc_add_fk_update_constraint 2024-07-11T20:32:11Z Rafael Solorzano <61289 @1.183.0 2024-08-22T18:35:35Z CCBC Service Account # release v1.183.0 @1.184.0 2024-08-26T16:51:24Z CCBC Service Account # release v1.184.0 @1.184.1 2024-08-26T19:57:58Z CCBC Service Account # release v1.184.1 +@1.184.2 2024-08-26T20:34:36Z CCBC Service Account # release v1.184.2 diff --git a/package.json b/package.json index 5c0a139aa..b41240848 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.184.1", + "version": "1.184.2", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From ba0f715209180105f09c5ef805e68208187b4bf3 Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:26:32 +0000 Subject: [PATCH 5/5] chore: release v1.185.0 --- CHANGELOG.md | 6 ++++++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a48bffe4..8bb06336a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# [1.185.0](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.184.2...v1.185.0) (2024-08-26) + +### Features + +- add indication to Template upload ([d880cec](https://github.com/bcgov/CONN-CCBC-portal/commit/d880cecf266e7881f2c37de471a2afbd5816421b)) + ## [1.184.2](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.184.1...v1.184.2) (2024-08-26) ### Bug Fixes diff --git a/db/sqitch.plan b/db/sqitch.plan index b8b78b8be..b09a97387 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -645,3 +645,4 @@ tables/cbc_add_fk_update_constraint 2024-07-11T20:32:11Z Rafael Solorzano <61289 @1.184.0 2024-08-26T16:51:24Z CCBC Service Account # release v1.184.0 @1.184.1 2024-08-26T19:57:58Z CCBC Service Account # release v1.184.1 @1.184.2 2024-08-26T20:34:36Z CCBC Service Account # release v1.184.2 +@1.185.0 2024-08-26T22:26:30Z CCBC Service Account # release v1.185.0 diff --git a/package.json b/package.json index b41240848..3815d0eb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.184.2", + "version": "1.185.0", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ",