From da4195c7cc76195ad282f3ab21d970ceb9126528 Mon Sep 17 00:00:00 2001 From: Dylan Barkowsky <37922247+dbarkowsky@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:25:42 -0700 Subject: [PATCH 1/2] PIMS-2002 Properties Export (#2641) --- .../services/properties/propertiesServices.ts | 83 ++++++++++++------- express-api/tests/testUtils/factories.ts | 22 ++--- .../buildings/buildingService.test.ts | 12 +-- .../services/parcels/parcelsService.test.ts | 14 ++-- .../properties/propertyServices.test.ts | 48 ++++++++--- .../src/components/property/PropertyTable.tsx | 3 +- react-app/src/components/table/DataTable.tsx | 34 +++++--- react-app/src/hooks/api/usePropertiesApi.ts | 4 +- 8 files changed, 142 insertions(+), 78 deletions(-) diff --git a/express-api/src/services/properties/propertiesServices.ts b/express-api/src/services/properties/propertiesServices.ts index cb53d70e11..649df0089c 100644 --- a/express-api/src/services/properties/propertiesServices.ts +++ b/express-api/src/services/properties/propertiesServices.ts @@ -30,7 +30,7 @@ import { constructFindOptionFromQuerySingleSelect, } from '@/utilities/helperFunctions'; import userServices from '../users/usersServices'; -import { Brackets, FindManyOptions, FindOptionsWhere, ILike, In, QueryRunner } from 'typeorm'; +import { Brackets, FindOptionsWhere, ILike, In, QueryRunner } from 'typeorm'; import { SSOUser } from '@bcgov/citz-imb-sso-express'; import { PropertyType } from '@/constants/propertyType'; import { ProjectStatus } from '@/constants/projectStatus'; @@ -799,35 +799,62 @@ const getPropertiesForExport = async (filter: PropertyUnionFilter) => { const buildingIds = filteredProperties .filter((p) => p.PropertyTypeId === PropertyType.BUILDING) .map((b) => b.Id); - // Use IDs from filtered properties to get those properites with joins - const parcelQueryOptions: FindManyOptions = { - relations: { - CreatedBy: true, - UpdatedBy: true, - Evaluations: true, - Fiscals: true, - }, - where: { - Id: In(parcelIds), - }, - }; - const buildingQueryOptions: FindManyOptions = { - relations: { - CreatedBy: true, - UpdatedBy: true, - Evaluations: true, - Fiscals: true, - }, - where: { Id: In(buildingIds) }, - }; - let properties: (Parcel | Building)[] = []; - properties = properties.concat( - await AppDataSource.getRepository(Parcel).find(parcelQueryOptions), + + /** + * For some reason, getting the data in multiple calls and filtering here is faster than letting TypeORM do it. + * + * Getting evals, fiscals, and filtering separately: 850-1050ms + * Getting as as part of joins, WHERE clause with TypeORM: 1587-1672ms + */ + + const ongoingFinds = []; + ongoingFinds.push(AppDataSource.getRepository(Parcel).find()); + ongoingFinds.push(AppDataSource.getRepository(Building).find()); + // Order these to guarantee the find operation later gets the most recent one. + ongoingFinds.push( + AppDataSource.getRepository(ParcelEvaluation).find({ order: { Year: 'DESC' } }), ); - properties = properties.concat( - await AppDataSource.getRepository(Building).find(buildingQueryOptions), + ongoingFinds.push( + AppDataSource.getRepository(ParcelFiscal).find({ order: { FiscalYear: 'DESC' } }), ); - return properties; + ongoingFinds.push( + AppDataSource.getRepository(BuildingEvaluation).find({ order: { Year: 'DESC' } }), + ); + ongoingFinds.push( + AppDataSource.getRepository(BuildingFiscal).find({ order: { FiscalYear: 'DESC' } }), + ); + + // Wait for all database requests to resolve, then build the parcels and buildings lists + // Use IDs from filtered properties above to filter lists + const resolvedFinds = await Promise.all(ongoingFinds); + const parcelEvaluations = resolvedFinds.at(2) as ParcelEvaluation[]; + const parcelFiscals = resolvedFinds.at(3) as ParcelFiscal[]; + const buildingEvaluations = resolvedFinds.at(4) as BuildingEvaluation[]; + const buildingFiscals = resolvedFinds.at(5) as BuildingFiscal[]; + const parcels: Parcel[] = (resolvedFinds.at(0) as Parcel[]) + .filter((p: Parcel) => parcelIds.includes(p.Id)) + .map((p: Parcel) => { + const evaluation = parcelEvaluations.find((pe) => pe.ParcelId === p.Id); + const fiscal = parcelFiscals.find((pf) => pf.ParcelId === p.Id); + return { + ...p, + Evaluations: evaluation ? [evaluation] : undefined, + Fiscals: fiscal ? [fiscal] : undefined, + }; + }); + const buildings: Building[] = (resolvedFinds.at(1) as Building[]) + .filter((b: Building) => buildingIds.includes(b.Id)) + .map((b: Building) => { + const evaluation = buildingEvaluations.find((be) => be.BuildingId === b.Id); + const fiscal = buildingFiscals.find((bf) => bf.BuildingId === b.Id); + return { + ...b, + Evaluations: evaluation ? [evaluation] : undefined, + Fiscals: fiscal ? [fiscal] : undefined, + }; + }); + + return [...parcels, ...buildings]; }; /** diff --git a/express-api/tests/testUtils/factories.ts b/express-api/tests/testUtils/factories.ts index 1c050ab582..13e6483d4a 100644 --- a/express-api/tests/testUtils/factories.ts +++ b/express-api/tests/testUtils/factories.ts @@ -217,7 +217,7 @@ export const produceSSO = (props?: Partial): SSOUser => { }; }; -export const produceParcel = (): Parcel => { +export const produceParcel = (props?: Partial): Parcel => { const id = faker.number.int({ max: 10 }); return { Id: faker.number.int({ max: 10 }), @@ -252,11 +252,12 @@ export const produceParcel = (): Parcel => { CreatedBy: undefined, UpdatedById: undefined, UpdatedBy: undefined, - Fiscals: produceParcelFiscal(id), - Evaluations: produceParcelEvaluation(id), + Fiscals: produceParcelFiscals(id), + Evaluations: produceParcelEvaluations(id), DeletedById: null, DeletedOn: null, DeletedBy: undefined, + ...props, }; }; @@ -284,7 +285,7 @@ export const produceEmail = (props: Partial): IEmail => { return email; }; -export const produceBuilding = (): Building => { +export const produceBuilding = (props?: Partial): Building => { const agencyId = faker.number.int(); const id = faker.number.int({ max: 10 }); return { @@ -328,17 +329,18 @@ export const produceBuilding = (): Building => { CreatedBy: undefined, UpdatedById: undefined, UpdatedBy: undefined, - Fiscals: produceBuildingFiscal(id), - Evaluations: produceBuildingEvaluation(id), + Fiscals: produceBuildingFiscals(id), + Evaluations: produceBuildingEvaluations(id), PID: undefined, PIN: undefined, DeletedById: null, DeletedOn: null, DeletedBy: undefined, + ...props, }; }; -export const produceBuildingEvaluation = ( +export const produceBuildingEvaluations = ( buildingId: number, props?: Partial, ): BuildingEvaluation[] => { @@ -355,7 +357,7 @@ export const produceBuildingEvaluation = ( return [evaluation]; }; -export const produceBuildingFiscal = ( +export const produceBuildingFiscals = ( buildingId: number, props?: Partial, ): BuildingFiscal[] => { @@ -370,7 +372,7 @@ export const produceBuildingFiscal = ( return [fiscal]; }; -export const produceParcelEvaluation = ( +export const produceParcelEvaluations = ( parcelId: number, props?: Partial, ): ParcelEvaluation[] => { @@ -388,7 +390,7 @@ export const produceParcelEvaluation = ( return [evaluation]; }; -export const produceParcelFiscal = ( +export const produceParcelFiscals = ( parcelId: number, props?: Partial, ): ParcelFiscal[] => { diff --git a/express-api/tests/unit/services/buildings/buildingService.test.ts b/express-api/tests/unit/services/buildings/buildingService.test.ts index 88b5ebe7bd..8ae538e13c 100644 --- a/express-api/tests/unit/services/buildings/buildingService.test.ts +++ b/express-api/tests/unit/services/buildings/buildingService.test.ts @@ -2,8 +2,8 @@ import { AppDataSource } from '@/appDataSource'; import { Building } from '@/typeorm/Entities/Building'; import { produceBuilding, - produceBuildingEvaluation, - produceBuildingFiscal, + produceBuildingEvaluations, + produceBuildingFiscals, produceSSO, produceUser, } from 'tests/testUtils/factories'; @@ -26,11 +26,11 @@ const _buildingSave = jest const _buildingFiscalFindOne = jest .spyOn(AppDataSource.getRepository(BuildingFiscal), 'findOne') - .mockImplementation(async () => produceBuildingFiscal(1)[0]); + .mockImplementation(async () => produceBuildingFiscals(1)[0]); const _buildingEvaluationFindOne = jest .spyOn(AppDataSource.getRepository(BuildingEvaluation), 'findOne') - .mockImplementation(async () => produceBuildingEvaluation(1)[0]); + .mockImplementation(async () => produceBuildingEvaluations(1)[0]); const _buildingFindOne = jest .spyOn(buildingRepo, 'findOne') @@ -38,11 +38,11 @@ const _buildingFindOne = jest jest .spyOn(AppDataSource.getRepository(BuildingFiscal), 'find') - .mockImplementation(async () => produceBuildingFiscal(1)); + .mockImplementation(async () => produceBuildingFiscals(1)); jest .spyOn(AppDataSource.getRepository(BuildingEvaluation), 'find') - .mockImplementation(async () => produceBuildingEvaluation(1)); + .mockImplementation(async () => produceBuildingEvaluations(1)); const _mockStartTransaction = jest.fn(async () => {}); const _mockRollbackTransaction = jest.fn(async () => {}); diff --git a/express-api/tests/unit/services/parcels/parcelsService.test.ts b/express-api/tests/unit/services/parcels/parcelsService.test.ts index fb3059c05a..931da63a55 100644 --- a/express-api/tests/unit/services/parcels/parcelsService.test.ts +++ b/express-api/tests/unit/services/parcels/parcelsService.test.ts @@ -2,8 +2,8 @@ import { AppDataSource } from '@/appDataSource'; import { Parcel } from '@/typeorm/Entities/Parcel'; import { produceParcel, - produceParcelEvaluation, - produceParcelFiscal, + produceParcelEvaluations, + produceParcelFiscals, produceSSO, produceUser, } from 'tests/testUtils/factories'; @@ -28,7 +28,7 @@ const _parcelSave = jest const _parcelFindOne = jest.spyOn(parcelRepo, 'findOne').mockImplementation(async () => { const parcel = produceParcel(); const { Id } = parcel; - produceParcelFiscal(Id); + produceParcelFiscals(Id); return parcel; }); @@ -42,10 +42,10 @@ const _parcelFindOne = jest.spyOn(parcelRepo, 'findOne').mockImplementation(asyn const _parcelEvaluationFindOne = jest .spyOn(AppDataSource.getRepository(ParcelEvaluation), 'findOne') - .mockImplementation(async () => produceParcelEvaluation(1)[0]); + .mockImplementation(async () => produceParcelEvaluations(1)[0]); const _parcelFiscalFindOne = jest .spyOn(AppDataSource.getRepository(ParcelFiscal), 'findOne') - .mockImplementation(async () => produceParcelFiscal(1)[0]); + .mockImplementation(async () => produceParcelFiscals(1)[0]); // const _parcelFindOne = jest // .spyOn(parcelRepo, 'findOne') @@ -60,10 +60,10 @@ jest.spyOn(userServices, 'getAgencies').mockImplementation(async () => []); jest .spyOn(AppDataSource.getRepository(ParcelEvaluation), 'find') - .mockImplementation(async () => produceParcelEvaluation(1)); + .mockImplementation(async () => produceParcelEvaluations(1)); jest .spyOn(AppDataSource.getRepository(ParcelFiscal), 'find') - .mockImplementation(async () => produceParcelFiscal(1)); + .mockImplementation(async () => produceParcelFiscals(1)); const _mockStartTransaction = jest.fn(async () => {}); const _mockRollbackTransaction = jest.fn(async () => {}); diff --git a/express-api/tests/unit/services/properties/propertyServices.test.ts b/express-api/tests/unit/services/properties/propertyServices.test.ts index f65d9dcd9e..f83b972e26 100644 --- a/express-api/tests/unit/services/properties/propertyServices.test.ts +++ b/express-api/tests/unit/services/properties/propertyServices.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { AppDataSource } from '@/appDataSource'; +import { PropertyType } from '@/constants/propertyType'; import { Roles } from '@/constants/roles'; import propertyServices, { getAgencyOrThrowIfMismatched, @@ -36,10 +37,10 @@ import { produceAdminArea, produceUser, produceImportResult, - produceParcelEvaluation, - produceParcelFiscal, - produceBuildingEvaluation, - produceBuildingFiscal, + produceParcelEvaluations, + produceParcelFiscals, + produceBuildingEvaluations, + produceBuildingFiscals, produceSSO, produceProjectStatus, produceProjectProperty, @@ -80,8 +81,17 @@ const _propertyUnionCreateQueryBuilder: any = { take: () => _propertyUnionCreateQueryBuilder, skip: () => _propertyUnionCreateQueryBuilder, orderBy: () => _propertyUnionCreateQueryBuilder, - getMany: () => [producePropertyUnion()], - getManyAndCount: () => [[producePropertyUnion()], 1], + getMany: () => [ + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING }), + ], + getManyAndCount: () => [ + [ + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING }), + ], + 1, + ], }; const _projectStatusCreateQueryBuilder: any = { @@ -148,12 +158,24 @@ jest .spyOn(AppDataSource.getRepository(PropertyUnion), 'createQueryBuilder') .mockImplementation(() => _propertyUnionCreateQueryBuilder); -jest +const parcelRepoSpy = jest .spyOn(AppDataSource.getRepository(Parcel), 'find') .mockImplementation(async () => [produceParcel()]); -jest +const buildingRepoSpy = jest .spyOn(AppDataSource.getRepository(Building), 'find') .mockImplementation(async () => [produceBuilding()]); +jest + .spyOn(AppDataSource.getRepository(ParcelEvaluation), 'find') + .mockImplementation(async () => produceParcelEvaluations(1)); +jest + .spyOn(AppDataSource.getRepository(ParcelFiscal), 'find') + .mockImplementation(async () => produceParcelFiscals(1)); +jest + .spyOn(AppDataSource.getRepository(BuildingEvaluation), 'find') + .mockImplementation(async () => produceBuildingEvaluations(1)); +jest + .spyOn(AppDataSource.getRepository(BuildingFiscal), 'find') + .mockImplementation(async () => produceBuildingFiscals(1)); jest .spyOn(AppDataSource.getRepository(Parcel), 'save') .mockImplementation(async () => produceParcel()); @@ -198,13 +220,13 @@ const _mockEntityManager = { } else if (entityClass === Building) { return produceBuilding(); } else if (entityClass === ParcelEvaluation) { - return produceParcelEvaluation(1, { Year: 2023 }); + return produceParcelEvaluations(1, { Year: 2023 }); } else if (entityClass === ParcelFiscal) { - return produceParcelFiscal(1, { FiscalYear: 2023 }); + return produceParcelFiscals(1, { FiscalYear: 2023 }); } else if (entityClass === BuildingEvaluation) { - return produceBuildingEvaluation(1, { Year: 2023 }); + return produceBuildingEvaluations(1, { Year: 2023 }); } else if (entityClass === BuildingFiscal) { - return produceBuildingFiscal(1, { FiscalYear: 2023 }); + return produceBuildingFiscals(1, { FiscalYear: 2023 }); } else { return []; } @@ -295,6 +317,8 @@ describe('UNIT - Property Services', () => { describe('getPropertiesforExport', () => { it('should get a list of properties based on the filter', async () => { + parcelRepoSpy.mockImplementationOnce(async () => [produceParcel({ Id: 1 })]); + buildingRepoSpy.mockImplementationOnce(async () => [produceBuilding({ Id: 1 })]); const result = await propertyServices.getPropertiesForExport({ pid: 'contains,123', pin: 'contains,456', diff --git a/react-app/src/components/property/PropertyTable.tsx b/react-app/src/components/property/PropertyTable.tsx index 90e867927b..5c5c269ff4 100644 --- a/react-app/src/components/property/PropertyTable.tsx +++ b/react-app/src/components/property/PropertyTable.tsx @@ -251,8 +251,9 @@ const PropertyTable = (props: IPropertyTable) => { property.AdministrativeAreaId, )?.Name, Postal: property.Postal, - PID: property.PID, + PID: pidFormatter(property.PID), PIN: property.PIN, + 'Is Sensitive': property.IsSensitive, 'Assessed Value': property.Evaluations?.length ? property.Evaluations.sort( ( diff --git a/react-app/src/components/table/DataTable.tsx b/react-app/src/components/table/DataTable.tsx index e7127a2018..9a7315810d 100644 --- a/react-app/src/components/table/DataTable.tsx +++ b/react-app/src/components/table/DataTable.tsx @@ -48,6 +48,7 @@ import { CommonFiltering } from '@/interfaces/ICommonFiltering'; import { useSearchParams } from 'react-router-dom'; import { Roles } from '@/constants/roles'; import { AuthContext } from '@/contexts/authContext'; +import { SnackBarContext } from '@/contexts/snackbarContext'; type RenderCellParams = GridRenderCellParams; @@ -207,6 +208,8 @@ export const FilterSearchDataGrid = (props: FilterSearchDataGridProps) => { const [dataSourceLoading, setDataSourceLoading] = useState(false); const tableApiRef = useGridApiRef(); // Ref to MUI DataGrid const previousController = useRef(); + const snackbar = useContext(SnackBarContext); + interface ITableModelCollection { pagination?: GridPaginationModel; sort?: GridSortModel; @@ -535,23 +538,30 @@ export const FilterSearchDataGrid = (props: FilterSearchDataGridProps) => { rows = props.excelDataSource ? await props.excelDataSource(sortFilterObj, signal) : await props.dataSource(sortFilterObj, signal); - if (props.customExcelMap) rows = props.customExcelMap(rows); } else { // Client-side tables rows = gridFilteredSortedRowEntriesSelector(tableApiRef).map((row) => row.model); + } + if (rows) { if (props.customExcelMap) rows = props.customExcelMap(rows); + // Convert back to MUI table model + rows = rows.map((r, i) => ({ + model: r, + id: i, + })); + downloadExcelFile({ + data: rows, + tableName: props.excelTitle, + filterName: selectValue, + includeDate: true, + }); + } else { + snackbar.setMessageState({ + style: snackbar.styles.warning, + text: 'Table failed to export.', + open: true, + }); } - // Convert back to MUI table model - rows = rows.map((r, i) => ({ - model: r, - id: i, - })); - downloadExcelFile({ - data: rows, - tableName: props.excelTitle, - filterName: selectValue, - includeDate: true, - }); setIsExporting(false); }} > diff --git a/react-app/src/hooks/api/usePropertiesApi.ts b/react-app/src/hooks/api/usePropertiesApi.ts index 106d016ccd..ee4a8b9ca9 100644 --- a/react-app/src/hooks/api/usePropertiesApi.ts +++ b/react-app/src/hooks/api/usePropertiesApi.ts @@ -127,8 +127,8 @@ const usePropertiesApi = (absoluteFetch: IFetch) => { }, { signal }, ); - if ((parsedBody as Record).error) { - return []; + if ((parsedBody as Record)?.error) { + return; } return parsedBody as (Parcel | Building)[]; }; From 117fc0d07a472fe834cb8c24dad8b0f12b72fb12 Mon Sep 17 00:00:00 2001 From: LawrenceLau2020 <68400651+LawrenceLau2020@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:07:20 -0700 Subject: [PATCH 2/2] PIMS-2001: Add ERP date for general user projects (#2642) Co-authored-by: Dylan Barkowsky <37922247+dbarkowsky@users.noreply.github.com> --- .../projects/EnhancedReferralDates.tsx | 83 +++++++++++++++++++ .../src/components/projects/ProjectDetail.tsx | 39 +++++++++ .../projects/ProjectNotificationsTable.tsx | 75 ----------------- 3 files changed, 122 insertions(+), 75 deletions(-) create mode 100644 react-app/src/components/projects/EnhancedReferralDates.tsx diff --git a/react-app/src/components/projects/EnhancedReferralDates.tsx b/react-app/src/components/projects/EnhancedReferralDates.tsx new file mode 100644 index 0000000000..0738a73bef --- /dev/null +++ b/react-app/src/components/projects/EnhancedReferralDates.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { Box } from '@mui/material'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { DateField } from '@mui/x-date-pickers/DateField'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import dayjs from 'dayjs'; +import { NotificationQueue } from '@/hooks/api/useProjectNotificationApi'; +import { NotificationType } from '@/constants/notificationTypes'; + +interface EnhancedReferralDatesProps { + rows: NotificationQueue[]; +} + +const EnhancedReferralDates: React.FC = ({ rows = [] }) => { + const initalERN = rows.find((row) => row.TemplateId === NotificationType.NEW_PROPERTIES_ON_ERP); + const thirtyDayERN = rows.find( + (row) => row.TemplateId === NotificationType.THIRTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, + ); + const sixtyDayERN = rows.find( + (row) => row.TemplateId === NotificationType.SIXTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, + ); + const nintyDayERN = rows.find( + (row) => row.TemplateId === NotificationType.NINTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, + ); + + const allErnNotificatonsFound = [initalERN, thirtyDayERN, sixtyDayERN, nintyDayERN].every( + (notification) => notification != undefined, + ); + + if (!allErnNotificatonsFound) return null; + + return ( + + fieldset': { + borderColor: 'rgba(0,0,0)', + }, + }, + '& .MuiFormLabel-root.MuiInputLabel-root': { + color: 'rgba(0, 0, 0)', + }, + '& .MuiInputBase-input.MuiOutlinedInput-input.Mui-disabled': { + color: 'rgba(0,0,0)', + WebkitTextFillColor: 'rgba(0,0,0)', + }, + }} + > + + + + + + + ); +}; + +export default EnhancedReferralDates; diff --git a/react-app/src/components/projects/ProjectDetail.tsx b/react-app/src/components/projects/ProjectDetail.tsx index f69db8a077..6c9e2e38dd 100644 --- a/react-app/src/components/projects/ProjectDetail.tsx +++ b/react-app/src/components/projects/ProjectDetail.tsx @@ -50,6 +50,8 @@ import { LookupContext } from '@/contexts/lookupContext'; import { Agency } from '@/hooks/api/useAgencyApi'; import { getStatusString } from '@/constants/chesNotificationStatus'; import { NoteTypes } from '@/constants/noteTypes'; +import EnhancedReferralDates from './EnhancedReferralDates'; +import { NotificationType } from '@/constants/notificationTypes'; interface IProjectDetail { onClose: () => void; @@ -96,6 +98,26 @@ const ProjectDetail = (props: IProjectDetail) => { const { submit: resendNotification } = useDataSubmitter(api.notifications.resendNotification); const { submit: cancelNotification } = useDataSubmitter(api.notifications.cancelNotification); + const hasERPNotifications = useMemo(() => { + // Check if notifications is an object and has an items array + if (!notifications || !Array.isArray(notifications.items)) { + return false; + } + const notificationItems = notifications.items; + if (notificationItems.length === 0) { + return false; + } + + const types = [ + NotificationType.THIRTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, + NotificationType.SIXTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, + NotificationType.NINTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, + ]; + + // Check if any of the notifications match the types + return notificationItems.some((n) => types.includes(n.TemplateId)); + }, [notifications]); + const { ungroupedAgencies, agencyOptions } = useGroupedAgenciesApi(); interface IStatusHistoryStruct { Notes: Array; @@ -251,6 +273,7 @@ const ProjectDetail = (props: IProjectDetail) => { const agencyInterest = 'Agency Interest'; const documentationHistory = 'Documentation History'; const notificationsHeader = 'Notifications'; + const enhancedReferralDates = 'Enhanced Referral Dates'; const sideBarList = [ { title: projectInformation }, @@ -258,6 +281,10 @@ const ProjectDetail = (props: IProjectDetail) => { { title: financialInformation }, { title: documentationHistory }, ]; + // + if (hasERPNotifications) { + sideBarList.splice(1, 0, { title: enhancedReferralDates }); + } // only show Agency Interest and notifications for admins if (isAdmin) { sideBarList.splice(3, 0, { title: agencyInterest }); @@ -293,6 +320,18 @@ const ProjectDetail = (props: IProjectDetail) => { onEdit={() => setOpenProjectInfoDialog(true)} disableEdit={!isAdmin} /> + {hasERPNotifications && ( + {}} + disableEdit={true} + > + + + )} { if (!props.rows) return <>; - // Prepare values for Enhanced Referral Notification fields - const initalERN = props.rows.find( - (row) => row.TemplateId === NotificationType.NEW_PROPERTIES_ON_ERP, - ); - const thirtyDayERN = props.rows.find( - (row) => row.TemplateId === NotificationType.THIRTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, - ); - const sixtyDayERN = props.rows.find( - (row) => row.TemplateId === NotificationType.SIXTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, - ); - const nintyDayERN = props.rows.find( - (row) => row.TemplateId === NotificationType.NINTY_DAY_ERP_NOTIFICATION_OWNING_AGENCY, - ); - - const allErnNotificatonsFound = [initalERN, thirtyDayERN, sixtyDayERN, nintyDayERN].every( - (notification) => notification != undefined, - ); - return !props.rows ? ( No notifications were sent. ) : ( <> - {allErnNotificatonsFound ? ( - - Enhanced Referral Notification Dates - fieldset': { - borderColor: 'rgba(0,0,0)', - }, - }, - '& .MuiFormLabel-root.MuiInputLabel-root': { - color: 'rgba(0, 0, 0)', - }, - '& .MuiInputBase-input.MuiOutlinedInput-input.Mui-disabled': { - color: 'rgba(0,0,0)', - WebkitTextFillColor: 'rgba(0,0,0)', - }, - }} - > - - - - - - - ) : ( - <> - )} - Total Notifications: {props.rows.length}