diff --git a/backend/src/v1/complaint/complaint.service.ts b/backend/src/v1/complaint/complaint.service.ts index 848ff9557..942547665 100644 --- a/backend/src/v1/complaint/complaint.service.ts +++ b/backend/src/v1/complaint/complaint.service.ts @@ -177,6 +177,7 @@ export class ComplaintService { private readonly _generateMapQueryBuilder = ( type: COMPLAINT_TYPE, includeCosOrganization: boolean, + count: boolean, ): SelectQueryBuilder => { let builder: SelectQueryBuilder; @@ -185,14 +186,12 @@ export class ComplaintService { builder = this._allegationComplaintRepository .createQueryBuilder("allegation") .leftJoin("allegation.complaint_identifier", "complaint") - .select(["complaint.complaint_identifier", "complaint.location_geometry_point"]) .leftJoin("allegation.violation_code", "violation_code"); break; case "GIR": builder = this._girComplaintRepository .createQueryBuilder("general") .leftJoin("general.complaint_identifier", "complaint") - .select(["complaint.complaint_identifier", "complaint.location_geometry_point"]) .leftJoin("general.gir_type_code", "gir"); break; case "HWCR": @@ -200,13 +199,23 @@ export class ComplaintService { builder = this._wildlifeComplaintRepository .createQueryBuilder("wildlife") .leftJoin("wildlife.complaint_identifier", "complaint") - .select(["complaint.complaint_identifier", "complaint.location_geometry_point"]) .leftJoin("wildlife.species_code", "species_code") .leftJoin("wildlife.hwcr_complaint_nature_code", "complaint_nature_code") .leftJoin("wildlife.attractant_hwcr_xref", "attractants", "attractants.active_ind = true") .leftJoin("attractants.attractant_code", "attractant_code"); break; } + + if (count) { + builder.select("COUNT(DISTINCT complaint.complaint_identifier)", "count"); + } else { + builder + .select("complaint.complaint_identifier", "complaint_identifier") + .distinctOn(["complaint.complaint_identifier"]) + .groupBy("complaint.complaint_identifier") + .addSelect("complaint.location_geometry_point", "location_geometry_point"); + } + builder .leftJoin("complaint.complaint_status_code", "complaint_status") .leftJoin("complaint.reported_by_code", "reported_by") @@ -1113,6 +1122,7 @@ export class ComplaintService { model: ComplaintMapSearchClusteredParameters, hasCEEBRole: boolean, token?: string, + count: boolean = false, ): Promise> => { const { query, ...filters } = model; @@ -1120,7 +1130,7 @@ export class ComplaintService { //-- search for complaints // Only these options require the cos_geo_org_unit_flat_mvw view (cos_organization), which is very slow. const includeCosOrganization: boolean = Boolean(query || filters.community || filters.zone || filters.region); - let builder = this._generateMapQueryBuilder(complaintType, includeCosOrganization); + let builder = this._generateMapQueryBuilder(complaintType, includeCosOrganization, count); //-- apply filters if used if (Object.keys(filters).length !== 0) { @@ -1175,13 +1185,14 @@ export class ComplaintService { token?: string, ): Promise => { try { - const builder = await this._generateFilteredMapQueryBuilder(complaintType, model, hasCEEBRole, token); + const builder = await this._generateFilteredMapQueryBuilder(complaintType, model, hasCEEBRole, token, true); //-- filter for locations without coordinates builder.andWhere("ST_X(complaint.location_geometry_point) = 0"); builder.andWhere("ST_Y(complaint.location_geometry_point) = 0"); - return builder.getCount(); + const results = await builder.getRawOne(); + return results.count ? Number(results.count) : 0; } catch (error) { this.logger.error(error); } @@ -1217,9 +1228,9 @@ export class ComplaintService { type: "Feature", properties: { cluster: false, - id: item.complaint_complaint_identifier, + id: item.complaint_identifier, }, - geometry: item.complaint_location_geometry_point, + geometry: item.location_geometry_point, } as PointFeature; }); diff --git a/backend/test/mocks/mock-allegation-complaint-repository.ts b/backend/test/mocks/mock-allegation-complaint-repository.ts index b0e8a0584..51a6bbee3 100644 --- a/backend/test/mocks/mock-allegation-complaint-repository.ts +++ b/backend/test/mocks/mock-allegation-complaint-repository.ts @@ -468,6 +468,8 @@ const singleItem = { allegation_complaint_guid: "686ea89d-693b-4fdf-9266-226171e6dbd3", }; +const count = { count: "55" }; + export const MockAllegationComplaintRepository = () => ({ find: jest.fn().mockResolvedValue(manyItems), findOneBy: jest.fn().mockResolvedValue(singleItem), @@ -494,7 +496,10 @@ export const MockAllegationComplaintRepository = () => ({ andWhere: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue(manyItems), getRawMany: jest.fn().mockResolvedValue(manyItems), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockReturnThis(), getOne: jest.fn().mockResolvedValue(singleItem), + getRawOne: jest.fn().mockResolvedValue(count), getQuery: jest.fn(), select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), diff --git a/backend/test/mocks/mock-code-table-repositories.ts b/backend/test/mocks/mock-code-table-repositories.ts index d44d14a63..ccabe39f8 100644 --- a/backend/test/mocks/mock-code-table-repositories.ts +++ b/backend/test/mocks/mock-code-table-repositories.ts @@ -788,8 +788,10 @@ export const MockCosOrganizationUnitCodeTableRepository = () => ({ map: jest.fn().mockReturnThis(), createQueryBuilder: jest.fn(() => ({ select: jest.fn().mockReturnThis(), - distinctOn: jest.fn().mockReturnThis(), + addSelect: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockReturnThis(), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockReturnThis(), leftJoinAndSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), @@ -811,9 +813,11 @@ export const MockRegionCodeTableServiceRepository = () => ({ map: jest.fn().mockReturnThis(), createQueryBuilder: jest.fn(() => ({ select: jest.fn().mockReturnThis(), - distinctOn: jest.fn().mockReturnThis(), + addSelect: jest.fn().mockReturnThis(), distinct: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue(regions), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockResolvedValue(regions), leftJoinAndSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), getMany: jest.fn().mockReturnThis(), @@ -826,9 +830,11 @@ export const MockZoneCodeTableServiceRepository = () => ({ map: jest.fn().mockReturnThis(), createQueryBuilder: jest.fn(() => ({ select: jest.fn().mockReturnThis(), - distinctOn: jest.fn().mockReturnThis(), + addSelect: jest.fn().mockReturnThis(), distinct: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue(zones), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockResolvedValue(zones), leftJoinAndSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), getMany: jest.fn().mockReturnThis(), @@ -841,9 +847,11 @@ export const MockCommunityCodeTableServiceRepository = () => ({ map: jest.fn().mockReturnThis(), createQueryBuilder: jest.fn(() => ({ select: jest.fn().mockReturnThis(), - distinctOn: jest.fn().mockReturnThis(), + addSelect: jest.fn().mockReturnThis(), distinct: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue(communities), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockResolvedValue(communities), leftJoinAndSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), getMany: jest.fn().mockReturnThis(), diff --git a/backend/test/mocks/mock-complaints-repositories.ts b/backend/test/mocks/mock-complaints-repositories.ts index 9a9cca93f..025e7160a 100644 --- a/backend/test/mocks/mock-complaints-repositories.ts +++ b/backend/test/mocks/mock-complaints-repositories.ts @@ -697,6 +697,8 @@ const officers = [ }, ]; +const count = { count: "55" }; + export const MockComplaintsAgencyRepository = () => ({ getIdirFromRequest: jest.fn().mockReturnThis(), find: jest.fn().mockReturnThis(), @@ -733,8 +735,11 @@ export const MockComplaintsRepository = () => ({ execute: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue(complaints), getRawMany: jest.fn().mockResolvedValue(complaints), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockResolvedValue(complaints), getCount: jest.fn().mockResolvedValue(complaints.length), getOne: jest.fn().mockResolvedValue(complaints[3]), + getRawOne: jest.fn().mockResolvedValue(count), update: jest.fn().mockResolvedValue({ affected: 1 }), })), }); @@ -754,8 +759,11 @@ export const MockComplaintsRepositoryV2 = () => ({ execute: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue(complaints), getRawMany: jest.fn().mockResolvedValue(complaints), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockResolvedValue(complaints), getCount: jest.fn().mockResolvedValue(complaints.length), getOne: jest.fn().mockResolvedValue(complaints[3]), + getRawOne: jest.fn().mockResolvedValue(count), update: jest.fn().mockResolvedValue({ affected: 1 }), }; }), diff --git a/backend/test/mocks/mock-general-incident-complaint-repository.ts b/backend/test/mocks/mock-general-incident-complaint-repository.ts index d23e777e5..f6e67cfb9 100644 --- a/backend/test/mocks/mock-general-incident-complaint-repository.ts +++ b/backend/test/mocks/mock-general-incident-complaint-repository.ts @@ -468,6 +468,8 @@ const singleItem = { allegation_complaint_guid: "686ea89d-693b-4fdf-9266-226171e6dbd3", }; +const count = { count: "55" }; + export const MockGeneralIncidentComplaintRepository = () => ({ find: jest.fn().mockResolvedValue(manyItems), findOneBy: jest.fn().mockResolvedValue(singleItem), @@ -494,7 +496,10 @@ export const MockGeneralIncidentComplaintRepository = () => ({ andWhere: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue(manyItems), getRawMany: jest.fn().mockResolvedValue(manyItems), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockReturnThis(), getOne: jest.fn().mockResolvedValue(singleItem), + getRawOne: jest.fn().mockResolvedValue(count), getQuery: jest.fn(), select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), diff --git a/backend/test/mocks/mock-wildlife-conflict-complaint-repository.ts b/backend/test/mocks/mock-wildlife-conflict-complaint-repository.ts index 240d9b630..0beffefa3 100644 --- a/backend/test/mocks/mock-wildlife-conflict-complaint-repository.ts +++ b/backend/test/mocks/mock-wildlife-conflict-complaint-repository.ts @@ -344,6 +344,8 @@ const singleItem = { other_attractants_text: null, }; +const count = { count: "55" }; + export const MockWildlifeConflictComplaintRepository = () => ({ find: jest.fn().mockResolvedValue(manyItems), findOneBy: jest.fn().mockResolvedValue(singleItem), @@ -373,7 +375,10 @@ export const MockWildlifeConflictComplaintRepository = () => ({ andWhere: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue(manyItems), getRawMany: jest.fn().mockResolvedValue(manyItems), + distinctOn: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockReturnThis(), getOne: jest.fn().mockResolvedValue(singleItem), + getRawOne: jest.fn().mockResolvedValue(count), getQuery: jest.fn(), select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), diff --git a/frontend/cypress/e2e/complaints-on-map-view.cy.ts b/frontend/cypress/e2e/complaints-on-map-view.cy.ts index 69d3d997a..3d440eee8 100644 --- a/frontend/cypress/e2e/complaints-on-map-view.cy.ts +++ b/frontend/cypress/e2e/complaints-on-map-view.cy.ts @@ -112,9 +112,8 @@ describe("Complaints on map tests", () => { cy.get("div.leaflet-container").should("exist"); cy.get(".leaflet-popup").should("not.exist"); - cy.wait(1000); - cy.get(".leaflet-marker-icon").each(($marker, index) => { + cy.get(".map-marker").each(($marker, index) => { // Click the first marker (index 0) if (index === 0) { cy.wrap($marker).should("exist").click({ force: true }); @@ -122,6 +121,7 @@ describe("Complaints on map tests", () => { }); // wait for the popup to load + cy.wait(1000); cy.get(".leaflet-popup").should("exist"); diff --git a/frontend/src/app/common/api.ts b/frontend/src/app/common/api.ts index 265343357..76ec2f568 100644 --- a/frontend/src/app/common/api.ts +++ b/frontend/src/app/common/api.ts @@ -117,6 +117,9 @@ export const get = ( axios .get(url, config) .then((response: AxiosResponse) => { + if (!response) { + return reject(new Error("No response")); + } const { data, status } = response; if (status === STATUS_CODES.Unauthorized) { diff --git a/frontend/src/app/components/mapping/leaflet-map-with-server-side-clustering.tsx b/frontend/src/app/components/mapping/leaflet-map-with-server-side-clustering.tsx index 583f6a803..057daa989 100644 --- a/frontend/src/app/components/mapping/leaflet-map-with-server-side-clustering.tsx +++ b/frontend/src/app/components/mapping/leaflet-map-with-server-side-clustering.tsx @@ -53,13 +53,11 @@ const LeafletMapWithServerSideClustering: React.FC = ({ }; const handlePopupClose = () => { - dispatch(setComplaint(null)); setPopupOpen(false); - refreshMapData(); + dispatch(setComplaint(null)); }; useEffect(() => { - setPopupOpen(false); if (defaultClusterView) { if (clusters.length > 0) { // Calculate the bounds of all markers