Add address search to table; filter table on 2nd tab

dev
Platon Yasev 3 years ago
parent 04a31955d1
commit acd67f8d3a

@ -2,7 +2,7 @@ import { AutoComplete, Input } from "antd";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { SearchOutlined } from "@ant-design/icons"; import { SearchOutlined } from "@ant-design/icons";
import { api } from "../api"; import { api } from "../api";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useMap } from "react-map-gl"; import { useMap } from "react-map-gl";
import parse from "wellknown"; import parse from "wellknown";
import { usePopup } from "../stores/usePopup.js"; import { usePopup } from "../stores/usePopup.js";
@ -20,12 +20,15 @@ function useDebounce(value, delay) {
return debouncedValue; return debouncedValue;
} }
export const AddressSearch = () => { const renderLabel = (address) => <div>{address}</div>;
export const AddressSearch = ({ autoFocus = false }) => {
const { map } = useMap(); const { map } = useMap();
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const debouncedValue = useDebounce(value); const debouncedValue = useDebounce(value);
const { setPopup } = usePopup(); const { setPopup } = usePopup();
const { setClickedPointConfig } = useClickedPointConfig(); const { setClickedPointConfig } = useClickedPointConfig();
const inputRef = useRef();
const { data } = useQuery(["address", debouncedValue], async () => { const { data } = useQuery(["address", debouncedValue], async () => {
const result = await api.get( const result = await api.get(
@ -39,7 +42,7 @@ export const AddressSearch = () => {
if (!data) return []; if (!data) return [];
return data.results.map((item) => ({ return data.results.map((item) => ({
label: item.address, label: renderLabel(item.address),
value: `${item.address}$${item.id}`, value: `${item.address}$${item.id}`,
item: item, item: item,
})); }));
@ -69,6 +72,12 @@ export const AddressSearch = () => {
setClickedPointConfig(feature.properties.id, false); setClickedPointConfig(feature.properties.id, false);
}; };
useEffect(() => {
if (autoFocus && inputRef?.current) {
inputRef.current.focus();
}
}, [autoFocus]);
return ( return (
<div> <div>
<AutoComplete <AutoComplete
@ -81,11 +90,14 @@ export const AddressSearch = () => {
onSelect={handleSelect} onSelect={handleSelect}
allowClear={true} allowClear={true}
onClear={() => setValue("")} onClear={() => setValue("")}
autoFocus={autoFocus}
popupClassName={"overflow-visible"}
> >
<Input <Input
prefix={<SearchOutlined />} prefix={<SearchOutlined />}
placeholder="Введите адрес точки" placeholder="Введите адрес точки"
className="text-ellipsis" className="text-ellipsis"
ref={inputRef}
/> />
</AutoComplete> </AutoComplete>
</div> </div>

@ -16,7 +16,7 @@ export const PENDING_COLOR = {
export const CANCELLED_COLOR = "#CC2222"; export const CANCELLED_COLOR = "#CC2222";
export const APPROVE_COLOR = "#ff7d00"; export const APPROVE_COLOR = "#ff7d00";
export const WORKING_COLOR = "#006e01"; export const WORKING_COLOR = "#006e01";
export const UNMATCHED_COLOR = "#b4b4b4"; export const UNMATCHED_COLOR = "rgba(196,195,195,0.6)";
export const PVZ_COLOR = "#3f5be8"; export const PVZ_COLOR = "#3f5be8";
export const OTHER_POSTAMATES_COLOR = "#26a2a2"; export const OTHER_POSTAMATES_COLOR = "#26a2a2";

@ -8,7 +8,6 @@ import {
PVZ_COLOR, PVZ_COLOR,
WORKING_COLOR, WORKING_COLOR,
} from "./Layers/layers-config"; } from "./Layers/layers-config";
import { Title } from "../components/Title";
const LegendPointItem = ({ color, name }) => { const LegendPointItem = ({ color, name }) => {
return ( return (
@ -47,44 +46,58 @@ export function Legend() {
return ( return (
<div className="absolute bottom-[20px] left-[20px] text-xs text-grey z-10 bg-white-background rounded-xl p-3 space-y-3"> <div className="absolute bottom-[20px] left-[20px] text-xs text-grey z-10 bg-white-background rounded-xl p-3 space-y-3">
{mode !== MODES.WORKING && (
<div>
<Title text={"Статус локации"} className="text-center" />
<div className="space-y-1">
{mode === MODES.PENDING && (
<>
<LegendColorRampItem
colors={pendingColors}
name="К рассмотрению"
/>
<LegendPointItem name="Работает" color={WORKING_COLOR} />
<LegendPointItem name="Отменен" color={CANCELLED_COLOR} />
</>
)}
{mode === MODES.ON_APPROVAL && (
<>
<LegendPointItem
name="Согласование-установка"
color={APPROVE_COLOR}
/>
<LegendPointItem name="Работает" color={WORKING_COLOR} />
<LegendPointItem name="Отменен" color={CANCELLED_COLOR} />
</>
)}
</div>
</div>
)}
<div> <div>
<Title text={"Прочее"} className="text-center" />
<div className="space-y-1"> <div className="space-y-1">
<LegendPointItem name="ПВЗ" color={PVZ_COLOR} /> {mode === MODES.PENDING && (
<LegendPointItem <>
name="Постаматы прочих сетей" <LegendColorRampItem
color={OTHER_POSTAMATES_COLOR} colors={pendingColors}
/> name="Локации к рассмотрению"
/>
<LegendPointItem
name="Работающие постаматы"
color={WORKING_COLOR}
/>
<LegendPointItem
name="Отмененные локации"
color={CANCELLED_COLOR}
/>
</>
)}
{mode === MODES.ON_APPROVAL && (
<>
<LegendPointItem
name="Согласование-установка"
color={APPROVE_COLOR}
/>
<LegendPointItem
name="Работающие постаматы"
color={WORKING_COLOR}
/>
<LegendPointItem
name="Отмененные локации"
color={CANCELLED_COLOR}
/>
</>
)}
{mode === MODES.WORKING && (
<>
<LegendPointItem
name="Работающие постаматы"
color={WORKING_COLOR}
/>
</>
)}
</div> </div>
</div> </div>
<div className="space-y-1">
<LegendPointItem name="ПВЗ" color={PVZ_COLOR} />
<LegendPointItem
name="Постаматы прочих сетей"
color={OTHER_POSTAMATES_COLOR}
/>
</div>
</div> </div>
); );
} }

@ -28,7 +28,7 @@ export const FeatureProperties = ({ feature, dynamicStatus, postamatId }) => {
return isWorking ? [...config, ...workingPointFields] : config; return isWorking ? [...config, ...workingPointFields] : config;
}; };
const getValue = ({ field, render, empty, type }) => { const getValue = ({ field, render, empty, type, fallbackField }) => {
let value = feature.properties[field]; let value = feature.properties[field];
if (field === "status" && dynamicStatus) { if (field === "status" && dynamicStatus) {
@ -40,6 +40,7 @@ export const FeatureProperties = ({ feature, dynamicStatus, postamatId }) => {
} }
if (type === "region") { if (type === "region") {
value = value ? value : feature.properties[fallbackField];
value = render(value, data?.normalized); value = render(value, data?.normalized);
} else { } else {
value = render ? render(value) : value; value = render ? render(value) : value;

@ -11,12 +11,14 @@ export const commonPopupConfig = [
{ {
name: "Район", name: "Район",
field: "area_id", field: "area_id",
fallbackField: "area",
render: getRegionNameById, render: getRegionNameById,
type: "region", type: "region",
}, },
{ {
name: "Округ", name: "Округ",
field: "district_id", field: "district_id",
fallbackField: "district",
render: getRegionNameById, render: getRegionNameById,
type: "region", type: "region",
}, },

@ -61,6 +61,12 @@
padding: 8px; padding: 8px;
} }
.ant-select-item-option-content {
overflow: initial;
white-space: initial;
text-overflow: initial;
}
.generate-button { .generate-button {
color: #fff; color: #fff;
border-color: #cc2222ff !important; border-color: #cc2222ff !important;

@ -8,7 +8,9 @@ import { useMergeTableData } from "../useMergeTableData";
import { Header } from "./Header"; import { Header } from "./Header";
import { useOnApprovalPointsFilters } from "../../../stores/useOnApprovalPointsFilters"; import { useOnApprovalPointsFilters } from "../../../stores/useOnApprovalPointsFilters";
import { MakeWorkingModal } from "./MakeWorkingTable/MakeWorkingModal"; import { MakeWorkingModal } from "./MakeWorkingTable/MakeWorkingModal";
import { useColumns } from "../useColumns.js"; import { useColumns } from "../useColumns.jsx";
import { useLayersVisibility } from "../../../stores/useLayersVisibility.js";
import { LAYER_IDS } from "../../../Map/Layers/constants.js";
const extraCols = [ const extraCols = [
{ {
@ -30,16 +32,31 @@ export const OnApprovalTable = ({ fullWidth }) => {
const [isMakeWorkingModalOpened, setIsMakeWorkingModalOpened] = const [isMakeWorkingModalOpened, setIsMakeWorkingModalOpened] =
useState(false); useState(false);
const columns = useColumns(extraCols); const columns = useColumns(extraCols);
const { isVisible } = useLayersVisibility();
const clearSelected = () => setSelectedIds([]); const clearSelected = () => setSelectedIds([]);
const { data, isInitialLoading } = useQuery( const { data, isInitialLoading } = useQuery(
["on-approval-points", page, region], ["on-approval-points", page, region, isVisible],
async () => { async () => {
const statuses = [];
if (isVisible[LAYER_IDS.approve]) {
statuses.push(STATUSES.onApproval);
}
if (isVisible[LAYER_IDS.working]) {
statuses.push(STATUSES.working);
}
if (isVisible[LAYER_IDS.cancelled]) {
statuses.push(STATUSES.cancelled);
}
const params = new URLSearchParams({ const params = new URLSearchParams({
page, page,
page_size: pageSize, page_size: pageSize,
"status[]": [STATUSES.onApproval, STATUSES.working, STATUSES.cancelled], "status[]": statuses,
}); });
return await getPoints(params, region); return await getPoints(params, region);

@ -6,7 +6,7 @@ import { usePendingTableData } from "./usePendingTableData";
import { HeaderWrapper } from "../HeaderWrapper"; import { HeaderWrapper } from "../HeaderWrapper";
import { useExportPendingData } from "./useExportPendingData"; import { useExportPendingData } from "./useExportPendingData";
import { useCanEdit } from "../../../api"; import { useCanEdit } from "../../../api";
import { useColumns } from "../useColumns.js"; import { useColumns } from "../useColumns.jsx";
export const PendingTable = ({ fullWidth }) => { export const PendingTable = ({ fullWidth }) => {
const { selection, include, exclude } = usePointSelection(); const { selection, include, exclude } = usePointSelection();

@ -8,7 +8,7 @@ import { Table } from "../Table";
import { HeaderWrapper } from "../HeaderWrapper"; import { HeaderWrapper } from "../HeaderWrapper";
import { useExportWorkingData } from "./useExportWorkingData"; import { useExportWorkingData } from "./useExportWorkingData";
import { useWorkingPointsFilters } from "../../../stores/useWorkingPointsFilters"; import { useWorkingPointsFilters } from "../../../stores/useWorkingPointsFilters";
import { useColumns } from "./columns.js"; import { useColumns } from "./useColumns.jsx";
export const WorkingTable = ({ fullWidth }) => { export const WorkingTable = ({ fullWidth }) => {
const [pageSize, setPageSize] = useState(PAGE_SIZE); const [pageSize, setPageSize] = useState(PAGE_SIZE);

@ -1,6 +1,9 @@
import { useGetRegions } from "../../../components/RegionSelect.jsx"; import { useGetRegions } from "../../../components/RegionSelect.jsx";
import { useMemo } from "react"; import { useMemo } from "react";
import { getRegionNameById } from "../../../Map/Popup/mode-popup/config.js"; import { getRegionNameById } from "../../../Map/Popup/mode-popup/config.js";
import { SearchOutlined } from "@ant-design/icons";
import { Button, Popover } from "antd";
import { AddressSearch } from "../../../Map/AddressSearch.jsx";
export const useColumns = () => { export const useColumns = () => {
const { data: regions } = useGetRegions(); const { data: regions } = useGetRegions();
@ -8,7 +11,20 @@ export const useColumns = () => {
return useMemo(() => { return useMemo(() => {
return [ return [
{ {
title: "Адрес", title: (
<div className="flex items-center justify-between">
<span>Адрес</span>
<Popover
content={<AddressSearch autoFocus={true} />}
trigger="click"
placement={"right"}
>
<Button>
<SearchOutlined />
</Button>
</Popover>
</div>
),
dataIndex: "address", dataIndex: "address",
key: "address", key: "address",
width: 200, width: 200,
@ -47,6 +63,13 @@ export const useColumns = () => {
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
}, },
{
title: "Прогнозный трафик",
dataIndex: "prediction_current",
key: "prediction_current",
width: "120px",
ellipsis: true,
},
{ {
title: "Факт", title: "Факт",
dataIndex: "fact", dataIndex: "fact",
@ -68,6 +91,13 @@ export const useColumns = () => {
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
}, },
{
title: "Id постамата",
dataIndex: "postamat_id",
key: "postamat_id",
width: "70px",
ellipsis: true,
},
]; ];
}, [regions?.normalized]); }, [regions?.normalized]);
}; };

@ -2,6 +2,9 @@ import { STATUS_LABEL_MAPPER } from "../../config";
import { useMemo } from "react"; import { useMemo } from "react";
import { useGetRegions } from "../../components/RegionSelect.jsx"; import { useGetRegions } from "../../components/RegionSelect.jsx";
import { getRegionNameById } from "../../Map/Popup/mode-popup/config.js"; import { getRegionNameById } from "../../Map/Popup/mode-popup/config.js";
import { Button, Popover } from "antd";
import { AddressSearch } from "../../Map/AddressSearch.jsx";
import { SearchOutlined } from "@ant-design/icons";
export const useColumns = (fields = []) => { export const useColumns = (fields = []) => {
const { data: regions } = useGetRegions(); const { data: regions } = useGetRegions();
@ -9,13 +12,20 @@ export const useColumns = (fields = []) => {
return useMemo(() => { return useMemo(() => {
return [ return [
{ {
title: "id", title: (
dataIndex: "id", <div className="flex items-center justify-between">
key: "id", <span>Адрес</span>
width: 50, <Popover
}, content={<AddressSearch autoFocus={true} />}
{ trigger="click"
title: "Адрес", placement={"right"}
>
<Button>
<SearchOutlined />
</Button>
</Popover>
</div>
),
dataIndex: "address", dataIndex: "address",
key: "address", key: "address",
width: 200, width: 200,
Loading…
Cancel
Save