parent
581a312238
commit
f73e799271
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
function s(r,i){for(var o=0;o<i.length;o++){const e=i[o];if(typeof e!="string"&&!Array.isArray(e)){for(const t in e)if(t!=="default"&&!(t in r)){const n=Object.getOwnPropertyDescriptor(e,t);n&&Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:()=>e[t]})}}}return Object.freeze(Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}))}var a={},c=a.printMsg=function(){console.log("This is a message from the demo package")};const f=s({__proto__:null,printMsg:c,default:a},[a]);export{f as i};
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 15 KiB |
@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<link href="/vite.svg" rel="icon" type="image/svg+xml"/>
|
||||
<link href="/favicon.ico" rel="icon"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>PostNet by Spatial</title>
|
||||
<script crossorigin src="/assets/index.17466bb7.js" type="module"></script>
|
||||
<link href="/assets/index.ca3dcc1b.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,52 @@
|
||||
import { Layer, Source } from "react-map-gl";
|
||||
import { selectedRegionLayer } from "./layers-config";
|
||||
import { usePendingPointsFilters } from "../../stores/usePendingPointsFilters";
|
||||
import { useOnApprovalPointsFilters } from "../../stores/useOnApprovalPointsFilters";
|
||||
import { useWorkingPointsFilters } from "../../stores/useWorkingPointsFilters";
|
||||
import { useMode } from "../../stores/useMode";
|
||||
import { MODES } from "../../config";
|
||||
|
||||
const SelectedRegionLayer = ({ data }) => {
|
||||
return (
|
||||
<Source id="selected-region" type="geojson" data={data}>
|
||||
<Layer {...selectedRegionLayer} />
|
||||
</Source>
|
||||
);
|
||||
};
|
||||
|
||||
export const SelectedRegion = () => {
|
||||
const {
|
||||
filters: { region: pendingRegion },
|
||||
} = usePendingPointsFilters();
|
||||
const {
|
||||
filters: { region: onApprovalRegion },
|
||||
} = useOnApprovalPointsFilters();
|
||||
const {
|
||||
filters: { region: workingRegion },
|
||||
} = useWorkingPointsFilters();
|
||||
|
||||
const { mode } = useMode();
|
||||
|
||||
const shouldRenderPendingRegion =
|
||||
mode === MODES.PENDING && pendingRegion?.geometry;
|
||||
const shouldRenderOnApprovalRegion =
|
||||
mode === MODES.ON_APPROVAL && onApprovalRegion?.geometry;
|
||||
const shouldRenderWorkingRegion =
|
||||
mode === MODES.WORKING && workingRegion?.geometry;
|
||||
|
||||
return (
|
||||
<>
|
||||
{shouldRenderPendingRegion && (
|
||||
<SelectedRegionLayer data={pendingRegion.geometry} />
|
||||
)}
|
||||
|
||||
{shouldRenderOnApprovalRegion && (
|
||||
<SelectedRegionLayer data={onApprovalRegion.geometry} />
|
||||
)}
|
||||
|
||||
{shouldRenderWorkingRegion && (
|
||||
<SelectedRegionLayer data={workingRegion.geometry} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,9 @@
|
||||
import { Button } from "antd";
|
||||
|
||||
export const ClearFiltersButton = ({ onClick, disabled }) => {
|
||||
return (
|
||||
<Button block className={"mt-2"} onClick={onClick} disabled={disabled}>
|
||||
Сбросить фильтры
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@ -1,63 +0,0 @@
|
||||
import { RegionSelect } from "./RegionSelect";
|
||||
import { CategoriesSelect } from "./InitialSidebar/CategoriesSelect";
|
||||
import { PredictionSlider } from "./InitialSidebar/PredictionSlider";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Tooltip } from "antd";
|
||||
import { DISABLED_FILTER_TEXT, MODES } from "../../config";
|
||||
import { useMode } from "../../stores/useMode";
|
||||
import { DeltaTrafficSlider } from "./WorkingFilters/DeltaSlider";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { api } from "../../api";
|
||||
import { FactTrafficSlider } from "./WorkingFilters/FactTrafficSlider";
|
||||
import { AgeSlider } from "./WorkingFilters/AgeSlider";
|
||||
|
||||
export const Filters = ({ disabled }) => {
|
||||
const [hover, setHover] = useState(false);
|
||||
const { mode } = useMode();
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setHover(false), 1500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [hover]);
|
||||
|
||||
const { data: fullRange } = useQuery(["max-min"], async () => {
|
||||
const { data } = await api.get(`/api/placement_points/filters/`);
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setHover(true);
|
||||
};
|
||||
const handleMouseLeave = () => {
|
||||
setHover(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={DISABLED_FILTER_TEXT}
|
||||
placement="right"
|
||||
open={disabled && hover}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<div className="space-y-5">
|
||||
<RegionSelect disabled={disabled} />
|
||||
{mode === MODES.INITIAL && (
|
||||
<>
|
||||
<CategoriesSelect disabled={disabled} />
|
||||
<PredictionSlider disabled={disabled} fullRange={fullRange} />
|
||||
</>
|
||||
)}
|
||||
{mode === MODES.WORKING && (
|
||||
<div className={"space-y-12"}>
|
||||
<DeltaTrafficSlider fullRange={fullRange} />
|
||||
<FactTrafficSlider fullRange={fullRange} />
|
||||
<AgeSlider fullRange={fullRange} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@ -1,13 +0,0 @@
|
||||
import { Logo } from "../../icons/Logo";
|
||||
import { ModeSelector } from "../../components/ModeSelector";
|
||||
|
||||
export const Header = () => {
|
||||
return (
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<Logo />
|
||||
<div className={"flex items-center gap-x-3"}>
|
||||
<ModeSelector />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,59 @@
|
||||
import { Title } from "../../../components/Title";
|
||||
import { Checkbox } from "antd";
|
||||
import { LAYER_IDS } from "../../../Map/Layers/constants";
|
||||
import { RegionSelect } from "../../../components/RegionSelect";
|
||||
import { useOnApprovalPointsFilters } from "../../../stores/useOnApprovalPointsFilters";
|
||||
import { useLayersVisibility } from "../../../stores/useLayersVisibility";
|
||||
import { ClearFiltersButton } from "../../../components/ClearFiltersButton";
|
||||
|
||||
export const OnApprovalPointsFilters = () => {
|
||||
const {
|
||||
filters: { region },
|
||||
setRegion,
|
||||
clear,
|
||||
} = useOnApprovalPointsFilters();
|
||||
const { isVisible, toggleVisibility, showLayers } = useLayersVisibility();
|
||||
|
||||
const hasActiveFilters =
|
||||
region ||
|
||||
!isVisible[LAYER_IDS.approve] ||
|
||||
!isVisible[LAYER_IDS.working] ||
|
||||
!isVisible[LAYER_IDS.cancelled];
|
||||
|
||||
const clearFilters = () => {
|
||||
clear();
|
||||
showLayers([LAYER_IDS.approve, LAYER_IDS.working, LAYER_IDS.cancelled]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<RegionSelect value={region?.id} onChange={setRegion} />
|
||||
<div>
|
||||
<Title text="Статусы" />
|
||||
<div className="flex flex-col space-y-2">
|
||||
<Checkbox
|
||||
onChange={() => toggleVisibility(LAYER_IDS.approve)}
|
||||
checked={isVisible[LAYER_IDS.approve]}
|
||||
>
|
||||
Согласование-установка
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
className={"!ml-0"}
|
||||
onChange={() => toggleVisibility(LAYER_IDS.working)}
|
||||
checked={isVisible[LAYER_IDS.working]}
|
||||
>
|
||||
Работает
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
className={"!ml-0"}
|
||||
onChange={() => toggleVisibility(LAYER_IDS.cancelled)}
|
||||
checked={isVisible[LAYER_IDS.cancelled]}
|
||||
>
|
||||
Отменен
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
{hasActiveFilters && <ClearFiltersButton onClick={clearFilters} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,90 @@
|
||||
import { DISABLED_FILTER_TEXT } from "../../../config";
|
||||
import { Button, Tooltip } from "antd";
|
||||
import { SelectedLocations } from "./SelectedLocations";
|
||||
import { TakeToWorkButton } from "./TakeToWorkButton";
|
||||
import { RegionSelect } from "../../../components/RegionSelect";
|
||||
import { CategoriesSelect } from "./CategoriesSelect";
|
||||
import { PredictionSlider } from "./PredictionSlider";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
useHasManualEdits,
|
||||
usePointSelection,
|
||||
} from "../../../stores/usePointSelection";
|
||||
import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters";
|
||||
import { ClearFiltersButton } from "../../../components/ClearFiltersButton";
|
||||
import { getDynamicActiveFilters } from "../utils";
|
||||
|
||||
export const PendingPointsFilters = ({ fullRange }) => {
|
||||
const hasManualEdits = useHasManualEdits();
|
||||
const { reset: resetPointSelection } = usePointSelection();
|
||||
const { filters, setRegion, clear } = usePendingPointsFilters();
|
||||
|
||||
const [hover, setHover] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setHover(false), 1500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [hover]);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setHover(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setHover(false);
|
||||
};
|
||||
|
||||
const activeDynamicFilters = getDynamicActiveFilters(filters, fullRange, [
|
||||
"prediction",
|
||||
]);
|
||||
|
||||
const clearFilters = () => clear(fullRange?.prediction);
|
||||
|
||||
const hasActiveFilters =
|
||||
filters.region ||
|
||||
activeDynamicFilters.prediction ||
|
||||
filters.categories.length !== 0;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-1 justify-between">
|
||||
<div>
|
||||
<Tooltip
|
||||
title={DISABLED_FILTER_TEXT}
|
||||
placement="right"
|
||||
open={hasManualEdits && hover}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<div className="space-y-5">
|
||||
<RegionSelect
|
||||
disabled={hasManualEdits}
|
||||
value={filters.region?.id}
|
||||
onChange={setRegion}
|
||||
/>
|
||||
<CategoriesSelect disabled={hasManualEdits} />
|
||||
<PredictionSlider disabled={hasManualEdits} fullRange={fullRange} />
|
||||
</div>
|
||||
|
||||
{hasActiveFilters && (
|
||||
<ClearFiltersButton
|
||||
onClick={clearFilters}
|
||||
disabled={hasManualEdits}
|
||||
/>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
||||
{hasManualEdits ? (
|
||||
<Button block className={"mt-2"} onClick={resetPointSelection}>
|
||||
Отменить ручное редактирование
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SelectedLocations />
|
||||
<TakeToWorkButton />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -1,20 +1,23 @@
|
||||
import { INITIAL, useFilters } from "../../../stores/useFilters";
|
||||
import { useEffect } from "react";
|
||||
import { SliderComponent as Slider } from "../../../components/SliderComponent";
|
||||
import {
|
||||
INITIAL,
|
||||
useWorkingPointsFilters,
|
||||
} from "../../../stores/useWorkingPointsFilters";
|
||||
|
||||
export const FactTrafficSlider = ({ fullRange }) => {
|
||||
const {
|
||||
filters: { factTraffic },
|
||||
setFactTraffic,
|
||||
} = useFilters();
|
||||
} = useWorkingPointsFilters();
|
||||
|
||||
const handleAfterChange = (range) => setFactTraffic(range);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fullRange) return;
|
||||
|
||||
const min = fullRange.fact[0];
|
||||
const max = fullRange.fact[1];
|
||||
const min = fullRange.factTraffic[0];
|
||||
const max = fullRange.factTraffic[1];
|
||||
|
||||
if (
|
||||
factTraffic[0] === INITIAL.factTraffic[0] &&
|
||||
@ -0,0 +1,37 @@
|
||||
import { RegionSelect } from "../../../components/RegionSelect";
|
||||
import { useWorkingPointsFilters } from "../../../stores/useWorkingPointsFilters";
|
||||
import { DeltaTrafficSlider } from "./DeltaSlider";
|
||||
import { FactTrafficSlider } from "./FactTrafficSlider";
|
||||
import { AgeSlider } from "./AgeSlider";
|
||||
import { ClearFiltersButton } from "../../../components/ClearFiltersButton";
|
||||
import { getDynamicActiveFilters } from "../utils";
|
||||
|
||||
export const WorkingPointsFilters = ({ fullRange }) => {
|
||||
const { filters, setRegion, clear } = useWorkingPointsFilters();
|
||||
|
||||
const activeDynamicFilters = getDynamicActiveFilters(filters, fullRange, [
|
||||
"deltaTraffic",
|
||||
"factTraffic",
|
||||
"age",
|
||||
]);
|
||||
|
||||
const hasActiveFilters =
|
||||
filters.region ||
|
||||
activeDynamicFilters.deltaTraffic ||
|
||||
activeDynamicFilters.factTraffic ||
|
||||
activeDynamicFilters.age;
|
||||
|
||||
const handleClear = () => clear(fullRange);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RegionSelect value={filters.region?.id} onChange={setRegion} />
|
||||
<div className={"space-y-12 mt-4"}>
|
||||
<DeltaTrafficSlider fullRange={fullRange} />
|
||||
<FactTrafficSlider fullRange={fullRange} />
|
||||
<AgeSlider fullRange={fullRange} />
|
||||
</div>
|
||||
{hasActiveFilters && <ClearFiltersButton onClick={handleClear} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,13 @@
|
||||
export const getDynamicActiveFilters = (filters, fullRange, props) => {
|
||||
if (!fullRange || !props) return false;
|
||||
|
||||
const result = {};
|
||||
|
||||
props.forEach((prop) => {
|
||||
result[prop] =
|
||||
filters[prop][0] !== fullRange[prop][0] ||
|
||||
filters[prop][1] !== fullRange[prop][1];
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
@ -1,134 +0,0 @@
|
||||
import { Table } from "../Table";
|
||||
import { columns } from "../InitialTable/columns";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { getPoints } from "../../../api";
|
||||
import { useCallback, useState } from "react";
|
||||
import { PAGE_SIZE } from "../constants";
|
||||
import { STATUSES } from "../../../config";
|
||||
import { useMergeTableData } from "../useMergeTableData";
|
||||
import { Button } from "antd";
|
||||
import { useUpdateStatus } from "../../../hooks/useUpdateStatus";
|
||||
import { HeaderWrapper } from "../HeaderWrapper";
|
||||
import { useExportApproveAndWorkingData } from "./useExportApproveAndWorkingData";
|
||||
import { useFilters } from "../../../stores/useFilters";
|
||||
import { usePopup } from "../../../stores/usePopup";
|
||||
import { StatusSelect } from "../../../components/StatusSelect";
|
||||
|
||||
const ChangeStatusButton = ({ selectedIds, selectedStatus }) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { setPopup } = usePopup();
|
||||
|
||||
const { mutate: updateStatus } = useUpdateStatus({
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(["approve-working-points"]);
|
||||
setPopup(null);
|
||||
},
|
||||
});
|
||||
|
||||
const handleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const params = new URLSearchParams({
|
||||
status: selectedStatus,
|
||||
"location_ids[]": selectedIds,
|
||||
});
|
||||
|
||||
updateStatus(params);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button type="primary" onClick={handleClick}>
|
||||
Обновить статус
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ selectedIds, onClearSelected }) => {
|
||||
const [status, setStatus] = useState(STATUSES.initial);
|
||||
|
||||
const handleClear = (e) => {
|
||||
e.stopPropagation();
|
||||
onClearSelected();
|
||||
};
|
||||
|
||||
return (
|
||||
<HeaderWrapper
|
||||
leftColumn={
|
||||
selectedIds.length > 0 && (
|
||||
<>
|
||||
<StatusSelect value={status} onChange={setStatus} />
|
||||
<ChangeStatusButton
|
||||
selectedIds={selectedIds}
|
||||
selectedStatus={status}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
rightColumn={
|
||||
selectedIds.length > 0 && (
|
||||
<Button onClick={handleClear}>Очистить все</Button>
|
||||
)
|
||||
}
|
||||
classes={{
|
||||
leftColumn: "flex items-center gap-x-4",
|
||||
rightColumn: "flex item-center gap-x-4",
|
||||
}}
|
||||
exportProvider={useExportApproveAndWorkingData}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ApproveAndWorkingTable = ({ fullWidth }) => {
|
||||
const [pageSize, setPageSize] = useState(PAGE_SIZE);
|
||||
const [page, setPage] = useState(1);
|
||||
const [selectedIds, setSelectedIds] = useState([]);
|
||||
const {
|
||||
filters: { region },
|
||||
} = useFilters();
|
||||
|
||||
const clearSelected = () => setSelectedIds([]);
|
||||
|
||||
const { data, isInitialLoading } = useQuery(
|
||||
["approve-working-points", page, region],
|
||||
async () => {
|
||||
const params = new URLSearchParams({
|
||||
page,
|
||||
page_size: pageSize,
|
||||
"status[]": [STATUSES.approve, STATUSES.working, STATUSES.cancelled],
|
||||
});
|
||||
|
||||
return await getPoints(params, region);
|
||||
},
|
||||
{ keepPreviousData: true }
|
||||
);
|
||||
|
||||
const { data: mergedData, isClickedPointLoading } = useMergeTableData(
|
||||
data,
|
||||
setPageSize
|
||||
);
|
||||
|
||||
const handlePageChange = useCallback((page) => setPage(page), []);
|
||||
|
||||
const rowSelection = {
|
||||
selectedRowKeys: selectedIds,
|
||||
onChange: (selectedRowKeys) => setSelectedIds(selectedRowKeys),
|
||||
hideSelectAll: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
header={
|
||||
<Header selectedIds={selectedIds} onClearSelected={clearSelected} />
|
||||
}
|
||||
rowSelection={rowSelection}
|
||||
data={mergedData}
|
||||
onPageChange={handlePageChange}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
isClickedPointLoading={isClickedPointLoading}
|
||||
columns={columns}
|
||||
fullWidth={fullWidth}
|
||||
loading={isInitialLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,33 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { usePopup } from "../../../stores/usePopup";
|
||||
import { useUpdateStatus } from "../../../hooks/useUpdateStatus";
|
||||
import { Button } from "antd";
|
||||
|
||||
export const ChangeStatusButton = ({ selectedIds, selectedStatus }) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { setPopup } = usePopup();
|
||||
|
||||
const { mutate: updateStatus } = useUpdateStatus({
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(["on-approval-points"]);
|
||||
setPopup(null);
|
||||
},
|
||||
});
|
||||
|
||||
const handleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const params = new URLSearchParams({
|
||||
status: selectedStatus,
|
||||
"location_ids[]": selectedIds,
|
||||
});
|
||||
|
||||
updateStatus(params);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button type="primary" onClick={handleClick}>
|
||||
Обновить статус
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,42 @@
|
||||
import { useState } from "react";
|
||||
import { STATUSES } from "../../../config";
|
||||
import { HeaderWrapper } from "../HeaderWrapper";
|
||||
import { StatusSelect } from "../../../components/StatusSelect";
|
||||
import { Button } from "antd";
|
||||
import { useExportOnApprovalData } from "./useExportOnApprovalData";
|
||||
import { ChangeStatusButton } from "./ChangeStatusButton";
|
||||
|
||||
export const Header = ({ selectedIds, onClearSelected }) => {
|
||||
const [status, setStatus] = useState(STATUSES.pending);
|
||||
|
||||
const handleClear = (e) => {
|
||||
e.stopPropagation();
|
||||
onClearSelected();
|
||||
};
|
||||
|
||||
return (
|
||||
<HeaderWrapper
|
||||
leftColumn={
|
||||
selectedIds.length > 0 && (
|
||||
<>
|
||||
<StatusSelect value={status} onChange={setStatus} />
|
||||
<ChangeStatusButton
|
||||
selectedIds={selectedIds}
|
||||
selectedStatus={status}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
rightColumn={
|
||||
selectedIds.length > 0 && (
|
||||
<Button onClick={handleClear}>Очистить все</Button>
|
||||
)
|
||||
}
|
||||
classes={{
|
||||
leftColumn: "flex items-center gap-x-4",
|
||||
rightColumn: "flex item-center gap-x-4",
|
||||
}}
|
||||
exportProvider={useExportOnApprovalData}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,65 @@
|
||||
import { Table } from "../Table";
|
||||
import { columns } from "../InitialTable/columns";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getPoints } from "../../../api";
|
||||
import { useCallback, useState } from "react";
|
||||
import { PAGE_SIZE } from "../constants";
|
||||
import { STATUSES } from "../../../config";
|
||||
import { useMergeTableData } from "../useMergeTableData";
|
||||
import { Header } from "./Header";
|
||||
import { useOnApprovalPointsFilters } from "../../../stores/useOnApprovalPointsFilters";
|
||||
|
||||
export const OnApprovalTable = ({ fullWidth }) => {
|
||||
const [pageSize, setPageSize] = useState(PAGE_SIZE);
|
||||
const [page, setPage] = useState(1);
|
||||
const [selectedIds, setSelectedIds] = useState([]);
|
||||
const {
|
||||
filters: { region },
|
||||
} = useOnApprovalPointsFilters();
|
||||
|
||||
const clearSelected = () => setSelectedIds([]);
|
||||
|
||||
const { data, isInitialLoading } = useQuery(
|
||||
["on-approval-points", page, region],
|
||||
async () => {
|
||||
const params = new URLSearchParams({
|
||||
page,
|
||||
page_size: pageSize,
|
||||
"status[]": [STATUSES.onApproval, STATUSES.working, STATUSES.cancelled],
|
||||
});
|
||||
|
||||
return await getPoints(params, region);
|
||||
},
|
||||
{ keepPreviousData: true }
|
||||
);
|
||||
|
||||
const { data: mergedData, isClickedPointLoading } = useMergeTableData(
|
||||
data,
|
||||
setPageSize
|
||||
);
|
||||
|
||||
const handlePageChange = useCallback((page) => setPage(page), []);
|
||||
|
||||
const rowSelection = {
|
||||
selectedRowKeys: selectedIds,
|
||||
onChange: (selectedRowKeys) => setSelectedIds(selectedRowKeys),
|
||||
hideSelectAll: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
header={
|
||||
<Header selectedIds={selectedIds} onClearSelected={clearSelected} />
|
||||
}
|
||||
rowSelection={rowSelection}
|
||||
data={mergedData}
|
||||
onPageChange={handlePageChange}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
isClickedPointLoading={isClickedPointLoading}
|
||||
columns={columns}
|
||||
fullWidth={fullWidth}
|
||||
loading={isInitialLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,11 +1,11 @@
|
||||
import { useFilters } from "../../../stores/useFilters";
|
||||
import { usePointSelection } from "../../../stores/usePointSelection";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { exportPoints } from "../../../api";
|
||||
import { handleExportSuccess } from "../ExportButton";
|
||||
import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters";
|
||||
|
||||
export const useExportInitialData = (enabled, onSettled) => {
|
||||
const { filters } = useFilters();
|
||||
export const useExportPendingData = (enabled, onSettled) => {
|
||||
const { filters } = usePendingPointsFilters();
|
||||
const { prediction, status, categories, region } = filters;
|
||||
const { selection } = usePointSelection();
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
|
||||
const INITIAL = {
|
||||
region: null,
|
||||
};
|
||||
|
||||
const store = (set) => ({
|
||||
filters: INITIAL,
|
||||
|
||||
setRegion: (value) =>
|
||||
set((state) => {
|
||||
state.filters.region = value;
|
||||
}),
|
||||
|
||||
clear: () =>
|
||||
set((state) => {
|
||||
state.filters = INITIAL;
|
||||
}),
|
||||
});
|
||||
|
||||
export const useOnApprovalPointsFilters = create(immer(store));
|
||||
@ -0,0 +1,42 @@
|
||||
import { create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
|
||||
export const INITIAL = {
|
||||
prediction: [0, 0],
|
||||
categories: [],
|
||||
region: null,
|
||||
};
|
||||
|
||||
const store = (set) => ({
|
||||
filters: INITIAL,
|
||||
setPrediction: (value) => {
|
||||
set((state) => {
|
||||
state.filters.prediction = value;
|
||||
});
|
||||
},
|
||||
|
||||
setCategories: (categories) =>
|
||||
set((state) => {
|
||||
state.filters.categories = categories;
|
||||
}),
|
||||
|
||||
setRegion: (value) =>
|
||||
set((state) => {
|
||||
state.filters.region = value;
|
||||
}),
|
||||
|
||||
clear: (predictionFullRange) =>
|
||||
set((state) => {
|
||||
if (!predictionFullRange) {
|
||||
state.filters = INITIAL;
|
||||
return state;
|
||||
}
|
||||
|
||||
state.filters = {
|
||||
...INITIAL,
|
||||
prediction: [predictionFullRange[0], predictionFullRange[1]],
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
export const usePendingPointsFilters = create(immer(store));
|
||||
Loading…
Reference in new issue