Work with location_id field as ID field; add region filter to map points

dev
Platon Yasev 3 years ago
parent c230e6382e
commit dcdfd15586

@ -5,16 +5,33 @@ import { BASE_URL } from "../api";
import { useFilters } from "../stores/useFilters"; import { useFilters } from "../stores/useFilters";
import { usePointSelection } from "../stores/usePointSelection"; 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 = () => { export const Points = () => {
const { isVisible } = useLayersVisibility(); const { isVisible } = useLayersVisibility();
const { filters } = useFilters(); const { filters } = useFilters();
const { prediction, status, categories } = filters; const { prediction, status, categories, region } = filters;
const { selection } = usePointSelection(); const { selection } = usePointSelection();
const includedArr = [...selection.included]; const includedArr = [...selection.included];
const excludedArr = [...selection.excluded]; const excludedArr = [...selection.excluded];
const includedExpression = ["in", ["get", "id"], ["literal", includedArr]]; const includedExpression = [
const excludedExpression = ["in", ["get", "id"], ["literal", excludedArr]]; "in",
["get", "location_id"],
["literal", includedArr],
];
const excludedExpression = [
"in",
["get", "location_id"],
["literal", excludedArr],
];
const predictionExpression = [ const predictionExpression = [
[">=", ["get", "prediction_current"], prediction[0]], [">=", ["get", "prediction_current"], prediction[0]],
["<=", ["get", "prediction_current"], prediction[1]], ["<=", ["get", "prediction_current"], prediction[1]],
@ -25,13 +42,17 @@ export const Points = () => {
? ["in", ["get", "category"], ["literal", categories]] ? ["in", ["get", "category"], ["literal", categories]]
: true; : true;
const regionExpression = ["==", ["get", getRegionField(region)], region?.id];
const matchFilterExpression = [ const matchFilterExpression = [
"all", "all",
statusExpression, statusExpression,
["!", excludedExpression], ["!", excludedExpression],
[ [
"any", "any",
["all", ...predictionExpression, categoryExpression], region
? ["all", ...predictionExpression, categoryExpression, regionExpression]
: ["all", ...predictionExpression, categoryExpression],
includedExpression, includedExpression,
], ],
]; ];
@ -42,7 +63,17 @@ export const Points = () => {
["!", includedExpression], ["!", includedExpression],
[ [
"any", "any",
["!", ["all", ...predictionExpression, categoryExpression]], [
"!",
region
? [
"all",
...predictionExpression,
categoryExpression,
regionExpression,
]
: ["all", ...predictionExpression, categoryExpression],
],
excludedExpression, excludedExpression,
], ],
]; ];

@ -9,7 +9,7 @@ import { useClickedPointConfig } from "../stores/useClickedPointConfig";
const popupConfig = [ const popupConfig = [
{ {
name: "Id", name: "Id",
field: "id", field: "location_id",
}, },
{ {
name: "Адрес", name: "Адрес",
@ -44,7 +44,7 @@ const popupConfig = [
const residentialPointConfig = [ const residentialPointConfig = [
{ {
name: "Id", name: "Id",
field: "id", field: "location_id",
}, },
{ {
name: "Адрес", name: "Адрес",
@ -110,13 +110,9 @@ const SingleFeaturePopup = ({ feature }) => {
const { include, selection, exclude } = usePointSelection(); const { include, selection, exclude } = usePointSelection();
const { setClickedPointConfig } = useClickedPointConfig(); const { setClickedPointConfig } = useClickedPointConfig();
const doesMatchFilter = feature.layer.id === "match-points"; const doesMatchFilter = feature.layer.id === "match-points";
const featureId = feature.properties.id; const featureId = feature.properties.location_id;
useEffect( useEffect(() => setClickedPointConfig(featureId, doesMatchFilter), [feature]);
() =>
setClickedPointConfig(feature.properties.location_id, doesMatchFilter),
[feature]
);
const isResidential = feature.properties.category === CATEGORIES.residential; const isResidential = feature.properties.category === CATEGORIES.residential;
@ -132,7 +128,6 @@ const SingleFeaturePopup = ({ feature }) => {
} else { } else {
include(featureId); include(featureId);
} }
// onSelect();
}; };
return ( return (

@ -14,7 +14,9 @@ export const Filters = ({ disabled }) => {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [hover]); }, [hover]);
const handleMouseEnter = () => setHover(true); const handleMouseEnter = () => {
setHover(true);
};
const handleMouseLeave = () => { const handleMouseLeave = () => {
setHover(false); setHover(false);
}; };
@ -24,12 +26,10 @@ export const Filters = ({ disabled }) => {
title={DISABLED_FILTER_TEXT} title={DISABLED_FILTER_TEXT}
placement="right" placement="right"
open={disabled && hover} open={disabled && hover}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
> >
<div <div className="space-y-5">
className="space-y-5"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<RegionSelect disabled={disabled} /> <RegionSelect disabled={disabled} />
<CategoriesSelect disabled={disabled} /> <CategoriesSelect disabled={disabled} />
<PredictionSlider disabled={disabled} /> <PredictionSlider disabled={disabled} />

@ -7,15 +7,11 @@ import { useEffect } from "react";
export const PredictionSlider = ({ disabled }) => { export const PredictionSlider = ({ disabled }) => {
const { filters, setPrediction } = useFilters(); const { filters, setPrediction } = useFilters();
const { data } = useQuery( const { data } = useQuery(["max-min"], async () => {
["max-min"], const { data } = await api.get(`/api/placement_points/filters/`);
async () => {
const { data } = await api.get(`/api/placement_points/filters/`); return data;
});
return data;
},
{ enabled: false }
);
const handleAfterChange = (range) => setPrediction(range); const handleAfterChange = (range) => setPrediction(range);
@ -28,15 +24,13 @@ export const PredictionSlider = ({ disabled }) => {
setPrediction([min, max]); setPrediction([min, max]);
}, [data]); }, [data]);
// if (!data) return null;
return ( return (
<Slider <Slider
title={"Прогнозный трафик"} title={"Прогнозный трафик"}
value={filters.prediction} value={filters.prediction}
onAfterChange={handleAfterChange} onAfterChange={handleAfterChange}
min={200} min={data?.prediction_current[0]}
max={299} max={data?.prediction_current[1]}
range range
disabled={disabled} disabled={disabled}
/> />

@ -6,17 +6,20 @@ import getBbox from "@turf/bbox";
import { polygon as getPolygon } from "@turf/helpers"; import { polygon as getPolygon } from "@turf/helpers";
import { api } from "../../api"; import { api } from "../../api";
import { useFilters } from "../../stores/useFilters"; import { useFilters } from "../../stores/useFilters";
import parse from "wellknown";
const { TreeNode } = TreeSelect; const { TreeNode } = TreeSelect;
const normalizeRegions = (rawRegions) => { const normalizeRegions = (rawRegions) => {
if (!rawRegions) return {}; if (!rawRegions) return {};
return rawRegions.reduce((acc, raw) => { return rawRegions.reduce((acc, ao) => {
acc[`ao-${raw.id}`] = raw; acc[ao.id] = ao;
if (raw.children) { acc[ao.id].type = "ao";
raw.children.forEach((child) => { if (ao.rayons) {
acc[`rayon-${child.id}`] = child; ao.rayons.forEach((rayon) => {
acc[rayon.id] = rayon;
acc[rayon.id].type = "rayon";
}); });
} }
return acc; return acc;
@ -37,8 +40,7 @@ export const RegionSelect = ({ disabled }) => {
const getRegions = async () => { const getRegions = async () => {
setLoading(true); setLoading(true);
try { try {
const response = await api.get("/api/ao_and_rayons"); const response = await api.get("/api/placement_points/ao_rayons");
console.log(response.data);
setData(response.data); setData(response.data);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -55,10 +57,12 @@ export const RegionSelect = ({ disabled }) => {
const selectedRegion = normalizedData[value]; 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); const bbox = getBbox(polygon);
setRegion({ id: value, geometry: polygon }); setRegion({ id: value, geometry: polygon, type: selectedRegion.type });
map.fitBounds( map.fitBounds(
[ [
@ -96,19 +100,11 @@ export const RegionSelect = ({ disabled }) => {
} }
disabled={disabled} disabled={disabled}
> >
{data?.map((parent) => { {data?.map((ao) => {
return ( return (
<TreeNode <TreeNode key={ao.id} value={ao.id} title={ao.name}>
key={`ao-${parent.id}`} {ao.rayons?.map((rayon) => (
value={`ao-${parent.id}`} <TreeNode key={rayon.id} value={rayon.id} title={rayon.name} />
title={parent.name}
>
{parent.children?.map((child) => (
<TreeNode
key={`rayon-${child.id}`}
value={`rayon-${child.id}`}
title={child.name}
/>
))} ))}
</TreeNode> </TreeNode>
); );

@ -13,8 +13,8 @@ import scrollIntoView from "scroll-into-view-if-needed";
const columns = [ const columns = [
{ {
title: "Id", title: "Id",
dataIndex: "id", dataIndex: "location_id",
key: "id", key: "location_id",
width: 50, width: 50,
ellipsis: true, ellipsis: true,
}, },
@ -74,12 +74,11 @@ const useTableData = (page) => {
const [pageSize, setPageSize] = useState(PAGE_SIZE); const [pageSize, setPageSize] = useState(PAGE_SIZE);
const { filters } = useFilters(); const { filters } = useFilters();
const { prediction, status, categories } = filters; const { prediction, status, categories } = filters;
const { selection } = usePointSelection();
const { clickedPointConfig } = useClickedPointConfig(); const { clickedPointConfig } = useClickedPointConfig();
const [finalData, setFinalData] = useState(); const [finalData, setFinalData] = useState();
const { data } = useQuery( const { data } = useQuery(
["table", page, filters, selection], ["table", page, filters],
async () => { async () => {
const params = new URLSearchParams({ const params = new URLSearchParams({
page, page,
@ -87,8 +86,6 @@ const useTableData = (page) => {
"prediction_current[]": prediction, "prediction_current[]": prediction,
"status[]": status, "status[]": status,
"categories[]": categories, "categories[]": categories,
"included[]": [...selection.included],
"excluded[]": [...selection.excluded],
}); });
const { data } = await api.get( const { data } = await api.get(
@ -112,7 +109,7 @@ const useTableData = (page) => {
if (!data || clickedPointConfig === null) return; if (!data || clickedPointConfig === null) return;
const clickedPoint = data.results.find( const clickedPoint = data.results.find(
(item) => item.location_id === clickedPointConfig.locationId (item) => item.location_id === clickedPointConfig.id
); );
if (clickedPoint) { if (clickedPoint) {
@ -130,7 +127,7 @@ const useTableData = (page) => {
["clicked-point", clickedPointConfig], ["clicked-point", clickedPointConfig],
async () => { async () => {
const params = new URLSearchParams({ const params = new URLSearchParams({
"location_ids[]": [clickedPointConfig.locationId], "location_ids[]": [clickedPointConfig.id],
}); });
const { data } = await api.get( const { data } = await api.get(
@ -187,9 +184,9 @@ export const Table = React.memo(({ height = 200 }) => {
const handlePageChange = useCallback((page) => setPage(page), []); const handlePageChange = useCallback((page) => setPage(page), []);
const getSelectedRowKeys = useCallback(() => { 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( const clickedPoint = data?.results.find(
(item) => item.location_id === clickedPointConfig?.locationId (item) => item.location_id === clickedPointConfig?.id
); );
const inExcludedList = (id) => selection.excluded.has(id); const inExcludedList = (id) => selection.excluded.has(id);
@ -207,7 +204,7 @@ export const Table = React.memo(({ height = 200 }) => {
const rowSelection = { const rowSelection = {
selectedRowKeys: getSelectedRowKeys(), selectedRowKeys: getSelectedRowKeys(),
onSelect: (record, selected) => { onSelect: (record, selected) => {
const { id } = record; const { location_id: id } = record;
if (selected) { if (selected) {
include(id); include(id);
} else { } else {
@ -247,7 +244,7 @@ export const Table = React.memo(({ height = 200 }) => {
}} }}
dataSource={data?.results} dataSource={data?.results}
columns={columns} columns={columns}
rowKey="id" rowKey="location_id"
scroll={SCROLL} scroll={SCROLL}
sticky={true} sticky={true}
onRow={(record) => { onRow={(record) => {
@ -268,9 +265,7 @@ export const Table = React.memo(({ height = 200 }) => {
}} }}
rowSelection={rowSelection} rowSelection={rowSelection}
rowClassName={(record) => rowClassName={(record) =>
record.location_id === clickedPointConfig?.locationId record.location_id === clickedPointConfig?.id ? "scroll-row" : ""
? "scroll-row"
: ""
} }
/> />
</Collapse.Panel> </Collapse.Panel>

@ -4,14 +4,14 @@ import { immer } from "zustand/middleware/immer";
const store = (set) => ({ const store = (set) => ({
clickedPointConfig: null, clickedPointConfig: null,
setClickedPointConfig: (locationId, shouldSelect = true) => { setClickedPointConfig: (id, shouldSelect = true) => {
set((state) => { set((state) => {
if (locationId === null) { if (id === null) {
state.clickedPointConfig = null; state.clickedPointConfig = null;
return state; return state;
} }
state.clickedPointConfig = { state.clickedPointConfig = {
locationId, id,
shouldSelect, shouldSelect,
}; };
}); });

Loading…
Cancel
Save