From 9d9a1d13bf593b07ac15593fbc400c15298f3e55 Mon Sep 17 00:00:00 2001 From: RekHoto Date: Sat, 16 Sep 2023 22:18:18 +0400 Subject: [PATCH] points to merge count fix, add to db fix, update status dbTable fix, download template fix, popup name fix, hide table header, popup fields rearrangement, changed logo, cancelled points styling --- src/Map/Header.jsx | 6 +- src/Map/Layers/PendingPoints.jsx | 5 +- src/Map/Layers/layers-config.js | 11 +++- src/Map/Legend.jsx | 6 +- src/Map/Popup/Popup.jsx | 2 +- .../Popup/mode-popup/FeatureProperties.jsx | 6 +- src/Map/Popup/mode-popup/config.js | 10 ++- src/api.js | 61 ++++++++++--------- src/hooks/useUpdateStatus.js | 5 +- src/icons/Logo.jsx | 35 +++++++++++ src/modules/ImportMode/LoadingStage.jsx | 9 ++- src/modules/ImportMode/MergePointsModal.jsx | 4 +- .../ImportMode/PointsFileUploadModal.jsx | 14 ++--- .../PendingPointsFilters.jsx | 5 +- .../Table/PendingTable/usePendingTableData.js | 2 - src/modules/Table/Table.jsx | 1 + src/utils.js | 9 +++ 17 files changed, 129 insertions(+), 62 deletions(-) diff --git a/src/Map/Header.jsx b/src/Map/Header.jsx index d821ded..c81ec01 100644 --- a/src/Map/Header.jsx +++ b/src/Map/Header.jsx @@ -1,4 +1,4 @@ -import { Logo } from "../icons/Logo"; +import { HeaderLogo } from "../icons/Logo"; import { AddressSearch } from "./AddressSearch"; import { twMerge } from "tailwind-merge"; import { ModeSelector } from "../components/ModeSelector"; @@ -6,8 +6,8 @@ import { ModeSelector } from "../components/ModeSelector"; export const Header = () => { return (
-
- +
+
diff --git a/src/Map/Layers/PendingPoints.jsx b/src/Map/Layers/PendingPoints.jsx index ab3fcaf..5bbaeae 100644 --- a/src/Map/Layers/PendingPoints.jsx +++ b/src/Map/Layers/PendingPoints.jsx @@ -9,7 +9,7 @@ import { STATUSES } from "../../config"; import { useRegionFilterExpression } from "./useRegionFilterExpression"; import { LAYER_IDS } from "./constants"; import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../stores/usePendingPointsFilters"; -import { fieldHasChanged } from "../../utils.js"; +import { fieldHasChanged, predictionHasChanged } from "../../utils.js"; import { useSourceLayerName } from "../../api.js"; import { useMode } from "../../stores/useMode.js"; @@ -28,7 +28,7 @@ const useFilterExpression = () => { const includedExpression = ["in", ["get", "id"], ["literal", includedArr]]; const excludedExpression = ["in", ["get", "id"], ["literal", excludedArr]]; - const predictionExpression = [ + const rawPredictionExpression = [ [">=", ["get", "prediction_current"], prediction[0]], ["<=", ["get", "prediction_current"], prediction[1]], ]; @@ -51,6 +51,7 @@ const useFilterExpression = () => { : true; const statusExpression = isImportMode ? true : rawStatusExpression; + const predictionExpression = predictionHasChanged(filters, ranges) ? rawPredictionExpression : [true]; const matchFilterExpression = [ "all", diff --git a/src/Map/Layers/layers-config.js b/src/Map/Layers/layers-config.js index c99b2b1..e87863a 100644 --- a/src/Map/Layers/layers-config.js +++ b/src/Map/Layers/layers-config.js @@ -37,7 +37,7 @@ export const PENDING_COLOR = { ], }; -export const CANCELLED_COLOR = "#CC2222"; +export const CANCELLED_COLOR = "#A6A6A6"; export const APPROVE_COLOR = "#ff7d00"; export const WORKING_COLOR = "#006e01"; export const UNMATCHED_COLOR = "rgba(196,195,195,0.6)"; @@ -112,7 +112,14 @@ export const workingPointBackgroundLayer = { "circle-stroke-color": "#252525", }, }; -export const cancelledPointLayer = getPointConfig(CANCELLED_COLOR); +export const cancelledPointLayer = { + ...getPointConfig(CANCELLED_COLOR), + paint: { + ...getPointConfig(CANCELLED_COLOR).paint, + "circle-stroke-width": 1, + "circle-stroke-color": "#252525", + }, +}; export const pvzPointLayer = getPointConfig(PVZ_COLOR); export const otherPostamatesLayer = getPointConfig(OTHER_POSTAMATES_COLOR); diff --git a/src/Map/Legend.jsx b/src/Map/Legend.jsx index 62272e3..54fce21 100644 --- a/src/Map/Legend.jsx +++ b/src/Map/Legend.jsx @@ -10,13 +10,13 @@ import {Logo} from "../icons/Logo.jsx"; import {Collapse, Image} from "antd"; import React from "react"; -const LegendPointItem = ({color, imageSrc, name, hideImage}) => { +const LegendPointItem = ({color, imageSrc, name, hideImage, border}) => { return (
{imageSrc && } {color && !imageSrc && ( )} @@ -100,6 +100,7 @@ export function Legend({ postGroups, otherGroups }) { )} @@ -113,6 +114,7 @@ export function Legend({ postGroups, otherGroups }) { )} diff --git a/src/Map/Popup/Popup.jsx b/src/Map/Popup/Popup.jsx index f56d645..a1ad29f 100644 --- a/src/Map/Popup/Popup.jsx +++ b/src/Map/Popup/Popup.jsx @@ -50,7 +50,7 @@ const MultipleFeaturesPopup = ({ features, points }) => {
{features.map((feature) => { const featureId = feature.properties.id; - const point = points.find(p => p.id = featureId); + const point = points.find(p => p.id === featureId); const isSelected = (doesMatchFilter(filters, ranges, feature) && !selection.excluded.has(featureId)) || selection.included.has(featureId); const handleSelect = () => { diff --git a/src/Map/Popup/mode-popup/FeatureProperties.jsx b/src/Map/Popup/mode-popup/FeatureProperties.jsx index 1ccbb19..7d36798 100644 --- a/src/Map/Popup/mode-popup/FeatureProperties.jsx +++ b/src/Map/Popup/mode-popup/FeatureProperties.jsx @@ -1,7 +1,7 @@ import { CATEGORIES, STATUSES } from "../../../config"; import { commonPopupConfig, - residentialPopupConfig, + residentialPopupFields, rivalsConfig, workingPointFields, } from "./config"; @@ -71,8 +71,8 @@ export const FeatureProperties = ({ feature, dynamicStatus, postamatId, point }) return rivalsConfig; } - const config = isResidential ? residentialPopupConfig : commonPopupConfig; - return isWorking ? [...config, ...workingPointFields] : config; + const config = isWorking ? [...commonPopupConfig, ...workingPointFields] : commonPopupConfig; + return isResidential ? [...config, ...residentialPopupFields] : config; }; const getValue = ({ field, render, empty, type, fallbackField }) => { diff --git a/src/Map/Popup/mode-popup/config.js b/src/Map/Popup/mode-popup/config.js index 349eddf..e356442 100644 --- a/src/Map/Popup/mode-popup/config.js +++ b/src/Map/Popup/mode-popup/config.js @@ -41,8 +41,7 @@ export const commonPopupConfig = [ }, ]; -export const residentialPopupConfig = [ - ...commonPopupConfig, +export const residentialPopupFields = [ { name: "Кол-во квартир", field: "flat_cnt", @@ -59,8 +58,15 @@ export const residentialPopupConfig = [ name: "Материал стен", field: "mat_nes", }, +] + +export const residentialPopupConfig = [ + ...commonPopupConfig, + ...residentialPopupFields ]; + + export const workingPointFields = [ { name: "План", field: "plan_current" }, { name: "Факт", field: "fact" }, diff --git a/src/api.js b/src/api.js index f3b3962..3aabd15 100644 --- a/src/api.js +++ b/src/api.js @@ -26,7 +26,7 @@ export const useDbTableName = () => { export const useSourceLayerName = () => { const {isImportMode} = useMode(); - if (isImportMode) return "public.prepoints_with_dist"; + if (isImportMode) return "public.service_preplacementpoint"; return "public.points_with_dist"; } @@ -100,6 +100,7 @@ export const importPoints = async (id) => { }; export const useGetTotalInitialPointsCount = () => { + const { isImportMode } = useMode(); const dbTable = useDbTableName(); return useQuery( ["all-initial-count", dbTable], @@ -107,8 +108,8 @@ export const useGetTotalInitialPointsCount = () => { const params = new URLSearchParams({ page: 1, page_size: 1, - "status[]": [STATUSES.pending], }); + if (!isImportMode) params.append("status[]", STATUSES.pending); return await getPoints(params, null, dbTable); }, @@ -118,8 +119,8 @@ export const useGetTotalInitialPointsCount = () => { export const useGetFilteredPendingPointsCount = (isMerge) => { const { filters, ranges } = usePendingPointsFilters(); + const { isImportMode } = useMode(); const { - prediction, categories, region, } = filters; @@ -132,23 +133,28 @@ export const useGetFilteredPendingPointsCount = (isMerge) => { const params = new URLSearchParams({ page: 1, page_size: 1, - "prediction_current[]": prediction, - "status[]": [STATUSES.pending], "categories[]": categories, "included[]": includedIds, }); + if (!isImportMode) params.append("status[]", STATUSES.pending); + appendFiltersInUse(params, filters, ranges); - if (isMerge) params.append("matching_status", "New") return params; } + const getMergeParams = () => { + return new URLSearchParams({ + "matching_status": "New", + }); + } + const dbTable = useDbTableName(); return useQuery( ["filtered-points", filters, dbTable, includedIds], async () => { - const params = getParams(); + const params = isMerge ? getMergeParams() : getParams(); return await getPoints(params, region, dbTable); }, @@ -156,34 +162,31 @@ export const useGetFilteredPendingPointsCount = (isMerge) => { ); }; -export const useMergePointsToDb = () => { - const { filters, ranges } = usePendingPointsFilters(); - const { - prediction, - categories, - } = filters; - const { - selection: { included }, - } = usePointSelection(); - - const includedIds = [...included]; - const getParams = () => { - const params = new URLSearchParams({ - page: 1, - page_size: 1, - "prediction_current[]": prediction, - "categories[]": categories, - "included[]": includedIds, +export const useGetPointsToMergeCount = () => { + const getMergeParams = () => { + return new URLSearchParams({ + "matching_status": "New", }); - appendFiltersInUse(params, filters, ranges); - - return params; } + const dbTable = useDbTableName(); + + return useQuery( + ["filtered-points", dbTable], + async () => { + const params = getMergeParams(); + + return await getPoints(params, null, dbTable); + }, + { select: (data) => data.count, keepPreviousData: true } + ); +}; + +export const useMergePointsToDb = () => { return useMutation({ mutationFn: () => { return api.post( - `/api/pre_placement_points/move_points/?${getParams().toString()}` + `/api/pre_placement_points/move_points/` ); }, }); diff --git a/src/hooks/useUpdateStatus.js b/src/hooks/useUpdateStatus.js index 170ca5d..84f84fb 100644 --- a/src/hooks/useUpdateStatus.js +++ b/src/hooks/useUpdateStatus.js @@ -1,14 +1,15 @@ import { useMutation } from "@tanstack/react-query"; -import { api } from "../api"; +import { api, useDbTableName } from "../api"; import { useUpdateLayerCounter } from "../stores/useUpdateLayerCounter"; export const useUpdateStatus = ({ onSuccess }) => { const { toggleUpdateCounter } = useUpdateLayerCounter(); + const dbTable = useDbTableName(); return useMutation({ mutationFn: (params) => { return api.put( - `/api/placement_points/update_status/?${params.toString()}` + `/api/${dbTable}/update_status/?${params.toString()}` ); }, onSuccess: () => { diff --git a/src/icons/Logo.jsx b/src/icons/Logo.jsx index 44a6ff6..dddd395 100644 --- a/src/icons/Logo.jsx +++ b/src/icons/Logo.jsx @@ -21,3 +21,38 @@ export const Logo = ({ width = 40, height = 40, fill }) => { ); }; + +export const HeaderLogo = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/src/modules/ImportMode/LoadingStage.jsx b/src/modules/ImportMode/LoadingStage.jsx index eff5ba2..64d4aa1 100644 --- a/src/modules/ImportMode/LoadingStage.jsx +++ b/src/modules/ImportMode/LoadingStage.jsx @@ -1,7 +1,8 @@ import { useLayoutEffect, useRef, useState } from "react"; -import { uploadPointsFile } from "../../api.js"; +import { downloadImportTemplate, uploadPointsFile } from "../../api.js"; import { Button, Upload } from "antd"; import { UploadOutlined } from "@ant-design/icons"; +import { download } from "../../utils.js"; export const LoadingStage = ({setFileId}) => { const ref = useRef(null); @@ -24,6 +25,11 @@ export const LoadingStage = ({setFileId}) => { } }; + const onTemplateDownload = async () => { + const data = await downloadImportTemplate(); + await download('template.xlsx', data); + } + useLayoutEffect(() => { if (ref && ref.current && !isClicked) { ref.current.click(); @@ -40,6 +46,7 @@ export const LoadingStage = ({setFileId}) => { > + ); } \ No newline at end of file diff --git a/src/modules/ImportMode/MergePointsModal.jsx b/src/modules/ImportMode/MergePointsModal.jsx index 9b7d43c..d3df527 100644 --- a/src/modules/ImportMode/MergePointsModal.jsx +++ b/src/modules/ImportMode/MergePointsModal.jsx @@ -1,6 +1,6 @@ import { Button, Modal, Spin } from "antd"; import { useState } from "react"; -import {useGetFilteredPendingPointsCount, useMergePointsToDb} from "../../api.js"; +import { useGetPointsToMergeCount, useMergePointsToDb } from "../../api.js"; import { CheckCircleOutlined, InfoCircleOutlined, LoadingOutlined } from "@ant-design/icons"; import { useMode } from "../../stores/useMode.js"; @@ -8,7 +8,7 @@ export const MergePointsModal = ({isOpened, onClose}) => { const {setImportMode} = useMode(); const [isLoading, setIsLoading] = useState(false); const { data: filteredCount, isInitialLoading: isFilteredLoading } = - useGetFilteredPendingPointsCount(true); + useGetPointsToMergeCount(); const [isSuccess, setIsSuccess] = useState(false); const {mutateAsync: mergePoints} = useMergePointsToDb(); diff --git a/src/modules/ImportMode/PointsFileUploadModal.jsx b/src/modules/ImportMode/PointsFileUploadModal.jsx index f4fa10e..465a7f5 100644 --- a/src/modules/ImportMode/PointsFileUploadModal.jsx +++ b/src/modules/ImportMode/PointsFileUploadModal.jsx @@ -1,10 +1,13 @@ import { Button, Modal, Spin } from "antd"; import { useState } from "react"; -import { downloadImportTemplate, importPoints } from "../../api.js"; +import { importPoints } from "../../api.js"; import { LoadingStage } from "./LoadingStage.jsx"; import { ReportStage } from "./ReportStage.jsx"; -import {CheckCircleOutlined, CloseCircleOutlined, LoadingOutlined} from "@ant-design/icons"; -import { download } from "../../utils.js"; +import { + CheckCircleOutlined, + CloseCircleOutlined, + LoadingOutlined +} from "@ant-design/icons"; export const PointsFileUploadModal = ({onClose, isOpened}) => { const [fileId, setFileId] = useState(); @@ -13,10 +16,6 @@ export const PointsFileUploadModal = ({onClose, isOpened}) => { const [isReportStage, setIsReportStage] = useState(false); const [isError, setIsError] = useState(false); - const onTemplateDownload = async () => { - const data = await downloadImportTemplate(); - await download('template.xlsx', data); - } const onImportPoints = async () => { setIsImporting(true); try { @@ -107,7 +106,6 @@ export const PointsFileUploadModal = ({onClose, isOpened}) => { footer={getFooter()} > {getContent()} - ); } \ No newline at end of file diff --git a/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx b/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx index d6e2d38..d800891 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 { useDbTableName, useCanEdit, useGetPendingPointsRange } from "../../../api"; import { AdvancedFiltersWrapper } from "./AdvancedFilters/AdvancedFiltersWrapper.jsx"; +import { predictionHasChanged } from "../../../utils.js"; export const PendingPointsFilters = () => { const hasManualEdits = useHasManualEdits(); @@ -36,9 +37,7 @@ export const PendingPointsFilters = () => { if (gtChanged || ltChanged) setFilterWithKey(newRanges[key], key); }); - const gtChanged = ranges.prediction[0] !== newRanges.prediction[0]; - const ltChanged = ranges.prediction[1] !== newRanges.prediction[1]; - if (gtChanged || ltChanged) setPrediction(newRanges.prediction); + if (predictionHasChanged(newRanges, ranges)) setPrediction(newRanges.prediction); setRanges({...ranges, ...newRanges}); }, [data]); diff --git a/src/modules/Table/PendingTable/usePendingTableData.js b/src/modules/Table/PendingTable/usePendingTableData.js index bbd3221..d2cb08e 100644 --- a/src/modules/Table/PendingTable/usePendingTableData.js +++ b/src/modules/Table/PendingTable/usePendingTableData.js @@ -9,7 +9,6 @@ import { useMode } from "../../../stores/useMode.js"; export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort) => { const { filters, ranges } = usePendingPointsFilters(); const { - prediction, categories, region, } = filters; @@ -21,7 +20,6 @@ export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort const params = new URLSearchParams({ page, page_size: pageSize, - "prediction_current[]": prediction, "categories[]": categories, ordering: sort, }); diff --git a/src/modules/Table/Table.jsx b/src/modules/Table/Table.jsx index ca49d03..b8d0cc3 100644 --- a/src/modules/Table/Table.jsx +++ b/src/modules/Table/Table.jsx @@ -67,6 +67,7 @@ export const Table = React.memo( showSizeChanger: false, position: "bottomCenter", }} + showHeader={data?.results && data.results.length > 0} dataSource={data?.results} columns={columns} onChange={onChange} diff --git a/src/utils.js b/src/utils.js index 1fbffab..829d9f1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -116,6 +116,15 @@ export const appendFiltersInUse = (params, filters, ranges) => { params.append(`${filterKey}__lt`, filters[`${filterKey}__lt`] + 1); } }); + if (predictionHasChanged(filters, ranges)) { + params.append("prediction_current[]", filters.prediction); + } +} + +export const predictionHasChanged = (filters, ranges) => { + const gtChanged = ranges.prediction[0] !== filters.prediction[0]; + const ltChanged = ranges.prediction[1] !== filters.prediction[1]; + return gtChanged || ltChanged; } export const isNil = (value) =>