diff --git a/src/Map/MapComponent.jsx b/src/Map/MapComponent.jsx
index 3e4bf08..c1da0d4 100644
--- a/src/Map/MapComponent.jsx
+++ b/src/Map/MapComponent.jsx
@@ -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 = () => {
-
+
);
};
diff --git a/src/modules/Table/InitialTable/InitialTable.jsx b/src/modules/Table/InitialTable/InitialTable.jsx
new file mode 100644
index 0000000..5063aec
--- /dev/null
+++ b/src/modules/Table/InitialTable/InitialTable.jsx
@@ -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 (
+
+ );
+};
diff --git a/src/modules/Table/InitialTable/useInitialTableData.js b/src/modules/Table/InitialTable/useInitialTableData.js
new file mode 100644
index 0000000..c4ae7ae
--- /dev/null
+++ b/src/modules/Table/InitialTable/useInitialTableData.js
@@ -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,
+ };
+};
diff --git a/src/modules/Table/Table.jsx b/src/modules/Table/Table.jsx
index 48d76eb..f63815e 100644
--- a/src/modules/Table/Table.jsx
+++ b/src/modules/Table/Table.jsx
@@ -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,216 +64,81 @@ 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 }) => {
- 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);
+ isClickedPointLoading,
+ page,
+ onPageChange,
+ }) => {
+ const { clickedPointConfig } = useClickedPointConfig();
+ const { map } = useMap();
+ const tableRef = useRef(null);
+
+ const SCROLL = {
+ y: `${height}px`,
+ x: "max-content",
+ };
+
+ useEffect(() => {
+ if (clickedPointConfig === null || isClickedPointLoading) return;
+
+ const row = document.querySelector(".scroll-row");
+ if (row) {
+ scrollIntoView(row, { behavior: "smooth" });
}
- },
- hideSelectAll: true,
- };
-
- useEffect(() => {
- if (clickedPointConfig === null || isClickedPointLoading) return;
-
- const row = document.querySelector(".scroll-row");
- if (row) {
- scrollIntoView(row, { behavior: "smooth" });
- }
- }, [clickedPointConfig, data]);
-
- return (
-
-
-
- }}
- // loading={isLoading}
- pagination={{
- pageSize,
- current: page,
- onChange: handlePageChange,
- total: data?.count,
- showSizeChanger: false,
- position: "bottomCenter",
- }}
- dataSource={data?.results}
- columns={columns}
- rowKey="location_id"
- scroll={SCROLL}
- sticky={true}
- onRow={(record) => {
- return {
- onClick: () => {
- const geometry = parse(record.geometry);
- map.flyTo({
- center: [geometry.coordinates[0], geometry.coordinates[1]],
- zoom: 15,
- speed: 5,
- });
- },
- };
- }}
- rowSelection={rowSelection}
- rowClassName={(record) =>
- record.location_id === clickedPointConfig?.id ? "scroll-row" : ""
- }
- />
-
-
-
- );
-});
+ }, [clickedPointConfig, data]);
+
+ return (
+
+
+
+ }}
+ pagination={{
+ pageSize,
+ current: page,
+ onChange: onPageChange,
+ total: data?.count,
+ showSizeChanger: false,
+ position: "bottomCenter",
+ }}
+ dataSource={data?.results}
+ columns={columns}
+ rowKey="location_id"
+ scroll={SCROLL}
+ sticky={true}
+ onRow={(record) => {
+ return {
+ onClick: () => {
+ const geometry = parse(record.geometry);
+ map.flyTo({
+ center: [
+ geometry.coordinates[0],
+ geometry.coordinates[1],
+ ],
+ zoom: 15,
+ speed: 5,
+ });
+ },
+ };
+ }}
+ rowSelection={rowSelection}
+ rowClassName={(record) =>
+ record.location_id === clickedPointConfig?.id
+ ? "scroll-row"
+ : ""
+ }
+ />
+
+
+
+ );
+ }
+);
diff --git a/src/modules/Table/TableWrapper.jsx b/src/modules/Table/TableWrapper.jsx
new file mode 100644
index 0000000..3870831
--- /dev/null
+++ b/src/modules/Table/TableWrapper.jsx
@@ -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 ;
+ }
+
+ return null;
+};
diff --git a/src/modules/Table/constants.js b/src/modules/Table/constants.js
new file mode 100644
index 0000000..5ade0a7
--- /dev/null
+++ b/src/modules/Table/constants.js
@@ -0,0 +1 @@
+export const PAGE_SIZE = 30;
diff --git a/src/modules/Table/useGetClickedPoint.js b/src/modules/Table/useGetClickedPoint.js
new file mode 100644
index 0000000..3277c71
--- /dev/null
+++ b/src/modules/Table/useGetClickedPoint.js
@@ -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 };
+};
diff --git a/src/modules/Table/useMergeTableData.js b/src/modules/Table/useMergeTableData.js
new file mode 100644
index 0000000..8ea30f8
--- /dev/null
+++ b/src/modules/Table/useMergeTableData.js
@@ -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 };
+};