From dcdfd155861bd4e507dc8af5ad13674c0f4c3b61 Mon Sep 17 00:00:00 2001 From: Platon Yasev Date: Thu, 2 Mar 2023 21:12:16 +0300 Subject: [PATCH] Work with location_id field as ID field; add region filter to map points --- src/Map/Points.jsx | 41 +++++++++++++++++++++--- src/Map/Popup.jsx | 13 +++----- src/modules/Sidebar/Filters.jsx | 12 +++---- src/modules/Sidebar/PredictionSlider.jsx | 20 ++++-------- src/modules/Sidebar/RegionSelect.jsx | 38 ++++++++++------------ src/modules/Table/Table.jsx | 25 ++++++--------- src/stores/useClickedPointConfig.js | 6 ++-- 7 files changed, 83 insertions(+), 72 deletions(-) diff --git a/src/Map/Points.jsx b/src/Map/Points.jsx index 07b0fa4..e2331fa 100644 --- a/src/Map/Points.jsx +++ b/src/Map/Points.jsx @@ -5,16 +5,33 @@ import { BASE_URL } from "../api"; import { useFilters } from "../stores/useFilters"; import { usePointSelection } from "../stores/usePointSelection"; +const getRegionField = (selectedRegion) => { + if (!selectedRegion) return null; + if (selectedRegion.type === "ao") { + return "okrug_id"; + } + + return "rayon_id"; +}; + export const Points = () => { const { isVisible } = useLayersVisibility(); const { filters } = useFilters(); - const { prediction, status, categories } = filters; + const { prediction, status, categories, region } = filters; const { selection } = usePointSelection(); const includedArr = [...selection.included]; const excludedArr = [...selection.excluded]; - const includedExpression = ["in", ["get", "id"], ["literal", includedArr]]; - const excludedExpression = ["in", ["get", "id"], ["literal", excludedArr]]; + const includedExpression = [ + "in", + ["get", "location_id"], + ["literal", includedArr], + ]; + const excludedExpression = [ + "in", + ["get", "location_id"], + ["literal", excludedArr], + ]; const predictionExpression = [ [">=", ["get", "prediction_current"], prediction[0]], ["<=", ["get", "prediction_current"], prediction[1]], @@ -25,13 +42,17 @@ export const Points = () => { ? ["in", ["get", "category"], ["literal", categories]] : true; + const regionExpression = ["==", ["get", getRegionField(region)], region?.id]; + const matchFilterExpression = [ "all", statusExpression, ["!", excludedExpression], [ "any", - ["all", ...predictionExpression, categoryExpression], + region + ? ["all", ...predictionExpression, categoryExpression, regionExpression] + : ["all", ...predictionExpression, categoryExpression], includedExpression, ], ]; @@ -42,7 +63,17 @@ export const Points = () => { ["!", includedExpression], [ "any", - ["!", ["all", ...predictionExpression, categoryExpression]], + [ + "!", + region + ? [ + "all", + ...predictionExpression, + categoryExpression, + regionExpression, + ] + : ["all", ...predictionExpression, categoryExpression], + ], excludedExpression, ], ]; diff --git a/src/Map/Popup.jsx b/src/Map/Popup.jsx index 7d66e58..5775e54 100644 --- a/src/Map/Popup.jsx +++ b/src/Map/Popup.jsx @@ -9,7 +9,7 @@ import { useClickedPointConfig } from "../stores/useClickedPointConfig"; const popupConfig = [ { name: "Id", - field: "id", + field: "location_id", }, { name: "Адрес", @@ -44,7 +44,7 @@ const popupConfig = [ const residentialPointConfig = [ { name: "Id", - field: "id", + field: "location_id", }, { name: "Адрес", @@ -110,13 +110,9 @@ const SingleFeaturePopup = ({ feature }) => { const { include, selection, exclude } = usePointSelection(); const { setClickedPointConfig } = useClickedPointConfig(); const doesMatchFilter = feature.layer.id === "match-points"; - const featureId = feature.properties.id; + const featureId = feature.properties.location_id; - useEffect( - () => - setClickedPointConfig(feature.properties.location_id, doesMatchFilter), - [feature] - ); + useEffect(() => setClickedPointConfig(featureId, doesMatchFilter), [feature]); const isResidential = feature.properties.category === CATEGORIES.residential; @@ -132,7 +128,6 @@ const SingleFeaturePopup = ({ feature }) => { } else { include(featureId); } - // onSelect(); }; return ( diff --git a/src/modules/Sidebar/Filters.jsx b/src/modules/Sidebar/Filters.jsx index 46c231a..c92cb9e 100644 --- a/src/modules/Sidebar/Filters.jsx +++ b/src/modules/Sidebar/Filters.jsx @@ -14,7 +14,9 @@ export const Filters = ({ disabled }) => { return () => clearTimeout(timer); }, [hover]); - const handleMouseEnter = () => setHover(true); + const handleMouseEnter = () => { + setHover(true); + }; const handleMouseLeave = () => { setHover(false); }; @@ -24,12 +26,10 @@ export const Filters = ({ disabled }) => { title={DISABLED_FILTER_TEXT} placement="right" open={disabled && hover} + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} > -
+
diff --git a/src/modules/Sidebar/PredictionSlider.jsx b/src/modules/Sidebar/PredictionSlider.jsx index 1275e92..805a504 100644 --- a/src/modules/Sidebar/PredictionSlider.jsx +++ b/src/modules/Sidebar/PredictionSlider.jsx @@ -7,15 +7,11 @@ import { useEffect } from "react"; export const PredictionSlider = ({ disabled }) => { const { filters, setPrediction } = useFilters(); - const { data } = useQuery( - ["max-min"], - async () => { - const { data } = await api.get(`/api/placement_points/filters/`); - - return data; - }, - { enabled: false } - ); + const { data } = useQuery(["max-min"], async () => { + const { data } = await api.get(`/api/placement_points/filters/`); + + return data; + }); const handleAfterChange = (range) => setPrediction(range); @@ -28,15 +24,13 @@ export const PredictionSlider = ({ disabled }) => { setPrediction([min, max]); }, [data]); - // if (!data) return null; - return ( diff --git a/src/modules/Sidebar/RegionSelect.jsx b/src/modules/Sidebar/RegionSelect.jsx index d892fca..f37cd45 100644 --- a/src/modules/Sidebar/RegionSelect.jsx +++ b/src/modules/Sidebar/RegionSelect.jsx @@ -6,17 +6,20 @@ import getBbox from "@turf/bbox"; import { polygon as getPolygon } from "@turf/helpers"; import { api } from "../../api"; import { useFilters } from "../../stores/useFilters"; +import parse from "wellknown"; const { TreeNode } = TreeSelect; const normalizeRegions = (rawRegions) => { if (!rawRegions) return {}; - return rawRegions.reduce((acc, raw) => { - acc[`ao-${raw.id}`] = raw; - if (raw.children) { - raw.children.forEach((child) => { - acc[`rayon-${child.id}`] = child; + return rawRegions.reduce((acc, ao) => { + acc[ao.id] = ao; + acc[ao.id].type = "ao"; + if (ao.rayons) { + ao.rayons.forEach((rayon) => { + acc[rayon.id] = rayon; + acc[rayon.id].type = "rayon"; }); } return acc; @@ -37,8 +40,7 @@ export const RegionSelect = ({ disabled }) => { const getRegions = async () => { setLoading(true); try { - const response = await api.get("/api/ao_and_rayons"); - console.log(response.data); + const response = await api.get("/api/placement_points/ao_rayons"); setData(response.data); } catch (err) { console.error(err); @@ -55,10 +57,12 @@ export const RegionSelect = ({ disabled }) => { const selectedRegion = normalizedData[value]; - const polygon = getPolygon(selectedRegion.geometry[0]); + const polygonWrapperGeom = parse(selectedRegion.polygon); + + const polygon = getPolygon(polygonWrapperGeom.coordinates[0]); const bbox = getBbox(polygon); - setRegion({ id: value, geometry: polygon }); + setRegion({ id: value, geometry: polygon, type: selectedRegion.type }); map.fitBounds( [ @@ -96,19 +100,11 @@ export const RegionSelect = ({ disabled }) => { } disabled={disabled} > - {data?.map((parent) => { + {data?.map((ao) => { return ( - - {parent.children?.map((child) => ( - + + {ao.rayons?.map((rayon) => ( + ))} ); diff --git a/src/modules/Table/Table.jsx b/src/modules/Table/Table.jsx index 623bc5f..569132e 100644 --- a/src/modules/Table/Table.jsx +++ b/src/modules/Table/Table.jsx @@ -13,8 +13,8 @@ import scrollIntoView from "scroll-into-view-if-needed"; const columns = [ { title: "Id", - dataIndex: "id", - key: "id", + dataIndex: "location_id", + key: "location_id", width: 50, ellipsis: true, }, @@ -74,12 +74,11 @@ const useTableData = (page) => { const [pageSize, setPageSize] = useState(PAGE_SIZE); const { filters } = useFilters(); const { prediction, status, categories } = filters; - const { selection } = usePointSelection(); const { clickedPointConfig } = useClickedPointConfig(); const [finalData, setFinalData] = useState(); const { data } = useQuery( - ["table", page, filters, selection], + ["table", page, filters], async () => { const params = new URLSearchParams({ page, @@ -87,8 +86,6 @@ const useTableData = (page) => { "prediction_current[]": prediction, "status[]": status, "categories[]": categories, - "included[]": [...selection.included], - "excluded[]": [...selection.excluded], }); const { data } = await api.get( @@ -112,7 +109,7 @@ const useTableData = (page) => { if (!data || clickedPointConfig === null) return; const clickedPoint = data.results.find( - (item) => item.location_id === clickedPointConfig.locationId + (item) => item.location_id === clickedPointConfig.id ); if (clickedPoint) { @@ -130,7 +127,7 @@ const useTableData = (page) => { ["clicked-point", clickedPointConfig], async () => { const params = new URLSearchParams({ - "location_ids[]": [clickedPointConfig.locationId], + "location_ids[]": [clickedPointConfig.id], }); const { data } = await api.get( @@ -187,9 +184,9 @@ export const Table = React.memo(({ height = 200 }) => { const handlePageChange = useCallback((page) => setPage(page), []); const getSelectedRowKeys = useCallback(() => { - const ids = data?.results.map((item) => item.id) ?? []; + const ids = data?.results.map((item) => item.location_id) ?? []; const clickedPoint = data?.results.find( - (item) => item.location_id === clickedPointConfig?.locationId + (item) => item.location_id === clickedPointConfig?.id ); const inExcludedList = (id) => selection.excluded.has(id); @@ -207,7 +204,7 @@ export const Table = React.memo(({ height = 200 }) => { const rowSelection = { selectedRowKeys: getSelectedRowKeys(), onSelect: (record, selected) => { - const { id } = record; + const { location_id: id } = record; if (selected) { include(id); } else { @@ -247,7 +244,7 @@ export const Table = React.memo(({ height = 200 }) => { }} dataSource={data?.results} columns={columns} - rowKey="id" + rowKey="location_id" scroll={SCROLL} sticky={true} onRow={(record) => { @@ -268,9 +265,7 @@ export const Table = React.memo(({ height = 200 }) => { }} rowSelection={rowSelection} rowClassName={(record) => - record.location_id === clickedPointConfig?.locationId - ? "scroll-row" - : "" + record.location_id === clickedPointConfig?.id ? "scroll-row" : "" } /> diff --git a/src/stores/useClickedPointConfig.js b/src/stores/useClickedPointConfig.js index 9c01cac..2b23117 100644 --- a/src/stores/useClickedPointConfig.js +++ b/src/stores/useClickedPointConfig.js @@ -4,14 +4,14 @@ import { immer } from "zustand/middleware/immer"; const store = (set) => ({ clickedPointConfig: null, - setClickedPointConfig: (locationId, shouldSelect = true) => { + setClickedPointConfig: (id, shouldSelect = true) => { set((state) => { - if (locationId === null) { + if (id === null) { state.clickedPointConfig = null; return state; } state.clickedPointConfig = { - locationId, + id, shouldSelect, }; });