Merge branch 'feature/points_with_dist' into 'dev'

Feature/points with dist

See merge request spatial/postamates_frontend!23
dev
Timofey Malinin 3 years ago
commit b4bc4f3fda

@ -23,7 +23,7 @@ if (import.meta.env.MODE === "development") {
mountStoreDevtool("PointSelection", usePointSelection); mountStoreDevtool("PointSelection", usePointSelection);
} }
const version = '0.0.4'; const version = '0.0.5';
function App() { function App() {

@ -19,7 +19,7 @@ export function LastMLRun() {
trigger="click" trigger="click"
placement={"leftBottom"} placement={"leftBottom"}
> >
<Tooltip title="Слои"> <Tooltip title="Последний запуск">
<Button className="absolute bottom-[64px] right-[20px] flex items-center justify-center p-3"> <Button className="absolute bottom-[64px] right-[20px] flex items-center justify-center p-3">
<InfoCircleOutlined className="w-4 h-4" /> <InfoCircleOutlined className="w-4 h-4" />
</Button> </Button>

@ -43,7 +43,7 @@ export const Layers = ({ postGroups, otherGroups }) => {
type="vector" type="vector"
tiles={[`${BASE_URL}/martin/public.service_post_and_pvz/{z}/{x}/{y}.pbf`]} tiles={[`${BASE_URL}/martin/public.service_post_and_pvz/{z}/{x}/{y}.pbf`]}
> >
{postGroups && postGroups.map((item) => { {postGroups?.map((item) => {
return item.groups.map((itemGroup) => return item.groups.map((itemGroup) =>
<PVZ <PVZ
id={itemGroup.id} id={itemGroup.id}

@ -9,34 +9,11 @@ import { STATUSES } from "../../config";
import { useRegionFilterExpression } from "./useRegionFilterExpression"; import { useRegionFilterExpression } from "./useRegionFilterExpression";
import { LAYER_IDS } from "./constants"; import { LAYER_IDS } from "./constants";
import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../stores/usePendingPointsFilters"; import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../stores/usePendingPointsFilters";
import { usePostamatesAndPvzGroups } from "../../api.js"; import {fieldHasChanged} from "../../utils.js";
import { useMemo } from "react";
const statusExpression = ["==", ["get", "status"], STATUSES.pending]; const statusExpression = ["==", ["get", "status"], STATUSES.pending];
const useFilterExpression = () => { const useFilterExpression = () => {
const { data: postamatesAndPvzGroups } = usePostamatesAndPvzGroups();
const filteredPostamatesCategories = useMemo(() => {
if (!postamatesAndPvzGroups) return [];
return postamatesAndPvzGroups
.filter((category) => category.visible)
.map((category) => {
return {
...category,
groups: [...category.groups.filter((group) => group.visible)],
}
})
}, [postamatesAndPvzGroups]);
const postamatesGroups = useMemo(() => {
if (!filteredPostamatesCategories) return [];
return filteredPostamatesCategories
.map((category) => {
return [...category.groups.filter((group) => group.visible)]
}).flat();
}, [postamatesAndPvzGroups]);
const { filters, ranges } = usePendingPointsFilters(); const { filters, ranges } = usePendingPointsFilters();
const { prediction, categories, region } = filters; const { prediction, categories, region } = filters;
const { selection } = usePointSelection(); const { selection } = usePointSelection();
@ -53,14 +30,6 @@ const useFilterExpression = () => {
["<=", ["get", "prediction_current"], prediction[1]], ["<=", ["get", "prediction_current"], prediction[1]],
]; ];
const dynamicKeyFiltersExpressions = postamatesGroups.map((group) => {
const key = `d${group.id}`
return [
[">=", ["get", key], filters[`${key}__gt`]],
["<=", ["get", key], filters[`${key}__lt`]],
];
});
const staticKeyFiltersExpressions = RANGE_FILTERS_KEYS.map((key) => { const staticKeyFiltersExpressions = RANGE_FILTERS_KEYS.map((key) => {
return [ return [
[">=", ["get", key], filters[`${key}__gt`]], [">=", ["get", key], filters[`${key}__gt`]],
@ -68,25 +37,9 @@ const useFilterExpression = () => {
]; ];
}); });
const dynamicKeyExpressions = dynamicKeyFiltersExpressions.filter((expression) => {
const filterKey = expression[0][1][1];
const range = ranges[filterKey];
const gtValue = expression[0][2] || 0;
const gtInitial = range ? range[0] : 0;
const ltValue = expression[1][2] || 0;
const ltInitial = range ? range[1] : 0;
return !(gtValue === gtInitial && ltValue === ltInitial);
}).flat();
const staticKeyExpressions = staticKeyFiltersExpressions.filter((expression) => { const staticKeyExpressions = staticKeyFiltersExpressions.filter((expression) => {
const filterKey = expression[0][1][1]; const filterKey = expression[0][1][1];
const gtValue = expression[0][2]; return fieldHasChanged(filters, ranges, filterKey).result;
const gtInitial = ranges[filterKey][0];
const ltValue = expression[1][2];
const ltInitial = ranges[filterKey][1];
return !(gtValue === gtInitial && ltValue === ltInitial);
}).flat(); }).flat();
const categoryExpression = const categoryExpression =
@ -101,8 +54,8 @@ const useFilterExpression = () => {
[ [
"any", "any",
regionExpression regionExpression
? ["all", ...predictionExpression, ...staticKeyExpressions, ...dynamicKeyExpressions, categoryExpression, regionExpression] ? ["all", ...predictionExpression, ...staticKeyExpressions, categoryExpression, regionExpression]
: ["all", ...predictionExpression, ...staticKeyExpressions, ...dynamicKeyExpressions, categoryExpression], : ["all", ...predictionExpression, ...staticKeyExpressions, categoryExpression],
includedExpression, includedExpression,
], ],
]; ];

@ -28,7 +28,7 @@ export const LayersVisibility = ({ postGroups, otherGroups }) => {
</Checkbox> </Checkbox>
</> </>
)} )}
{postGroups && postGroups.map((item) => { {postGroups?.map((item) => {
return ( return (
<Checkbox <Checkbox
key={item.id} key={item.id}

@ -125,12 +125,12 @@ export function Legend({ postGroups, otherGroups }) {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
{postGroups && postGroups?.map((item) => { {postGroups?.map((item) => {
return <LegendGroupItem key={item.id} item={item} color={PVZ_COLOR}/> return <LegendGroupItem key={item.id} item={item} color={PVZ_COLOR}/>
})} })}
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
{otherGroups && otherGroups?.map((item) => { {otherGroups?.map((item) => {
return <LegendGroupItem key={item.id} item={item} color={OTHER_POSTAMATES_COLOR}/> return <LegendGroupItem key={item.id} item={item} color={OTHER_POSTAMATES_COLOR}/>
})} })}
</div> </div>

@ -23,8 +23,8 @@ import { Header } from "./Header";
import { icons } from "../icons/icons-config"; import { icons } from "../icons/icons-config";
import { LastMLRun } from "./LastMLRun"; import { LastMLRun } from "./LastMLRun";
import { useGetPendingPointsRange, useOtherGroups, usePostamatesAndPvzGroups } from "../api.js"; import { useGetPendingPointsRange, useOtherGroups, usePostamatesAndPvzGroups } from "../api.js";
import {transliterate} from "../utils.js"; import { getFilteredGroups, transliterate } from "../utils.js";
import { usePendingPointsFilters } from "../stores/usePendingPointsFilters.js"; import { RANGE_FILTERS_KEYS, RANGE_FILTERS_MAP, usePendingPointsFilters } from "../stores/usePendingPointsFilters.js";
export const MapComponent = () => { export const MapComponent = () => {
const mapRef = useRef(null); const mapRef = useRef(null);
@ -35,10 +35,9 @@ export const MapComponent = () => {
const { setLayersVisibility } = useLayersVisibility(); const { setLayersVisibility } = useLayersVisibility();
const { mode } = useMode(); const { mode } = useMode();
const { tableState, openTable } = useTable(); const { tableState, openTable } = useTable();
const { toggleVisibility } = useLayersVisibility();
const { ranges, setRanges } = usePendingPointsFilters(); const { ranges, setRanges } = usePendingPointsFilters();
const { data: fullRange, isInitialLoading } = useGetPendingPointsRange(); const { data: fullRange } = useGetPendingPointsRange();
useEffect(() => { useEffect(() => {
if (fullRange) setRanges({...ranges, ...fullRange}); if (fullRange) setRanges({...ranges, ...fullRange});
@ -48,45 +47,21 @@ export const MapComponent = () => {
const { data: otherGroups } = useOtherGroups(); const { data: otherGroups } = useOtherGroups();
const filteredPostamatesGroups = useMemo(() => { const filteredPostamatesGroups = useMemo(() => {
if (!postamatesAndPvzGroups) return []; return getFilteredGroups(postamatesAndPvzGroups);
return postamatesAndPvzGroups
.filter((category) => category.visible)
.map((category) => {
toggleVisibility(LAYER_IDS.pvz_category + category.id)
return {
...category,
groups: [...category.groups.filter((group) => group.visible)],
}
})
}, [postamatesAndPvzGroups]); }, [postamatesAndPvzGroups]);
const filteredOtherGroups = useMemo(() => { const filteredOtherGroups = useMemo(() => {
if (!otherGroups) return []; return getFilteredGroups(otherGroups);
return otherGroups
.filter((category) => !category.visible)
.map((category) => {
return {
...category,
groups: [...category.groups.filter((group) => group.visible)],
}
})
}, [otherGroups]); }, [otherGroups]);
const mapIcons = useMemo(() => { const mapIcons = useMemo(() => {
const res = []; const res = [];
filteredPostamatesGroups.map((category) => { [...filteredOtherGroups, ...filteredPostamatesGroups].map((category) => {
category.groups.map((group) => { category.groups.map((group) => {
res.push({name: transliterate(group.name + group.id), url: group.image}) res.push({name: transliterate(group.name + group.id), url: group.image});
}) })
}); });
filteredOtherGroups.map((category) => {
category.groups.map((group) => {
res.push({name: transliterate(group.name + group.id), url: group.image})
})
});
return [...res, ...icons]; return [...res, ...icons];
}, [icons, filteredPostamatesGroups, filteredOtherGroups]); }, [icons, filteredPostamatesGroups, filteredOtherGroups]);
@ -95,6 +70,8 @@ export const MapComponent = () => {
filteredPostamatesGroups.map((item) => { filteredPostamatesGroups.map((item) => {
item.groups.map((groupItem) => { item.groups.map((groupItem) => {
RANGE_FILTERS_KEYS.push(`d${groupItem.id}`);
RANGE_FILTERS_MAP[`d${groupItem.id}`] = groupItem.name;
res.push(LAYER_IDS.pvz + groupItem.id); res.push(LAYER_IDS.pvz + groupItem.id);
}) })
}); });

@ -11,6 +11,7 @@ import { usePopup } from "../../stores/usePopup.js";
import { PanoramaIcon } from "../../icons/PanoramaIcon"; import { PanoramaIcon } from "../../icons/PanoramaIcon";
import {usePostamatesAndPvzGroups} from "../../api.js"; import {usePostamatesAndPvzGroups} from "../../api.js";
import {useMemo} from "react"; import {useMemo} from "react";
import {getFilteredGroups} from "../../utils.js";
const SingleFeaturePopup = ({ feature, groups, categories }) => { const SingleFeaturePopup = ({ feature, groups, categories }) => {
const { mode } = useMode(); const { mode } = useMode();
@ -94,24 +95,16 @@ export const MapPopup = ({ features, lat, lng, onClose }) => {
const { data: postamatesAndPvzGroups } = usePostamatesAndPvzGroups(); const { data: postamatesAndPvzGroups } = usePostamatesAndPvzGroups();
const filteredPostamatesCategories = useMemo(() => { const filteredPostamatesCategories = useMemo(() => {
if (!postamatesAndPvzGroups) return []; return getFilteredGroups(postamatesAndPvzGroups);
return postamatesAndPvzGroups
.filter((category) => category.visible)
.map((category) => {
return {
...category,
groups: [...category.groups.filter((group) => group.visible)],
}
})
}, [postamatesAndPvzGroups]); }, [postamatesAndPvzGroups]);
const postamatesGroups = useMemo(() => { const postamatesGroups = useMemo(() => {
if (!filteredPostamatesCategories) return []; if (!filteredPostamatesCategories) return [];
return filteredPostamatesCategories return filteredPostamatesCategories
.map((category) => { .map((category) => {
return [...category.groups.filter((group) => group.visible)] return [...category.groups]
}).flat(); }).flat();
}, [postamatesAndPvzGroups]); }, [filteredPostamatesCategories]);
const getContent = () => { const getContent = () => {
if (features.length === 1) { if (features.length === 1) {

@ -2,7 +2,7 @@ import axios from "axios";
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { STATUSES } from "./config"; import { STATUSES } from "./config";
import { usePointSelection } from "./stores/usePointSelection"; import { usePointSelection } from "./stores/usePointSelection";
import { usePendingPointsFilters } from "./stores/usePendingPointsFilters"; import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "./stores/usePendingPointsFilters";
export const BASE_URL = import.meta.env.VITE_API_URL; export const BASE_URL = import.meta.env.VITE_API_URL;
@ -189,16 +189,20 @@ export const useGetPendingPointsRange = () => {
select: (data) => { select: (data) => {
const distToGroupsArr = data.dist_to_groups.map((groupRange) => { const distToGroupsArr = data.dist_to_groups.map((groupRange) => {
return { return {
[`d${groupRange.group_id}`]: groupRange.dist, [`d${groupRange.group_id}`]: [Math.floor(groupRange.dist[0]), Math.ceil(groupRange.dist[1])],
} }
}) });
const distToGroups = Object.assign({}, ...distToGroupsArr); const distToGroups = Object.assign({}, ...distToGroupsArr);
const rangesArr = RANGE_FILTERS_KEYS.map((key) => {
if ((/d[0-9]/.test(key))) return;
return {
[key]: [Math.floor(data[key][0]), Math.ceil(data[key][1])]
}
}).filter(item => !!item);
const ranges = Object.assign({}, ...rangesArr);
return { return {
prediction: data.prediction_current, prediction: data.prediction_current,
doors: data.doors, ...ranges,
flat_cnt: data.flat_cnt,
flats_cnt: data.flats_cnt,
target_post_cnt: data.target_post_cnt,
...distToGroups ...distToGroups
}; };
}, },

@ -1,221 +1,234 @@
import { usePendingPointsFilters } from "../../../../stores/usePendingPointsFilters"; import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../../../stores/usePendingPointsFilters";
import { FilterSlider } from "./Slider.jsx"; import { FilterSlider } from "./Slider.jsx";
import { Collapse } from "antd"; import { Button, Collapse } from "antd";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { Title } from "../../../../components/Title.jsx"; import { Title } from "../../../../components/Title.jsx";
import { usePostamatesAndPvzGroups } from "../../../../api.js"; import { usePostamatesAndPvzGroups } from "../../../../api.js";
import {fieldHasChanged, getFilteredGroups} from "../../../../utils.js";
export const AdvancedFilters = () => { export const AdvancedFilters = () => {
const { const { filters, ranges, tempFilters, setFilterWithKey, setTempFilterWithKey } = usePendingPointsFilters();
filters,
setFilterWithKey,
ranges
} = usePendingPointsFilters();
const { data: postamatesAndPvzGroups } = usePostamatesAndPvzGroups(); const { data: postamatesAndPvzGroups } = usePostamatesAndPvzGroups();
const filteredPostamatesGroups = useMemo(() => { const filteredPostamatesGroups = useMemo(() => {
if (!postamatesAndPvzGroups) return []; return getFilteredGroups(postamatesAndPvzGroups);
return postamatesAndPvzGroups
.filter((category) => category.visible)
.map((category) => {
return {
...category,
groups: [...category.groups.filter((group) => group.visible)],
}
})
}, [postamatesAndPvzGroups]); }, [postamatesAndPvzGroups]);
const selectedCnt = useMemo(() => {
let counter = 0;
RANGE_FILTERS_KEYS.map((key) => {
if (fieldHasChanged(tempFilters, ranges, key).result) counter += 1;
})
return counter;
}, [tempFilters]);
const onApply = () => {
RANGE_FILTERS_KEYS.map((key) => {
setFilterWithKey([tempFilters[`${key}__gt`], tempFilters[`${key}__lt`]], key);
})
};
const onCancel = () => {
RANGE_FILTERS_KEYS.map((key) => {
setTempFilterWithKey([filters[`${key}__gt`], filters[`${key}__lt`]], key);
})
}
return ( return (
<div className="ml-4 px-3 py-[24px] bg-white rounded-xl z-20 overflow-y-scroll mt-[5vh] shadow-2xl" style={{maxHeight: '90vh', width: '350px', maxWidth: '450px'}}> <div className="ml-4 bg-white rounded-xl z-20 mt-[5vh] shadow-2xl" style={{maxHeight: '90vh', width: '350px', maxWidth: '450px'}}>
<Collapse <div className="font-semibold p-4 border-0 border-b border-solid border-gray-300">
bordered={false} Расширенные фильтры
expandIconPosition={"end"} </div>
style={{ <div style={{maxHeight: 'calc(90vh - 150px)'}} className="overflow-y-scroll py-3 px-6">
background: 'none', <Collapse
}} bordered={false}
className="filter_group my-4" expandIconPosition={"end"}
> style={{
<Collapse.Panel background: 'none',
key={"filter_common"} }}
header={<Title type={"primary"} text={"Общие"} classNameText="text-black" />} className="filter_group my-4"
forceRender
> >
<div className="mt-4 mb-12"> <Collapse.Panel
<div> key={"filter_common"}
<FilterSlider header={<Title type={"primary"} text={"Общие"} classNameText="text-black" />}
filterRange={[filters.doors__gt, filters.doors__lt]} forceRender
setFilterRange={setFilterWithKey} >
title={"Кол-во подъездов в жилом доме"} <div className="mt-4 mb-12">
fullRange={ranges.doors || [0, 0]} <div>
filterKey={"doors"} <FilterSlider
/> filterRange={[tempFilters.doors__gt, tempFilters.doors__lt]}
</div> title={"Кол-во подъездов в жилом доме"}
<div> fullRange={ranges.doors || [0, 0]}
<FilterSlider filterKey={"doors"}
filterRange={[filters.flat_cnt__gt, filters.flat_cnt__lt]} />
setFilterRange={setFilterWithKey} </div>
title={"Кол-во квартир в подъезде жилого дома"} <div>
fullRange={ranges.flat_cnt || [0, 0]} <FilterSlider
filterKey={"flat_cnt"} filterRange={[tempFilters.flat_cnt__gt, tempFilters.flat_cnt__lt]}
/> title={"Кол-во квартир в подъезде жилого дома"}
fullRange={ranges.flat_cnt || [0, 0]}
filterKey={"flat_cnt"}
/>
</div>
</div> </div>
</div>
</Collapse.Panel> </Collapse.Panel>
</Collapse> </Collapse>
<Collapse <Collapse
bordered={false} bordered={false}
expandIconPosition={"end"} expandIconPosition={"end"}
style={{ style={{
background: 'none', background: 'none',
}} }}
className="filter_group my-4" className="filter_group my-4"
>
<Collapse.Panel
key={"filter_dist"}
header={<Title type={"primary"} text={"Кол-во объектов в окрестности 500м"} classNameText="text-black" />}
forceRender
> >
<div className="mt-4 mb-12"> <Collapse.Panel
<div> key={"filter_dist"}
<FilterSlider header={<Title type={"primary"} text={"Кол-во объектов в окрестности 500м"} classNameText="text-black" />}
filterRange={[filters.rival_post_cnt__gt, filters.rival_post_cnt__lt]} forceRender
setFilterRange={setFilterWithKey} >
title={"Кол-во постаматов других сетей"} <div className="mt-4 mb-12">
fullRange={[0, 5000]} <div>
/> <FilterSlider
</div> filterRange={[tempFilters.rival_post_cnt__gt, tempFilters.rival_post_cnt__lt]}
<div> title={"Кол-во постаматов других сетей"}
<FilterSlider fullRange={ranges.rival_post_cnt || [0, 0]}
filterRange={[filters.rival_pvz_cnt__gt, filters.rival_pvz_cnt__lt]} filterKey={"rival_post_cnt"}
setFilterRange={setFilterWithKey} />
title={"Кол-во ПВЗ"} </div>
fullRange={[0, 5000]} <div>
/> <FilterSlider
</div> filterRange={[tempFilters.rival_pvz_cnt__gt, tempFilters.rival_pvz_cnt__lt]}
<div> title={"Кол-во ПВЗ"}
<FilterSlider fullRange={ranges.rival_pvz_cnt || [0, 0]}
filterRange={[filters.target_post_cnt__gt, filters.target_post_cnt__lt]} filterKey={"rival_pvz_cnt"}
setFilterRange={setFilterWithKey} />
title={"Кол-во постаматов Мой постамат"} </div>
fullRange={ranges.target_post_cnt || [0, 0]} <div>
filterKey={"target_post_cnt"} <FilterSlider
/> filterRange={[tempFilters.target_post_cnt__gt, tempFilters.target_post_cnt__lt]}
</div> title={"Кол-во постаматов Мой постамат"}
<div> fullRange={ranges.target_post_cnt || [0, 0]}
<FilterSlider filterKey={"target_post_cnt"}
filterRange={[filters.flats_cnt__gt, filters.flats_cnt__lt]} />
setFilterRange={setFilterWithKey} </div>
title={"Кол-во квартир в окрестности"} <div>
fullRange={ranges.flats_cnt || [0, 0]} <FilterSlider
filterKey={"flats_cnt"} filterRange={[tempFilters.flats_cnt__gt, tempFilters.flats_cnt__lt]}
/> title={"Кол-во квартир в окрестности"}
</div> fullRange={ranges.flats_cnt || [0, 0]}
<div> filterKey={"flats_cnt"}
<FilterSlider />
filterRange={[filters.tc_cnt__gt, filters.tc_cnt__lt]} </div>
setFilterRange={setFilterWithKey} <div>
title={"Кол-во торговых центров"} <FilterSlider
fullRange={ranges.tc_cnt || [0, 0]} filterRange={[tempFilters.tc_cnt__gt, tempFilters.tc_cnt__lt]}
filterKey={"tc_cnt"} title={"Кол-во торговых центров"}
/> fullRange={ranges.tc_cnt || [0, 0]}
</div> filterKey={"tc_cnt"}
<div> />
<FilterSlider </div>
filterRange={[filters.culture_cnt__gt, filters.culture_cnt__lt]} <div>
setFilterRange={setFilterWithKey} <FilterSlider
title={"Кол-во объектов культуры (театры, музей и тд)"} filterRange={[tempFilters.culture_cnt__gt, tempFilters.culture_cnt__lt]}
fullRange={ranges.tc_cnt || [0, 0]} title={"Кол-во объектов культуры (театры, музей и тд)"}
filterKey={"tc_cnt"} fullRange={ranges.culture_cnt || [0, 0]}
/> filterKey={"culture_cnt"}
</div> />
<div> </div>
<FilterSlider <div>
filterRange={[filters.mfc_cnt__gt, filters.mfc_cnt__lt]} <FilterSlider
setFilterRange={setFilterWithKey} filterRange={[tempFilters.mfc_cnt__gt, tempFilters.mfc_cnt__lt]}
title={"Кол-во МФЦ"} title={"Кол-во МФЦ"}
fullRange={ranges.tc_cnt || [0, 0]} fullRange={ranges.mfc_cnt || [0, 0]}
filterKey={"tc_cnt"} filterKey={"mfc_cnt"}
/> />
</div> </div>
<div> <div>
<FilterSlider <FilterSlider
filterRange={[filters.public_stop_cnt__gt, filters.public_stop_cnt__lt]} filterRange={[tempFilters.public_stop_cnt__gt, tempFilters.public_stop_cnt__lt]}
setFilterRange={setFilterWithKey} title={"Кол-во остановок ОТ"}
title={"Кол-во остановок ОТ"} fullRange={ranges.public_stop_cnt || [0, 0]}
fullRange={ranges.public_stop_cnt || [0, 0]} filterKey={"public_stop_cnt"}
filterKey={"public_stop_cnt"} />
/> </div>
</div> <div>
<div> <FilterSlider
<FilterSlider filterRange={[tempFilters.supermarket_cnt__gt, tempFilters.supermarket_cnt__lt]}
filterRange={[filters.supermarket_cnt__gt, filters.supermarket_cnt__lt]} title={"Кол-во супермаркетов"}
setFilterRange={setFilterWithKey} fullRange={ranges.supermarket_cnt || [0, 0]}
title={"Кол-во супермаркетов"} filterKey={"supermarket_cnt"}
fullRange={ranges.supermarket_cnt || [0, 0]} />
filterKey={"supermarket_cnt"} </div>
/> <div>
</div> <FilterSlider
<div> filterRange={[tempFilters.target_dist__gt, tempFilters.target_dist__lt]}
<FilterSlider title={"Расстояние до постамата Мой постамат"}
filterRange={[filters.target_dist__gt, filters.target_dist__lt]} fullRange={ranges.target_dist || [0, 0]}
setFilterRange={setFilterWithKey} filterKey={"target_dist"}
title={"Расстояние до постамата Мой постамат"} />
fullRange={ranges.target_dist || [0, 0]} </div>
filterKey={"target_dist"} <div>
/> <FilterSlider
</div> filterRange={[tempFilters.metro_dist__gt, tempFilters.metro_dist__lt]}
<div> title={"Расстояние до метро"}
<FilterSlider fullRange={ranges.metro_dist || [0, 0]}
filterRange={[filters.metro_dist__gt, filters.metro_dist__lt]} filterKey={"metro_dist"}
setFilterRange={setFilterWithKey} />
title={"Расстояние до метро"} </div>
fullRange={ranges.metro_dist || [0, 0]}
filterKey={"metro_dist"}
/>
</div> </div>
</div> </Collapse.Panel>
</Collapse.Panel> </Collapse>
</Collapse>
{filteredPostamatesGroups.map((category) => { {filteredPostamatesGroups.map((category) => {
return ( return (
<Collapse <Collapse
bordered={false}
expandIconPosition={"end"}
style={{
background: 'none',
}}
className="filter_group my-4"
>
<Collapse.Panel
key={`filter_${category.id}`} key={`filter_${category.id}`}
header={<Title type={"primary"} text={category.name} classNameText="text-black" />} bordered={false}
forceRender expandIconPosition={"end"}
style={{
background: 'none',
}}
className="filter_group my-4"
> >
<div className="mt-4 mb-12"> <Collapse.Panel
{category.groups.map((group) => { key={`filter_${category.id}`}
return ( header={<Title type={"primary"} text={category.name} classNameText="text-black" />}
<div> forceRender
<FilterSlider >
filterRange={[filters[`d${group.id}__gt`], filters[`d${group.id}__lt`]]} <div className="mt-4 mb-12">
setFilterRange={setFilterWithKey} {category.groups.map((group) => {
title={group.name} return (
fullRange={ranges[`d${group.id}`] || [0, 0]} <div key={group.id}>
filterKey={`d${group.id}`} <FilterSlider
dynamicKey filterRange={[tempFilters[`d${group.id}__gt`], tempFilters[`d${group.id}__lt`]]}
/> title={group.name}
</div> fullRange={ranges[`d${group.id}`] || [0, 0]}
) filterKey={`d${group.id}`}
})} dynamicKey
</div> />
</div>
</Collapse.Panel> )
</Collapse> })}
) </div>
})} </Collapse.Panel>
</Collapse>
)
})}
</div>
<div className="flex items-center justify-between p-4 border-0 border-t border-solid border-gray-300">
<span>Выбрано: {selectedCnt}</span>
<div className="flex gap-2">
<Button onClick={() => onCancel()}>
Отменить
</Button>
<Button onClick={() => onApply()} type="primary">
Применить
</Button>
</div>
</div>
</div> </div>
); );
}; };

@ -1,7 +1,46 @@
import {Button, Dropdown} from "antd"; import { Button, Dropdown, Popover } from "antd";
import {AdvancedFilters} from "./AdvancedFilters.jsx"; import { AdvancedFilters } from "./AdvancedFilters.jsx";
import { RightOutlined } from "@ant-design/icons";
import { useMemo } from "react";
import {
RANGE_FILTERS_KEYS,
RANGE_FILTERS_MAP,
usePendingPointsFilters
} from "../../../../stores/usePendingPointsFilters.js";
import {fieldHasChanged} from "../../../../utils.js";
export const AdvancedFiltersWrapper = () => { export const AdvancedFiltersWrapper = () => {
const { filters, ranges } = usePendingPointsFilters();
const selectedCnt = useMemo(() => {
let counter = 0;
RANGE_FILTERS_KEYS.map((key) => {
if (fieldHasChanged(filters, ranges, key).result) counter += 1;
})
return counter;
}, [filters]);
const getPopoverContent = () => {
const keys = RANGE_FILTERS_KEYS.map((key) => {
if (fieldHasChanged(filters, ranges, key).result) return key;
}).filter(k => !!k);
if (keys.length === 0) {
return <p className="my-0.5 text-white">Не выбрано ни одного фильтра</p>
}
return (
<div>
{keys.map((key) => {
if (RANGE_FILTERS_MAP[key]) return (
<p className="my-0.5 text-white " key={key}>{RANGE_FILTERS_MAP[key]}</p>
);
})}
</div>
);
}
const filtersRender = () => { const filtersRender = () => {
return ( return (
<AdvancedFilters /> <AdvancedFilters />
@ -17,9 +56,22 @@ export const AdvancedFiltersWrapper = () => {
> >
<Button <Button
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="w-full" className="w-full text-left flex justify-between items-center border-0 p-0 mt-16"
> >
Расширенные фильтры <div className="flex gap-2 items-center">
Расширенные фильтры
<Popover
content={getPopoverContent}
trigger="hover"
placement={"rightBottom"}
className="rounded-xl mt-0.5 bg-gray-200 p-1 flex justify-center items-center w-[25px] h-[25px] z-10 hover:text-black"
color="#000000cc"
zIndex={4000}
>
{selectedCnt}
</Popover>
</div>
<RightOutlined className="mt-0.5 mr-1"/>
</Button> </Button>
</Dropdown> </Dropdown>
); );

@ -1,11 +1,13 @@
import { SliderComponent as Slider } from "../../../../components/SliderComponent"; import { SliderComponent as Slider } from "../../../../components/SliderComponent";
import { useEffect } from "react"; import { useEffect } from "react";
import { import {
INITIAL, INITIAL, usePendingPointsFilters,
} from "../../../../stores/usePendingPointsFilters"; } from "../../../../stores/usePendingPointsFilters";
export const FilterSlider = ({ filterRange, setFilterRange, disabled, fullRange, title, filterKey, dynamicKey }) => { export const FilterSlider = ({ filterRange, disabled, fullRange, title, filterKey, dynamicKey }) => {
const handleAfterChange = (range) => setFilterRange(range, filterKey);
const { setFilterWithKey, setTempFilterWithKey } = usePendingPointsFilters();
const handleAfterChange = (range) => setTempFilterWithKey(range, filterKey);
useEffect(() => { useEffect(() => {
if (!fullRange) return; if (!fullRange) return;
@ -24,7 +26,8 @@ export const FilterSlider = ({ filterRange, setFilterRange, disabled, fullRange,
filterRange[1] === 0); filterRange[1] === 0);
if (shouldSetFullRange || (shouldSetDynamicKeyRange && dynamicKey)) { if (shouldSetFullRange || (shouldSetDynamicKeyRange && dynamicKey)) {
setFilterRange([min, max], filterKey); setTempFilterWithKey([min, max], filterKey);
setFilterWithKey([min, max], filterKey);
} }
}, [fullRange]); }, [fullRange]);

@ -3,6 +3,7 @@ import { getPoints } from "../../../api";
import { useMergeTableData } from "../useMergeTableData"; import { useMergeTableData } from "../useMergeTableData";
import { STATUSES } from "../../../config"; import { STATUSES } from "../../../config";
import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../../stores/usePendingPointsFilters"; import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../../stores/usePendingPointsFilters";
import {fieldHasChanged} from "../../../utils.js";
export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort) => { export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort) => {
const { filters, ranges } = usePendingPointsFilters(); const { filters, ranges } = usePendingPointsFilters();
@ -23,12 +24,7 @@ export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort
}; };
RANGE_FILTERS_KEYS.map((filterKey) => { RANGE_FILTERS_KEYS.map((filterKey) => {
const gtValue = filters[`${filterKey}__gt`]; if (!fieldHasChanged(filters, ranges, filterKey).result) return;
const gtInitial = ranges[filterKey][0];
const ltValue = filters[`${filterKey}__lt`];
const ltInitial = ranges[filterKey][1];
if (gtValue === gtInitial && ltValue === ltInitial) return;
tempParams = { tempParams = {
...tempParams, ...tempParams,
[`${filterKey}__gt`]: filters[`${filterKey}__gt`] - 1, [`${filterKey}__gt`]: filters[`${filterKey}__gt`] - 1,

@ -18,6 +18,22 @@ export const RANGE_FILTERS_KEYS = [
'metro_dist', 'metro_dist',
] ]
export const RANGE_FILTERS_MAP = {
doors: "Кол-во подъездов в жилом доме",
flat_cnt: "Кол-во квартир в подъезде жилого дома",
rival_post_cnt: "Кол-во постаматов других сетей",
rival_pvz_cnt: "Кол-во ПВЗ",
target_post_cnt: "Кол-во постаматов Мой постамат",
flats_cnt: "Кол-во квартир в окрестности",
tc_cnt: "Кол-во торговых центров",
culture_cnt: "Кол-во объектов культуры (театры, музей и тд)",
mfc_cnt: "Кол-во МФЦ",
public_stop_cnt: "Кол-во остановок ОТ",
supermarket_cnt: "Кол-во супермаркетов",
target_dist: "Расстояние до постамата Мой постамат",
metro_dist: "Расстояние до метро",
}
export const INITIAL = { export const INITIAL = {
prediction: [0, 0], prediction: [0, 0],
categories: [], categories: [],
@ -70,6 +86,7 @@ const INITIAL_RANGES = {
const store = (set) => ({ const store = (set) => ({
filters: INITIAL, filters: INITIAL,
ranges: INITIAL_RANGES, ranges: INITIAL_RANGES,
tempFilters: INITIAL,
setPrediction: (value) => { setPrediction: (value) => {
set((state) => { set((state) => {
@ -93,6 +110,12 @@ const store = (set) => ({
state.filters[`${key}__lt`] = value[1]; state.filters[`${key}__lt`] = value[1];
}), }),
setTempFilterWithKey: (value, key) =>
set((state) => {
state.tempFilters[`${key}__gt`] = value[0];
state.tempFilters[`${key}__lt`] = value[1];
}),
setRanges: (value) => setRanges: (value) =>
set((state) => { set((state) => {
state.ranges = value; state.ranges = value;

@ -19,5 +19,33 @@ export function transliterate(word){
}).join(""); }).join("");
} }
export function getFilteredGroups(categories) {
if (!categories) return [];
return categories
.filter((category) => category.visible)
.map((category) => {
return {
...category,
groups: [...category.groups.filter((group) => group.visible)],
}
})
}
export function fieldHasChanged(filters, ranges, filterKey) {
const r = ranges[filterKey]
const gtValue = filters[`${filterKey}__gt`];
const gtInitial = r ? r[0] : 0 ;
const ltValue = filters[`${filterKey}__lt`];
const ltInitial =r ? r[1] : 0;
const result = !(gtValue === gtInitial && ltValue === ltInitial)
return {
result,
gtValue,
ltValue,
}
}
export const isNil = (value) => export const isNil = (value) =>
value === undefined || value === null || value === ""; value === undefined || value === null || value === "";

Loading…
Cancel
Save