parent
9a8dacc38a
commit
2b1494993c
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,22 @@
|
|||||||
|
import { Logo } from "../icons/Logo";
|
||||||
|
import { AddressSearch } from "../modules/Sidebar/AddressSearch";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import { ModeSelector } from "../components/ModeSelector";
|
||||||
|
|
||||||
|
export const MapHeader = ({ isSidebarCollapsed }) => {
|
||||||
|
return (
|
||||||
|
<div className="absolute top-[20px] left-[30px] flex items-center gap-x-10">
|
||||||
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
"hidden",
|
||||||
|
isSidebarCollapsed && "flex items-center gap-x-3 "
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Logo />
|
||||||
|
<ModeSelector />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AddressSearch />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,215 +0,0 @@
|
|||||||
import { Popup } from "react-map-gl";
|
|
||||||
import { Button, Col, Row } from "antd";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
import { usePointSelection } from "../stores/usePointSelection";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { CATEGORIES, MODES } from "../config";
|
|
||||||
import { useClickedPointConfig } from "../stores/useClickedPointConfig";
|
|
||||||
import { useMode } from "../stores/useMode";
|
|
||||||
import { useUpdateLayerCounter } from "../stores/useUpdateLayerCounter";
|
|
||||||
import { LAYER_IDS } from "./Layers/constants";
|
|
||||||
|
|
||||||
const popupConfig = [
|
|
||||||
{
|
|
||||||
name: "Id",
|
|
||||||
field: "location_id",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Адрес",
|
|
||||||
field: "address",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Район",
|
|
||||||
field: "rayon_id",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Округ",
|
|
||||||
field: "okrug_id",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Название",
|
|
||||||
field: "name",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Категория",
|
|
||||||
field: "category",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Статус",
|
|
||||||
field: "status",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Прогнозный трафик",
|
|
||||||
field: "prediction_current",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const residentialPointConfig = [
|
|
||||||
{
|
|
||||||
name: "Id",
|
|
||||||
field: "location_id",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Адрес",
|
|
||||||
field: "address",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Район",
|
|
||||||
field: "rayon_id",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Округ",
|
|
||||||
field: "okrug_id",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Название",
|
|
||||||
field: "name",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Категория",
|
|
||||||
field: "category",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Статус",
|
|
||||||
field: "status",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Прогнозный трафик",
|
|
||||||
field: "prediction_current",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Кол-во квартир",
|
|
||||||
field: "flat_cnt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Год постройки",
|
|
||||||
field: "year_bld",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Кол-во этажей",
|
|
||||||
field: "levels",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Материал стен",
|
|
||||||
field: "mat_nes",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const PopupWrapper = ({ lat, lng, onClose, children }) => {
|
|
||||||
return (
|
|
||||||
<Popup
|
|
||||||
longitude={lng}
|
|
||||||
latitude={lat}
|
|
||||||
onClose={onClose}
|
|
||||||
closeOnClick={false}
|
|
||||||
style={{ minWidth: "300px" }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Popup>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const SingleFeaturePopup = ({ feature }) => {
|
|
||||||
const { include, selection, exclude } = usePointSelection();
|
|
||||||
const { setClickedPointConfig } = useClickedPointConfig();
|
|
||||||
const { mode } = useMode();
|
|
||||||
const doesMatchFilter = feature.layer.id === LAYER_IDS["initial-match"];
|
|
||||||
const featureId = feature.properties.location_id;
|
|
||||||
|
|
||||||
const { updateCounter } = useUpdateLayerCounter();
|
|
||||||
|
|
||||||
useEffect(() => setClickedPointConfig(featureId, doesMatchFilter), [feature]);
|
|
||||||
|
|
||||||
const isResidential = feature.properties.category === CATEGORIES.residential;
|
|
||||||
|
|
||||||
const config = isResidential ? residentialPointConfig : popupConfig;
|
|
||||||
|
|
||||||
const isSelected =
|
|
||||||
(doesMatchFilter || selection.included.has(featureId)) &&
|
|
||||||
!selection.excluded.has(featureId);
|
|
||||||
|
|
||||||
const handleSelect = () => {
|
|
||||||
if (isSelected) {
|
|
||||||
exclude(featureId);
|
|
||||||
} else {
|
|
||||||
include(featureId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div key={`popup-${updateCounter}`}>
|
|
||||||
{config.map(({ field, name }) => {
|
|
||||||
return (
|
|
||||||
<Row className={twMerge("p-1")} key={field}>
|
|
||||||
<Col className={"font-semibold"} span={12}>
|
|
||||||
{name}
|
|
||||||
</Col>
|
|
||||||
<Col span={12}>{feature.properties[field]}</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
{mode === MODES.INITIAL && (
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
className="mt-2 mx-auto"
|
|
||||||
block
|
|
||||||
onClick={handleSelect}
|
|
||||||
>
|
|
||||||
{isSelected ? "Исключить из выборки" : "Добавить в выборку"}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const MultipleFeaturesPopup = ({ features, onSelect }) => {
|
|
||||||
return (
|
|
||||||
<div className="space-y-2 p-1">
|
|
||||||
{features.map((feature) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
type={
|
|
||||||
feature.layer.id === LAYER_IDS["initial-match"] ? "primary" : ""
|
|
||||||
}
|
|
||||||
className="flex items-center gap-x-1"
|
|
||||||
block
|
|
||||||
onClick={() => onSelect(feature)}
|
|
||||||
key={feature.properties.location_id}
|
|
||||||
>
|
|
||||||
<span>{feature.properties.location_id}</span>
|
|
||||||
<span>{feature.properties.category}</span>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MapPopup = ({ features, lat, lng, onClose }) => {
|
|
||||||
const [selectedFeature, setSelectedFeature] = useState(null);
|
|
||||||
|
|
||||||
const getContent = () => {
|
|
||||||
if (features.length === 1) {
|
|
||||||
return <SingleFeaturePopup feature={features[0]} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedFeature) {
|
|
||||||
return <SingleFeaturePopup feature={selectedFeature} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MultipleFeaturesPopup
|
|
||||||
features={features}
|
|
||||||
onSelect={setSelectedFeature}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PopupWrapper lat={lat} lng={lng} onClose={onClose}>
|
|
||||||
{getContent()}
|
|
||||||
</PopupWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
import { Button } from "antd";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { MODES } from "../../config";
|
||||||
|
import { useMode } from "../../stores/useMode";
|
||||||
|
import { LAYER_IDS } from "../Layers/constants";
|
||||||
|
import { PopupWrapper } from "./PopupWrapper";
|
||||||
|
import { InitialPointPopup } from "./mode-popup/InitialPointPopup";
|
||||||
|
import { ApproveWorkingPointPopup } from "./mode-popup/ApproveWorkingPointPopup";
|
||||||
|
import { WorkingPointPopup } from "./mode-popup/WorkingPointPopup";
|
||||||
|
|
||||||
|
const SingleFeaturePopup = ({ feature }) => {
|
||||||
|
const { mode } = useMode();
|
||||||
|
|
||||||
|
if (mode === MODES.APPROVE_WORKING) {
|
||||||
|
return <ApproveWorkingPointPopup feature={feature} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === MODES.WORKING) {
|
||||||
|
return <WorkingPointPopup feature={feature} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <InitialPointPopup feature={feature} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MultipleFeaturesPopup = ({ features, onSelect }) => {
|
||||||
|
return (
|
||||||
|
<div className="space-y-2 p-1">
|
||||||
|
{features.map((feature) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
type={
|
||||||
|
feature.layer.id === LAYER_IDS["initial-match"] ? "primary" : ""
|
||||||
|
}
|
||||||
|
className="flex items-center gap-x-1"
|
||||||
|
block
|
||||||
|
onClick={() => onSelect(feature)}
|
||||||
|
key={feature.properties.location_id}
|
||||||
|
>
|
||||||
|
<span>{feature.properties.location_id}</span>
|
||||||
|
<span>{feature.properties.category}</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MapPopup = ({ features, lat, lng, onClose }) => {
|
||||||
|
const [selectedFeature, setSelectedFeature] = useState(null);
|
||||||
|
|
||||||
|
const getContent = () => {
|
||||||
|
if (features.length === 1) {
|
||||||
|
return <SingleFeaturePopup feature={features[0]} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedFeature) {
|
||||||
|
return <SingleFeaturePopup feature={selectedFeature} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MultipleFeaturesPopup
|
||||||
|
features={features}
|
||||||
|
onSelect={setSelectedFeature}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopupWrapper lat={lat} lng={lng} onClose={onClose}>
|
||||||
|
{getContent()}
|
||||||
|
</PopupWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { Popup } from "react-map-gl";
|
||||||
|
|
||||||
|
export const PopupWrapper = ({ lat, lng, onClose, children }) => {
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
longitude={lng}
|
||||||
|
latitude={lat}
|
||||||
|
onClose={onClose}
|
||||||
|
closeOnClick={false}
|
||||||
|
style={{ minWidth: "300px" }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { useClickedPointConfig } from "../../../stores/useClickedPointConfig";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { FeatureProperties } from "./FeatureProperties";
|
||||||
|
import { Title } from "../../../components/Title";
|
||||||
|
import { StatusSelect } from "../../../modules/Table/ApproveAndWorkingTable/ApproveAndWorkingTable";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { useUpdateStatus } from "../../../hooks/useUpdateStatus";
|
||||||
|
|
||||||
|
export const ApproveWorkingPointPopup = ({ feature }) => {
|
||||||
|
const featureId = feature.properties.location_id;
|
||||||
|
const { setClickedPointConfig } = useClickedPointConfig();
|
||||||
|
const [status, setStatus] = useState(feature.properties.status);
|
||||||
|
|
||||||
|
useEffect(() => setClickedPointConfig(featureId, false), [feature]);
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const { mutate: updateStatus } = useUpdateStatus({
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries(["approve-working-points"]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleStatusChange = (value) => {
|
||||||
|
setStatus(value);
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
status: value,
|
||||||
|
"location_ids[]": [featureId],
|
||||||
|
});
|
||||||
|
|
||||||
|
updateStatus(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FeatureProperties feature={feature} dynamicStatus={status} />
|
||||||
|
<div className="flex justify-center mt-4">
|
||||||
|
<div className={"flex flex-col items-center"}>
|
||||||
|
<Title text="Сменить статус" />
|
||||||
|
<StatusSelect value={status} onChange={handleStatusChange} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { CATEGORIES } from "../../../config";
|
||||||
|
import { popupConfig, residentialPointConfig } from "./config";
|
||||||
|
import { Col, Row } from "antd";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
export const FeatureProperties = ({ feature, dynamicStatus }) => {
|
||||||
|
const isResidential = feature.properties.category === CATEGORIES.residential;
|
||||||
|
const config = isResidential ? residentialPointConfig : popupConfig;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{config.map(({ field, name }) => {
|
||||||
|
const value =
|
||||||
|
dynamicStatus && field === "status"
|
||||||
|
? dynamicStatus
|
||||||
|
: feature.properties[field];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row className={twMerge("p-1")} key={field}>
|
||||||
|
<Col className={"font-semibold"} span={12}>
|
||||||
|
{name}
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>{value}</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { usePointSelection } from "../../../stores/usePointSelection";
|
||||||
|
import { useClickedPointConfig } from "../../../stores/useClickedPointConfig";
|
||||||
|
import { LAYER_IDS } from "../../Layers/constants";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { FeatureProperties } from "./FeatureProperties";
|
||||||
|
import { Button } from "antd";
|
||||||
|
|
||||||
|
export const InitialPointPopup = ({ feature }) => {
|
||||||
|
const { include, selection, exclude } = usePointSelection();
|
||||||
|
const { setClickedPointConfig } = useClickedPointConfig();
|
||||||
|
const doesMatchFilter = feature.layer.id === LAYER_IDS["initial-match"];
|
||||||
|
const featureId = feature.properties.location_id;
|
||||||
|
|
||||||
|
useEffect(() => setClickedPointConfig(featureId, doesMatchFilter), [feature]);
|
||||||
|
|
||||||
|
const isSelected =
|
||||||
|
(doesMatchFilter || selection.included.has(featureId)) &&
|
||||||
|
!selection.excluded.has(featureId);
|
||||||
|
|
||||||
|
const handleSelect = () => {
|
||||||
|
if (isSelected) {
|
||||||
|
exclude(featureId);
|
||||||
|
} else {
|
||||||
|
include(featureId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FeatureProperties feature={feature} />
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="mt-2 mx-auto"
|
||||||
|
block
|
||||||
|
onClick={handleSelect}
|
||||||
|
>
|
||||||
|
{isSelected ? "Исключить из выборки" : "Добавить в выборку"}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { useClickedPointConfig } from "../../../stores/useClickedPointConfig";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { FeatureProperties } from "./FeatureProperties";
|
||||||
|
|
||||||
|
export const WorkingPointPopup = ({ feature }) => {
|
||||||
|
const featureId = feature.properties.location_id;
|
||||||
|
const { setClickedPointConfig } = useClickedPointConfig();
|
||||||
|
|
||||||
|
useEffect(() => setClickedPointConfig(featureId, false), [feature]);
|
||||||
|
|
||||||
|
return <FeatureProperties feature={feature} dynamicStatus={status} />;
|
||||||
|
};
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
export const popupConfig = [
|
||||||
|
{
|
||||||
|
name: "Id",
|
||||||
|
field: "location_id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Адрес",
|
||||||
|
field: "address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Район",
|
||||||
|
field: "rayon_id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Округ",
|
||||||
|
field: "okrug_id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Название",
|
||||||
|
field: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Категория",
|
||||||
|
field: "category",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Статус",
|
||||||
|
field: "status",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Прогнозный трафик",
|
||||||
|
field: "prediction_current",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const residentialPointConfig = [
|
||||||
|
{
|
||||||
|
name: "Id",
|
||||||
|
field: "location_id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Адрес",
|
||||||
|
field: "address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Район",
|
||||||
|
field: "rayon_id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Округ",
|
||||||
|
field: "okrug_id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Название",
|
||||||
|
field: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Категория",
|
||||||
|
field: "category",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Статус",
|
||||||
|
field: "status",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Прогнозный трафик",
|
||||||
|
field: "prediction_current",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Кол-во квартир",
|
||||||
|
field: "flat_cnt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Год постройки",
|
||||||
|
field: "year_bld",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Кол-во этажей",
|
||||||
|
field: "levels",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Материал стен",
|
||||||
|
field: "mat_nes",
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { useMode } from "../stores/useMode";
|
||||||
|
import { Button } from "antd";
|
||||||
|
import { AIIcon } from "../icons/AIIcon";
|
||||||
|
import { MODES } from "../config";
|
||||||
|
import { ApproveIcon } from "../icons/ApproveIcon";
|
||||||
|
import { WorkingIcon } from "../icons/WorkingIcon";
|
||||||
|
|
||||||
|
export const ModeSelector = () => {
|
||||||
|
const { mode, setMode } = useMode();
|
||||||
|
|
||||||
|
const handleClick = (selectedMode) => {
|
||||||
|
setMode(selectedMode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getType = (currentMode) => {
|
||||||
|
if (currentMode === mode) return "primary";
|
||||||
|
|
||||||
|
return "default";
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
icon={<AIIcon />}
|
||||||
|
type={getType(MODES.INITIAL)}
|
||||||
|
onClick={() => handleClick(MODES.INITIAL)}
|
||||||
|
title="Локации к рассмотрению"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={<ApproveIcon />}
|
||||||
|
type={getType(MODES.APPROVE_WORKING)}
|
||||||
|
onClick={() => handleClick(MODES.APPROVE_WORKING)}
|
||||||
|
title="Локации на согласовании"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={<WorkingIcon />}
|
||||||
|
type={getType(MODES.WORKING)}
|
||||||
|
onClick={() => handleClick(MODES.WORKING)}
|
||||||
|
title="Локации в работе"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
export const AIIcon = ({ width = 24, height = 24 }) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
>
|
||||||
|
<polygon
|
||||||
|
className="ai-st0"
|
||||||
|
points="11.4,3.1 11.4,4.6 11.4,17.8 11.1,18.6 10.9,19.2 10.4,19.8 9.9,20.1 9.2,20.3 8.6,20.3 8.2,20.3
|
||||||
|
7.6,20.3 7,19.8 6.6,19.3 6.2,18.7 5.4,18.7 4.9,18.4 4.4,18 4.1,17.6 3.9,17.1 3.9,16 3.4,15.4 3,14.9 2,13.4 1.8,12.6 1.4,11.8
|
||||||
|
1.4,9.9 1.4,9 1.9,8.3 2.5,7.7 3,7.3 2.9,6.5 2.8,5.7 3,5.1 3.4,4.7 4.1,4.3 5,4.1 5.6,4 5.6,3.3 5.8,2.7 6.4,1.9 7,1.3 7.6,1.1
|
||||||
|
8.4,0.9 8.9,0.9 9.4,1.1 10.1,1.5 10.8,2.1 "
|
||||||
|
/>
|
||||||
|
<polyline className="ai-st0" points="17.4,3 16.1,4.7 11.5,4.6 " />
|
||||||
|
<polyline className="ai-st0" points="11.2,8.7 16,8.6 19.8,12.3 " />
|
||||||
|
<line className="ai-st0" x1="11.2" y1="12.2" x2="13.4" y2="12.2" />
|
||||||
|
<polyline className="ai-st0" points="11.4,16.6 14.8,16.6 16.4,18.2 " />
|
||||||
|
<g>
|
||||||
|
<circle className="ai-st1" cx="18.1" cy="2" r="1.9" />
|
||||||
|
<circle className="ai-st2" cx="18.1" cy="1.9" r="1.1" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<circle className="ai-st1" cx="18.5" cy="6.5" r="1.9" />
|
||||||
|
<circle className="ai-st2" cx="18.5" cy="6.4" r="1.1" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<circle className="ai-st1" cx="20.8" cy="13.3" r="1.9" />
|
||||||
|
<circle className="ai-st2" cx="20.9" cy="13.3" r="1.1" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<circle className="ai-st1" cx="15" cy="12.2" r="1.9" />
|
||||||
|
<circle className="ai-st2" cx="15" cy="12.1" r="1.1" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<circle className="ai-st1" cx="17.4" cy="19.2" r="1.9" />
|
||||||
|
<circle className="ai-st2" cx="17.4" cy="19.2" r="1.1" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<circle className="ai-st1" cx="13" cy="22" r="1.9" />
|
||||||
|
<circle className="ai-st2" cx="13.1" cy="22" r="1.1" />
|
||||||
|
</g>
|
||||||
|
<polyline
|
||||||
|
className="ai-st0"
|
||||||
|
points="5.9,8.1 4.5,9.5 4.4,11.2 4.8,12.4 6.5,14.8 "
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
import "./styles.css";
|
||||||
|
|
||||||
|
export const ApproveIcon = ({ width = 24, height = 24 }) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="-293 385 24 24"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
className="ml-[2px] mt-[2px]"
|
||||||
|
>
|
||||||
|
<polyline
|
||||||
|
className="approve-st0"
|
||||||
|
points="-273.7,390.2 -273.7,388.3 -274,387.5 -274.7,387 -275.8,387 -290.4,387 -291.1,387.2 -291.8,387.6
|
||||||
|
-292.2,388.3 -292.3,403.6 -292.2,404.4 -291.9,404.9 -291.3,405.2 -290.8,405.3 -275.9,405.3 -275.1,405.2 -274.3,404.9
|
||||||
|
-273.8,404.3 -273.8,402.3 "
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
className="approve-st1"
|
||||||
|
d="M-287.2,397h-1.2c-0.4,0-0.7-0.3-0.7-0.7v-1.2c0-0.4,0.3-0.7,0.7-0.7h1.2c0.4,0,0.7,0.3,0.7,0.7v1.2
|
||||||
|
C-286.6,396.7-286.9,397-287.2,397z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
className="approve-st1"
|
||||||
|
d="M-287.2,400.5h-1.2c-0.4,0-0.7-0.3-0.7-0.7v-1.2c0-0.4,0.3-0.7,0.7-0.7h1.2c0.4,0,0.7,0.3,0.7,0.7v1.2
|
||||||
|
C-286.5,400.2-286.8,400.5-287.2,400.5z"
|
||||||
|
/>
|
||||||
|
<polygon
|
||||||
|
className="approve-st2"
|
||||||
|
points="-288.7,390.7 -288.3,391.2 -288.2,391.3 -288,391.3 -287.6,391.2 -287.2,390.7 -286.7,390.3
|
||||||
|
-286.5,390.3 -286.2,390.3 -286,390.5 -285.9,390.9 -286,391.1 -286.4,391.6 -287.9,392.9 -288.1,392.9 -288.3,392.9 -288.6,392.6
|
||||||
|
-289.6,391.6 -289.8,391.4 -289.8,391 -289.6,390.7 -289.4,390.6 -289.1,390.5 -288.9,390.5 "
|
||||||
|
/>
|
||||||
|
<polygon
|
||||||
|
className="approve-st3"
|
||||||
|
points="-283.9,390.9 -279.1,390.9 -278.8,391.1 -278.7,391.3 -278.6,391.8 -278.7,392 -279,392.4
|
||||||
|
-279.2,392.5 -279.6,392.5 -284,392.5 -284.2,392.5 -284.5,392.3 -284.7,391.9 -284.7,391.7 -284.6,391.5 -284.3,391.1
|
||||||
|
-284.1,390.9 "
|
||||||
|
/>
|
||||||
|
<polygon
|
||||||
|
className="approve-st3"
|
||||||
|
points="-284,394.4 -280,394.4 -279.8,394.6 -279.7,394.8 -279.6,395.2 -279.7,395.5 -279.9,395.9 -280.1,396
|
||||||
|
-280.4,396 -284.1,396 -284.3,395.9 -284.5,395.7 -284.7,395.4 -284.7,395.2 -284.6,394.9 -284.4,394.6 -284.2,394.4 "
|
||||||
|
/>
|
||||||
|
<polygon
|
||||||
|
className="approve-st3"
|
||||||
|
points="-284.1,397.9 -280.9,397.9 -280.7,398.1 -280.6,398.3 -280.6,398.7 -280.6,399 -280.8,399.4
|
||||||
|
-280.9,399.5 -281.2,399.5 -284.2,399.5 -284.3,399.4 -284.5,399.2 -284.7,398.9 -284.7,398.7 -284.6,398.4 -284.4,398.1
|
||||||
|
-284.3,397.9 "
|
||||||
|
/>
|
||||||
|
<polygon
|
||||||
|
className="approve-st2"
|
||||||
|
points="-274.2,391.7 -273.4,391.6 -273.1,391.7 -273,392.7 -272.6,392.8 -272.1,393 -271.6,393.2
|
||||||
|
-271.1,392.6 -270.7,392.8 -270.4,393.1 -270.1,393.5 -270,393.7 -270.6,394.2 -270.5,394.6 -270.4,394.9 -270.3,395.4
|
||||||
|
-269.5,395.4 -269.4,395.7 -269.4,396.2 -269.4,396.4 -269.6,396.7 -269.6,396.9 -270.4,396.8 -270.5,397.3 -270.6,397.7
|
||||||
|
-270.8,397.9 -270.1,398.6 -270.3,398.9 -270.6,399.2 -271.1,399.5 -271.2,399.6 -271.7,399 -271.7,398.9 -272.1,399 -272.4,399.1
|
||||||
|
-272.8,399.2 -273,399.3 -273,400 -273.2,400.2 -274.2,400.2 -274.4,400 -274.4,399.3 -274.4,399.1 -275,398.9 -275.5,398.7
|
||||||
|
-275.9,399.4 -276.2,399.5 -276.5,399.3 -277.1,398.8 -277,398.5 -276.4,397.9 -276.8,397.5 -276.9,397.2 -277.1,396.8
|
||||||
|
-277.9,396.8 -278,396.3 -278,395.6 -277.8,395.3 -277,395.3 -276.7,394.6 -276.6,394.2 -276.9,393.9 -277.1,393.6 -277.2,393.5
|
||||||
|
-277.1,393.2 -276.7,392.7 -276.5,392.6 -276.2,392.7 -275.4,393.2 -275.1,392.9 -274.7,392.8 -274.5,392.7 -274.5,391.8 "
|
||||||
|
/>
|
||||||
|
<circle className="approve-st4" cx="-273.8" cy="396" r="1.8" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
export const FiltersIcon = ({ width = 24, height = 24 }) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
>
|
||||||
|
<polygon points="23.7,4.1 23.7,6.1 0.1,6.2 0.1,4.1 " />
|
||||||
|
<g>
|
||||||
|
<circle cx="6.3" cy="5.2" r="3.1" />
|
||||||
|
<circle fill="#fff" cx="6.3" cy="5.2" r="1.4" />
|
||||||
|
</g>
|
||||||
|
<polygon points="23.6,11.1 23.7,13.1 0,13.1 0,11 " />
|
||||||
|
<g>
|
||||||
|
<circle cx="18.6" cy="12.1" r="3.1" />
|
||||||
|
<circle fill="#fff" cx="18.6" cy="12.1" r="1.4" />
|
||||||
|
</g>
|
||||||
|
<polygon points="23.8,18.2 23.9,20.1 0.2,20.2 0.2,18.2 " />
|
||||||
|
<g>
|
||||||
|
<circle cx="10.2" cy="19.1" r="3.1" />
|
||||||
|
<circle fill="#fff" cx="10.2" cy="19.1" r="1.4" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
export const Logo = ({ width = 24, height = 24 }) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<polygon
|
||||||
|
style={{ fill: "#3B555E" }}
|
||||||
|
points="19.6,24 21.4,24 22.5,23.9 23,23.8 23.5,23.4 23.8,22.9 24,22.3 24,20.4 24,1.9 23.9,1.4 23.6,0.8
|
||||||
|
23.3,0.4 22.9,0.1 22.3,0 14.2,0 8.9,0 11.2,4.6 19.5,4.5 19.6,4.5 "
|
||||||
|
/>
|
||||||
|
<polygon
|
||||||
|
style={{ fill: "#E63941" }}
|
||||||
|
points="13.6,6.4 17.7,6.4 16.2,9.4 14.1,14.2 12.3,18.4 11.7,18.4 9.8,14.5 6.8,8.8 4.5,4.5 4.7,23.9 1.7,24
|
||||||
|
1,23.8 0.5,23.2 0.2,22.7 0,22.1 0,1.8 0.1,1.3 0.4,0.8 0.8,0.4 1.1,0.2 1.4,0.1 2,0.1 7,0.1 11.9,10.6 "
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
export const WorkingIcon = ({ width = 24, height = 24 }) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="-293 385 24 24"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
>
|
||||||
|
<path className="working-st0" d="M-279.1,402.7" />
|
||||||
|
<polyline
|
||||||
|
className="working-st1"
|
||||||
|
points="-275,391.8 -275,390.5 -275.3,389.9 -276,389.6 -277,389.5 -290.8,389.6 -291.4,389.7 -292.1,390
|
||||||
|
-292.5,390.4 -292.5,401.4 -292.5,401.9 -292.1,402.3 -291.6,402.5 -291.1,402.6 -280,402.5 "
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
className="working-st2"
|
||||||
|
points="-290.8,396 -289.3,396 -288.7,398.4 -287.6,394.3 -286.1,399.7 -285.1,392 -284.2,397 -283.2,395.9
|
||||||
|
-281.8,396 "
|
||||||
|
/>
|
||||||
|
<circle className="working-st3" cx="-277" cy="397.6" r="4.6" />
|
||||||
|
<circle className="working-st4" cx="-277.1" cy="397.5" r="2.9" />
|
||||||
|
<polygon
|
||||||
|
className="working-st3"
|
||||||
|
points="-270.9,405.7 -269,403.8 -273.5,399.6 -275.4,401 "
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
.ai-st0 {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-st1 {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-st2 {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approve-st0 {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approve-st1 {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 0.5;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approve-st2 {
|
||||||
|
fill: #fa0000;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 0.5;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approve-st3 {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 0.5;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approve-st4 {
|
||||||
|
fill: #ffffff;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 0.5;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.working-st0 {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 7;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.working-st1 {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.working-st2 {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 0.5;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.working-st3 {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.working-st4 {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { Button } from "antd";
|
import { Button } from "antd";
|
||||||
import { MenuOutlined } from "@ant-design/icons";
|
import { FiltersIcon } from "../../icons/FiltersIcon";
|
||||||
|
|
||||||
export const FiltersButton = ({ toggleCollapse }) => {
|
export const FiltersButton = ({ toggleCollapse }) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
icon={<MenuOutlined />}
|
icon={<FiltersIcon width={16} height={16} />}
|
||||||
onClick={toggleCollapse}
|
onClick={toggleCollapse}
|
||||||
className="border-l-0 rounded-bl-none rounded-tl-none absolute top-[100px]"
|
className="border-l-0 rounded-bl-none rounded-tl-none absolute top-[100px] flex items-center justify-center"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
useGetFilteredInitialPointsCount,
|
||||||
|
useGetTotalInitialPointsCount,
|
||||||
|
} from "../../../api";
|
||||||
|
import { usePointSelection } from "../../../stores/usePointSelection";
|
||||||
|
import { Spin } from "antd";
|
||||||
|
|
||||||
|
export const SelectedLocations = () => {
|
||||||
|
const { data: totalCount, isInitialLoading: isTotalLoading } =
|
||||||
|
useGetTotalInitialPointsCount();
|
||||||
|
const { data: filteredCount, isInitialLoading: isFilteredLoading } =
|
||||||
|
useGetFilteredInitialPointsCount();
|
||||||
|
|
||||||
|
const showSpinner = isTotalLoading || isFilteredLoading;
|
||||||
|
|
||||||
|
const {
|
||||||
|
selection: { excluded },
|
||||||
|
} = usePointSelection();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"flex items-center justify-between"}>
|
||||||
|
<span>Отобрано локаций</span>
|
||||||
|
|
||||||
|
{showSpinner ? (
|
||||||
|
<Spin />
|
||||||
|
) : (
|
||||||
|
<span>{`${filteredCount - excluded.size} / ${totalCount}`}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Reference in new issue