From 5cac8b055a498a61ef7058fc873351f5c33d2880 Mon Sep 17 00:00:00 2001 From: Herrera Date: Mon, 20 May 2024 17:39:21 -0700 Subject: [PATCH 1/5] PSP-8227 : Add search for "historical file number" on the property search dropdown to search to search across Historical Numbers --- .../Property/Models/PropertyFilterModel.cs | 32 ++-- .../Concepts/Property/PropertyViewMap.cs | 174 +++++++++--------- .../Concepts/Property/PropertyViewModel.cs | 2 + .../Helpers/Extensions/PropertyExtensions.cs | 20 +- source/backend/dal/Models/PropertyFilter.cs | 5 + .../dal/Repositories/PropertyRepository.cs | 2 +- .../propertyPicker/LeasePropertySelector.tsx | 1 + .../properties/filter/IPropertyFilter.ts | 3 + .../properties/filter/PropertyFilter.test.tsx | 1 + .../properties/filter/PropertyFilter.tsx | 11 +- .../properties/list/PropertyListView.test.tsx | 51 +++++ .../properties/list/PropertyListView.tsx | 65 ++++--- .../PropertyListView.test.tsx.snap | 25 ++- .../src/features/properties/list/columns.tsx | 126 ++++++++++--- 14 files changed, 366 insertions(+), 152 deletions(-) diff --git a/source/backend/api/Areas/Property/Models/PropertyFilterModel.cs b/source/backend/api/Areas/Property/Models/PropertyFilterModel.cs index 8eed2b97ff..c9370813cf 100644 --- a/source/backend/api/Areas/Property/Models/PropertyFilterModel.cs +++ b/source/backend/api/Areas/Property/Models/PropertyFilterModel.cs @@ -28,6 +28,11 @@ public class PropertyFilterModel : PageFilter /// public string PlanNumber { get; set; } + /// + /// get/set - Search by historical LIS or PS file numbers. + /// + public string Historical { get; set; } + /// /// get/set - The property ownership status. /// @@ -54,11 +59,11 @@ public PropertyFilterModel(Dictionary(query, StringComparer.OrdinalIgnoreCase); - this.Sort = filter.GetStringArrayValue(nameof(this.Sort)); - var tempSort = this.Sort.ToList(); + Sort = filter.GetStringArrayValue(nameof(Sort)); + var tempSort = Sort.ToList(); // Convert sort to db format - for (int i = 0; i < this.Sort.Length; i++) + for (int i = 0; i < Sort.Length; i++) { if (tempSort[i].StartsWith("Location")) { @@ -72,20 +77,21 @@ public PropertyFilterModel(Dictionary() - .Map(dest => dest.Id, src => src.PropertyId) - .Map(dest => dest.Pid, src => src.Pid) -.Map(dest => dest.PidPadded, src => src.PidPadded) -.Map(dest => dest.Pin, src => src.Pin) -.Map(dest => dest.PropertyTypeCode, src => src.PropertyTypeCode) -.Map(dest => dest.PropertyStatusTypeCode, src => src.PropertyStatusTypeCode) -.Map(dest => dest.PropertyDataSourceTypeCode, src => src.PropertyDataSourceTypeCode) -.Map(dest => dest.PropertyDataSourceEffectiveDate, src => src.PropertyDataSourceEffectiveDate) -.Map(dest => dest.PropertyClassificationTypeCode, src => src.PropertyClassificationTypeCode) -.Map(dest => dest.PropertyTenureTypeCode, src => src.PropertyTenureTypeCode) -.Map(dest => dest.StreetAddress1, src => src.StreetAddress1) -.Map(dest => dest.StreetAddress2, src => src.StreetAddress2) -.Map(dest => dest.StreetAddress3, src => src.StreetAddress3) -.Map(dest => dest.MunicipalityName, src => src.MunicipalityName) -.Map(dest => dest.PostalCode, src => src.PostalCode) -.Map(dest => dest.ProvinceStateCode, src => src.ProvinceStateCode) -.Map(dest => dest.ProvinceName, src => src.ProvinceName) -.Map(dest => dest.CountryCode, src => src.CountryCode) -.Map(dest => dest.CountryName, src => src.CountryName) -.Map(dest => dest.Name, src => src.Name) -.Map(dest => dest.Description, src => src.Description) -.Map(dest => dest.AddressId, src => src.AddressId) -.Map(dest => dest.RegionCode, src => src.RegionCode) -.Map(dest => dest.DistrictCode, src => src.DistrictCode) -.Map(dest => dest.PropertyAreaUnitTypeCode, src => src.PropertyAreaUnitTypeCode) -.Map(dest => dest.LandArea, src => src.LandArea) -.Map(dest => dest.LandLegalDescription, src => src.LandLegalDescription) -.Map(dest => dest.SurveyPlanNumber, src => src.SurveyPlanNumber) -.Map(dest => dest.EncumbranceReason, src => src.EncumbranceReason) -.Map(dest => dest.IsSensitive, src => src.IsSensitive) -.Map(dest => dest.IsOwned, src => src.IsOwned) -.Map(dest => dest.IsRetired, src => src.IsRetired) -.Map(dest => dest.IsVisibleToOtherAgencies, src => src.IsVisibleToOtherAgencies) -.Map(dest => dest.Zoning, src => src.Zoning) -.Map(dest => dest.ZoningPotential, src => src.ZoningPotential) -.Map(dest => dest.IsDisposed, src => src.IsDisposed) -.Map(dest => dest.IsOtherInterest, src => src.IsOtherInterest) -.Map(dest => dest.HasActiveAcquisitionFile, src => src.HasActiveAcquisitionFile) -.Map(dest => dest.HasActiveResearchFile, src => src.HasActiveResearchFile) -.Map(dest => dest.IsPayableLease, src => src.IsPayableLease) -.Map(dest => dest.IsActivePayableLease, src => src.IsActivePayableLease) -.Map(dest => dest.IsReceivableLease, src => src.IsReceivableLease) -.Map(dest => dest.IsActiveReceivableLease, src => src.IsActiveReceivableLease); + .Map(dest => dest.Id, src => src.PropertyId) + .Map(dest => dest.Pid, src => src.Pid) + .Map(dest => dest.PidPadded, src => src.PidPadded) + .Map(dest => dest.Pin, src => src.Pin) + .Map(dest => dest.PropertyTypeCode, src => src.PropertyTypeCode) + .Map(dest => dest.PropertyStatusTypeCode, src => src.PropertyStatusTypeCode) + .Map(dest => dest.PropertyDataSourceTypeCode, src => src.PropertyDataSourceTypeCode) + .Map(dest => dest.PropertyDataSourceEffectiveDate, src => src.PropertyDataSourceEffectiveDate) + .Map(dest => dest.PropertyClassificationTypeCode, src => src.PropertyClassificationTypeCode) + .Map(dest => dest.PropertyTenureTypeCode, src => src.PropertyTenureTypeCode) + .Map(dest => dest.StreetAddress1, src => src.StreetAddress1) + .Map(dest => dest.StreetAddress2, src => src.StreetAddress2) + .Map(dest => dest.StreetAddress3, src => src.StreetAddress3) + .Map(dest => dest.MunicipalityName, src => src.MunicipalityName) + .Map(dest => dest.PostalCode, src => src.PostalCode) + .Map(dest => dest.ProvinceStateCode, src => src.ProvinceStateCode) + .Map(dest => dest.ProvinceName, src => src.ProvinceName) + .Map(dest => dest.CountryCode, src => src.CountryCode) + .Map(dest => dest.CountryName, src => src.CountryName) + .Map(dest => dest.Name, src => src.Name) + .Map(dest => dest.Description, src => src.Description) + .Map(dest => dest.AddressId, src => src.AddressId) + .Map(dest => dest.RegionCode, src => src.RegionCode) + .Map(dest => dest.DistrictCode, src => src.DistrictCode) + .Map(dest => dest.PropertyAreaUnitTypeCode, src => src.PropertyAreaUnitTypeCode) + .Map(dest => dest.LandArea, src => src.LandArea) + .Map(dest => dest.LandLegalDescription, src => src.LandLegalDescription) + .Map(dest => dest.SurveyPlanNumber, src => src.SurveyPlanNumber) + .Map(dest => dest.EncumbranceReason, src => src.EncumbranceReason) + .Map(dest => dest.IsSensitive, src => src.IsSensitive) + .Map(dest => dest.IsOwned, src => src.IsOwned) + .Map(dest => dest.IsRetired, src => src.IsRetired) + .Map(dest => dest.IsVisibleToOtherAgencies, src => src.IsVisibleToOtherAgencies) + .Map(dest => dest.Zoning, src => src.Zoning) + .Map(dest => dest.ZoningPotential, src => src.ZoningPotential) + .Map(dest => dest.IsDisposed, src => src.IsDisposed) + .Map(dest => dest.IsOtherInterest, src => src.IsOtherInterest) + .Map(dest => dest.HasActiveAcquisitionFile, src => src.HasActiveAcquisitionFile) + .Map(dest => dest.HasActiveResearchFile, src => src.HasActiveResearchFile) + .Map(dest => dest.IsPayableLease, src => src.IsPayableLease) + .Map(dest => dest.IsActivePayableLease, src => src.IsActivePayableLease) + .Map(dest => dest.IsReceivableLease, src => src.IsReceivableLease) + .Map(dest => dest.IsActiveReceivableLease, src => src.IsActiveReceivableLease) + .Map(dest => dest.HistoricalFileNumberStr, src => src.HistoricalFileNumberStr); config.NewConfig() - .Map(dest => dest.PropertyId, src => src.Id) - .Map(dest => dest.Pid, src => src.Pid) -.Map(dest => dest.PidPadded, src => src.PidPadded) -.Map(dest => dest.Pin, src => src.Pin) -.Map(dest => dest.PropertyTypeCode, src => src.PropertyTypeCode) -.Map(dest => dest.PropertyStatusTypeCode, src => src.PropertyStatusTypeCode) -.Map(dest => dest.PropertyDataSourceTypeCode, src => src.PropertyDataSourceTypeCode) -.Map(dest => dest.PropertyDataSourceEffectiveDate, src => src.PropertyDataSourceEffectiveDate) -.Map(dest => dest.PropertyClassificationTypeCode, src => src.PropertyClassificationTypeCode) -.Map(dest => dest.PropertyTenureTypeCode, src => src.PropertyTenureTypeCode) -.Map(dest => dest.StreetAddress1, src => src.StreetAddress1) -.Map(dest => dest.StreetAddress2, src => src.StreetAddress2) -.Map(dest => dest.StreetAddress3, src => src.StreetAddress3) -.Map(dest => dest.MunicipalityName, src => src.MunicipalityName) -.Map(dest => dest.PostalCode, src => src.PostalCode) -.Map(dest => dest.ProvinceStateCode, src => src.ProvinceStateCode) -.Map(dest => dest.ProvinceName, src => src.ProvinceName) -.Map(dest => dest.CountryCode, src => src.CountryCode) -.Map(dest => dest.CountryName, src => src.CountryName) -.Map(dest => dest.Name, src => src.Name) -.Map(dest => dest.Description, src => src.Description) -.Map(dest => dest.AddressId, src => src.AddressId) -.Map(dest => dest.RegionCode, src => src.RegionCode) -.Map(dest => dest.DistrictCode, src => src.DistrictCode) -.Map(dest => dest.PropertyAreaUnitTypeCode, src => src.PropertyAreaUnitTypeCode) -.Map(dest => dest.LandArea, src => src.LandArea) -.Map(dest => dest.LandLegalDescription, src => src.LandLegalDescription) -.Map(dest => dest.SurveyPlanNumber, src => src.SurveyPlanNumber) -.Map(dest => dest.EncumbranceReason, src => src.EncumbranceReason) -.Map(dest => dest.IsSensitive, src => src.IsSensitive) -.Map(dest => dest.IsOwned, src => src.IsOwned) -.Map(dest => dest.IsRetired, src => src.IsRetired) -.Map(dest => dest.IsVisibleToOtherAgencies, src => src.IsVisibleToOtherAgencies) -.Map(dest => dest.Zoning, src => src.Zoning) -.Map(dest => dest.ZoningPotential, src => src.ZoningPotential) -.Map(dest => dest.IsDisposed, src => src.IsDisposed) -.Map(dest => dest.IsOtherInterest, src => src.IsOtherInterest) -.Map(dest => dest.HasActiveAcquisitionFile, src => src.HasActiveAcquisitionFile) -.Map(dest => dest.HasActiveResearchFile, src => src.HasActiveResearchFile) -.Map(dest => dest.IsPayableLease, src => src.IsPayableLease) -.Map(dest => dest.IsActivePayableLease, src => src.IsActivePayableLease) -.Map(dest => dest.IsReceivableLease, src => src.IsReceivableLease) -.Map(dest => dest.IsActiveReceivableLease, src => src.IsActiveReceivableLease); + .Map(dest => dest.PropertyId, src => src.Id) + .Map(dest => dest.Pid, src => src.Pid) + .Map(dest => dest.PidPadded, src => src.PidPadded) + .Map(dest => dest.Pin, src => src.Pin) + .Map(dest => dest.PropertyTypeCode, src => src.PropertyTypeCode) + .Map(dest => dest.PropertyStatusTypeCode, src => src.PropertyStatusTypeCode) + .Map(dest => dest.PropertyDataSourceTypeCode, src => src.PropertyDataSourceTypeCode) + .Map(dest => dest.PropertyDataSourceEffectiveDate, src => src.PropertyDataSourceEffectiveDate) + .Map(dest => dest.PropertyClassificationTypeCode, src => src.PropertyClassificationTypeCode) + .Map(dest => dest.PropertyTenureTypeCode, src => src.PropertyTenureTypeCode) + .Map(dest => dest.StreetAddress1, src => src.StreetAddress1) + .Map(dest => dest.StreetAddress2, src => src.StreetAddress2) + .Map(dest => dest.StreetAddress3, src => src.StreetAddress3) + .Map(dest => dest.MunicipalityName, src => src.MunicipalityName) + .Map(dest => dest.PostalCode, src => src.PostalCode) + .Map(dest => dest.ProvinceStateCode, src => src.ProvinceStateCode) + .Map(dest => dest.ProvinceName, src => src.ProvinceName) + .Map(dest => dest.CountryCode, src => src.CountryCode) + .Map(dest => dest.CountryName, src => src.CountryName) + .Map(dest => dest.Name, src => src.Name) + .Map(dest => dest.Description, src => src.Description) + .Map(dest => dest.AddressId, src => src.AddressId) + .Map(dest => dest.RegionCode, src => src.RegionCode) + .Map(dest => dest.DistrictCode, src => src.DistrictCode) + .Map(dest => dest.PropertyAreaUnitTypeCode, src => src.PropertyAreaUnitTypeCode) + .Map(dest => dest.LandArea, src => src.LandArea) + .Map(dest => dest.LandLegalDescription, src => src.LandLegalDescription) + .Map(dest => dest.SurveyPlanNumber, src => src.SurveyPlanNumber) + .Map(dest => dest.EncumbranceReason, src => src.EncumbranceReason) + .Map(dest => dest.IsSensitive, src => src.IsSensitive) + .Map(dest => dest.IsOwned, src => src.IsOwned) + .Map(dest => dest.IsRetired, src => src.IsRetired) + .Map(dest => dest.IsVisibleToOtherAgencies, src => src.IsVisibleToOtherAgencies) + .Map(dest => dest.Zoning, src => src.Zoning) + .Map(dest => dest.ZoningPotential, src => src.ZoningPotential) + .Map(dest => dest.IsDisposed, src => src.IsDisposed) + .Map(dest => dest.IsOtherInterest, src => src.IsOtherInterest) + .Map(dest => dest.HasActiveAcquisitionFile, src => src.HasActiveAcquisitionFile) + .Map(dest => dest.HasActiveResearchFile, src => src.HasActiveResearchFile) + .Map(dest => dest.IsPayableLease, src => src.IsPayableLease) + .Map(dest => dest.IsActivePayableLease, src => src.IsActivePayableLease) + .Map(dest => dest.IsReceivableLease, src => src.IsReceivableLease) + .Map(dest => dest.IsActiveReceivableLease, src => src.IsActiveReceivableLease) + .Map(dest => dest.HistoricalFileNumberStr, src => src.HistoricalFileNumberStr); } } } diff --git a/source/backend/apimodels/Models/Concepts/Property/PropertyViewModel.cs b/source/backend/apimodels/Models/Concepts/Property/PropertyViewModel.cs index c1ba480e84..0f005ac35d 100644 --- a/source/backend/apimodels/Models/Concepts/Property/PropertyViewModel.cs +++ b/source/backend/apimodels/Models/Concepts/Property/PropertyViewModel.cs @@ -95,6 +95,8 @@ public class PropertyViewModel public bool? IsActiveReceivableLease { get; set; } + public string HistoricalFileNumberStr { get; set; } + #endregion } } diff --git a/source/backend/dal/Helpers/Extensions/PropertyExtensions.cs b/source/backend/dal/Helpers/Extensions/PropertyExtensions.cs index 1d90dda435..835b99ff60 100644 --- a/source/backend/dal/Helpers/Extensions/PropertyExtensions.cs +++ b/source/backend/dal/Helpers/Extensions/PropertyExtensions.cs @@ -4,6 +4,7 @@ using System.Text.RegularExpressions; using LinqKit; using Microsoft.EntityFrameworkCore; +using Pims.Api.Models.CodeTypes; using Pims.Core.Extensions; using Pims.Dal.Entities; using Pims.Dal.Security; @@ -32,7 +33,7 @@ public static class PropertyExtensions var query = context.PimsPropertyVws .AsNoTracking(); - var predicate = GenerateCommonPropertyQuery(user, filter); + var predicate = GenerateCommonPropertyQuery(context, user, filter); query = query.Where(predicate); if (filter.Sort?.Any() == true) @@ -53,7 +54,7 @@ public static class PropertyExtensions /// /// /// - private static ExpressionStarter GenerateCommonPropertyQuery(ClaimsPrincipal user, Entity.Models.PropertyFilter filter) + private static ExpressionStarter GenerateCommonPropertyQuery(PimsContext context, ClaimsPrincipal user, Entity.Models.PropertyFilter filter) { filter.ThrowIfNull(nameof(filter)); filter.ThrowIfNull(nameof(user)); @@ -80,11 +81,25 @@ private static ExpressionStarter GenerateCommonPropertyQuery(Cla { predicateBuilder = predicateBuilder.And(p => EF.Functions.Like(p.StreetAddress1, $"%{filter.Address}%") || EF.Functions.Like(p.MunicipalityName, $"%{filter.Address}%")); } + if (!string.IsNullOrWhiteSpace(filter.PlanNumber)) { predicateBuilder = predicateBuilder.And(p => p.SurveyPlanNumber.Equals(filter.PlanNumber)); } + if (!string.IsNullOrWhiteSpace(filter.Historical)) + { + var predicateBuilderHistorical = PredicateBuilder.New(p => true); + predicateBuilderHistorical = predicateBuilderHistorical.And(x => x.PimsHistoricalFileNumbers + .Any(y => y.HistoricalFileNumberTypeCode == HistoricalFileNumberTypes.LISNO.ToString() && y.HistoricalFileNumber.Contains(filter.Historical))); + predicateBuilderHistorical = predicateBuilderHistorical.Or(x => x.PimsHistoricalFileNumbers + .Any(y => y.HistoricalFileNumberTypeCode == HistoricalFileNumberTypes.PSNO.ToString() && y.HistoricalFileNumber.Contains(filter.Historical))); + predicateBuilderHistorical = predicateBuilderHistorical.Or(x => x.PimsHistoricalFileNumbers + .Any(y => y.HistoricalFileNumberTypeCode == HistoricalFileNumberTypes.OTHER.ToString() && y.HistoricalFileNumber.Contains(filter.Historical))); + + predicateBuilder = predicateBuilder.And(x => context.PimsProperties.Where(predicateBuilderHistorical).AsExpandable().Where(b => b.PropertyId == x.PropertyId).Any()); + } + var isRetired = filter.Ownership.Contains("isRetired"); ExpressionStarter ownershipBuilder; @@ -116,6 +131,7 @@ private static ExpressionStarter GenerateCommonPropertyQuery(Cla // psp-7658 is retired properties should be omitted by default. ownershipBuilder = PredicateBuilder.New(p => p.IsRetired != true); } + predicateBuilder = predicateBuilder.And(ownershipBuilder); return predicateBuilder; diff --git a/source/backend/dal/Models/PropertyFilter.cs b/source/backend/dal/Models/PropertyFilter.cs index 65fb8c160f..fef39dafe8 100644 --- a/source/backend/dal/Models/PropertyFilter.cs +++ b/source/backend/dal/Models/PropertyFilter.cs @@ -26,6 +26,11 @@ public class PropertyFilter : PageFilter /// public string PlanNumber { get; set; } + /// + /// get/set - Search by historical LIS or PS file numbers. + /// + public string Historical { get; set; } + /// /// get/set - The property ownership status. /// diff --git a/source/backend/dal/Repositories/PropertyRepository.cs b/source/backend/dal/Repositories/PropertyRepository.cs index 4db5e494b4..ffb590cc33 100644 --- a/source/backend/dal/Repositories/PropertyRepository.cs +++ b/source/backend/dal/Repositories/PropertyRepository.cs @@ -64,7 +64,7 @@ public Paged GetPage(PropertyFilter filter) } var skip = (filter.Page - 1) * filter.Quantity; - var query = this.Context.GeneratePropertyQuery(this.User, filter); + var query = Context.GeneratePropertyQuery(this.User, filter); var items = query .Skip(skip) .Take(filter.Quantity) diff --git a/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx b/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx index 9faefba6b9..88db69d7d9 100644 --- a/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx +++ b/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx @@ -63,6 +63,7 @@ export const LeasePropertySelector: React.FunctionComponent { quantity: undefined, latitude: '', longitude: '', + historical: '', ownership: 'isCoreInventory,isPropertyOfInterest,isOtherInterest', }); }); diff --git a/source/frontend/src/features/properties/filter/PropertyFilter.tsx b/source/frontend/src/features/properties/filter/PropertyFilter.tsx index aa392b7646..dc511f9a57 100644 --- a/source/frontend/src/features/properties/filter/PropertyFilter.tsx +++ b/source/frontend/src/features/properties/filter/PropertyFilter.tsx @@ -96,6 +96,10 @@ export const PropertyFilter: React.FC { @@ -103,6 +107,7 @@ export const PropertyFilter: React.FC @@ -149,6 +154,9 @@ export const PropertyFilter: React.FC )} + {values.searchBy === 'historical' && ( + + )} diff --git a/source/frontend/src/features/properties/list/PropertyListView.test.tsx b/source/frontend/src/features/properties/list/PropertyListView.test.tsx index 7748bbf986..b0a7359ad8 100644 --- a/source/frontend/src/features/properties/list/PropertyListView.test.tsx +++ b/source/frontend/src/features/properties/list/PropertyListView.test.tsx @@ -26,11 +26,15 @@ import { MultiSelectOption } from '@/features/acquisition/list/interfaces'; import { IGeocoderPidsResponse } from '@/hooks/pims-api/interfaces/IGeocoder'; import { IPropertyFilter } from '../filter/IPropertyFilter'; import { ApiGen_Concepts_PropertyView } from '@/models/api/generated/ApiGen_Concepts_PropertyView'; +import { useApiHistoricalNumbers } from '@/hooks/pims-api/useApiHistoricalNumbers'; +import { ApiGen_Concepts_HistoricalFileNumber } from '@/models/api/generated/ApiGen_Concepts_HistoricalFileNumber'; +import { ApiGen_CodeTypes_HistoricalFileNumberTypes } from '@/models/api/generated/ApiGen_CodeTypes_HistoricalFileNumberTypes'; // Set all module functions to vi.fn vi.mock('@/hooks/pims-api/useApiGeocoder'); vi.mock('@/hooks/pims-api/useApiProperties'); +vi.mock('@/hooks/pims-api/useApiHistoricalNumbers'); const mockApiGetPropertiesPagedApi = vi.fn(); vi.mocked(useApiProperties).mockReturnValue({ @@ -46,6 +50,13 @@ vi.mocked(useApiGeocoder).mockReturnValue({ ) => Promise>, } as unknown as ReturnType); +const mockApiGetHistoricalFileNumbersApi = vi.fn(); +vi.mocked(useApiHistoricalNumbers).mockReturnValue({ + getByPropertyId: mockApiGetHistoricalFileNumbersApi as ( + propertyId: number, + ) => Promise>, +} as unknown as ReturnType); + const mockAxios = new MockAdapter(axios); const history = createMemoryHistory(); @@ -85,6 +96,32 @@ const setupMockApi = (properties?: ApiGen_Concepts_PropertyView[]) => { items: mockProperties, }, } as any); + + mockApiGetHistoricalFileNumbersApi.mockResolvedValue({ + data: [ + { + id: 100, + propertyId: 1, + property: null, + historicalFileNumberTypeCode: { + id: ApiGen_CodeTypes_HistoricalFileNumberTypes.LISNO.toString(), + description: 'LIS #', + isDisabled: false, + displayOrder: 1, + }, + historicalFileNumber: '301-9999', + otherHistFileNumberTypeCode: null, + isDisabled: false, + appCreateTimestamp: '2024-05-14T20:04:00.82', + appLastUpdateTimestamp: '2024-05-14T20:04:00.82', + appLastUpdateUserid: 'dbo', + appCreateUserid: 'dbo', + appLastUpdateUserGuid: null, + appCreateUserGuid: null, + rowVersion: 1, + }, + ], + }); }; describe('Property list view', () => { @@ -95,6 +132,7 @@ describe('Property list view', () => { mockApiGetPropertiesPagedApi.mockClear(); }); + afterEach(() => { history.push({ search: '' }); cleanup(); @@ -202,6 +240,7 @@ describe('Property list view', () => { page: 1, pinOrPid: '', planNumber: '', + historical: '', quantity: 10, searchBy: 'pinOrPid', sort: undefined, @@ -221,4 +260,16 @@ describe('Property list view', () => { expect(getByTestId('tooltip-icon-retired-tooltip')).toBeVisible(); }); + + it('displays the historical file number for LIS', async () => { + setupMockApi([mockApiPropertyView()]); + + const { + component: { findByText }, + } = setup(); + + const results = await findByText(/301-9999/i); + expect(results).toBeInTheDocument(); + }); + }); diff --git a/source/frontend/src/features/properties/list/PropertyListView.tsx b/source/frontend/src/features/properties/list/PropertyListView.tsx index dacbc2dce5..35c0ba855a 100644 --- a/source/frontend/src/features/properties/list/PropertyListView.tsx +++ b/source/frontend/src/features/properties/list/PropertyListView.tsx @@ -1,8 +1,7 @@ import './PropertyListView.scss'; -import { Form, Formik, FormikProps } from 'formik'; +import { AxiosResponse } from 'axios'; import isEmpty from 'lodash/isEmpty'; -import noop from 'lodash/noop'; import Multiselect from 'multiselect-react-dropdown'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import { Row } from 'react-bootstrap'; @@ -17,10 +16,12 @@ import { Table } from '@/components/Table'; import { TableSort } from '@/components/Table/TableSort'; import * as API from '@/constants/API'; import { MultiSelectOption } from '@/features/acquisition/list/interfaces'; +import { useApiHistoricalNumbers } from '@/hooks/pims-api/useApiHistoricalNumbers'; import { useApiProperties } from '@/hooks/pims-api/useApiProperties'; import { useProperties } from '@/hooks/repositories/useProperties'; import useLookupCodeHelpers from '@/hooks/useLookupCodeHelpers'; import useDeepCompareEffect from '@/hooks/util/useDeepCompareEffect'; +import { ApiGen_Concepts_HistoricalFileNumber } from '@/models/api/generated/ApiGen_Concepts_HistoricalFileNumber'; import { ApiGen_Concepts_PropertyView } from '@/models/api/generated/ApiGen_Concepts_PropertyView'; import { generateMultiSortCriteria } from '@/utils'; import { toFilteredApiPaginateParams } from '@/utils/CommonFunctions'; @@ -38,18 +39,20 @@ export const ownershipFilterOptions: MultiSelectOption[] = [ { id: 'isRetired', text: 'Retired' }, ]; +export interface IPropertyResultRecord { + id: number; + property: ApiGen_Concepts_PropertyView; + fileNumbers: ApiGen_Concepts_HistoricalFileNumber[]; +} + const PropertyListView: React.FC> = () => { const { getByType } = useLookupCodeHelpers(); - const tableFormRef = useRef< - FormikProps<{ properties: ApiGen_Concepts_PropertyView[] }> | undefined - >(); - const municipalities = useMemo(() => getByType(API.ADMINISTRATIVE_AREA_TYPES), [getByType]); const columns = useMemo(() => columnDefinitions({ municipalities }), [municipalities]); // We'll start our table without any data - const [data, setData] = useState(); + const [pageResultRecords, setPageResultRecords] = useState(); // Filtering and pagination state const [filter, setFilter] = useState(defaultPropertyFilter); @@ -70,6 +73,7 @@ const PropertyListView: React.FC> = () => { }, [setFilter, setPageIndex], ); + // This will get called when the table needs new data const onRequestData = useCallback( ({ pageIndex }: { pageIndex: number }) => { @@ -79,6 +83,7 @@ const PropertyListView: React.FC> = () => { ); const { getPropertiesViewPagedApi } = useApiProperties(); + const { getByPropertyId } = useApiHistoricalNumbers(); const fetchData = useCallback( async ({ @@ -94,7 +99,8 @@ const PropertyListView: React.FC> = () => { }) => { // Give this fetch an ID const fetchId = ++fetchIdRef.current; - setData(undefined); + setPageResultRecords(undefined); + // Call API with appropriate search parameters const queryParams = toFilteredApiPaginateParams( pageIndex, @@ -104,16 +110,40 @@ const PropertyListView: React.FC> = () => { ); const { data } = await getPropertiesViewPagedApi(queryParams); + // Fetch historical file numbers + const propertiesIds = data.items.map(x => x.id); + const findHistoricalFileNumbersCalls: Promise< + AxiosResponse + >[] = []; + + propertiesIds.forEach(async (id: number) => { + findHistoricalFileNumbersCalls.push(getByPropertyId(id)); + }); + + const historicNumberResponses = await Promise.all(findHistoricalFileNumbersCalls); + const historicalFileNumbers = historicNumberResponses.reduce( + (accumulator, value) => accumulator.concat(value.data), + [], + ); + + const resultRecords = data.items.map(x => { + return { + id: x.id, + property: x, + fileNumbers: historicalFileNumbers.filter(y => x.id === y.propertyId), + } as IPropertyResultRecord; + }); + setTotalItems(data.total); // The server could send back total page count. // For now we'll just calculate it. if (fetchId === fetchIdRef.current && data?.items) { - setData(data.items); + setPageResultRecords(resultRecords); setPageCount(Math.ceil(data.total / pageSize)); } }, - [setData, setPageCount, getPropertiesViewPagedApi], + [getPropertiesViewPagedApi, getByPropertyId], ); // Listen for changes in pagination and use the state to fetch our new data @@ -213,11 +243,11 @@ const PropertyListView: React.FC> = () => {
- + name="propertiesTable" columns={columns} - data={data || []} - loading={data === undefined} + data={pageResultRecords || []} + loading={pageResultRecords === undefined} externalSort={{ sort: sort, setSort: setSort }} totalItems={totalItems} pageIndex={pageIndex} @@ -229,15 +259,6 @@ const PropertyListView: React.FC> = () => { setFilter({ ...filter, ...values }); }} onPageSizeChange={newSize => setPageSize(newSize)} - renderBodyComponent={({ body }) => ( - -
{body}
-
- )} />
diff --git a/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap b/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap index b0b89220a4..3d3f70bdb8 100644 --- a/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap +++ b/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap @@ -378,6 +378,13 @@ exports[`Property list view > matches snapshot 1`] = ` > Plan # + @@ -733,13 +740,13 @@ exports[`Property list view > matches snapshot 1`] = `
matches snapshot 1`] = ` class="th" colspan="1" role="columnheader" - style="box-sizing: border-box; flex: 40 0 auto; min-width: 30px; width: 40px; justify-content: right; text-align: right; flex-wrap: wrap; align-items: center; display: flex;" + style="box-sizing: border-box; flex: 30 0 auto; min-width: 30px; width: 30px; justify-content: right; text-align: right; flex-wrap: wrap; align-items: center; display: flex;" >
matches snapshot 1`] = ` PIN
+
+
+ Historical File # +
+
[] => [ +export const columns = ({ municipalities }: Props): ColumnWithProps[] => [ { Header: 'PID', - accessor: 'pid', align: 'right', - width: 40, - Cell: (props: CellProps) => { + width: 30, + Cell: (props: CellProps) => { return ( <> - {props.row.original.pid} + {props.row.original.property.pid} - {props.row.original.isRetired ? ( + {props.row.original.property.isRetired ? ( p.property.pin, align: 'right', - width: 40, + width: 25, + }, + { + Header: 'Historical File #', + align: 'left', + clickable: false, + sortable: false, + width: 30, + maxWidth: 50, + Cell: (props: CellProps) => { + // File numbers types to display + const numberTypes: string[] = [ + ApiGen_CodeTypes_HistoricalFileNumberTypes.LISNO.toString(), + ApiGen_CodeTypes_HistoricalFileNumberTypes.PSNO.toString(), + ApiGen_CodeTypes_HistoricalFileNumberTypes.OTHER.toString(), + ]; + + const filteredNumberTypes = props.row.original.fileNumbers.filter(x => + numberTypes.includes(x.historicalFileNumberTypeCode.id), + ); + + const groupByType = groupBy(filteredNumberTypes, x => x.historicalFileNumberTypeCode.id); + + let lisNumbers = ''; + let psNumbers = ''; + let otherNumbers = ''; + if (groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.LISNO.toString()]?.length) { + lisNumbers = groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.LISNO.toString()] + .map(x => x.historicalFileNumber) + .join(', '); + } + + if (groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.PSNO.toString()]?.length) { + psNumbers = groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.PSNO.toString()] + .map(x => x.historicalFileNumber) + .join(', '); + } + + if (groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.OTHER.toString()]?.length) { + otherNumbers = groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.OTHER.toString()] + .map(x => x.historicalFileNumber) + .join(', '); + } + + return ( + + {lisNumbers ? ( + + ) : null} + {psNumbers ? ( + + ) : null} + {otherNumbers ? ( + + ) : null} + + ); + }, }, { Header: 'Civic Address', accessor: p => formatSplitAddress( - p.streetAddress1, - p.streetAddress2, - p.streetAddress3, - p.municipalityName, - p.provinceName, - p.postalCode, + p.property.streetAddress1, + p.property.streetAddress2, + p.property.streetAddress3, + p.property.municipalityName, + p.property.provinceName, + p.property.postalCode, ), align: 'left', minWidth: 100, @@ -74,7 +140,7 @@ export const columns = ({ }, { Header: 'Location', - accessor: p => p.municipalityName, + accessor: p => p.property.municipalityName, align: 'left', width: 50, sortable: true, @@ -92,9 +158,9 @@ export const columns = ({ }, { Header: 'Lot Size (in\u00A0ha)', - Cell: (props: CellProps) => { - const landArea = props.row.original.landArea ?? 0; - const landUnitCode = props.row.original.propertyAreaUnitTypeCode; + Cell: (props: CellProps) => { + const landArea = props.row.original.property.landArea ?? 0; + const landUnitCode = props.row.original.property.propertyAreaUnitTypeCode; const hectars = convertArea( landArea, landUnitCode ?? AreaUnitTypes.SquareMeters, @@ -121,10 +187,10 @@ export const columns = ({ align: 'left', sortable: true, width: 20, - Cell: (cellProps: CellProps) => { + Cell: (cellProps: CellProps) => { const { hasClaim } = useKeycloakWrapper(); - const property = cellProps.row.original; + const property = cellProps.row.original.property; const ownershipText = property.isOwned ? 'Core Inventory' : property.hasActiveResearchFile || property.hasActiveResearchFile @@ -151,9 +217,9 @@ export const columns = ({ align: 'right', sortable: false, width: 20, - Cell: (cellProps: CellProps) => { + Cell: (cellProps: CellProps) => { const { hasClaim } = useKeycloakWrapper(); - const property = cellProps.row.original; + const property = cellProps.row.original.property; return ( @@ -190,3 +256,13 @@ const StyledDiv = styled(InlineFlexDiv)` justify-content: space-around; width: 100%; `; + +const FileNumbersDiv = styled('div')` + label { + display: inline-block; + + span { + font-weight: bold; + } + } +`; From 6d2d46d48f1873ac17eff6494b20fc36abea2520 Mon Sep 17 00:00:00 2001 From: Herrera Date: Wed, 22 May 2024 09:35:43 -0700 Subject: [PATCH 2/5] - test update --- .../frontend/src/features/properties/list/PropertyListView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/frontend/src/features/properties/list/PropertyListView.tsx b/source/frontend/src/features/properties/list/PropertyListView.tsx index 35c0ba855a..f0760614bb 100644 --- a/source/frontend/src/features/properties/list/PropertyListView.tsx +++ b/source/frontend/src/features/properties/list/PropertyListView.tsx @@ -111,7 +111,7 @@ const PropertyListView: React.FC> = () => { const { data } = await getPropertiesViewPagedApi(queryParams); // Fetch historical file numbers - const propertiesIds = data.items.map(x => x.id); + const propertiesIds = data?.items.map(x => x.id) || []; const findHistoricalFileNumbersCalls: Promise< AxiosResponse >[] = []; From da2e0d1d7919b2fadc2bcb3bedd128930a0afdf9 Mon Sep 17 00:00:00 2001 From: Herrera Date: Wed, 22 May 2024 11:27:23 -0700 Subject: [PATCH 3/5] - test update --- .../frontend/src/features/properties/list/PropertyListView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/frontend/src/features/properties/list/PropertyListView.tsx b/source/frontend/src/features/properties/list/PropertyListView.tsx index f0760614bb..ee32363027 100644 --- a/source/frontend/src/features/properties/list/PropertyListView.tsx +++ b/source/frontend/src/features/properties/list/PropertyListView.tsx @@ -111,7 +111,7 @@ const PropertyListView: React.FC> = () => { const { data } = await getPropertiesViewPagedApi(queryParams); // Fetch historical file numbers - const propertiesIds = data?.items.map(x => x.id) || []; + const propertiesIds = data?.items?.map(x => x.id) || []; const findHistoricalFileNumbersCalls: Promise< AxiosResponse >[] = []; From 7226a18f3651e7efabbc471d5ba8db550065d417 Mon Sep 17 00:00:00 2001 From: Herrera Date: Wed, 22 May 2024 11:51:27 -0700 Subject: [PATCH 4/5] - type check --- .../features/properties/list/PropertyListView.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source/frontend/src/features/properties/list/PropertyListView.tsx b/source/frontend/src/features/properties/list/PropertyListView.tsx index ee32363027..e0141d9391 100644 --- a/source/frontend/src/features/properties/list/PropertyListView.tsx +++ b/source/frontend/src/features/properties/list/PropertyListView.tsx @@ -126,13 +126,14 @@ const PropertyListView: React.FC> = () => { [], ); - const resultRecords = data.items.map(x => { - return { - id: x.id, - property: x, - fileNumbers: historicalFileNumbers.filter(y => x.id === y.propertyId), - } as IPropertyResultRecord; - }); + const resultRecords = + data?.items?.map(x => { + return { + id: x.id, + property: x, + fileNumbers: historicalFileNumbers.filter(y => x.id === y.propertyId), + } as IPropertyResultRecord; + }) || []; setTotalItems(data.total); From 3024fc24bda9f8edd6ba12cda1ee0189075a38b7 Mon Sep 17 00:00:00 2001 From: Herrera Date: Fri, 24 May 2024 12:11:04 -0700 Subject: [PATCH 5/5] - updates --- .../acquisition/common/AcquisitionHeader.tsx | 6 +- .../disposition/common/DispositionHeader.tsx | 6 +- .../mapSideBar/lease/common/LeaseHeader.tsx | 6 +- .../property/MotiInventoryHeader.tsx | 1 + .../research/common/ResearchHeader.tsx | 6 +- .../header/HistoricalNumberContainer.tsx | 4 +- .../header/HistoricalNumberSectionView.tsx | 35 ++++- .../properties/list/PropertyListView.test.tsx | 32 ++--- .../properties/list/PropertyListView.tsx | 45 +------ .../PropertyListView.test.tsx.snap | 4 +- .../src/features/properties/list/columns.tsx | 123 +++++------------- 11 files changed, 111 insertions(+), 157 deletions(-) diff --git a/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionHeader.tsx b/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionHeader.tsx index 8436842a59..93e63c3584 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionHeader.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionHeader.tsx @@ -49,7 +49,11 @@ export const AcquisitionHeader: React.FunctionComponent< )} - + diff --git a/source/frontend/src/features/mapSideBar/disposition/common/DispositionHeader.tsx b/source/frontend/src/features/mapSideBar/disposition/common/DispositionHeader.tsx index e95d60cae8..d9dae97ded 100644 --- a/source/frontend/src/features/mapSideBar/disposition/common/DispositionHeader.tsx +++ b/source/frontend/src/features/mapSideBar/disposition/common/DispositionHeader.tsx @@ -32,7 +32,11 @@ export const DispositionHeader: React.FunctionComponent< D-{dispositionFile?.fileNumber} - + diff --git a/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx b/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx index 1b1f0e7ae0..4396691fc7 100644 --- a/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx +++ b/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx @@ -72,7 +72,11 @@ export const LeaseHeader: React.FC = ({ lease, lastUpdatedBy )} - + diff --git a/source/frontend/src/features/mapSideBar/property/MotiInventoryHeader.tsx b/source/frontend/src/features/mapSideBar/property/MotiInventoryHeader.tsx index 64e65360f9..a60f91110d 100644 --- a/source/frontend/src/features/mapSideBar/property/MotiInventoryHeader.tsx +++ b/source/frontend/src/features/mapSideBar/property/MotiInventoryHeader.tsx @@ -69,6 +69,7 @@ export const MotiInventoryHeader: React.FunctionComponent )} diff --git a/source/frontend/src/features/mapSideBar/research/common/ResearchHeader.tsx b/source/frontend/src/features/mapSideBar/research/common/ResearchHeader.tsx index bbfedf82e6..c6e1695475 100644 --- a/source/frontend/src/features/mapSideBar/research/common/ResearchHeader.tsx +++ b/source/frontend/src/features/mapSideBar/research/common/ResearchHeader.tsx @@ -64,7 +64,11 @@ const ResearchHeader: React.FunctionComponent< {districts} - + diff --git a/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberContainer.tsx b/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberContainer.tsx index 451f9e2830..33ea68bcfe 100644 --- a/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberContainer.tsx +++ b/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberContainer.tsx @@ -7,10 +7,12 @@ import { IHistoricalNumbersViewProps } from './HistoricalNumberSectionView'; export interface IHistoricalNumbersContainerProps { propertyIds: number[]; + displayValuesOnly: boolean; View: React.FunctionComponent; } const HistoricalNumbersContainer: React.FC = ({ propertyIds, + displayValuesOnly, View, }) => { const [historicalNumbers, setHistoricalNumbers] = useState< @@ -34,6 +36,6 @@ const HistoricalNumbersContainer: React.FC = ( }); }, [propertyIds, getHistoricalExecute]); - return ; + return ; }; export default HistoricalNumbersContainer; diff --git a/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberSectionView.tsx b/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberSectionView.tsx index 5255475a68..b2adcd22f6 100644 --- a/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberSectionView.tsx +++ b/source/frontend/src/features/mapSideBar/shared/header/HistoricalNumberSectionView.tsx @@ -9,6 +9,7 @@ import { ApiGen_Concepts_HistoricalFileNumber } from '@/models/api/generated/Api import { exists } from '@/utils'; export interface IHistoricalNumbersViewProps { + valuesOnly: boolean; historicalNumbers: ApiGen_Concepts_HistoricalFileNumber[]; } @@ -19,6 +20,7 @@ interface HistoricalGroup { const HistoricalNumberFieldView: React.FC = ({ historicalNumbers, + valuesOnly, }) => { const uniqueNumbers = useMemo(() => { const flatNumberArray: Dictionary = historicalNumbers @@ -40,6 +42,33 @@ const HistoricalNumberFieldView: React.FC = ({ return Object.values(flatNumberArray).sort(p => p.historicalType.displayOrder); }, [historicalNumbers]); + if (valuesOnly) { + return ( + + items={uniqueNumbers} + keyFunction={(p, index: number) => `historical-number-${p.historicalType.id}-${index}`} + renderFunction={p => ( + <> + + {p.historicalType.description}: + + + {Object.values(p.historicalValues).map((historicalValue, index) => { + return ( + + {historicalValue.historicalFileNumber} + {index + 1 < Object.values(p.historicalValues).length && , } + + ); + })} + + )} + delimiter={'; '} + maxCollapsedLength={2} + /> + ); + } + return ( <> @@ -48,16 +77,16 @@ const HistoricalNumberFieldView: React.FC = ({ keyFunction={(p, index: number) => `historical-number-${p.historicalType.id}-${index}`} renderFunction={p => ( <> - + {p.historicalType.description}: {Object.values(p.historicalValues).map((historicalValue, index) => { return ( - <> + {historicalValue.historicalFileNumber} {index + 1 < Object.values(p.historicalValues).length && , } - + ); })} diff --git a/source/frontend/src/features/properties/list/PropertyListView.test.tsx b/source/frontend/src/features/properties/list/PropertyListView.test.tsx index b0a7359ad8..29f61d6a13 100644 --- a/source/frontend/src/features/properties/list/PropertyListView.test.tsx +++ b/source/frontend/src/features/properties/list/PropertyListView.test.tsx @@ -131,6 +131,7 @@ describe('Property list view', () => { mockAxios.onAny().reply(200, {}); mockApiGetPropertiesPagedApi.mockClear(); + mockApiGetHistoricalFileNumbersApi.mockClear(); }); afterEach(() => { @@ -211,14 +212,11 @@ describe('Property list view', () => { it('allows property ownership to be selected', async () => { setupMockApi([mockApiPropertyView()]); + const { component: { container }, - findSpinner, } = setup({}); - // wait for table to finish loading - await waitFor(async () => expect(findSpinner()).not.toBeInTheDocument()); - const optionSelected = ownershipFilterOptions.find( o => o.id === 'isDisposed', ) as MultiSelectOption; @@ -231,20 +229,18 @@ describe('Property list view', () => { // select an option from the drop-down await focusOptionMultiselect(container, optionSelected, ownershipFilterOptions); - await waitFor(() => { - expect(mockApiGetPropertiesPagedApi).toHaveBeenCalledWith({ - address: '', - latitude: '', - longitude: '', - ownership: 'isCoreInventory,isPropertyOfInterest,isOtherInterest,isDisposed', - page: 1, - pinOrPid: '', - planNumber: '', - historical: '', - quantity: 10, - searchBy: 'pinOrPid', - sort: undefined, - }); + expect(mockApiGetPropertiesPagedApi).toHaveBeenCalledWith({ + address: '', + latitude: '', + longitude: '', + ownership: 'isCoreInventory,isPropertyOfInterest,isOtherInterest,isDisposed', + page: 1, + pinOrPid: '', + planNumber: '', + historical: '', + quantity: 10, + searchBy: 'pinOrPid', + sort: undefined, }); }); diff --git a/source/frontend/src/features/properties/list/PropertyListView.tsx b/source/frontend/src/features/properties/list/PropertyListView.tsx index e0141d9391..9def17c021 100644 --- a/source/frontend/src/features/properties/list/PropertyListView.tsx +++ b/source/frontend/src/features/properties/list/PropertyListView.tsx @@ -1,6 +1,5 @@ import './PropertyListView.scss'; -import { AxiosResponse } from 'axios'; import isEmpty from 'lodash/isEmpty'; import Multiselect from 'multiselect-react-dropdown'; import React, { useCallback, useMemo, useRef, useState } from 'react'; @@ -16,12 +15,10 @@ import { Table } from '@/components/Table'; import { TableSort } from '@/components/Table/TableSort'; import * as API from '@/constants/API'; import { MultiSelectOption } from '@/features/acquisition/list/interfaces'; -import { useApiHistoricalNumbers } from '@/hooks/pims-api/useApiHistoricalNumbers'; import { useApiProperties } from '@/hooks/pims-api/useApiProperties'; import { useProperties } from '@/hooks/repositories/useProperties'; import useLookupCodeHelpers from '@/hooks/useLookupCodeHelpers'; import useDeepCompareEffect from '@/hooks/util/useDeepCompareEffect'; -import { ApiGen_Concepts_HistoricalFileNumber } from '@/models/api/generated/ApiGen_Concepts_HistoricalFileNumber'; import { ApiGen_Concepts_PropertyView } from '@/models/api/generated/ApiGen_Concepts_PropertyView'; import { generateMultiSortCriteria } from '@/utils'; import { toFilteredApiPaginateParams } from '@/utils/CommonFunctions'; @@ -39,12 +36,6 @@ export const ownershipFilterOptions: MultiSelectOption[] = [ { id: 'isRetired', text: 'Retired' }, ]; -export interface IPropertyResultRecord { - id: number; - property: ApiGen_Concepts_PropertyView; - fileNumbers: ApiGen_Concepts_HistoricalFileNumber[]; -} - const PropertyListView: React.FC> = () => { const { getByType } = useLookupCodeHelpers(); const municipalities = useMemo(() => getByType(API.ADMINISTRATIVE_AREA_TYPES), [getByType]); @@ -52,7 +43,9 @@ const PropertyListView: React.FC> = () => { const columns = useMemo(() => columnDefinitions({ municipalities }), [municipalities]); // We'll start our table without any data - const [pageResultRecords, setPageResultRecords] = useState(); + const [pageResultRecords, setPageResultRecords] = useState< + ApiGen_Concepts_PropertyView[] | undefined + >(); // Filtering and pagination state const [filter, setFilter] = useState(defaultPropertyFilter); @@ -83,7 +76,6 @@ const PropertyListView: React.FC> = () => { ); const { getPropertiesViewPagedApi } = useApiProperties(); - const { getByPropertyId } = useApiHistoricalNumbers(); const fetchData = useCallback( async ({ @@ -110,41 +102,16 @@ const PropertyListView: React.FC> = () => { ); const { data } = await getPropertiesViewPagedApi(queryParams); - // Fetch historical file numbers - const propertiesIds = data?.items?.map(x => x.id) || []; - const findHistoricalFileNumbersCalls: Promise< - AxiosResponse - >[] = []; - - propertiesIds.forEach(async (id: number) => { - findHistoricalFileNumbersCalls.push(getByPropertyId(id)); - }); - - const historicNumberResponses = await Promise.all(findHistoricalFileNumbersCalls); - const historicalFileNumbers = historicNumberResponses.reduce( - (accumulator, value) => accumulator.concat(value.data), - [], - ); - - const resultRecords = - data?.items?.map(x => { - return { - id: x.id, - property: x, - fileNumbers: historicalFileNumbers.filter(y => x.id === y.propertyId), - } as IPropertyResultRecord; - }) || []; - setTotalItems(data.total); // The server could send back total page count. // For now we'll just calculate it. if (fetchId === fetchIdRef.current && data?.items) { - setPageResultRecords(resultRecords); + setPageResultRecords(data.items); setPageCount(Math.ceil(data.total / pageSize)); } }, - [getPropertiesViewPagedApi, getByPropertyId], + [setPageResultRecords, setPageCount, getPropertiesViewPagedApi], ); // Listen for changes in pagination and use the state to fetch our new data @@ -244,7 +211,7 @@ const PropertyListView: React.FC> = () => {
- + name="propertiesTable" columns={columns} data={pageResultRecords || []} diff --git a/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap b/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap index 3d3f70bdb8..f7509c2e62 100644 --- a/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap +++ b/source/frontend/src/features/properties/list/__snapshots__/PropertyListView.test.tsx.snap @@ -770,7 +770,7 @@ exports[`Property list view > matches snapshot 1`] = ` class="th" colspan="1" role="columnheader" - style="box-sizing: border-box; flex: 30 0 auto; min-width: 30px; width: 30px; justify-content: left; text-align: left; flex-wrap: wrap; align-items: center; display: flex;" + style="box-sizing: border-box; flex: 40 0 auto; min-width: 30px; width: 40px; justify-content: left; text-align: left; flex-wrap: wrap; align-items: center; display: flex;" >
matches snapshot 1`] = ` class="th" colspan="1" role="columnheader" - style="box-sizing: border-box; flex: 150 0 auto; min-width: 100px; width: 150px; justify-content: left; text-align: left; flex-wrap: wrap; align-items: center; display: flex;" + style="box-sizing: border-box; flex: 125 0 auto; min-width: 100px; width: 125px; justify-content: left; text-align: left; flex-wrap: wrap; align-items: center; display: flex;" >
[] => [ +export const columns = ({ + municipalities, +}: Props): ColumnWithProps[] => [ { Header: 'PID', align: 'right', width: 30, - Cell: (props: CellProps) => { + Cell: (props: CellProps) => { return ( <> - {props.row.original.property.pid} + {props.row.original.pid} - {props.row.original.property.isRetired ? ( + {props.row.original.isRetired ? ( p.property.pin, + accessor: p => p.pin, align: 'right', width: 25, }, @@ -62,64 +63,16 @@ export const columns = ({ municipalities }: Props): ColumnWithProps) => { - // File numbers types to display - const numberTypes: string[] = [ - ApiGen_CodeTypes_HistoricalFileNumberTypes.LISNO.toString(), - ApiGen_CodeTypes_HistoricalFileNumberTypes.PSNO.toString(), - ApiGen_CodeTypes_HistoricalFileNumberTypes.OTHER.toString(), - ]; - - const filteredNumberTypes = props.row.original.fileNumbers.filter(x => - numberTypes.includes(x.historicalFileNumberTypeCode.id), - ); - - const groupByType = groupBy(filteredNumberTypes, x => x.historicalFileNumberTypeCode.id); - - let lisNumbers = ''; - let psNumbers = ''; - let otherNumbers = ''; - if (groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.LISNO.toString()]?.length) { - lisNumbers = groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.LISNO.toString()] - .map(x => x.historicalFileNumber) - .join(', '); - } - - if (groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.PSNO.toString()]?.length) { - psNumbers = groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.PSNO.toString()] - .map(x => x.historicalFileNumber) - .join(', '); - } - - if (groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.OTHER.toString()]?.length) { - otherNumbers = groupByType[ApiGen_CodeTypes_HistoricalFileNumberTypes.OTHER.toString()] - .map(x => x.historicalFileNumber) - .join(', '); - } - + Cell: (props: CellProps) => { + const propertyArrayId = [props.row.original.id]; return ( - - {lisNumbers ? ( - - ) : null} - {psNumbers ? ( - - ) : null} - {otherNumbers ? ( - - ) : null} - + ); }, }, @@ -127,20 +80,20 @@ export const columns = ({ municipalities }: Props): ColumnWithProps formatSplitAddress( - p.property.streetAddress1, - p.property.streetAddress2, - p.property.streetAddress3, - p.property.municipalityName, - p.property.provinceName, - p.property.postalCode, + p.streetAddress1, + p.streetAddress2, + p.streetAddress3, + p.municipalityName, + p.provinceName, + p.postalCode, ), align: 'left', minWidth: 100, - width: 150, + width: 125, }, { Header: 'Location', - accessor: p => p.property.municipalityName, + accessor: p => p.municipalityName, align: 'left', width: 50, sortable: true, @@ -158,9 +111,9 @@ export const columns = ({ municipalities }: Props): ColumnWithProps) => { - const landArea = props.row.original.property.landArea ?? 0; - const landUnitCode = props.row.original.property.propertyAreaUnitTypeCode; + Cell: (props: CellProps) => { + const landArea = props.row.original.landArea ?? 0; + const landUnitCode = props.row.original.propertyAreaUnitTypeCode; const hectars = convertArea( landArea, landUnitCode ?? AreaUnitTypes.SquareMeters, @@ -187,10 +140,10 @@ export const columns = ({ municipalities }: Props): ColumnWithProps) => { + Cell: (cellProps: CellProps) => { const { hasClaim } = useKeycloakWrapper(); - const property = cellProps.row.original.property; + const property = cellProps.row.original; const ownershipText = property.isOwned ? 'Core Inventory' : property.hasActiveResearchFile || property.hasActiveResearchFile @@ -217,9 +170,9 @@ export const columns = ({ municipalities }: Props): ColumnWithProps) => { + Cell: (cellProps: CellProps) => { const { hasClaim } = useKeycloakWrapper(); - const property = cellProps.row.original.property; + const property = cellProps.row.original; return ( @@ -256,13 +209,3 @@ const StyledDiv = styled(InlineFlexDiv)` justify-content: space-around; width: 100%; `; - -const FileNumbersDiv = styled('div')` - label { - display: inline-block; - - span { - font-weight: bold; - } - } -`;