Merge branch 'feature/table_columns_reorder' into 'dev'

show/hide columns + table sort

See merge request spatial/postamates_frontend!12
dev
Timofey Malinin 3 years ago
commit a3a58daf7e

@ -36,7 +36,7 @@ export const getPoints = async (params, region) => {
const resultParams = enrichParamsWithRegionFilter(params, region); const resultParams = enrichParamsWithRegionFilter(params, region);
const { data } = await api.get( const { data } = await api.get(
`/api/placement_points?${resultParams.toString()}` `/api/placement_points/?${resultParams.toString()}`
); );
return data; return data;

@ -19,6 +19,8 @@ const extraCols = [
key: "postamat_id", key: "postamat_id",
width: "70px", width: "70px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
]; ];
@ -31,13 +33,19 @@ export const OnApprovalTable = ({ fullWidth }) => {
} = useOnApprovalPointsFilters(); } = useOnApprovalPointsFilters();
const [isMakeWorkingModalOpened, setIsMakeWorkingModalOpened] = const [isMakeWorkingModalOpened, setIsMakeWorkingModalOpened] =
useState(false); useState(false);
const { columns, defaultColumns, order, setOrder } = useColumns(extraCols, 'onApprovalTableOrder'); const { columns, orderColumns, sort, setSort } = useColumns(extraCols, 'onApprovalTableOrder');
const { isVisible } = useLayersVisibility(); const { isVisible } = useLayersVisibility();
const onSort = (sortDirection, key) => {
if (sortDirection === `ascend`) setSort(key);
if (sortDirection === `descend`) setSort(`-${key}`);
if (!sortDirection) setSort(null);
}
const clearSelected = () => setSelectedIds([]); const clearSelected = () => setSelectedIds([]);
const { data, isInitialLoading } = useQuery( const { data, isInitialLoading, isFetching } = useQuery(
["on-approval-points", page, region, isVisible], ["on-approval-points", page, region, isVisible, sort],
async () => { async () => {
const statuses = []; const statuses = [];
@ -60,6 +68,7 @@ export const OnApprovalTable = ({ fullWidth }) => {
statuses.length > 0 statuses.length > 0
? statuses ? statuses
: [STATUSES.onApproval, STATUSES.working, STATUSES.cancelled], : [STATUSES.onApproval, STATUSES.working, STATUSES.cancelled],
ordering: sort,
}); });
if (statuses.length === 0) { if (statuses.length === 0) {
@ -94,7 +103,7 @@ export const OnApprovalTable = ({ fullWidth }) => {
selectedIds={selectedIds} selectedIds={selectedIds}
onClearSelected={clearSelected} onClearSelected={clearSelected}
onOpenMakeWorkingModal={() => setIsMakeWorkingModalOpened(true)} onOpenMakeWorkingModal={() => setIsMakeWorkingModalOpened(true)}
orderColumns={{ defaultColumns, order, setOrder }} orderColumns={orderColumns}
/> />
} }
rowSelection={canEdit ? rowSelection : undefined} rowSelection={canEdit ? rowSelection : undefined}
@ -105,7 +114,10 @@ export const OnApprovalTable = ({ fullWidth }) => {
isClickedPointLoading={isClickedPointLoading} isClickedPointLoading={isClickedPointLoading}
columns={columns} columns={columns}
fullWidth={fullWidth} fullWidth={fullWidth}
loading={isInitialLoading} onChange={(val, filter, sorter) => {
onSort(sorter.order, sorter.columnKey);
}}
loading={isInitialLoading || isFetching}
/> />
{isMakeWorkingModalOpened && ( {isMakeWorkingModalOpened && (
<MakeWorkingModal <MakeWorkingModal

@ -10,19 +10,27 @@ import { useColumns } from "../useColumns.jsx";
import { PAGE_SIZE } from "../constants.js"; import { PAGE_SIZE } from "../constants.js";
import { usePopup } from "../../../stores/usePopup.js"; import { usePopup } from "../../../stores/usePopup.js";
const tableKey = 'pendingTable';
export const PendingTable = ({ fullWidth }) => { export const PendingTable = ({ fullWidth }) => {
const { selection, include, exclude } = usePointSelection(); const { selection, include, exclude } = usePointSelection();
const { clickedPointConfig, setClickedPointConfig } = useClickedPointConfig(); const { clickedPointConfig, setClickedPointConfig } = useClickedPointConfig();
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(PAGE_SIZE); const [pageSize, setPageSize] = useState(PAGE_SIZE);
const {columns, defaultColumns, order, setOrder} = useColumns([], 'pendingTableOrder'); const {columns, orderColumns, sort, setSort} = useColumns([], tableKey);
const { setPopup } = usePopup(); const { setPopup } = usePopup();
const onSort = (sortDirection, key) => {
if (sortDirection === `ascend`) setSort(key);
if (sortDirection === `descend`) setSort(`-${key}`);
if (!sortDirection) setSort(null);
}
const { data, isClickedPointLoading, isDataLoading } = usePendingTableData( const { data, isClickedPointLoading, isDataLoading } = usePendingTableData(
page, page,
() => setPage(1), () => setPage(1),
pageSize, pageSize,
setPageSize setPageSize,
sort
); );
const resetPageSize = () => setPageSize(PAGE_SIZE); const resetPageSize = () => setPageSize(PAGE_SIZE);
@ -79,7 +87,10 @@ export const PendingTable = ({ fullWidth }) => {
isClickedPointLoading={isClickedPointLoading} isClickedPointLoading={isClickedPointLoading}
columns={columns} columns={columns}
fullWidth={fullWidth} fullWidth={fullWidth}
header={<HeaderWrapper exportProvider={useExportPendingData} orderColumns={{defaultColumns, order, setOrder}} />} onChange={(val, filter, sorter) => {
onSort(sorter.order, sorter.columnKey);
}}
header={<HeaderWrapper exportProvider={useExportPendingData} orderColumns={orderColumns} />}
loading={isDataLoading} loading={isDataLoading}
/> />
); );

@ -4,12 +4,12 @@ import { useMergeTableData } from "../useMergeTableData";
import { STATUSES } from "../../../config"; import { STATUSES } from "../../../config";
import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters"; import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters";
export const usePendingTableData = (page, resetPage, pageSize, setPageSize) => { export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort) => {
const { filters } = usePendingPointsFilters(); const { filters } = usePendingPointsFilters();
const { prediction, categories, region } = filters; const { prediction, categories, region } = filters;
const { data, isInitialLoading } = useQuery( const { data, isInitialLoading, isFetching } = useQuery(
["table", page, filters], ["table", page, filters, sort],
async () => { async () => {
const params = new URLSearchParams({ const params = new URLSearchParams({
page, page,
@ -17,6 +17,7 @@ export const usePendingTableData = (page, resetPage, pageSize, setPageSize) => {
"prediction_current[]": prediction, "prediction_current[]": prediction,
"status[]": [STATUSES.pending], "status[]": [STATUSES.pending],
"categories[]": categories, "categories[]": categories,
ordering: sort,
}); });
return await getPoints(params, region); return await getPoints(params, region);
@ -40,6 +41,6 @@ export const usePendingTableData = (page, resetPage, pageSize, setPageSize) => {
data: mergedData, data: mergedData,
pageSize, pageSize,
isClickedPointLoading, isClickedPointLoading,
isDataLoading: isInitialLoading, isDataLoading: isInitialLoading || isFetching,
}; };
}; };

@ -22,6 +22,7 @@ export const Table = React.memo(
header, header,
fullWidth, fullWidth,
loading, loading,
onChange
}) => { }) => {
const { clickedPointConfig, setClickedPointConfig } = const { clickedPointConfig, setClickedPointConfig } =
useClickedPointConfig(); useClickedPointConfig();
@ -68,6 +69,7 @@ export const Table = React.memo(
}} }}
dataSource={data?.results} dataSource={data?.results}
columns={columns} columns={columns}
onChange={onChange}
rowKey="id" rowKey="id"
scroll={SCROLL} scroll={SCROLL}
sticky={true} sticky={true}

@ -1,5 +1,5 @@
import {useState} from "react"; import {useState} from "react";
import {Button, Dropdown} from "antd"; import {Button, Checkbox, Dropdown} from "antd";
import {SettingOutlined} from "@ant-design/icons"; import {SettingOutlined} from "@ant-design/icons";
import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd"; import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd";
@ -19,6 +19,15 @@ export const TableSettings = ({orderColumns}) => {
orderColumns.setOrder(updatedList); orderColumns.setOrder(updatedList);
}; };
const hideColumn = (columnIndex) => {
const updatedList = columnsList.map((item, index) => {
if (columnIndex === index) return {...item, show: !item.show};
return item;
});
setColumnsList(updatedList);
orderColumns.setOrder(updatedList);
}
const columnsListRender = () => { const columnsListRender = () => {
return ( return (
<div onClick={(e) => e.stopPropagation()} className='z-10 bg-white-background rounded-xl p-3 space-y-3'> <div onClick={(e) => e.stopPropagation()} className='z-10 bg-white-background rounded-xl p-3 space-y-3'>
@ -27,12 +36,14 @@ export const TableSettings = ({orderColumns}) => {
{(provided) => ( {(provided) => (
<div className="flex flex-col" {...provided.droppableProps} ref={provided.innerRef}> <div className="flex flex-col" {...provided.droppableProps} ref={provided.innerRef}>
{columnsList.map((item, index) => { {columnsList.map((item, index) => {
const num = item.position;
return ( return (
<Draggable key={`list-${item}`} draggableId={`list-${item}`} index={index}> <Draggable key={`list-${num}`} draggableId={`list-${num}`} index={index}>
{(provided) => ( {(provided) => (
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}> <div className="flex flex-row gap-2" ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<Checkbox onChange={() => hideColumn(index)} checked={item.show} />
<p> <p>
{ orderColumns.defaultColumns[item].title } { orderColumns.defaultColumns[num].name || orderColumns.defaultColumns[num].title }
</p> </p>
</div> </div>
)} )}

@ -10,16 +10,23 @@ import { useExportWorkingData } from "./useExportWorkingData";
import { useWorkingPointsFilters } from "../../../stores/useWorkingPointsFilters"; import { useWorkingPointsFilters } from "../../../stores/useWorkingPointsFilters";
import { useColumns } from "./useColumns.jsx"; import { useColumns } from "./useColumns.jsx";
const tableKey = 'workingTable'
export const WorkingTable = ({ fullWidth }) => { export const WorkingTable = ({ fullWidth }) => {
const [pageSize, setPageSize] = useState(PAGE_SIZE); const [pageSize, setPageSize] = useState(PAGE_SIZE);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const { const {
filters: { region, deltaTraffic, factTraffic, age }, filters: { region, deltaTraffic, factTraffic, age },
} = useWorkingPointsFilters(); } = useWorkingPointsFilters();
const {columns, defaultColumns, order, setOrder} = useColumns('workingTableOrder'); const {columns, orderColumns, sort, setSort} = useColumns(tableKey);
const { data, isInitialLoading } = useQuery( const onSort = (sortDirection, key) => {
["working-points", page, region, deltaTraffic, factTraffic, age], if (sortDirection === `ascend`) setSort(key);
if (sortDirection === `descend`) setSort(`-${key}`);
if (!sortDirection) setSort(null);
}
const { data, isInitialLoading, isFetching } = useQuery(
["working-points", page, region, deltaTraffic, factTraffic, age, sort],
async () => { async () => {
const params = new URLSearchParams({ const params = new URLSearchParams({
page, page,
@ -28,6 +35,7 @@ export const WorkingTable = ({ fullWidth }) => {
"delta_current[]": deltaTraffic, "delta_current[]": deltaTraffic,
"fact[]": factTraffic, "fact[]": factTraffic,
"age_day[]": age, "age_day[]": age,
ordering: sort
}); });
return await getPoints(params, region); return await getPoints(params, region);
@ -51,8 +59,11 @@ export const WorkingTable = ({ fullWidth }) => {
isClickedPointLoading={isClickedPointLoading} isClickedPointLoading={isClickedPointLoading}
columns={columns} columns={columns}
fullWidth={fullWidth} fullWidth={fullWidth}
header={<HeaderWrapper exportProvider={useExportWorkingData} orderColumns={{defaultColumns, order, setOrder}} />} onChange={(val, filter, sorter) => {
loading={isInitialLoading} onSort(sorter.order, sorter.columnKey);
}}
header={<HeaderWrapper exportProvider={useExportWorkingData} orderColumns={orderColumns} />}
loading={isInitialLoading || isFetching}
/> />
); );
}; };

@ -1,20 +1,27 @@
import { useGetRegions } from "../../../components/RegionSelect.jsx"; import {useGetRegions} from "../../../components/RegionSelect.jsx";
import { useMemo } from "react"; import {useMemo} from "react";
import { getRegionNameById } from "../../../Map/Popup/mode-popup/config.js"; import {getRegionNameById} from "../../../Map/Popup/mode-popup/config.js";
import { SearchOutlined } from "@ant-design/icons"; import {SearchOutlined} from "@ant-design/icons";
import { Button, Popover } from "antd"; import {Button, Popover} from "antd";
import { AddressSearch } from "../../../Map/AddressSearch.jsx"; import {AddressSearch} from "../../../Map/AddressSearch.jsx";
import { useTable } from "../../../stores/useTable.js"; import {useTable} from "../../../stores/useTable.js";
import useLocalStorage from "../../../hooks/useLocalStorage.js"; import useLocalStorage from "../../../hooks/useLocalStorage.js";
const DEFAULT_LENGTH = 11; const DEFAULT_LENGTH = 11;
export const useColumns = ({ key }) => { export const useColumns = (key) => {
const { data: regions } = useGetRegions(); const {data: regions} = useGetRegions();
const { const {
tableState: { fullScreen }, tableState: {fullScreen},
} = useTable(); } = useTable();
const [order, setOrder] = useLocalStorage(key, [...Array(DEFAULT_LENGTH).keys()]) const [order, setOrder] = useLocalStorage(`${key}Order`, [...Array(DEFAULT_LENGTH).keys()].map((position) => {
return {
position,
show: true,
}
}));
const [sort, setSort] = useLocalStorage(`${key}Sort`, null);
const defaultColumns = useMemo(() => { const defaultColumns = useMemo(() => {
return [ return [
@ -23,20 +30,23 @@ export const useColumns = ({ key }) => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span>Адрес</span> <span>Адрес</span>
<Popover <Popover
content={<AddressSearch autoFocus={true} />} content={<AddressSearch autoFocus={true}/>}
trigger="click" trigger="click"
placement={"right"} placement={"right"}
> >
<Button> <Button>
<SearchOutlined /> <SearchOutlined/>
</Button> </Button>
</Popover> </Popover>
</div> </div>
) : ( ) : (
"Адрес" "Адрес"
), ),
name: "Адрес",
dataIndex: "address", dataIndex: "address",
key: "address", key: "address",
sorter: true,
showSorterTooltip: false,
width: 200, width: 200,
}, },
{ {
@ -45,6 +55,8 @@ export const useColumns = ({ key }) => {
key: "area", key: "area",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
render: (_, record) => { render: (_, record) => {
return getRegionNameById(record.area, regions?.normalized); return getRegionNameById(record.area, regions?.normalized);
}, },
@ -58,6 +70,8 @@ export const useColumns = ({ key }) => {
render: (_, record) => { render: (_, record) => {
return getRegionNameById(record.district, regions?.normalized); return getRegionNameById(record.district, regions?.normalized);
}, },
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Название", title: "Название",
@ -65,6 +79,8 @@ export const useColumns = ({ key }) => {
key: "name", key: "name",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Категория", title: "Категория",
@ -72,6 +88,8 @@ export const useColumns = ({ key }) => {
key: "category", key: "category",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "План", title: "План",
@ -79,6 +97,8 @@ export const useColumns = ({ key }) => {
key: "plan_current", key: "plan_current",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Факт", title: "Факт",
@ -86,6 +106,8 @@ export const useColumns = ({ key }) => {
key: "fact", key: "fact",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Расхождение с прогнозом", title: "Расхождение с прогнозом",
@ -93,6 +115,8 @@ export const useColumns = ({ key }) => {
key: "delta_current", key: "delta_current",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Зрелость", title: "Зрелость",
@ -100,12 +124,16 @@ export const useColumns = ({ key }) => {
key: "age_day", key: "age_day",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Дата начала работы", title: "Дата начала работы",
dataIndex: "start_date", dataIndex: "start_date",
key: "start_date", key: "start_date",
width: "120px", width: "120px",
sorter: true,
showSorterTooltip: false,
render: (value) => { render: (value) => {
if (!value) return "Нет данных"; if (!value) return "Нет данных";
@ -119,15 +147,31 @@ export const useColumns = ({ key }) => {
key: "postamat_id", key: "postamat_id",
width: "70px", width: "70px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
]; ];
}, [regions?.normalized, fullScreen]); }, [regions?.normalized, fullScreen]);
const columns = useMemo(() => { const columns = useMemo(() => {
return order.map((item) => { return order.flatMap((item) => !item.show ? [] : defaultColumns[item.position])
return defaultColumns[item]; .map((column) => {
}) if (sort && sort.includes(column.key)) return {
...column,
defaultSortOrder: sort.includes('-') ? 'descend' : 'ascend',
};
return column;
});
}, [defaultColumns, order, fullScreen]); }, [defaultColumns, order, fullScreen]);
return {columns, defaultColumns, order, setOrder}; return {
columns,
orderColumns: {
defaultColumns,
order,
setOrder,
},
sort,
setSort
};
}; };

@ -15,7 +15,14 @@ export const useColumns = (fields = [], key) => {
tableState: { fullScreen }, tableState: { fullScreen },
} = useTable(); } = useTable();
const [order, setOrder] = useLocalStorage(key, [...Array(DEFAULT_LENGTH + fields.length).keys()]) const [order, setOrder] = useLocalStorage(`${key}Order`, [...Array(DEFAULT_LENGTH).keys()].map((position) => {
return {
position,
show: true,
}
}));
const [sort, setSort] = useLocalStorage(`${key}Sort`, null);
const defaultColumns = useMemo(() => { const defaultColumns = useMemo(() => {
return [ return [
@ -36,9 +43,12 @@ export const useColumns = (fields = [], key) => {
) : ( ) : (
"Адрес" "Адрес"
), ),
name: "Адрес",
dataIndex: "address", dataIndex: "address",
key: "address", key: "address",
width: 200, width: 200,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Район", title: "Район",
@ -46,6 +56,8 @@ export const useColumns = (fields = [], key) => {
key: "area", key: "area",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
render: (_, record) => { render: (_, record) => {
return getRegionNameById(record.area, regions?.normalized); return getRegionNameById(record.area, regions?.normalized);
}, },
@ -56,6 +68,8 @@ export const useColumns = (fields = [], key) => {
key: "district", key: "district",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
render: (_, record) => { render: (_, record) => {
return getRegionNameById(record.district, regions?.normalized); return getRegionNameById(record.district, regions?.normalized);
}, },
@ -66,6 +80,8 @@ export const useColumns = (fields = [], key) => {
key: "name", key: "name",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Категория", title: "Категория",
@ -73,6 +89,8 @@ export const useColumns = (fields = [], key) => {
key: "category", key: "category",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
{ {
title: "Статус", title: "Статус",
@ -80,6 +98,8 @@ export const useColumns = (fields = [], key) => {
key: "status", key: "status",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
render: (_, record) => { render: (_, record) => {
return STATUS_LABEL_MAPPER[record.status]; return STATUS_LABEL_MAPPER[record.status];
}, },
@ -90,16 +110,32 @@ export const useColumns = (fields = [], key) => {
key: "prediction_current", key: "prediction_current",
width: "120px", width: "120px",
ellipsis: true, ellipsis: true,
sorter: true,
showSorterTooltip: false,
}, },
...fields, ...fields,
]; ];
}, [regions?.normalized, fields, fullScreen]); }, [regions?.normalized, fields, fullScreen]);
const columns = useMemo(() => { const columns = useMemo(() => {
return order.map((item) => { return order.flatMap((item) => !item.show ? [] : defaultColumns[item.position])
return defaultColumns[item]; .map((column) => {
}) if (sort && sort.includes(column.key)) return {
...column,
defaultSortOrder: sort.includes('-') ? 'descend' : 'ascend',
};
return column;
});
}, [defaultColumns, order, fullScreen]); }, [defaultColumns, order, fullScreen]);
return {columns, defaultColumns, order, setOrder}; return {
columns,
orderColumns: {
defaultColumns,
order,
setOrder,
},
sort,
setSort
};
}; };

Loading…
Cancel
Save