Refactor table data merging

dev
Platon Yasev 3 years ago
parent 057a9416b5
commit cc8959c835

@ -7,11 +7,11 @@ import { MapPopup } from "./Popup";
import { Basemap } from "./Basemap";
import { SignOut } from "../SignOut";
import debounce from "lodash.debounce";
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";
import { TableWrapper } from "../modules/Table/TableWrapper";
export const MapComponent = () => {
const mapRef = useRef(null);
@ -117,7 +117,7 @@ export const MapComponent = () => {
<SignOut />
</Map>
</div>
<Table />
<TableWrapper />
</MapProvider>
);
};

@ -0,0 +1,57 @@
import { useCallback, useState } from "react";
import { Table } from "../Table";
import { usePointSelection } from "../../../stores/usePointSelection";
import { useClickedPointConfig } from "../../../stores/useClickedPointConfig";
import { useInitialTableData } from "./useInitialTableData";
export const InitialTable = () => {
const { selection, include, exclude } = usePointSelection();
const { clickedPointConfig } = useClickedPointConfig();
const [page, setPage] = useState(1);
const { data, pageSize, isClickedPointLoading } = useInitialTableData(page);
const getSelectedRowKeys = useCallback(() => {
const ids = data?.results.map((item) => item.location_id) ?? [];
const clickedPoint = data?.results.find(
(item) => item.location_id === clickedPointConfig?.id
);
const inExcludedList = (id) => selection.excluded.has(id);
const shouldNotSelect = (id) =>
id === clickedPoint?.id && clickedPointConfig?.shouldSelect === false;
return [
...ids.filter((id) => {
return !inExcludedList(id) && !shouldNotSelect(id);
}),
...selection.included,
];
}, [data, clickedPointConfig, selection]);
const rowSelection = {
selectedRowKeys: getSelectedRowKeys(),
onSelect: (record, selected) => {
const { location_id: id } = record;
if (selected) {
include(id);
} else {
exclude(id);
}
},
hideSelectAll: true,
};
const handlePageChange = useCallback((page) => setPage(page), []);
return (
<Table
rowSelection={rowSelection}
data={data}
onPageChange={handlePageChange}
page={page}
pageSize={pageSize}
isClickedPointLoading={isClickedPointLoading}
/>
);
};

@ -0,0 +1,56 @@
import { useState } from "react";
import { PAGE_SIZE } from "../constants";
import { useFilters } from "../../../stores/useFilters";
import { usePointSelection } from "../../../stores/usePointSelection";
import { useQuery } from "@tanstack/react-query";
import { api } from "../../../api";
import { useMergeTableData } from "../useMergeTableData";
export const useInitialTableData = (page) => {
const [pageSize, setPageSize] = useState(PAGE_SIZE);
const { filters } = useFilters();
const { prediction, status, categories, region } = filters;
const { selection } = usePointSelection();
const { data } = useQuery(
["table", page, filters, selection],
async () => {
const params = new URLSearchParams({
page,
page_size: pageSize,
"prediction_current[]": prediction,
"status[]": status,
"categories[]": categories,
"included[]": [...selection.included],
});
if (region) {
if (region.type === "ao") {
params.append("ao[]", region.id);
}
if (region.type === "rayon") {
params.append("rayon[]", region.id);
}
}
const { data } = await api.get(
`/api/placement_points?${params.toString()}`
);
return data;
},
{ keepPreviousData: true }
);
const { data: mergedData, isClickedPointLoading } = useMergeTableData(
data,
setPageSize
);
return {
data: mergedData,
pageSize,
isClickedPointLoading,
};
};

