diff --git a/src/app/map/[id]/components/Markers/DataSourceMarkers.tsx b/src/app/map/[id]/components/Markers/DataSourceMarkers.tsx index ccc9544c..2518ce78 100644 --- a/src/app/map/[id]/components/Markers/DataSourceMarkers.tsx +++ b/src/app/map/[id]/components/Markers/DataSourceMarkers.tsx @@ -16,6 +16,7 @@ export function DataSourceMarkers({ dataSourceMarkers, isMembers, mapConfig, + hideFilteredMarkers = false, }: { dataSourceMarkers: { dataSourceId: string; markers: MarkerFeature[] }; isMembers: boolean; @@ -23,6 +24,7 @@ export function DataSourceMarkers({ markerDisplayModes?: Record; markerColors?: Record; }; + hideFilteredMarkers?: boolean; }) { const { filteredRecords, publicFilters } = useContext( PublicFiltersContext, @@ -40,27 +42,44 @@ export function DataSourceMarkers({ MarkerDisplayMode.Clusters; const safeMarkers = useMemo(() => { - if (Object.keys(publicFilters).length === 0) { + const hasClientFilters = Object.keys(publicFilters).length > 0; + + let features = dataSourceMarkers.markers; + + // When hideFilteredMarkers is true, remove server-side unmatched markers + if (hideFilteredMarkers) { + features = features.filter((f) => f.properties.matched !== false); + } + + if (!hasClientFilters) { return { type: "FeatureCollection", - features: dataSourceMarkers.markers, + features, }; } const recordIds = (filteredRecords || []) .map((r: { id: string | number }) => r.id) .filter(Boolean); + + const mappedFeatures = features.map((f) => ({ + ...f, + properties: { + ...f.properties, + [MARKER_CLIENT_EXCLUDED_KEY]: !recordIds.includes(f.properties.id), + }, + })); + return { type: "FeatureCollection", - features: dataSourceMarkers.markers.map((f) => ({ - ...f, - properties: { - ...f.properties, - [MARKER_CLIENT_EXCLUDED_KEY]: !recordIds.includes(f.properties.id), - }, - })), + features: mappedFeatures, }; - }, [dataSourceMarkers.markers, filteredRecords, publicFilters]); + }, [ + dataSourceMarkers.markers, + filteredRecords, + publicFilters, + hideFilteredMarkers, + ]); const sourceId = `${dataSourceMarkers.dataSourceId}-markers`; const publicMapColor = diff --git a/src/app/map/[id]/components/Markers/index.tsx b/src/app/map/[id]/components/Markers/index.tsx index 1528ec2f..7e0d02f4 100644 --- a/src/app/map/[id]/components/Markers/index.tsx +++ b/src/app/map/[id]/components/Markers/index.tsx @@ -36,6 +36,7 @@ export default function Markers() { dataSourceMarkers={memberMarkers} isMembers mapConfig={mapConfig} + hideFilteredMarkers={viewConfig.hideFilteredMarkers} /> )} {otherMarkers.map((markers) => { @@ -52,6 +53,7 @@ export default function Markers() { dataSourceMarkers={markers} isMembers={false} mapConfig={mapConfig} + hideFilteredMarkers={viewConfig.hideFilteredMarkers} /> ); })} diff --git a/src/app/map/[id]/components/table/MapTableFilter.tsx b/src/app/map/[id]/components/table/MapTableFilter.tsx index 4dea4ae0..0ded6e25 100644 --- a/src/app/map/[id]/components/table/MapTableFilter.tsx +++ b/src/app/map/[id]/components/table/MapTableFilter.tsx @@ -3,6 +3,7 @@ import { ListFilter, XIcon } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useDataSources } from "@/app/map/[id]/hooks/useDataSources"; import { useMapConfig } from "@/app/map/[id]/hooks/useMapConfig"; +import { useMapViews } from "@/app/map/[id]/hooks/useMapViews"; import { usePlacedMarkersQuery } from "@/app/map/[id]/hooks/usePlacedMarkers"; import { useTable } from "@/app/map/[id]/hooks/useTable"; import { useTurfsQuery } from "@/app/map/[id]/hooks/useTurfsQuery"; @@ -19,6 +20,8 @@ import { CommandList, } from "@/shadcn/ui/command"; import { Input } from "@/shadcn/ui/input"; +import { Label } from "@/shadcn/ui/label"; +import { Switch } from "@/shadcn/ui/switch"; import { Toggle } from "@/shadcn/ui/toggle"; import { buildName } from "@/utils/dataRecord"; import { mapColors } from "../../styles"; @@ -52,6 +55,7 @@ export default function MapTableFilter({ function MultiFilter({ filter, setFilter: _setFilter }: TableFilterProps) { const { mapConfig } = useMapConfig(); + const { viewConfig, updateViewConfig } = useMapViews(); const { data: turfs = [] } = useTurfsQuery(); const { data: placedMarkers = [] } = usePlacedMarkersQuery(); const { getDataSourceById } = useDataSources(); @@ -215,6 +219,25 @@ function MultiFilter({ filter, setFilter: _setFilter }: TableFilterProps) { onOperatorChange={updateOperator} /> ) : null} + + {/* Show filtered markers toggle */} + {children.length > 0 ? ( +
+ + updateViewConfig({ hideFilteredMarkers: checked }) + } + /> + +
+ ) : null} diff --git a/src/app/map/[id]/utils/mapView.ts b/src/app/map/[id]/utils/mapView.ts index 7edede49..ea05dd92 100644 --- a/src/app/map/[id]/utils/mapView.ts +++ b/src/app/map/[id]/utils/mapView.ts @@ -20,5 +20,6 @@ export const createNewViewConfig = (): MapViewConfig => { calculationType: DEFAULT_CALCULATION_TYPE, colorScheme: ColorScheme.RedBlue, reverseColorScheme: false, + hideFilteredMarkers: true, }; }; diff --git a/src/server/models/MapView.ts b/src/server/models/MapView.ts index 89e55b06..5adf5c7b 100644 --- a/src/server/models/MapView.ts +++ b/src/server/models/MapView.ts @@ -143,6 +143,7 @@ export const mapViewConfigSchema = z.object({ .record(z.string(), z.array(steppedColorStepSchema)) .optional(), customColor: z.string().optional(), + hideFilteredMarkers: z.boolean().optional(), }); export type MapViewConfig = z.infer;