filters styling

dev
RekHoto 3 years ago
parent 5a10687778
commit cdbcc2dafe

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

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

@ -9,35 +9,11 @@ import { STATUSES } from "../../config";
import { useRegionFilterExpression } from "./useRegionFilterExpression";
import { LAYER_IDS } from "./constants";
import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../stores/usePendingPointsFilters";
import { usePostamatesAndPvzGroups } from "../../api.js";
import { useMemo } from "react";
import {fieldHasChanged} from "../../utils.js";
const statusExpression = ["==", ["get", "status"], STATUSES.pending];
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 { prediction, categories, region } = filters;
const { selection } = usePointSelection();
@ -54,14 +30,6 @@ const useFilterExpression = () => {
["<=", ["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) => {
return [
[">=", ["get", key], filters[`${key}__gt`]],
@ -69,11 +37,6 @@ const useFilterExpression = () => {
];
});
const dynamicKeyExpressions = dynamicKeyFiltersExpressions.filter((expression) => {
const filterKey = expression[0][1][1];
return fieldHasChanged(filters, ranges, filterKey).result;
}).flat();
const staticKeyExpressions = staticKeyFiltersExpressions.filter((expression) => {
const filterKey = expression[0][1][1];
return fieldHasChanged(filters, ranges, filterKey).result;
@ -91,8 +54,8 @@ const useFilterExpression = () => {
[
"any",
regionExpression
? ["all", ...predictionExpression, ...staticKeyExpressions, ...dynamicKeyExpressions, categoryExpression, regionExpression]
: ["all", ...predictionExpression, ...staticKeyExpressions, ...dynamicKeyExpressions, categoryExpression],
? ["all", ...predictionExpression, ...staticKeyExpressions, categoryExpression, regionExpression]
: ["all", ...predictionExpression, ...staticKeyExpressions, categoryExpression],
includedExpression,
],
];

@ -24,7 +24,7 @@ import { icons } from "../icons/icons-config";
import { LastMLRun } from "./LastMLRun";
import { useGetPendingPointsRange, useOtherGroups, usePostamatesAndPvzGroups } from "../api.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 = () => {
const mapRef = useRef(null);
@ -70,6 +70,8 @@ export const MapComponent = () => {
filteredPostamatesGroups.map((item) => {
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);
})
});
@ -195,7 +197,7 @@ export const MapComponent = () => {
);
img.onload = () =>
mapRef.current.addImage(icon.name, img);
img.src = 'https://docs.mapbox.com/mapbox-gl-js/assets/cat.png';
img.src = icon.url;
img.crossOrigin = "Anonymous";
});
}}

@ -194,10 +194,11 @@ export const useGetPendingPointsRange = () => {
});
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 {
prediction: data.prediction_current,

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

@ -1,8 +1,46 @@
import { Button, Dropdown } from "antd";
import { Button, Dropdown, Popover } from "antd";
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 = () => {
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 = () => {
return (
<AdvancedFilters />
@ -20,7 +58,19 @@ export const AdvancedFiltersWrapper = () => {
onClick={(e) => e.stopPropagation()}
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>
</Dropdown>

@ -6,8 +6,8 @@ import {
export const FilterSlider = ({ filterRange, disabled, fullRange, title, filterKey, dynamicKey }) => {
const { setFilterWithKey } = usePendingPointsFilters();
const handleAfterChange = (range) => setFilterWithKey(range, filterKey);
const { setFilterWithKey, setTempFilterWithKey } = usePendingPointsFilters();
const handleAfterChange = (range) => setTempFilterWithKey(range, filterKey);
useEffect(() => {
if (!fullRange) return;
@ -26,6 +26,7 @@ export const FilterSlider = ({ filterRange, disabled, fullRange, title, filterKe
filterRange[1] === 0);
if (shouldSetFullRange || (shouldSetDynamicKeyRange && dynamicKey)) {
setTempFilterWithKey([min, max], filterKey);
setFilterWithKey([min, max], filterKey);
}
}, [fullRange]);

@ -86,6 +86,7 @@ const INITIAL_RANGES = {
const store = (set) => ({
filters: INITIAL,
ranges: INITIAL_RANGES,
tempFilters: INITIAL,
setPrediction: (value) => {
set((state) => {
@ -109,6 +110,12 @@ const store = (set) => ({
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) =>
set((state) => {
state.ranges = value;

@ -1,5 +1,3 @@
import {usePendingPointsFilters} from "./stores/usePendingPointsFilters.js";
export function download(filename, data) {
const downloadLink = window.document.createElement("a");
downloadLink.href = window.URL.createObjectURL(
@ -33,11 +31,12 @@ export function getFilteredGroups(categories) {
})
}
export function fieldHasChanged(filters, ranges, filterKey) {
export function fieldHasChanged(filters, ranges, filterKey) {
const r = ranges[filterKey]
const gtValue = filters[`${filterKey}__gt`];
const gtInitial = ranges[filterKey][0];
const gtInitial = r ? r[0] : 0 ;
const ltValue = filters[`${filterKey}__lt`];
const ltInitial = ranges[filterKey][1];
const ltInitial =r ? r[1] : 0;
const result = !(gtValue === gtInitial && ltValue === ltInitial)

Loading…
Cancel
Save