Use eng statuses and show other layers

dev
Platon Yasev 3 years ago
parent 85f94a5070
commit e86255756a

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/index.html vendored

@ -6,8 +6,8 @@
<link href="/favicon.ico" rel="icon"/> <link href="/favicon.ico" rel="icon"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>PostNet by Spatial</title> <title>PostNet by Spatial</title>
<script type="module" crossorigin src="/assets/index.24e33a70.js"></script> <script crossorigin src="/assets/index.297e7fbd.js" type="module"></script>
<link rel="stylesheet" href="/assets/index.9d2c87bd.css"> <link href="/assets/index.ca3dcc1b.css" rel="stylesheet">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

@ -0,0 +1,32 @@
import { Layer } from "react-map-gl";
import { cancelledPointLayer } from "./layers-config";
import { useLayersVisibility } from "../../stores/useLayersVisibility";
import { STATUSES } from "../../config";
import { useRegionFilterExpression } from "./useRegionFilterExpression";
import { LAYER_IDS } from "./constants";
const statusExpression = ["==", ["get", "status"], STATUSES.cancelled];
export const CancelledPoints = () => {
const { isVisible } = useLayersVisibility();
const regionFilterExpression = useRegionFilterExpression();
const filter = regionFilterExpression
? ["all", statusExpression, regionFilterExpression]
: statusExpression;
return (
<>
<Layer
{...cancelledPointLayer}
id={LAYER_IDS.cancelled}
source={"points"}
source-layer={"public.service_placementpoint"}
layout={{
visibility: isVisible[LAYER_IDS.cancelled] ? "visible" : "none",
}}
filter={filter}
/>
</>
);
};

@ -3,6 +3,8 @@ import { Layer, Source } from "react-map-gl";
import { aoLayer, rayonLayer, selectedRegionLayer } from "./layers-config"; import { aoLayer, rayonLayer, selectedRegionLayer } from "./layers-config";
import { useFilters } from "../../stores/useFilters"; import { useFilters } from "../../stores/useFilters";
import { BASE_URL } from "../../api"; import { BASE_URL } from "../../api";
import { PVZ } from "./PVZ";
import { OtherPostamates } from "./OtherPostamates";
export const Layers = () => { export const Layers = () => {
const { const {
@ -50,6 +52,14 @@ export const Layers = () => {
)} )}
<Points prediction={prediction} /> <Points prediction={prediction} />
<Source
id="rivals"
type="vector"
tiles={[`${BASE_URL}/martin/public.service_rivals/{z}/{x}/{y}.pbf`]}
>
<PVZ />
<OtherPostamates />
</Source>
</> </>
); );
}; };

@ -0,0 +1,22 @@
import { Layer } from "react-map-gl";
import { otherPostamatesLayer } from "./layers-config";
import { useLayersVisibility } from "../../stores/useLayersVisibility";
import { LAYER_IDS } from "./constants";
export const OtherPostamates = () => {
const { isVisible } = useLayersVisibility();
return (
<>
<Layer
{...otherPostamatesLayer}
id={LAYER_IDS.other}
source={"rivals"}
source-layer={"public.service_rivals"}
layout={{
visibility: isVisible[LAYER_IDS.other] ? "visible" : "none",
}}
/>
</>
);
};

@ -0,0 +1,22 @@
import { Layer } from "react-map-gl";
import { pvzPointLayer } from "./layers-config";
import { useLayersVisibility } from "../../stores/useLayersVisibility";
import { LAYER_IDS } from "./constants";
export const PVZ = () => {
const { isVisible } = useLayersVisibility();
return (
<>
<Layer
{...pvzPointLayer}
id={LAYER_IDS.pvz}
source={"rivals"}
source-layer={"public.service_rivals"}
layout={{
visibility: isVisible[LAYER_IDS.pvz] ? "visible" : "none",
}}
/>
</>
);
};

