From 057a9416b5f12f57985e59ea05838def326c6805 Mon Sep 17 00:00:00 2001 From: Platon Yasev Date: Sun, 5 Mar 2023 13:19:10 +0300 Subject: [PATCH] Add address search --- src/Map/MapComponent.jsx | 2 + src/config.js | 6 ++ src/index.css | 4 ++ src/modules/Sidebar/AddressSearch.jsx | 82 ++++++++++++++++++++++++ src/modules/Sidebar/Header.jsx | 36 +++++++++++ src/modules/Sidebar/Sidebar.jsx | 2 + src/modules/Sidebar/TakeToWorkButton.jsx | 36 +++++++++-- src/stores/useMode.js | 15 +++++ src/utils.js | 0 9 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 src/modules/Sidebar/AddressSearch.jsx create mode 100644 src/modules/Sidebar/Header.jsx create mode 100644 src/stores/useMode.js create mode 100644 src/utils.js diff --git a/src/Map/MapComponent.jsx b/src/Map/MapComponent.jsx index 913b179..3e4bf08 100644 --- a/src/Map/MapComponent.jsx +++ b/src/Map/MapComponent.jsx @@ -11,6 +11,7 @@ import { Table } from "../modules/Table/Table"; import { usePopup } from "../stores/usePopup"; import { useClickedPointConfig } from "../stores/useClickedPointConfig"; import { Legend } from "../modules/Sidebar/Legend"; +import { AddressSearch } from "../modules/Sidebar/AddressSearch"; export const MapComponent = () => { const mapRef = useRef(null); @@ -110,6 +111,7 @@ export const MapComponent = () => { + diff --git a/src/config.js b/src/config.js index fa698fb..425895b 100644 --- a/src/config.js +++ b/src/config.js @@ -16,3 +16,9 @@ export const CATEGORIES = { export const DISABLED_FILTER_TEXT = "Фильтр заблокирован - было ручное редактирование"; + +export const MODES = { + INITIAL: "INITIAL", + APPROVE: "APPROVE", + WORKING: "WORKING", +}; diff --git a/src/index.css b/src/index.css index 86bbc70..09568aa 100644 --- a/src/index.css +++ b/src/index.css @@ -30,6 +30,10 @@ @apply bg-white-background rounded-xl max-h-[calc(100vh-100px)] overflow-y-auto; } +.ant-modal-header { + border-bottom: none; +} + .mapboxgl-ctrl-group, .maplibregl-ctrl-group { @apply bg-white-background; diff --git a/src/modules/Sidebar/AddressSearch.jsx b/src/modules/Sidebar/AddressSearch.jsx new file mode 100644 index 0000000..b68e1a9 --- /dev/null +++ b/src/modules/Sidebar/AddressSearch.jsx @@ -0,0 +1,82 @@ +import { AutoComplete, Input } from "antd"; +import { useQuery } from "@tanstack/react-query"; +import { SearchOutlined } from "@ant-design/icons"; +import { api } from "../../api"; +import { useEffect, useMemo, useState } from "react"; +import { useMap } from "react-map-gl"; +import parse from "wellknown"; + +function useDebounce(value, delay) { + const [debouncedValue, setDebouncedValue] = useState(value); + useEffect(() => { + const timer = setTimeout(() => setDebouncedValue(value), delay || 500); + return () => { + clearTimeout(timer); + }; + }, [value, delay]); + + return debouncedValue; +} + +export const AddressSearch = () => { + const { map } = useMap(); + const [value, setValue] = useState(""); + const debouncedValue = useDebounce(value); + + const { data } = useQuery(["address", debouncedValue], async () => { + const result = await api.get( + `/api/placement_points/search_address?page_size=100&address=${debouncedValue}` + ); + + return result.data; + }); + + const options = useMemo(() => { + if (!data) return []; + + return data.results.map((item) => ({ + label: item.address, + value: `${item.address}$${item.location_id}`, + item: item, + })); + }, [data]); + + const handleChange = (value) => { + if (!value) { + setValue(value); + } else { + setValue(value.split("$")[0]); + } + }; + + const handleSelect = (_value, option) => { + const geometry = parse(option.item.geometry); + map.flyTo({ + center: [geometry.coordinates[0], geometry.coordinates[1]], + zoom: 15, + speed: 5, + }); + }; + + return ( +
+ setValue("")} + > + } + placeholder="Введите адрес точки" + className="text-ellipsis" + /> + +
+ ); +}; diff --git a/src/modules/Sidebar/Header.jsx b/src/modules/Sidebar/Header.jsx new file mode 100644 index 0000000..910bea8 --- /dev/null +++ b/src/modules/Sidebar/Header.jsx @@ -0,0 +1,36 @@ +import { Radio } from "antd"; +import { MODES } from "../../config"; +import { useMode } from "../../stores/useMode"; + +const modeOptions = [ + { + label: "ЛКР", + value: MODES.INITIAL, + }, + { + label: "ЛВР", + value: MODES.APPROVE, + }, + { + label: "РП", + value: MODES.WORKING, + }, +]; + +export const Header = () => { + const { mode, setMode } = useMode(); + + const handleModeChange = ({ target: { value } }) => setMode(value); + + return ( +
+ +
+ ); +}; diff --git a/src/modules/Sidebar/Sidebar.jsx b/src/modules/Sidebar/Sidebar.jsx index 5c5e4ad..b5e1fae 100644 --- a/src/modules/Sidebar/Sidebar.jsx +++ b/src/modules/Sidebar/Sidebar.jsx @@ -7,6 +7,7 @@ import { import { TakeToWorkButton } from "./TakeToWorkButton"; import { Filters } from "./Filters"; import { ExportButton } from "./ExportButton"; +import { Header } from "./Header"; export const Sidebar = () => { const hasManualEdits = useHasManualEdits(); @@ -14,6 +15,7 @@ export const Sidebar = () => { return (
+
diff --git a/src/modules/Sidebar/TakeToWorkButton.jsx b/src/modules/Sidebar/TakeToWorkButton.jsx index 7406253..51778b7 100644 --- a/src/modules/Sidebar/TakeToWorkButton.jsx +++ b/src/modules/Sidebar/TakeToWorkButton.jsx @@ -1,10 +1,11 @@ -import { Button } from "antd"; +import { Alert, Button, Modal } from "antd"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { api } from "../../api"; import { useFilters } from "../../stores/useFilters"; import { usePointSelection } from "../../stores/usePointSelection"; import { STATUSES } from "../../config"; import { useUpdateLayerCounter } from "../../stores/useUpdateLayerCounter"; +import { useState } from "react"; export const TakeToWorkButton = () => { const { filters } = useFilters(); @@ -13,6 +14,8 @@ export const TakeToWorkButton = () => { const queryClient = useQueryClient(); const { toggleUpdateCounter } = useUpdateLayerCounter(); + const [isSuccessModalOpened, setIsSuccessModalOpened] = useState(false); + const { mutate } = useMutation({ mutationFn: () => { const params = new URLSearchParams({ @@ -30,6 +33,7 @@ export const TakeToWorkButton = () => { onSuccess: () => { queryClient.invalidateQueries(["table", 1, filters]); toggleUpdateCounter(); + setIsSuccessModalOpened(true); }, }); @@ -38,8 +42,32 @@ export const TakeToWorkButton = () => { }; return ( - + <> + + setIsSuccessModalOpened(false)} + > + Хорошо + , + ]} + > + + + ); }; diff --git a/src/stores/useMode.js b/src/stores/useMode.js new file mode 100644 index 0000000..8ccf9e2 --- /dev/null +++ b/src/stores/useMode.js @@ -0,0 +1,15 @@ +import { create } from "zustand"; +import { immer } from "zustand/middleware/immer"; +import { MODES } from "../config"; + +const store = (set) => ({ + mode: MODES.INITIAL, + + setMode: (mode) => { + set((state) => { + state.mode = mode; + }); + }, +}); + +export const useMode = create(immer(store)); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..e69de29