Add address search

dev
Platon Yasev 3 years ago
parent 958726e783
commit 057a9416b5

@ -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 = () => {
<Layers />
<Sidebar />
<AddressSearch />
<Legend />
<SignOut />

@ -16,3 +16,9 @@ export const CATEGORIES = {
export const DISABLED_FILTER_TEXT =
"Фильтр заблокирован - было ручное редактирование";
export const MODES = {
INITIAL: "INITIAL",
APPROVE: "APPROVE",
WORKING: "WORKING",
};

@ -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;

@ -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 (
<div className="absolute top-[20px] left-[360px]">
<AutoComplete
options={options}
style={{
width: 300,
}}
value={value}
onChange={handleChange}
onSelect={handleSelect}
allowClear={true}
onClear={() => setValue("")}
>
<Input
prefix={<SearchOutlined />}
placeholder="Введите адрес точки"
className="text-ellipsis"
/>
</AutoComplete>
</div>
);
};

@ -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 (
<div className="mb-4">
<Radio.Group
options={modeOptions}
onChange={handleModeChange}
value={mode}
optionType="button"
buttonStyle="solid"
/>
</div>
);
};

@ -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 (
<div className="absolute top-[20px] left-[20px] bg-white-background w-[320px] rounded-xl p-3 max-h-[calc(100%-40px)] overflow-y-auto z-10">
<Header />
<div className="space-y-5">
<LayersVisibility />
<Filters disabled={hasManualEdits} />

@ -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 (
<Button type="primary" block className={"mt-2"} onClick={takeToWork}>
Взять в работу
</Button>
<>
<Button type="primary" block className={"mt-2"} onClick={takeToWork}>
Взять в работу
</Button>
<Modal
title={" "}
centered
open={isSuccessModalOpened}
footer={[
<Button
key="ok-button"
type="primary"
onClick={() => setIsSuccessModalOpened(false)}
>
Хорошо
</Button>,
]}
>
<Alert
message="Успешно"
description="Выбранные точки отправлены на согласование. Посмотреть на них можно во второй
вкладке"
type="success"
showIcon
/>
</Modal>
</>
);
};

@ -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));
Loading…
Cancel
Save