@ -5,6 +5,7 @@ import { InitialPoints } from "./InitialPoints";
import { ApprovePoints } from "./ApprovePoints"; import { ApprovePoints } from "./ApprovePoints";
import { WorkingPoints } from "./WorkingPoints"; import { WorkingPoints } from "./WorkingPoints";
import { FilteredWorkingPoints } from "./FilteredWorkingPoints"; import { FilteredWorkingPoints } from "./FilteredWorkingPoints";
import { CancelledPoints } from "./CancelledPoints";
export const Points = () => { export const Points = () => {
const { updateCounter } = useUpdateLayerCounter(); const { updateCounter } = useUpdateLayerCounter();
@ -23,6 +24,7 @@ export const Points = () => {
<ApprovePoints /> <ApprovePoints />
<WorkingPoints /> <WorkingPoints />
<FilteredWorkingPoints /> <FilteredWorkingPoints />
<CancelledPoints />
</Source> </Source>
</> </>
); );

@ -7,4 +7,6 @@ export const LAYER_IDS = {
filteredWorking: "filtered-working-points", filteredWorking: "filtered-working-points",
cancelled: "cancelled-points", cancelled: "cancelled-points",
atd: "atd", atd: "atd",
pvz: "pvz",
other: "other",
}; };

@ -1,11 +1,13 @@
const POINT_SIZE = 5; const POINT_SIZE = 5;
const UNMATCH_POINT_SIZE = 3; const UNMATCH_POINT_SIZE = 3;
export const INITIAL_COLOR = "#CC2222"; export const INITIAL_COLOR = "#001cd2";
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 = "#b4b4b4";
export const PVZ_COLOR = "#da11b2";
export const OTHER_POSTAMATES_COLOR = "#26a2a2";
const DEFAULT_POINT_CONFIG = { const DEFAULT_POINT_CONFIG = {
type: "circle", type: "circle",
@ -35,6 +37,8 @@ export const unmatchInitialPointLayer = getPointConfig(
export const approvePointLayer = getPointConfig(APPROVE_COLOR); export const approvePointLayer = getPointConfig(APPROVE_COLOR);
export const workingPointLayer = getPointConfig(WORKING_COLOR); export const workingPointLayer = getPointConfig(WORKING_COLOR);
export const cancelledPointLayer = getPointConfig(CANCELLED_COLOR); export const cancelledPointLayer = getPointConfig(CANCELLED_COLOR);
export const pvzPointLayer = getPointConfig(PVZ_COLOR);
export const otherPostamatesLayer = getPointConfig(OTHER_POSTAMATES_COLOR);
const regionColor = "#676767"; const regionColor = "#676767";

@ -1,24 +1,47 @@
import { Title } from "../../components/Title";
import { useLayersVisibility } from "../../stores/useLayersVisibility"; import { useLayersVisibility } from "../../stores/useLayersVisibility";
import { useMode } from "../../stores/useMode"; import { useMode } from "../../stores/useMode";
import Checkbox from "antd/es/checkbox/Checkbox";
import { MODES } from "../../config";
import { LAYER_IDS } from "../Layers/constants";
export const LayersVisibility = () => { export const LayersVisibility = () => {
const { toggleVisibility, isVisible } = useLayersVisibility(); const { toggleVisibility, isVisible } = useLayersVisibility();
const { mode } = useMode(); const { mode } = useMode();
return ( return (
<div>
<Title text={"Слои"} className={"mb-1"} />
<div className={"space-y-1 flex flex-col"}> <div className={"space-y-1 flex flex-col"}>
{/*{mode === MODES.INITIAL && (*/} {mode === MODES.INITIAL && (
{/* <Checkbox*/} <>
{/* onChange={() => toggleVisibility(LAYER_IDS.initial)}*/} <Checkbox
{/* checked={isVisible[LAYER_IDS.initial]}*/} className={"!ml-0"}
{/* >*/} onChange={() => toggleVisibility(LAYER_IDS.working)}
{/* Локации к рассмотрению*/} checked={isVisible[LAYER_IDS.working]}
{/* </Checkbox>*/} >
{/*)}*/} Работающие постаматы
</Checkbox>
<Checkbox
className={"!ml-0"}
onChange={() => toggleVisibility(LAYER_IDS.cancelled)}
checked={isVisible[LAYER_IDS.cancelled]}
>
Отмененные локации
</Checkbox>
</>
)}
<Checkbox
className={"!ml-0"}
onChange={() => toggleVisibility(LAYER_IDS.pvz)}
checked={isVisible[LAYER_IDS.pvz]}
>
ПВЗ
</Checkbox>
<Checkbox
className={"!ml-0"}
onChange={() => toggleVisibility(LAYER_IDS.other)}
checked={isVisible[LAYER_IDS.other]}
>
Постаматы прочих сетей
</Checkbox>
{/*{mode === MODES.WORKING && (*/} {/*{mode === MODES.WORKING && (*/}
{/* <>*/} {/* <>*/}
{/* <Checkbox*/} {/* <Checkbox*/}
@ -31,6 +54,5 @@ export const LayersVisibility = () => {
{/* </>*/} {/* </>*/}
{/*)}*/} {/*)}*/}
</div> </div>
</div>
); );
}; };

@ -1,6 +1,13 @@
import { useMode } from "../stores/useMode"; import { useMode } from "../stores/useMode";
import { MODES } from "../config"; import { MODES } from "../config";
import { APPROVE_COLOR, WORKING_COLOR } from "./Layers/layers-config"; import {
APPROVE_COLOR,
CANCELLED_COLOR,
INITIAL_COLOR,
OTHER_POSTAMATES_COLOR,
PVZ_COLOR,
WORKING_COLOR,
} from "./Layers/layers-config";
import { Title } from "../components/Title"; import { Title } from "../components/Title";
const LegendPointItem = ({ color, name }) => { const LegendPointItem = ({ color, name }) => {
@ -18,28 +25,43 @@ const LegendPointItem = ({ color, name }) => {
export function Legend() { export function Legend() {
const { mode } = useMode(); const { mode } = useMode();
const getContent = () => {
if (mode === MODES.APPROVE_WORKING) {
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">
{mode !== MODES.WORKING && (
<div>
<Title text={"Статус локации"} className="text-center" />
<div className="space-y-1">
{mode === MODES.INITIAL && (
<>
<LegendPointItem name="К рассмотрению" color={INITIAL_COLOR} />
<LegendPointItem name="Работает" color={WORKING_COLOR} />
<LegendPointItem name="Отменен" color={CANCELLED_COLOR} />
</>
)}
{mode === MODES.APPROVE_WORKING && (
<> <>
<Title text={"Статус локации"} className={"text-center"} />
<div className="space-y-2">
<LegendPointItem <LegendPointItem
name="Согласование-установка" name="Согласование-установка"
color={APPROVE_COLOR} color={APPROVE_COLOR}
/> />
<LegendPointItem name="Работает" color={WORKING_COLOR} /> <LegendPointItem name="Работает" color={WORKING_COLOR} />
</div> <LegendPointItem name="Отменен" color={CANCELLED_COLOR} />
</> </>
); )}
} </div>
</div>
return null; )}
};
return ( <div>
<div className="absolute bottom-[20px] left-[20px] text-xs text-grey z-10 bg-white-background rounded-xl p-3"> <Title text={"Прочее"} className="text-center" />
{getContent()} <div className="space-y-1">
<LegendPointItem name="ПВЗ" color={PVZ_COLOR} />
<LegendPointItem
name="Постаматы прочих сетей"
color={OTHER_POSTAMATES_COLOR}
/>
</div>
</div>
</div> </div>
); );
} }

@ -140,6 +140,8 @@ export const MapComponent = () => {
LAYER_IDS["initial-unmatch"], LAYER_IDS["initial-unmatch"],
LAYER_IDS.approve, LAYER_IDS.approve,
LAYER_IDS.working, LAYER_IDS.working,
LAYER_IDS.cancelled,
LAYER_IDS.pvz,
]} ]}
onClick={handleClick} onClick={handleClick}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}

@ -7,9 +7,19 @@ import { PopupWrapper } from "./PopupWrapper";
import { InitialPointPopup } from "./mode-popup/InitialPointPopup"; import { InitialPointPopup } from "./mode-popup/InitialPointPopup";
import { ApproveWorkingPointPopup } from "./mode-popup/ApproveWorkingPointPopup"; import { ApproveWorkingPointPopup } from "./mode-popup/ApproveWorkingPointPopup";
import { WorkingPointPopup } from "./mode-popup/WorkingPointPopup"; import { WorkingPointPopup } from "./mode-popup/WorkingPointPopup";
import { FeatureProperties } from "./mode-popup/FeatureProperties";
const SingleFeaturePopup = ({ feature }) => { const SingleFeaturePopup = ({ feature }) => {
const { mode } = useMode(); const { mode } = useMode();
const isRivals =
feature.layer.id === LAYER_IDS.pvz || feature.layer.id === LAYER_IDS.other;
const isInitialLayer =
feature.layer.id === LAYER_IDS["initial-match"] ||
feature.layer.id === LAYER_IDS["initial-unmatch"];
if (isRivals) {
return <FeatureProperties feature={feature} />;
}
if (mode === MODES.APPROVE_WORKING) { if (mode === MODES.APPROVE_WORKING) {
return <ApproveWorkingPointPopup feature={feature} />; return <ApproveWorkingPointPopup feature={feature} />;
@ -19,7 +29,10 @@ const SingleFeaturePopup = ({ feature }) => {
return <WorkingPointPopup feature={feature} />; return <WorkingPointPopup feature={feature} />;
} }
if (mode === MODES.INITIAL && isInitialLayer)
return <InitialPointPopup feature={feature} />; return <InitialPointPopup feature={feature} />;
return <FeatureProperties feature={feature} />;
}; };
const MultipleFeaturesPopup = ({ features, onSelect }) => { const MultipleFeaturesPopup = ({ features, onSelect }) => {

@ -2,9 +2,9 @@ import { useClickedPointConfig } from "../../../stores/useClickedPointConfig";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { FeatureProperties } from "./FeatureProperties"; import { FeatureProperties } from "./FeatureProperties";
import { Title } from "../../../components/Title"; import { Title } from "../../../components/Title";
import { StatusSelect } from "../../../modules/Table/ApproveAndWorkingTable/ApproveAndWorkingTable";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { useUpdateStatus } from "../../../hooks/useUpdateStatus"; import { useUpdateStatus } from "../../../hooks/useUpdateStatus";
import { StatusSelect } from "../../../components/StatusSelect";
export const ApproveWorkingPointPopup = ({ feature }) => { export const ApproveWorkingPointPopup = ({ feature }) => {
const featureId = feature.properties.id; const featureId = feature.properties.id;

@ -17,18 +17,19 @@ export const FeatureProperties = ({ feature, dynamicStatus }) => {
return ( return (
<div> <div>
{finalConfig.map(({ field, name }) => { {finalConfig.map(({ field, name, render }) => {
const value = const value =
dynamicStatus && field === "status" dynamicStatus && field === "status"
? dynamicStatus ? dynamicStatus
: feature.properties[field]; : feature.properties[field];
const renderedValue = render ? render(value) : value;
return ( return (
<Row className={twMerge("p-1")} key={field}> <Row className={twMerge("p-1")} key={field}>
<Col className={"font-semibold"} span={12}> <Col className={"font-semibold"} span={12}>
{name} {name}
</Col> </Col>
<Col span={12}>{value}</Col> <Col span={12}>{renderedValue}</Col>
</Row> </Row>
); );
})} })}

@ -8,5 +8,5 @@ export const WorkingPointPopup = ({ feature }) => {
useEffect(() => setClickedPointConfig(featureId, false), [feature]); useEffect(() => setClickedPointConfig(featureId, false), [feature]);
return <FeatureProperties feature={feature} dynamicStatus={status} />; return <FeatureProperties feature={feature} />;
}; };

@ -1,3 +1,5 @@
import { STATUS_LABEL_MAPPER } from "../../../config";
export const popupConfig = [ export const popupConfig = [
{ {
name: "Id", name: "Id",
@ -26,6 +28,7 @@ export const popupConfig = [
{ {
name: "Статус", name: "Статус",
field: "status", field: "status",
render: (value) => STATUS_LABEL_MAPPER[value],
}, },
{ {
name: "Прогнозный трафик", name: "Прогнозный трафик",
@ -61,6 +64,7 @@ export const residentialPointConfig = [
{ {
name: "Статус", name: "Статус",
field: "status", field: "status",
render: (value) => STATUS_LABEL_MAPPER[value],
}, },
{ {
name: "Прогнозный трафик", name: "Прогнозный трафик",

@ -0,0 +1,31 @@
import { Select } from "antd";
import { STATUS_LABEL_MAPPER, STATUSES } from "../config";
const statusOptions = [
{ label: STATUS_LABEL_MAPPER[STATUSES.initial], value: STATUSES.initial },
{ label: STATUS_LABEL_MAPPER[STATUSES.approve], value: STATUSES.approve },
{ label: STATUS_LABEL_MAPPER[STATUSES.working], value: STATUSES.working },
{ label: STATUS_LABEL_MAPPER[STATUSES.cancelled], value: STATUSES.cancelled },
];
export const StatusSelect = ({ value, onChange, disabled }) => {
const handleClick = (e) => e.stopPropagation();
const handleChange = (value) => {
onChange(value);
};
return (
<Select
style={{
width: 250,
}}
value={value}
onChange={handleChange}
options={statusOptions}
disabled={disabled}
placeholder="Выберите статус"
onClick={handleClick}
/>
);
};

@ -1,10 +1,17 @@
import { LAYER_IDS } from "./Map/Layers/constants"; import { LAYER_IDS } from "./Map/Layers/constants";
export const STATUSES = { export const STATUSES = {
initial: "К рассмотрению", initial: "Pending",
approve: "Согласование-установка", approve: "Installation",
working: "Работает", working: "Working",
cancelled: "Отменено", cancelled: "Cancelled",
};
export const STATUS_LABEL_MAPPER = {
[STATUSES.initial]: "К рассмотрению",
[STATUSES.approve]: "Согласование-установка",
[STATUSES.working]: "Работает",
[STATUSES.cancelled]: "Отменен",
}; };
export const CATEGORIES = { export const CATEGORIES = {
@ -33,7 +40,11 @@ export const MODE_TO_STATUS_MAPPER = {
}; };
export const MODE_TO_LAYER_VISIBILITY_MAPPER = { export const MODE_TO_LAYER_VISIBILITY_MAPPER = {
[MODES.INITIAL]: [LAYER_IDS.initial], [MODES.INITIAL]: [LAYER_IDS.initial, LAYER_IDS.working, LAYER_IDS.cancelled],
[MODES.APPROVE_WORKING]: [LAYER_IDS.approve, LAYER_IDS.working], [MODES.APPROVE_WORKING]: [
LAYER_IDS.approve,
LAYER_IDS.working,
LAYER_IDS.cancelled,
],
[MODES.WORKING]: [LAYER_IDS.filteredWorking], [MODES.WORKING]: [LAYER_IDS.filteredWorking],
}; };

@ -49,6 +49,13 @@ export const Sidebar = forwardRef(({ isCollapsed }, ref) => {
> >
Работает Работает
</Checkbox> </Checkbox>
<Checkbox
className={"!ml-0"}
onChange={() => toggleVisibility(LAYER_IDS.cancelled)}
checked={isVisible[LAYER_IDS.cancelled]}
>
Отменен
</Checkbox>
</div> </div>
</div> </div>
)} )}

@ -6,40 +6,13 @@ import { useCallback, useState } from "react";
import { PAGE_SIZE } from "../constants"; import { PAGE_SIZE } from "../constants";
import { STATUSES } from "../../../config"; import { STATUSES } from "../../../config";
import { useMergeTableData } from "../useMergeTableData"; import { useMergeTableData } from "../useMergeTableData";
import { Button, Select } from "antd"; import { Button } from "antd";
import { useUpdateStatus } from "../../../hooks/useUpdateStatus"; import { useUpdateStatus } from "../../../hooks/useUpdateStatus";
import { HeaderWrapper } from "../HeaderWrapper"; import { HeaderWrapper } from "../HeaderWrapper";
import { useExportApproveAndWorkingData } from "./useExportApproveAndWorkingData"; import { useExportApproveAndWorkingData } from "./useExportApproveAndWorkingData";
import { useFilters } from "../../../stores/useFilters"; import { useFilters } from "../../../stores/useFilters";
import { usePopup } from "../../../stores/usePopup"; import { usePopup } from "../../../stores/usePopup";
import { StatusSelect } from "../../../components/StatusSelect";
const statusOptions = [
{ label: STATUSES.approve, value: STATUSES.approve },
{ label: STATUSES.working, value: STATUSES.working },
{ label: STATUSES.initial, value: STATUSES.initial },
];
export const StatusSelect = ({ value, onChange, disabled }) => {
const handleClick = (e) => e.stopPropagation();
const handleChange = (value) => {
onChange(value);
};
return (
<Select
style={{
width: 250,
}}
value={value}
onChange={handleChange}
options={statusOptions}
disabled={disabled}
placeholder="Выберите статус"
onClick={handleClick}
/>
);
};
const ChangeStatusButton = ({ selectedIds, selectedStatus }) => { const ChangeStatusButton = ({ selectedIds, selectedStatus }) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -71,7 +44,7 @@ const ChangeStatusButton = ({ selectedIds, selectedStatus }) => {
}; };
const Header = ({ selectedIds, onClearSelected }) => { const Header = ({ selectedIds, onClearSelected }) => {
const [status, setStatus] = useState(STATUSES.approve); const [status, setStatus] = useState(STATUSES.initial);
const handleClear = (e) => { const handleClear = (e) => {
e.stopPropagation(); e.stopPropagation();
@ -121,7 +94,7 @@ export const ApproveAndWorkingTable = ({ fullWidth }) => {
const params = new URLSearchParams({ const params = new URLSearchParams({
page, page,
page_size: pageSize, page_size: pageSize,
"status[]": [STATUSES.approve, STATUSES.working], "status[]": [STATUSES.approve, STATUSES.working, STATUSES.cancelled],
}); });
return await getPoints(params, region); return await getPoints(params, region);

@ -1,3 +1,5 @@
import { STATUS_LABEL_MAPPER } from "../../../config";
export const columns = [ export const columns = [
{ {
title: "Адрес", title: "Адрес",
@ -39,6 +41,9 @@ export const columns = [
key: "status", key: "status",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
render: (_, record) => {
return STATUS_LABEL_MAPPER[record.status];
},
}, },
{ {
title: "Прогнозный трафик", title: "Прогнозный трафик",

@ -8,8 +8,12 @@ const INITIAL_STATE = {
[LAYER_IDS.working]: false, [LAYER_IDS.working]: false,
[LAYER_IDS.filteredWorking]: false, [LAYER_IDS.filteredWorking]: false,
[LAYER_IDS.cancelled]: false, [LAYER_IDS.cancelled]: false,
[LAYER_IDS.pvz]: true,
[LAYER_IDS.other]: true,
}; };
const STATIC_LAYERS = [LAYER_IDS.pvz, LAYER_IDS.other];
const store = (set) => ({ const store = (set) => ({
isVisible: INITIAL_STATE, isVisible: INITIAL_STATE,
@ -24,7 +28,7 @@ const store = (set) => ({
state.isVisible[layerId] = true; state.isVisible[layerId] = true;
}); });
const invisibleLayersIds = Object.keys(state.isVisible).filter( const invisibleLayersIds = Object.keys(state.isVisible).filter(
(l) => !visibleLayerIds.includes(l) (l) => !visibleLayerIds.includes(l) && !STATIC_LAYERS.includes(l)
); );
invisibleLayersIds.forEach((layerId) => { invisibleLayersIds.forEach((layerId) => {
state.isVisible[layerId] = false; state.isVisible[layerId] = false;

Loading…
Cancel
Save