Merge branch 'feature/stage_two_fixes' into 'dev'

table fixes, download template, delete points

See merge request spatial/postamates_frontend!37
dev
Timofey Malinin 2 years ago
commit 67e214c772

@ -23,7 +23,7 @@ if (import.meta.env.MODE === "development") {
mountStoreDevtool("PointSelection", usePointSelection);
}
const version = '0.0.7';
const version = '0.0.8';
function App() {

@ -11,13 +11,15 @@ import { LAYER_IDS } from "./constants";
import { RANGE_FILTERS_KEYS, usePendingPointsFilters } from "../../stores/usePendingPointsFilters";
import { fieldHasChanged } from "../../utils.js";
import { useSourceLayerName } from "../../api.js";
import { useMode } from "../../stores/useMode.js";
const statusExpression = ["==", ["get", "status"], STATUSES.pending];
const rawStatusExpression = ["==", ["get", "status"], STATUSES.pending];
const useFilterExpression = () => {
const { filters, ranges } = usePendingPointsFilters();
const { prediction, categories, region } = filters;
const { selection } = usePointSelection();
const { isImportMode } = useMode();
const includedArr = [...selection.included];
const excludedArr = [...selection.excluded];
@ -48,6 +50,8 @@ const useFilterExpression = () => {
? ["in", ["get", "category"], ["literal", categories]]
: true;
const statusExpression = isImportMode ? true : rawStatusExpression;
const matchFilterExpression = [
"all",
statusExpression,

@ -67,6 +67,15 @@ export const exportPoints = async (params, region) => {
return data;
};
export const downloadImportTemplate = async () => {
const { data } = await api.get(
'/api/pre_placement_points/download_template/',
{ responseType: "arraybuffer" }
);
return data;
};
export const uploadPointsFile = async (file, config) => {
const formData = new FormData();
formData.append("file", file);
@ -260,11 +269,14 @@ export const useLastMLRun = () => {
}
export const useGetPendingPointsRange = (dbTable) => {
const { isImportMode } = useMode();
const statusFilter = isImportMode ? '' : `?status[]=${STATUSES.pending}`;
return useQuery(
["prediction-max-min", dbTable],
async () => {
const { data, isInitialLoading, isFetching } = await api.get(
`/api/${dbTable}/filters/?status[]=${STATUSES.pending}`
`/api/${dbTable}/filters/${statusFilter}`
);
return {data, isLoading: isInitialLoading || isFetching};
},
@ -316,4 +328,13 @@ export const useGetPopupPoints = (features) => {
);
return { data, isLoading: isInitialLoading || isFetching };
};
};
export const deletePoint = async (id) => {
const formData = new FormData();
formData.append("ids", id);
await api.delete(
`/api/pre_placement_points/delete_points/`,
{ data: formData }
)
}

@ -123,3 +123,19 @@
scrollbar-width: thin;
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
}
.import_status_new {
background: rgba(82, 196, 26, 0.25);
color: rgb(82, 196, 26);
border-color: rgb(82, 196, 26);
}
.import_status_error {
background: rgba(245, 34, 45, 0.25);
color: rgb(245, 34, 45);
border-color: rgb(245, 34, 45);
}
.import_status_matched {
background: rgba(47, 84, 235, 0.25);
color: rgb(47, 84, 235);
border-color: rgb(47, 84, 235);
}

@ -1,9 +1,10 @@
import { Button, Modal, Spin } from "antd";
import { useState } from "react";
import { importPoints } from "../../api.js";
import { downloadImportTemplate, importPoints } from "../../api.js";
import { LoadingStage } from "./LoadingStage.jsx";
import { ReportStage } from "./ReportStage.jsx";
import {CheckCircleOutlined, CloseCircleOutlined, LoadingOutlined} from "@ant-design/icons";
import { download } from "../../utils.js";
export const PointsFileUploadModal = ({onClose, isOpened}) => {
const [fileId, setFileId] = useState();
@ -12,6 +13,10 @@ export const PointsFileUploadModal = ({onClose, isOpened}) => {
const [isReportStage, setIsReportStage] = useState(false);
const [isError, setIsError] = useState(false);
const onTemplateDownload = async () => {
const data = await downloadImportTemplate();
await download('template.xlsx', data);
}
const onImportPoints = async () => {
setIsImporting(true);
try {
@ -102,6 +107,7 @@ export const PointsFileUploadModal = ({onClose, isOpened}) => {
footer={getFooter()}
>
{getContent()}
<Button className="p-0 text-xs text-grey underline" type="text" onClick={onTemplateDownload}>Скачать шаблон</Button>
</Modal>
);
}

@ -27,6 +27,10 @@ export const PendingPointsFilters = () => {
const newRanges = data?.fullRange;
if (!newRanges) return;
RANGE_FILTERS_KEYS.map((key) => {
if (!ranges[key]) {
setFilterWithKey(newRanges[key], key);
return;
}
const gtChanged = ranges[key][0] !== newRanges[key][0];
const ltChanged = ranges[key][1] !== newRanges[key][1];

@ -7,15 +7,16 @@ import { ClearFiltersButton } from "../../../components/ClearFiltersButton";
import { getDynamicActiveFilters } from "../utils";
import { Spin } from "antd";
import { useQuery } from "@tanstack/react-query";
import { api } from "../../../api.js";
import { api, useDbTableName } from "../../../api.js";
import { STATUSES } from "../../../config.js";
const useGetDataRange = () => {
const dbTable = useDbTableName();
return useQuery(
["working-max-min"],
async () => {
const { data } = await api.get(
`/api/placement_points/filters?status[]=${STATUSES.working}`
`/api/${dbTable}/filters?status[]=${STATUSES.working}`
);
return data;

@ -1,6 +1,6 @@
import { Table } from "../Table";
import { useQuery } from "@tanstack/react-query";
import { getPoints, useCanEdit } from "../../../api";
import { getPoints, useCanEdit, useDbTableName } from "../../../api";
import { useCallback, useState } from "react";
import { PAGE_SIZE } from "../constants";
import { STATUSES } from "../../../config";
@ -35,6 +35,7 @@ export const OnApprovalTable = ({ fullWidth }) => {
useState(false);
const { columns, orderColumns, sort, setSort } = useColumns(extraCols, 'onApprovalTableOrder');
const { isVisible } = useLayersVisibility();
const dbTable = useDbTableName();
const onSort = (sortDirection, key) => {
if (sortDirection === `ascend`) setSort(key);
@ -75,7 +76,7 @@ export const OnApprovalTable = ({ fullWidth }) => {
return { count: 0, results: [] };
}
return await getPoints(params, region);
return await getPoints(params, region, dbTable);
},
{ keepPreviousData: true }
);

@ -1,4 +1,4 @@
import React, {useCallback, useMemo, useState} from "react";
import React, {useCallback, useState} from "react";
import { Table } from "../Table";
import { usePointSelection } from "../../../stores/usePointSelection";
import { useClickedPointConfig } from "../../../stores/useClickedPointConfig";
@ -9,23 +9,7 @@ import { useCanEdit } from "../../../api";
import { useColumns } from "../useColumns.jsx";
import { PAGE_SIZE } from "../constants.js";
import { usePopup } from "../../../stores/usePopup.js";
import { useMode } from "../../../stores/useMode.js";
const MATCHING_STATUS = {
Unmatched: {
name: 'Новая',
color: '#52c41a'
},
Error: {
name: 'Ошибка',
color: '#f5222d'
},
Matched: {
name: 'Совпадение',
color: '#2f54eb'
},
}
import { usePendingTableFields } from "./usePendingTableFields.jsx";
const tableKey = 'pendingTable';
export const PendingTable = ({ fullWidth }) => {
@ -33,28 +17,8 @@ export const PendingTable = ({ fullWidth }) => {
const { clickedPointConfig, setClickedPointConfig } = useClickedPointConfig();
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(PAGE_SIZE);
const { isImportMode } = useMode();
const fields = useMemo(() => {
return isImportMode ? [{
title: "Результат геокодирования",
dataIndex: "matching_status",
key: "matching_status",
width: "120px",
ellipsis: true,
sorter: true,
showSorterTooltip: false,
render: (_, record) => {
if (!record.matching_status) return;
const name = MATCHING_STATUS[record.matching_status].name;
const color = MATCHING_STATUS[record.matching_status].color;
return (
<div className={`bg-[${color}] bg-opacity-25 text-[${color}] rounded-md px-2 py-1 text-center border-solid border-[2px] border-[${color}]`}>
{name}
</div>
);
},
}] : [];
}, [isImportMode])
const fields = usePendingTableFields();
const {columns, orderColumns, sort, setSort} = useColumns(fields, tableKey);
const { setPopup } = usePopup();

@ -4,6 +4,7 @@ import { useMergeTableData } from "../useMergeTableData";
import { STATUSES } from "../../../config";
import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters";
import { appendFiltersInUse } from "../../../utils.js";
import { useMode } from "../../../stores/useMode.js";
export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort) => {
const { filters, ranges } = usePendingPointsFilters();
@ -14,18 +15,19 @@ export const usePendingTableData = (page, resetPage, pageSize, setPageSize, sort
} = filters;
const dbTable = useDbTableName();
const { isImportMode } = useMode();
const getParams = () => {
const params = new URLSearchParams({
page,
page_size: pageSize,
"prediction_current[]": prediction,
"status[]": [STATUSES.pending],
"categories[]": categories,
ordering: sort,
});
appendFiltersInUse(params, filters, ranges);
if (!isImportMode) params.append("status[]", STATUSES.pending)
return params;
}

@ -0,0 +1,72 @@
import { useMode } from "../../../stores/useMode.js";
import React, { useMemo } from "react";
import { Button } from "antd";
import { BiTrash } from "react-icons/all.js";
import { deletePoint } from "../../../api.js";
const MATCHING_STATUS = {
New: {
name: 'Новая',
color: 'import_status_new'
},
Error: {
name: 'Ошибка',
color: 'import_status_error'
},
Matched: {
name: 'Совпадение',
color: 'import_status_matched'
},
}
export const usePendingTableFields = () => {
const { isImportMode } = useMode();
const deleteRow = async (e, id) => {
e.stopPropagation();
try {
await deletePoint(id);
} catch (e) {
//
}
};
const fields = useMemo(() => {
return isImportMode ? [
{
title: "Статус импорта",
dataIndex: "matching_status",
key: "matching_status",
width: "120px",
ellipsis: true,
sorter: true,
showSorterTooltip: false,
render: (_, record) => {
if (!record.matching_status) return;
const name = MATCHING_STATUS[record.matching_status].name;
const color = MATCHING_STATUS[record.matching_status].color;
return (
<div className={`bg-opacity-25 rounded-md px-2 py-1 text-center border-solid border-[2px] ${color}`}>
{name}
</div>
);
},
},
{
title: "Удалить",
key: "del",
width: "60px",
ellipsis: true,
render: (_, record) => {
if (!record.id) return;
return (
<Button type="text" onClick={(event) => deleteRow(event, record.id)}>
<BiTrash />
</Button>
);
},
}
] : [];
}, [isImportMode]);
return fields;
}

@ -1,10 +1,13 @@
import {useState} from "react";
import { useEffect, useState } from "react";
import {Button, Checkbox, Dropdown} from "antd";
import {SettingOutlined} from "@ant-design/icons";
import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd";
export const TableSettings = ({orderColumns}) => {
const [columnsList, setColumnsList] = useState(orderColumns.order)
const [columnsList, setColumnsList] = useState(orderColumns.order);
useEffect(() => {
setColumnsList(orderColumns.order);
}, [orderColumns]);
const handleDrop = (droppedItem) => {
// Ignore drop outside droppable container
@ -38,7 +41,9 @@ export const TableSettings = ({orderColumns}) => {
<div className="flex flex-col" {...provided.droppableProps} ref={provided.innerRef}>
{columnsList.map((item, index) => {
const num = item.position;
console.log(num, item);
if (!orderColumns.defaultColumns[num]) return;
console.log(num, item);
return (
<Draggable key={`list-${num}`} draggableId={`list-${num}`} index={index}>
{(provided) => (

@ -1,5 +1,5 @@
import { STATUS_LABEL_MAPPER } from "../../config";
import { useMemo } from "react";
import { useEffect, useMemo } from "react";
import { useGetRegions } from "../../components/RegionSelect.jsx";
import { getRegionNameById } from "../../Map/Popup/mode-popup/config.js";
import { Button, Popover } from "antd";
@ -404,6 +404,23 @@ export const useColumns = (fields = [], key) => {
}
}));
useEffect(() => {
const newColumns = defaultColumns.filter((column) => {
return !order.find(c => c.key === column.key);
});
const newOrderColumns = newColumns.map((column, index) => {
return {
key: column.key,
position: defaultColumns.length - index - 1,
show: true,
}
});
setOrder([
...order,
...newOrderColumns
]);
}, [defaultColumns]);
const columns = useMemo(() => {
return order.flatMap((item) => !item.show ? [] : defaultColumns[item.position])
.map((column) => {

Loading…
Cancel
Save