From c6ca36e1c29701b086b7f5ffda4f01549a2a205c Mon Sep 17 00:00:00 2001 From: RekHoto Date: Mon, 14 Aug 2023 08:20:28 +0400 Subject: [PATCH 1/2] filters and table columns --- src/Map/Layers/PendingPoints.jsx | 107 ++++++- .../AdvancedFilters/AdvancedFilters.jsx | 131 ++++++++ .../AdvancedFiltersWrapper.jsx | 25 ++ .../AdvancedFilters/Slider.jsx | 36 +++ .../PendingPointsFilters.jsx | 2 + .../Table/PendingTable/usePendingTableData.js | 62 +++- src/modules/Table/useColumns.jsx | 290 +++++++++++++++++- src/stores/usePendingPointsFilters.js | 104 +++++++ 8 files changed, 750 insertions(+), 7 deletions(-) create mode 100644 src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFilters.jsx create mode 100644 src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFiltersWrapper.jsx create mode 100644 src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/Slider.jsx diff --git a/src/Map/Layers/PendingPoints.jsx b/src/Map/Layers/PendingPoints.jsx index c5bd605..b5e8d33 100644 --- a/src/Map/Layers/PendingPoints.jsx +++ b/src/Map/Layers/PendingPoints.jsx @@ -14,7 +14,36 @@ const statusExpression = ["==", ["get", "status"], STATUSES.pending]; const useFilterExpression = () => { const { filters } = usePendingPointsFilters(); - const { prediction, categories, region } = filters; + const { + prediction, + categories, + region, + doors__gt, + doors__lt, + flat_cnt__gt, + flat_cnt__lt, + rival_post_cnt__gt, + rival_post_cnt__lt, + rival_pvz_cnt__gt, + rival_pvz_cnt__lt, + target_post_cnt__gt, + target_post_cnt__lt, + flats_cnt__gt, + flats_cnt__lt, + tc_cnt__gt, + tc_cnt__lt, + culture_cnt__gt, + culture_cnt__lt, + mfc_cnt__gt, + mfc_cnt__lt, + public_stop_cnt__gt, + public_stop_cnt__lt, + supermarket_cnt__gt, + supermarket_cnt__lt, + target_dist__gt, + target_dist__lt, + metro_dist__gt, + metro_dist__lt, } = filters; const { selection } = usePointSelection(); const includedArr = [...selection.included]; const excludedArr = [...selection.excluded]; @@ -28,6 +57,78 @@ const useFilterExpression = () => { ["<=", ["get", "prediction_current"], prediction[1]], ]; + const doorsExpression = [ + [">=", ["get", "doors"], doors__gt], + ["<=", ["get", "doors"], doors__lt], + ]; + + const flatExpression = [ + [">=", ["get", "flat_cnt"], flat_cnt__gt], + ["<=", ["get", "flat_cnt"], flat_cnt__lt], + ]; + + const rivalPostExpression = [ + [">=", ["get", "rival_post_cnt"], rival_post_cnt__gt], + ["<=", ["get", "rival_post_cnt"], rival_post_cnt__lt], + ]; + const rivalPvzExpression = [ + [">=", ["get", "rival_pvz_cnt"], rival_pvz_cnt__gt], + ["<=", ["get", "rival_pvz_cnt"], rival_pvz_cnt__lt], + ]; + const targetPostExpression = [ + [">=", ["get", "target_post_cnt"], target_post_cnt__gt], + ["<=", ["get", "target_post_cnt"], target_post_cnt__lt], + ]; + const flatsExpression = [ + [">=", ["get", "flats_cnt"], flats_cnt__gt], + ["<=", ["get", "flats_cnt"], flats_cnt__lt], + ]; + const tcExpression = [ + [">=", ["get", "tc_cnt"], tc_cnt__gt], + ["<=", ["get", "tc_cnt"], tc_cnt__lt], + ]; + const cultureExpression = [ + [">=", ["get", "culture_cnt"], culture_cnt__gt], + ["<=", ["get", "culture_cnt"], culture_cnt__lt], + ]; + const mfcExpression = [ + [">=", ["get", "mfc_cnt"], mfc_cnt__gt], + ["<=", ["get", "mfc_cnt"], mfc_cnt__lt], + ]; + const publicStopExpression = [ + [">=", ["get", "public_stop_cnt"], public_stop_cnt__gt], + ["<=", ["get", "public_stop_cnt"], public_stop_cnt__lt], + ]; + const supermarketExpression = [ + [">=", ["get", "supermarket_cnt"], supermarket_cnt__gt], + ["<=", ["get", "supermarket_cnt"], supermarket_cnt__lt], + ]; + const targetDistExpression = [ + [">=", ["get", "target_dist"], target_dist__gt], + ["<=", ["get", "target_dist"], target_dist__lt], + ]; + const metroDistExpression = [ + [">=", ["get", "metro_dist"], metro_dist__gt], + ["<=", ["get", "metro_dist"], metro_dist__lt], + ]; + + const filterExpressions = [ + ...doorsExpression, + ...flatExpression, + ...rivalPostExpression, + ...rivalPvzExpression, + ...targetPostExpression, + // ...flatsExpression, + ...tcExpression, + ...cultureExpression, + ...mfcExpression, + ...publicStopExpression, + ...supermarketExpression, + ...targetDistExpression, + // ...metroDistExpression + ] + + const categoryExpression = categories.length > 0 ? ["in", ["get", "category"], ["literal", categories]] @@ -40,8 +141,8 @@ const useFilterExpression = () => { [ "any", regionExpression - ? ["all", ...predictionExpression, categoryExpression, regionExpression] - : ["all", ...predictionExpression, categoryExpression], + ? ["all", ...predictionExpression, ...filterExpressions, categoryExpression, regionExpression] + : ["all", ...predictionExpression, ...filterExpressions, categoryExpression], includedExpression, ], ]; diff --git a/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFilters.jsx b/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFilters.jsx new file mode 100644 index 0000000..c3512d7 --- /dev/null +++ b/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFilters.jsx @@ -0,0 +1,131 @@ +import { usePendingPointsFilters } from "../../../../stores/usePendingPointsFilters"; +import {FilterSlider} from "./Slider.jsx"; + +export const AdvancedFilters = () => { + const { + filters, + setDoors, + setFlatCnt, + setRivalPostCnt, + setRivalPvzCnt, + setTargetPostCnt, + setFlatsCnt, + setTcCnt, + setCultureCnt, + setMfcCnt, + setPublicStopCnt, + setSupermarketCnt, + setTargetDist, + setMetroDist + } = usePendingPointsFilters(); + + return ( +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ {/*
*/} + {/* */} + {/*
*/} +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ {/*
*/} + {/* */} + {/*
*/} + +
+ ); +}; diff --git a/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFiltersWrapper.jsx b/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFiltersWrapper.jsx new file mode 100644 index 0000000..faa553a --- /dev/null +++ b/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/AdvancedFiltersWrapper.jsx @@ -0,0 +1,25 @@ +import {Button, Dropdown} from "antd"; +import {AdvancedFilters} from "./AdvancedFilters.jsx"; + +export const AdvancedFiltersWrapper = () => { + const filtersRender = () => { + return ( + + ) + }; + + return ( + filtersRender()} + placement='right' + > + + + ); +}; \ No newline at end of file diff --git a/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/Slider.jsx b/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/Slider.jsx new file mode 100644 index 0000000..2984fb8 --- /dev/null +++ b/src/modules/Sidebar/PendingPointsFilters/AdvancedFilters/Slider.jsx @@ -0,0 +1,36 @@ +import { SliderComponent as Slider } from "../../../../components/SliderComponent"; +import { useEffect } from "react"; +import { + INITIAL, +} from "../../../../stores/usePendingPointsFilters"; + +export const FilterSlider = ({ filterRange, setFilterRange, disabled, fullRange, title }) => { + const handleAfterChange = (range) => setFilterRange(range); + + useEffect(() => { + if (!fullRange) return; + + const min = fullRange[0]; + const max = fullRange[1]; + + const shouldSetFullRange = + filterRange[0] === INITIAL.prediction[0] && + filterRange[1] === INITIAL.prediction[1]; + + if (shouldSetFullRange) { + setFilterRange([min, max]); + } + }, [fullRange]); + + return ( + + ); +}; diff --git a/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx b/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx index f2d770a..b26e405 100644 --- a/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx +++ b/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx @@ -15,6 +15,7 @@ import { ClearFiltersButton } from "../../../components/ClearFiltersButton"; import { getDynamicActiveFilters } from "../utils"; import { api, useCanEdit } from "../../../api"; import { useQuery } from "@tanstack/react-query"; +import { AdvancedFiltersWrapper } from "./AdvancedFilters/AdvancedFiltersWrapper.jsx"; const useGetPendingPointsRange = () => { return useQuery( @@ -103,6 +104,7 @@ export const PendingPointsFilters = () => { fullRange={fullRange} isLoading={isInitialLoading} /> + {hasActiveFilters && ( diff --git a/src/modules/Table/PendingTable/usePendingTableData.js b/src/modules/Table/PendingTable/usePendingTableData.js index 7bf7243..ab993dc 100644 --- a/src/modules/Table/PendingTable/usePendingTableData.js +++ b/src/modules/Table/PendingTable/usePendingTableData.js @@ -6,9 +6,39 @@ import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort) => { const { filters } = usePendingPointsFilters(); - const { prediction, categories, region } = filters; + const { + prediction, + categories, + region, + doors__gt, + doors__lt, + flat_cnt__gt, + flat_cnt__lt, + rival_post_cnt__gt, + rival_post_cnt__lt, + rival_pvz_cnt__gt, + rival_pvz_cnt__lt, + target_post_cnt__gt, + target_post_cnt__lt, + flats_cnt__gt, + flats_cnt__lt, + tc_cnt__gt, + tc_cnt__lt, + culture_cnt__gt, + culture_cnt__lt, + mfc_cnt__gt, + mfc_cnt__lt, + public_stop_cnt__gt, + public_stop_cnt__lt, + supermarket_cnt__gt, + supermarket_cnt__lt, + target_dist__gt, + target_dist__lt, + metro_dist__gt, + metro_dist__lt + } = filters; - const { data, isInitialLoading, isFetching } = useQuery( + const {data, isInitialLoading, isFetching} = useQuery( ["table", page, filters, sort], async () => { const params = new URLSearchParams({ @@ -17,6 +47,32 @@ export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort "prediction_current[]": prediction, "status[]": [STATUSES.pending], "categories[]": categories, + doors__gt, + doors__lt, + flat_cnt__gt, + flat_cnt__lt, + rival_post_cnt__gt, + rival_post_cnt__lt, + rival_pvz_cnt__gt, + rival_pvz_cnt__lt, + target_post_cnt__gt, + target_post_cnt__lt, + // flats_cnt__gt, + // flats_cnt__lt, + tc_cnt__gt, + tc_cnt__lt, + culture_cnt__gt, + culture_cnt__lt, + mfc_cnt__gt, + mfc_cnt__lt, + public_stop_cnt__gt, + public_stop_cnt__lt, + supermarket_cnt__gt, + supermarket_cnt__lt, + target_dist__gt, + target_dist__lt, + metro_dist__gt, + metro_dist__lt, ordering: sort, }); @@ -32,7 +88,7 @@ export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort } ); - const { data: mergedData, isClickedPointLoading } = useMergeTableData( + const {data: mergedData, isClickedPointLoading} = useMergeTableData( data, setPageSize ); diff --git a/src/modules/Table/useColumns.jsx b/src/modules/Table/useColumns.jsx index 58f520b..25bed3a 100644 --- a/src/modules/Table/useColumns.jsx +++ b/src/modules/Table/useColumns.jsx @@ -8,7 +8,7 @@ import { SearchOutlined } from "@ant-design/icons"; import { useTable } from "../../stores/useTable.js"; import useLocalStorage from "../../hooks/useLocalStorage.js"; -const DEFAULT_LENGTH = 7; +const DEFAULT_LENGTH = 39; export const useColumns = (fields = [], key) => { const { data: regions } = useGetRegions(); const { @@ -113,6 +113,294 @@ export const useColumns = (fields = [], key) => { sorter: true, showSorterTooltip: false, }, + { + title: "Кол-во подъездов в жилом доме", + dataIndex: "doors", + key: "doors", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Класс энероэффективности жилого дома", + dataIndex: "enrg_cls", + key: "enrg_cls", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во квартир в подъезде жилого дома", + dataIndex: "flat_cnt", + key: "flat_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Год постройки жилого дома", + dataIndex: "year_bld", + key: "year_bld", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во этажей жилого дома", + dataIndex: "levels", + key: "levels", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Материал стен жилого дома", + dataIndex: "mat_nes", + key: "mat_nes", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во постаматов других сетей в окрестности 500м (далее аналогично)", + dataIndex: "rival_post_cnt", + key: "rival_post_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во ПВЗ", + dataIndex: "rival_pvz_cnt", + key: "rival_pvz_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во постаматов Мой постамат", + dataIndex: "target_post_cnt", + key: "target_post_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во квартир в окрестности", + dataIndex: "flats_cnt", + key: "flats_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во достопримечательностей", + dataIndex: "attraction_cnt", + key: "attraction_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во банков", + dataIndex: "bank_cnt", + key: "bank_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во торговых центров", + dataIndex: "tc_cnt", + key: "tc_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во бизнес-центров", + dataIndex: "bc_cnt", + key: "bc_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во клиник", + dataIndex: "clinic_cnt", + key: "clinic_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во объектов культуры (театры, музей и тд)", + dataIndex: "culture_cnt", + key: "culture_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во спортивных центров", + dataIndex: "sport_center_cnt", + key: "sport_center_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во лабораторий", + dataIndex: "lab_cnt", + key: "lab_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во школ", + dataIndex: "school_cnt", + key: "school_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во детских садов", + dataIndex: "kindergar_cnt", + key: "kindergar_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во МФЦ", + dataIndex: "mfc_cnt", + key: "mfc_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во аптек", + dataIndex: "pharmacy_cnt", + key: "pharmacy_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во остановок ОТ", + dataIndex: "public_stop_cnt", + key: "public_stop_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во объектов из HORECA", + dataIndex: "reca_cnt", + key: "reca_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во супермаркетов", + dataIndex: "supermarket_cnt", + key: "supermarket_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Кол-во премиальных супермаркетов", + dataIndex: "supermarket_premium_cnt", + key: "supermarket_premium_cnt", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Расстояние до постамата Мой постамата", + dataIndex: "target_dist", + key: "target_dist", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Расстояние до метро", + dataIndex: "metro_dist", + key: "metro_dist", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Стоимость жилой недвижимости ", + dataIndex: "property_price_bargains", + key: "property_price_bargains", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Бизнес-активность", + dataIndex: "business_activity", + key: "business_activity", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Эра постройки жилой недвижимости", + dataIndex: "property_era", + key: "property_era", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, + { + title: "Средняя этажность застройки", + dataIndex: "property_mean_floor", + key: "property_mean_floor", + width: "120px", + ellipsis: true, + sorter: true, + showSorterTooltip: false, + }, ...fields, ]; }, [regions?.normalized, fields, fullScreen]); diff --git a/src/stores/usePendingPointsFilters.js b/src/stores/usePendingPointsFilters.js index ff8ab77..6ef780f 100644 --- a/src/stores/usePendingPointsFilters.js +++ b/src/stores/usePendingPointsFilters.js @@ -6,6 +6,32 @@ export const INITIAL = { prediction: [0, 0], categories: [], region: null, + doors__gt: -1, + doors__lt: 500, + flat_cnt__gt: -1, + flat_cnt__lt: 5000, + rival_post_cnt__gt: -1, + rival_post_cnt__lt: 5000, + rival_pvz_cnt__gt: -1, + rival_pvz_cnt__lt: 5000, + target_post_cnt__gt: -1, + target_post_cnt__lt: 5000, + flats_cnt__gt: -1, + flats_cnt__lt: 5000, + tc_cnt__gt: -1, + tc_cnt__lt: 500, + culture_cnt__gt: -1, + culture_cnt__lt: 500, + mfc_cnt__gt: -1, + mfc_cnt__lt: 500, + public_stop_cnt__gt: -1, + public_stop_cnt__lt: 50, + supermarket_cnt__gt: -1, + supermarket_cnt__lt: 50, + target_dist__gt: -1, + target_dist__lt: 5000, + metro_dist__gt: -1, + metro_dist__lt: 50000, }; const store = (set) => ({ @@ -26,6 +52,84 @@ const store = (set) => ({ state.filters.region = value; }), + setDoors: (value) => + set((state) => { + state.filters.doors__gt = value[0]; + state.filters.doors__lt = value[1]; + }), + + setFlatCnt: (value) => + set((state) => { + state.filters.flat_cnt__gt = value[0]; + state.filters.flat_cnt__lt = value[1]; + }), + + setRivalPostCnt: (value) => + set((state) => { + state.filters.rival_post_cnt__gt = value[0]; + state.filters.rival_post_cnt__lt = value[1]; + }), + + setRivalPvzCnt: (value) => + set((state) => { + state.filters.rival_pvz_cnt__gt = value[0]; + state.filters.rival_pvz_cnt__lt = value[1]; + }), + + setTargetPostCnt: (value) => + set((state) => { + state.filters.target_post_cnt__gt = value[0]; + state.filters.target_post_cnt__lt = value[1]; + }), + + setFlatsCnt: (value) => + set((state) => { + state.filters.flats_cnt__gt = value[0]; + state.filters.flats_cnt__lt = value[1]; + }), + + setTcCnt: (value) => + set((state) => { + state.filters.tc_cnt__gt = value[0]; + state.filters.tc_cnt__lt = value[1]; + }), + + setCultureCnt: (value) => + set((state) => { + state.filters.culture_cnt__gt = value[0]; + state.filters.culture_cnt__lt = value[1]; + }), + + setMfcCnt: (value) => + set((state) => { + state.filters.mfc_cnt__gt = value[0]; + state.filters.mfc_cnt__lt = value[1]; + }), + + setPublicStopCnt: (value) => + set((state) => { + state.filters.public_stop_cnt__gt = value[0]; + state.filters.public_stop_cnt__lt = value[1]; + }), + + setSupermarketCnt: (value) => + set((state) => { + state.filters.supermarket_cnt__gt = value[0]; + state.filters.supermarket_cnt__lt = value[1]; + }), + + setTargetDist: (value) => + set((state) => { + state.filters.target_dist__gt = value[0]; + state.filters.target_dist__lt = value[1]; + }), + + setMetroDist: (value) => + set((state) => { + state.filters.metro_dist__gt = value[0]; + state.filters.metro_dist__lt = value[1]; + }), + clear: (fullRange) => set((state) => { if (!fullRange) { From b06a954170b25ec45b1be1640dfbc56a30d0519e Mon Sep 17 00:00:00 2001 From: RekHoto Date: Mon, 14 Aug 2023 08:25:32 +0400 Subject: [PATCH 2/2] fix --- src/modules/Table/TableSettings.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/Table/TableSettings.jsx b/src/modules/Table/TableSettings.jsx index 03ab133..2ecab96 100644 --- a/src/modules/Table/TableSettings.jsx +++ b/src/modules/Table/TableSettings.jsx @@ -30,7 +30,8 @@ export const TableSettings = ({orderColumns}) => { const columnsListRender = () => { return ( -
e.stopPropagation()} className='z-10 bg-white-background rounded-xl p-3 space-y-3'> +
e.stopPropagation()} className='z-10 bg-white-background rounded-xl p-3 space-y-3' + style={{ maxHeight: "80vh", overflowY: "scroll", margin: "24px 0 24px" }}> {(provided) => (