@ -1,12 +1,8 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, { useEffect, useRef } from "react";
import { Collapse, Empty, Table as AntdTable } from "antd";
import "./Table.css";
import { useQuery } from "@tanstack/react-query";
import { api } from "../../api";
import parse from "wellknown";
import { useMap } from "react-map-gl";
import { usePointSelection } from "../../stores/usePointSelection";
import { useFilters } from "../../stores/useFilters";
import { useClickedPointConfig } from "../../stores/useClickedPointConfig";
import scrollIntoView from "scroll-into-view-if-needed";
@ -68,164 +64,25 @@ const columns = [
},
];
const PAGE_SIZE = 30;
const useTableData = (page) => {
const [pageSize, setPageSize] = useState(PAGE_SIZE);
const { filters } = useFilters();
const { prediction, status, categories, region } = filters;
const { selection } = usePointSelection();
const { clickedPointConfig } = useClickedPointConfig();
const [finalData, setFinalData] = useState();
const { data } = useQuery(
["table", page, filters, selection],
async () => {
const params = new URLSearchParams({
page,
page_size: pageSize,
"prediction_current[]": prediction,
"status[]": status,
"categories[]": categories,
"included[]": [...selection.included],
});
if (region) {
if (region.type === "ao") {
params.append("ao[]", region.id);
}
if (region.type === "rayon") {
params.append("rayon[]", region.id);
}
}
const { data } = await api.get(
`/api/placement_points?${params.toString()}`
);
return data;
},
{ keepPreviousData: true }
);
useEffect(() => {
if (!data) return;
setFinalData(data);
}, [data]);
const [shouldLoadClickedPoint, setShouldLoadClickedPoint] = useState(false);
useEffect(() => {
if (!data || clickedPointConfig === null) return;
const clickedPoint = data.results.find(
(item) => item.location_id === clickedPointConfig.id
);
if (clickedPoint) {
return;
}
setShouldLoadClickedPoint(true);
}, [data, clickedPointConfig]);
const {
data: remoteClickedPoint,
isInitialLoading,
isFetching,
} = useQuery(
["clicked-point", clickedPointConfig],
async () => {
const params = new URLSearchParams({
"location_ids[]": [clickedPointConfig.id],
});
const { data } = await api.get(
`/api/placement_points?${params.toString()}`
);
return data;
},
{
enabled: shouldLoadClickedPoint,
onSuccess: () => setShouldLoadClickedPoint(false),
}
);
useEffect(() => {
if (!remoteClickedPoint) return;
setPageSize((prevState) => prevState + 1);
setFinalData((prevState) => ({
...prevState,
count: prevState.count + 1,
results: [remoteClickedPoint.results[0], ...prevState.results],
}));
}, [remoteClickedPoint]);
useEffect(() => {
if (clickedPointConfig === null) {
setPageSize(PAGE_SIZE);
setFinalData(data);
}
}, [clickedPointConfig]);
return {
data: finalData,
export const Table = React.memo(
({
height = 200,
rowSelection,
data,
pageSize,
isClickedPointLoading: isInitialLoading || isFetching,
};
};
export const Table = React.memo(({ height = 200 }) => {
isClickedPointLoading,
page,
onPageChange,
}) => {
const { clickedPointConfig } = useClickedPointConfig();
const { map } = useMap();
const tableRef = useRef(null);
const [page, setPage] = useState(1);
const { selection, include, exclude } = usePointSelection();
const { clickedPointConfig } = useClickedPointConfig();
const { data, pageSize, isClickedPointLoading } = useTableData(page);
const SCROLL = {
y: `${height}px`,
x: "max-content",
};
const handlePageChange = useCallback((page) => setPage(page), []);
const getSelectedRowKeys = useCallback(() => {
const ids = data?.results.map((item) => item.location_id) ?? [];
const clickedPoint = data?.results.find(
(item) => item.location_id === clickedPointConfig?.id
);
const inExcludedList = (id) => selection.excluded.has(id);
const shouldNotSelect = (id) =>
id === clickedPoint?.id && clickedPointConfig?.shouldSelect === false;
return [
...ids.filter((id) => {
return !inExcludedList(id) && !shouldNotSelect(id);
}),
...selection.included,
];
}, [data, clickedPointConfig, selection]);
const rowSelection = {
selectedRowKeys: getSelectedRowKeys(),
onSelect: (record, selected) => {
const { location_id: id } = record;
if (selected) {
include(id);
} else {
exclude(id);
}
},
hideSelectAll: true,
};
useEffect(() => {
if (clickedPointConfig === null || isClickedPointLoading) return;
@ -243,13 +100,11 @@ export const Table = React.memo(({ height = 200 }) => {
ref={tableRef}
className="table"
size="small"
// rowSelection={rowSelection}
locale={{ emptyText: <Empty description="Нет данных" /> }}
// loading={isLoading}
pagination={{
pageSize,
current: page,
onChange: handlePageChange,
onChange: onPageChange,
total: data?.count,
showSizeChanger: false,
position: "bottomCenter",
@ -264,7 +119,10 @@ export const Table = React.memo(({ height = 200 }) => {
onClick: () => {
const geometry = parse(record.geometry);
map.flyTo({
center: [geometry.coordinates[0], geometry.coordinates[1]],
center: [
geometry.coordinates[0],
geometry.coordinates[1],
],
zoom: 15,
speed: 5,
});
@ -273,11 +131,14 @@ export const Table = React.memo(({ height = 200 }) => {
}}
rowSelection={rowSelection}
rowClassName={(record) =>
record.location_id === clickedPointConfig?.id ? "scroll-row" : ""
record.location_id === clickedPointConfig?.id
? "scroll-row"
: ""
}
/>
</Collapse.Panel>
</Collapse>
</div>
);
});
}
);

@ -0,0 +1,13 @@
import { useMode } from "../../stores/useMode";
import { MODES } from "../../config";
import { InitialTable } from "./InitialTable/InitialTable";
export const TableWrapper = () => {
const { mode } = useMode();
if (mode === MODES.INITIAL) {
return <InitialTable />;
}
return null;
};

@ -0,0 +1 @@
export const PAGE_SIZE = 30;

@ -0,0 +1,28 @@
import { useClickedPointConfig } from "../../stores/useClickedPointConfig";
import { useQuery } from "@tanstack/react-query";
import { api } from "../../api";
export const useGetClickedPoint = (enabled, onSuccess) => {
const { clickedPointConfig } = useClickedPointConfig();
const { data, isInitialLoading, isFetching } = useQuery(
["clicked-point", clickedPointConfig],
async () => {
const params = new URLSearchParams({
"location_ids[]": [clickedPointConfig.id],
});
const { data } = await api.get(
`/api/placement_points?${params.toString()}`
);
return data;
},
{
enabled,
onSuccess,
}
);
return { data, isLoading: isInitialLoading || isFetching };
};

@ -0,0 +1,59 @@
import { useEffect, useState } from "react";
import { PAGE_SIZE } from "./constants";
import { useClickedPointConfig } from "../../stores/useClickedPointConfig";
import { useGetClickedPoint } from "./useGetClickedPoint";
export const useMergeTableData = (fullData, onPageSizeChange) => {
const [mergedData, setMergedData] = useState();
const [shouldLoadClickedPoint, setShouldLoadClickedPoint] = useState(false);
const { data: clickedPointData, isLoading: isClickedPointLoading } =
useGetClickedPoint(shouldLoadClickedPoint, () =>
setShouldLoadClickedPoint(false)
);
const { clickedPointConfig } = useClickedPointConfig();
useEffect(() => {
if (!fullData) return;
setMergedData(fullData);
}, [fullData]);
// find clicked point among already loaded data - if no - fetch it
useEffect(() => {
if (!fullData || clickedPointConfig === null) return;
const clickedPoint = fullData.results.find(
(item) => item.location_id === clickedPointConfig.id
);
if (clickedPoint) {
return;
}
setShouldLoadClickedPoint(true);
}, [fullData, clickedPointConfig]);
// merge data with clicked point
useEffect(() => {
if (!clickedPointData) return;
onPageSizeChange((prevState) => prevState + 1);
setMergedData((prevState) => ({
...prevState,
count: prevState.count + 1,
results: [clickedPointData.results[0], ...prevState.results],
}));
}, [clickedPointData]);
// reset data after popup disappeared
useEffect(() => {
if (clickedPointConfig === null) {
onPageSizeChange(PAGE_SIZE);
setMergedData(fullData);
}
}, [clickedPointConfig, fullData]);
return { data: mergedData, isClickedPointLoading };
};
Loading…
Cancel
Save