Add consolidation POST + file upload
This commit is contained in:
parent
81548eb4ae
commit
8747f6318c
@ -33,6 +33,9 @@ interface DataTableProps<T extends Record<string, any>> {
|
||||
scrollY?: number | string;
|
||||
onRow?: TableProps<T>["onRow"];
|
||||
}
|
||||
const locale = {
|
||||
emptyText: "Нет данных, чтобы отобразить",
|
||||
};
|
||||
|
||||
export function DataTable<T extends Record<string, any>>({
|
||||
config,
|
||||
@ -209,6 +212,7 @@ export function DataTable<T extends Record<string, any>>({
|
||||
return (
|
||||
<div ref={wrapperRef} style={{ flex: 1, height: "100%" }}>
|
||||
<Table<T>
|
||||
locale={locale}
|
||||
rowKey={rowKey}
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
|
||||
47
src/components/TableTag/TableTag.tsx
Normal file
47
src/components/TableTag/TableTag.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import React from "react";
|
||||
|
||||
type TableTagProps = {
|
||||
text: string;
|
||||
color?: string; // text color
|
||||
bgColor?: string; // background color
|
||||
borderColor?: string; // border color
|
||||
icon?: React.ReactNode;
|
||||
rounded?: boolean | string; // true = fully rounded
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const TableTag: React.FC<TableTagProps> = ({
|
||||
text,
|
||||
color = "#1677ff",
|
||||
bgColor = "#e6f4ff",
|
||||
borderColor = "#91caff",
|
||||
icon,
|
||||
rounded = true,
|
||||
style,
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<span
|
||||
className={className}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
padding: "2px 10px",
|
||||
fontSize: 13,
|
||||
lineHeight: "20px",
|
||||
fontWeight: 500,
|
||||
color: color,
|
||||
backgroundColor: bgColor,
|
||||
border: `1px solid ${borderColor}`,
|
||||
borderRadius: rounded === true ? 999 : (rounded as string),
|
||||
whiteSpace: "nowrap",
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{icon && <span style={{ display: "inline-flex" }}>{icon}</span>}
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
42
src/components/TableTag/presets.tsx
Normal file
42
src/components/TableTag/presets.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
export type Preset = {
|
||||
color: string;
|
||||
bgColor: string;
|
||||
borderColor: string;
|
||||
};
|
||||
export const TAG_PRESETS = {
|
||||
green: {
|
||||
color: "rgb(22, 101, 52)",
|
||||
bgColor: "rgb(236, 253, 243)",
|
||||
borderColor: "rgba(58, 150, 71, 0.2)",
|
||||
},
|
||||
red: {
|
||||
color: "rgb(153, 27, 27)",
|
||||
bgColor: "rgb(254, 226, 226)",
|
||||
borderColor: "rgba(239, 68, 68, 0.2)",
|
||||
},
|
||||
orange: {
|
||||
color: "rgb(154, 52, 18)",
|
||||
bgColor: "rgb(255, 237, 213)",
|
||||
borderColor: "rgba(251, 146, 60, 0.28)",
|
||||
},
|
||||
geekblue: {
|
||||
color: "rgb(36, 86, 197)",
|
||||
bgColor: "rgb(238, 244, 255)",
|
||||
borderColor: "rgba(37, 99, 235, 0.2)",
|
||||
},
|
||||
purple: {
|
||||
bgColor: "rgb(237, 233, 254)",
|
||||
borderColor: "rgba(124, 58, 237, 0.2)",
|
||||
color: "rgb(91, 33, 182)",
|
||||
},
|
||||
default: {
|
||||
bgColor: "rgb(241, 245, 249)",
|
||||
borderColor: "rgba(0, 0, 0, 0.08)",
|
||||
color: "rgb(102, 112, 133)",
|
||||
},
|
||||
gold: {
|
||||
bgColor: "rgb(254, 243, 199)",
|
||||
borderColor: "rgba(245, 158, 11, 0.25)",
|
||||
color: "rgb(146, 64, 14)",
|
||||
},
|
||||
} as const;
|
||||
@ -16,30 +16,8 @@ import PageTitle from "../components/PageTitle";
|
||||
import { fetchOneCompanyGroup } from "../services/api/directories/company_groups";
|
||||
import { getConsolidationsListThunk } from "../store/slices/consolidations/thunks";
|
||||
import { ConsoleTableWidget } from "../widgets/ConsoleTable/ConsoleTable";
|
||||
|
||||
function DrawerContent() {
|
||||
const { current_version_id, current_legal_entity_id } = useAppSelector(
|
||||
(state) => state.consolidations,
|
||||
);
|
||||
const [drawerContent, setDrawerContent] = useState([]);
|
||||
// if (!drawerContent.length) return;
|
||||
if (!current_legal_entity_id) return;
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
Version_id: {current_version_id}, legal_entity_id:{" "}
|
||||
{current_legal_entity_id}
|
||||
</div>
|
||||
<FileUploaderModal
|
||||
legal_entity_id={current_legal_entity_id}
|
||||
trigger_sse_cb={() => {}}
|
||||
/>
|
||||
{drawerContent.map((f) => (
|
||||
<FileItem key={f.id} file={f} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
import { setSelectedVersion } from "../store/slices/consolidations/consolidationsSlice";
|
||||
import { DocumentsDrawer } from "../widgets/FilesDrawer/FilesDrawer";
|
||||
|
||||
export function ConsolidationPage() {
|
||||
const [legalEntities, setLegalEntities] = useState([]);
|
||||
@ -65,6 +43,9 @@ export function ConsolidationPage() {
|
||||
// dispatch()
|
||||
}
|
||||
getAllInfo();
|
||||
return () => {
|
||||
dispatch(setSelectedVersion(null));
|
||||
};
|
||||
}, [dispatch, group_id]);
|
||||
return (
|
||||
<div
|
||||
@ -124,15 +105,12 @@ export function ConsolidationPage() {
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<Drawer
|
||||
size="large"
|
||||
<DocumentsDrawer
|
||||
open={drawerOpen}
|
||||
onClose={() => {
|
||||
setDrawerOpen(false);
|
||||
}}
|
||||
>
|
||||
<DrawerContent />
|
||||
</Drawer>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import { Select } from "antd";
|
||||
import PdfDataViewer from "../widgets/PDFResultViewer/PDFDataViewer";
|
||||
import { Config } from "../conf/config";
|
||||
import IFrameBlock from "../widgets/PDFResultViewer/IFrameBlock";
|
||||
import { getFilesThunk } from "../store/slices/fileSlice";
|
||||
import { getFilesThunk } from "../store/slices/files/thunks";
|
||||
|
||||
function PdfViewer({ fileId }: { fileId: string }) {
|
||||
return (
|
||||
|
||||
@ -2,14 +2,28 @@ import { api } from "../../api/client";
|
||||
import { MOCK_CONS, versionCardsMock } from "./mocks";
|
||||
|
||||
export async function getConsolidations(company_group_id: string) {
|
||||
// const res = await api.get(
|
||||
// `/company-groups/${company_group_id}/consolidations`,
|
||||
// );
|
||||
const res = await api.get(
|
||||
`/company-groups/${company_group_id}/consolidations`,
|
||||
);
|
||||
return res;
|
||||
|
||||
return MOCK_CONS.map((c) => ({ ...c, versions: [...c.versions] }));
|
||||
// return MOCK_CONS.map((c) => ({ ...c, versions: [...c.versions] }));
|
||||
}
|
||||
|
||||
export async function getVersionData(version_id: string) {
|
||||
const res = versionCardsMock[version_id] ?? null;
|
||||
// const res = versionCardsMock[version_id] ?? null;
|
||||
const res = await api.get(`/consolidations/versions/${version_id}`);
|
||||
return res;
|
||||
}
|
||||
type FormValues = {
|
||||
company_group_id: string;
|
||||
related_group_id: string;
|
||||
year: number;
|
||||
quarter: string; // example of 2nd static field
|
||||
legal_entity_ids: string[];
|
||||
};
|
||||
|
||||
export async function createConsolidation(values: FormValues) {
|
||||
const res = await api.post("/consolidations", values);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -20,3 +20,22 @@ export async function fetchGSKByComGroup(com_group_id: string) {
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
// export async function fetchGroupMembers(related_group_id: string) {
|
||||
// const res = await api.get(`/directories/related-groups/${related_group_id}`);
|
||||
// const members = res?.members ?? [];
|
||||
// console.log(members);
|
||||
// return members;
|
||||
// }
|
||||
|
||||
// legal-entities?company_group_id=<ГК>&closed=false
|
||||
|
||||
export async function fetchGroupMembers(company_group_id: string) {
|
||||
const res = await api.get(
|
||||
`/directories/legal-entities?company_group_id=${company_group_id}&closed=false`,
|
||||
);
|
||||
console.log(res);
|
||||
const members = res?.items ?? [];
|
||||
console.log(members);
|
||||
return members;
|
||||
}
|
||||
|
||||
27
src/services/api/files.ts
Normal file
27
src/services/api/files.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { api, apiFetch } from "../../api/client";
|
||||
|
||||
export async function uploadOneFile(
|
||||
legal_entity_id: string,
|
||||
file: any,
|
||||
consolidation_version_id: string | null,
|
||||
) {
|
||||
console.log("DATA", file);
|
||||
console.log(legal_entity_id);
|
||||
console.log(consolidation_version_id);
|
||||
const formData = new FormData();
|
||||
formData.append("files", file);
|
||||
formData.append("legal_entity_id", legal_entity_id);
|
||||
formData.append("year", "2025");
|
||||
formData.append("quarter", "Q3");
|
||||
console.log(formData);
|
||||
apiFetch(
|
||||
`/legal-entities/${legal_entity_id}/files?consolidation_version_id=${consolidation_version_id}`,
|
||||
{ method: "POST", body: formData },
|
||||
);
|
||||
// if (!res.ok) throw new Error((await res.text()) || "Upload error");
|
||||
}
|
||||
|
||||
export async function fetchLegalEntityFiles(legal_entity_id: string) {
|
||||
const res = api.get(`/legal-entities/${legal_entity_id}/files`);
|
||||
return res;
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Config } from "../../conf/config";
|
||||
|
||||
const initialState = {
|
||||
currentOpenFiles: [],
|
||||
};
|
||||
|
||||
export const getFilesThunk = createAsyncThunk(
|
||||
"files/getCurrentFiles",
|
||||
async (legalEntityId, thunkApi) => {
|
||||
const res = await fetch(
|
||||
`${Config.API_URL}/files/by-legal-entity?legal_entity_id=${legalEntityId}`,
|
||||
);
|
||||
if (!res.ok) throw new Error("Error in fetch");
|
||||
const json = await res.json();
|
||||
return thunkApi.fulfillWithValue(json);
|
||||
},
|
||||
);
|
||||
|
||||
const filesSlice = createSlice({
|
||||
name: "files",
|
||||
initialState,
|
||||
reducers: {
|
||||
setCurrentFiles: (state, action) => {
|
||||
state.currentOpenFiles = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder.addCase(getFilesThunk.fulfilled, (state, action) => {
|
||||
state.currentOpenFiles = action.payload;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { setCurrentFiles } = filesSlice.actions;
|
||||
export default filesSlice.reducer;
|
||||
39
src/store/slices/files/fileSlice.ts
Normal file
39
src/store/slices/files/fileSlice.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Config } from "../../../conf/config";
|
||||
import { getFilesThunk } from "./thunks";
|
||||
|
||||
const initialState = {
|
||||
currentOpenFiles: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const filesSlice = createSlice({
|
||||
name: "files",
|
||||
initialState,
|
||||
reducers: {
|
||||
setCurrentFiles: (state, action) => {
|
||||
state.currentOpenFiles = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder
|
||||
.addCase(getFilesThunk.pending, (state, action) => {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(getFilesThunk.fulfilled, (state, action) => {
|
||||
state.currentOpenFiles = action.payload;
|
||||
state.loading = false;
|
||||
})
|
||||
.addCase(getFilesThunk.rejected, (state, action) => {
|
||||
state.error = action.payload;
|
||||
state.currentOpenFiles = [];
|
||||
state.loading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { setCurrentFiles } = filesSlice.actions;
|
||||
export default filesSlice.reducer;
|
||||
11
src/store/slices/files/thunks.ts
Normal file
11
src/store/slices/files/thunks.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import { fetchLegalEntityFiles } from "../../../services/api/files";
|
||||
|
||||
export const getFilesThunk = createAsyncThunk(
|
||||
"files/getCurrentFiles",
|
||||
async (legalEntityId: string, thunkApi) => {
|
||||
const res = await fetchLegalEntityFiles(legalEntityId);
|
||||
console.log("RESULT", res);
|
||||
return thunkApi.fulfillWithValue(res.items);
|
||||
},
|
||||
);
|
||||
@ -1,5 +1,5 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import filesReducer from "./slices/fileSlice";
|
||||
import filesReducer from "./slices/files/fileSlice";
|
||||
import comGroupsReducer from "./slices/companyGroupsSlice/companyGroupsSlice";
|
||||
import consolidationsReducer from "./slices/consolidations/consolidationsSlice";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
@ -19,7 +19,7 @@ interface IConsolidationVersion {
|
||||
}
|
||||
|
||||
export interface IConsolidation {
|
||||
id: string;
|
||||
consolidation_id: string;
|
||||
year: number;
|
||||
quarter: QuarterEnum;
|
||||
reporting_date: string;
|
||||
|
||||
@ -28,7 +28,14 @@ type VersionHistory = {
|
||||
comment: string | null;
|
||||
};
|
||||
|
||||
type FileStatus =
|
||||
export type DocumentType =
|
||||
| "BALANCE"
|
||||
| "INCOME"
|
||||
| "OSV"
|
||||
| "DISCLOSURE"
|
||||
| "OTHER";
|
||||
|
||||
export type FileStatus =
|
||||
| "CONFIRMED"
|
||||
| "NEEDS_CHECK"
|
||||
| "RECOGNIZED"
|
||||
@ -36,10 +43,20 @@ type FileStatus =
|
||||
| "ERROR"
|
||||
| "MISSING";
|
||||
|
||||
export type FileRussianStatus =
|
||||
| "Требуется проверка"
|
||||
| "Ошибка"
|
||||
| "Отсутствует"
|
||||
| "Загружен"
|
||||
| "В обработке"
|
||||
| "Распознан";
|
||||
|
||||
type ChecklistItem = {
|
||||
document_ids: string[];
|
||||
document_type: DocumentType;
|
||||
status: FileStatus;
|
||||
missing_quarters: QuarterEnum[];
|
||||
account?: string | null;
|
||||
};
|
||||
|
||||
type VersionMember = {
|
||||
@ -48,6 +65,7 @@ type VersionMember = {
|
||||
inn: string;
|
||||
is_head_entity: string;
|
||||
checklist: ChecklistItem[];
|
||||
entity_type: "ИП" | "ЮЛ";
|
||||
};
|
||||
|
||||
export interface IVersionCard {
|
||||
@ -78,4 +96,8 @@ export type VersionCardRow = {
|
||||
inn: string;
|
||||
name: string;
|
||||
version_id: string;
|
||||
bb_status: FileRussianStatus;
|
||||
uploaded_osvs: string;
|
||||
ofr_status: FileRussianStatus;
|
||||
entity_type: "ЮЛ" | "ИП";
|
||||
};
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { Tag } from "antd";
|
||||
import type { TableConfig } from "../../components/GenericTable";
|
||||
import { ICompanyGroup } from "../../types/companyGroups/companyGroups";
|
||||
|
||||
|
||||
@ -5,6 +5,9 @@ import {
|
||||
VersionCardRow,
|
||||
} from "../../types/consolidations/version_members";
|
||||
import { FileOutlined, FileTextOutlined } from "@ant-design/icons";
|
||||
import { TableTag } from "../../components/TableTag/TableTag";
|
||||
import { statusColorMapping } from "./consts";
|
||||
import { TAG_PRESETS } from "../../components/TableTag/presets";
|
||||
|
||||
export const membersConfig: TableConfig<
|
||||
VersionCardRow & { docs: "Документы" }
|
||||
@ -27,6 +30,27 @@ export const membersConfig: TableConfig<
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "ofr_status",
|
||||
title: "ОФР",
|
||||
features: { multiselect: true },
|
||||
render: (v) => <TableTag {...statusColorMapping[v]} text={v} />,
|
||||
},
|
||||
|
||||
{
|
||||
key: "bb_status",
|
||||
title: "Бух.баланс",
|
||||
features: { multiselect: true },
|
||||
render: (v) => <TableTag {...statusColorMapping[v]} text={v} />,
|
||||
},
|
||||
{
|
||||
key: "uploaded_osvs",
|
||||
title: "ОСВ",
|
||||
features: { multiselect: true },
|
||||
render: (v) => <TableTag {...TAG_PRESETS.default} text={v} />,
|
||||
},
|
||||
{ key: "inn", title: "ИНН", features: { search: true, multiselect: true } },
|
||||
|
||||
{ key: "entity_type", title: "Тип", features: { multiselect: true } },
|
||||
],
|
||||
};
|
||||
|
||||
23
src/widgets/ConsoleLegalEntities/consts.ts
Normal file
23
src/widgets/ConsoleLegalEntities/consts.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Preset, TAG_PRESETS } from "../../components/TableTag/presets";
|
||||
import {
|
||||
FileRussianStatus,
|
||||
FileStatus,
|
||||
} from "../../types/consolidations/version_members";
|
||||
|
||||
export const statusMapping: Record<FileStatus, FileRussianStatus> = {
|
||||
CONFIRMED: "Загружен",
|
||||
ERROR: "Ошибка",
|
||||
MISSING: "Отсутствует",
|
||||
NEEDS_CHECK: "Требуется проверка",
|
||||
PROCESSING: "В обработке",
|
||||
RECOGNIZED: "Распознан",
|
||||
};
|
||||
|
||||
export const statusColorMapping: Record<FileRussianStatus, Preset> = {
|
||||
"В обработке": TAG_PRESETS.geekblue,
|
||||
Загружен: TAG_PRESETS.green,
|
||||
Отсутствует: TAG_PRESETS.red,
|
||||
Ошибка: TAG_PRESETS.red,
|
||||
Распознан: TAG_PRESETS.gold,
|
||||
"Требуется проверка": TAG_PRESETS.orange,
|
||||
};
|
||||
@ -2,6 +2,7 @@ import {
|
||||
IVersionCard,
|
||||
VersionCardRow,
|
||||
} from "../../types/consolidations/version_members";
|
||||
import { statusMapping } from "./consts";
|
||||
|
||||
export function normalizeData(
|
||||
cons_data: IVersionCard | null,
|
||||
@ -10,13 +11,30 @@ export function normalizeData(
|
||||
if (cons_data === null) {
|
||||
return result;
|
||||
}
|
||||
console.log(cons_data);
|
||||
for (const m of cons_data.members) {
|
||||
result.push({
|
||||
let row: VersionCardRow = {
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
inn: m.inn,
|
||||
version_id: cons_data.id,
|
||||
});
|
||||
entity_type: m.entity_type,
|
||||
};
|
||||
let uploaded_osvs = [];
|
||||
for (let c of m.checklist) {
|
||||
if (c.document_type === "BALANCE") {
|
||||
row.bb_status = statusMapping[c.status];
|
||||
} else if (c.document_type === "INCOME") {
|
||||
row.ofr_status = statusMapping[c.status];
|
||||
} else if (c.document_type === "OSV") {
|
||||
if (c.status !== "MISSING") {
|
||||
uploaded_osvs.push(c.account);
|
||||
}
|
||||
}
|
||||
}
|
||||
row.uploaded_osvs = [...uploaded_osvs].sort().join(", ");
|
||||
result.push(row);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -169,9 +169,7 @@ export function ConsoleTableWidget() {
|
||||
</Button>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() =>
|
||||
navigate(`/cons/${row.group_id}/versions/${row.id}`)
|
||||
}
|
||||
onClick={() => navigate(`/cons/${row.con_id}/versions/${row.id}`)}
|
||||
>
|
||||
К отчетности <ArrowRightOutlined />
|
||||
</Button>
|
||||
|
||||
@ -1,16 +1,8 @@
|
||||
import { Tag } from "antd";
|
||||
import { TableConfig } from "../../components/GenericTable";
|
||||
import { ConsRow, LockOut } from "../../types/consolidations/consolidations";
|
||||
import { statusColorMapping } from "./consts";
|
||||
|
||||
const statusColor: Record<string, string> = {
|
||||
INITIATED: "gold",
|
||||
DOCUMENTS_PROCESSING: "blue",
|
||||
VERIFIED: "green",
|
||||
CANCELLED: "default",
|
||||
REJECTED: "red",
|
||||
ARCHIVED: "default",
|
||||
};
|
||||
import { TableTag } from "../../components/TableTag/TableTag";
|
||||
import { TAG_PRESETS } from "../../components/TableTag/presets";
|
||||
|
||||
export const consConfigBase: TableConfig<ConsRow> = {
|
||||
rowKey: "id",
|
||||
@ -24,11 +16,7 @@ export const consConfigBase: TableConfig<ConsRow> = {
|
||||
key: "status",
|
||||
title: "Статус",
|
||||
features: { multiselect: true, sort: true },
|
||||
render: (v) => (
|
||||
<Tag color={statusColorMapping[v]} variant="outlined">
|
||||
{v}
|
||||
</Tag>
|
||||
),
|
||||
render: (v) => <TableTag {...statusColorMapping[v]} text={v as string} />,
|
||||
},
|
||||
{
|
||||
key: "reporting_date",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { TAG_PRESETS, type Preset } from "../../components/TableTag/presets";
|
||||
import {
|
||||
ConsRussianStatus,
|
||||
ConsStatus,
|
||||
@ -18,17 +19,17 @@ export const statusMapping: Record<ConsStatus, ConsRussianStatus> = {
|
||||
REJECTED: "Отклонён",
|
||||
};
|
||||
|
||||
export const statusColorMapping: Record<ConsRussianStatus, string> = {
|
||||
"В архиве": "default",
|
||||
"Верификация ГО": "geekblue",
|
||||
"Верификация РФ": "geekblue",
|
||||
Верифицирован: "green",
|
||||
Инициация: "default",
|
||||
"Консолидация ГО": "purple",
|
||||
"Консолидация РФ": "purple",
|
||||
Корректировка: "gold",
|
||||
"Коррекция данных": "gold",
|
||||
"Обработка документов": "gold",
|
||||
Отклонён: "red",
|
||||
Отменён: "red",
|
||||
export const statusColorMapping: Record<ConsRussianStatus, Preset> = {
|
||||
"В архиве": TAG_PRESETS.default,
|
||||
"Верификация ГО": TAG_PRESETS.geekblue,
|
||||
"Верификация РФ": TAG_PRESETS.geekblue,
|
||||
Верифицирован: TAG_PRESETS.green,
|
||||
Инициация: TAG_PRESETS.default,
|
||||
"Консолидация ГО": TAG_PRESETS.purple,
|
||||
"Консолидация РФ": TAG_PRESETS.purple,
|
||||
Корректировка: TAG_PRESETS.gold,
|
||||
"Коррекция данных": TAG_PRESETS.gold,
|
||||
"Обработка документов": TAG_PRESETS.gold,
|
||||
Отклонён: TAG_PRESETS.red,
|
||||
Отменён: TAG_PRESETS.red,
|
||||
};
|
||||
|
||||
@ -16,11 +16,11 @@ export function flattenCons(cons: IConsolidation[]): ConsRow[] {
|
||||
let rep_date = new Date(con.reporting_date) ?? null;
|
||||
out.push({
|
||||
id: v.id,
|
||||
group_id: con.id,
|
||||
group_id: con.consolidation_id,
|
||||
isPrimary: i === 0,
|
||||
versionIndex: i,
|
||||
groupSize: con.versions.length,
|
||||
con_id: con.id,
|
||||
con_id: con.consolidation_id,
|
||||
year: con.year,
|
||||
quarter: con.quarter,
|
||||
reporting_date: rep_date,
|
||||
|
||||
62
src/widgets/FilesDrawer/FilesDrawer.tsx
Normal file
62
src/widgets/FilesDrawer/FilesDrawer.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
// DocumentsDrawer.tsx
|
||||
import { Drawer, List, Button } from "antd";
|
||||
import { useState } from "react";
|
||||
import { useDocumentsPolling } from "./useDocumentsPolling";
|
||||
import { UploadDocumentsModal } from "./UploadDocumentsModal";
|
||||
import { useAppDispatch, useAppSelector } from "../../store/store";
|
||||
import { getFilesThunk } from "../../store/slices/files/thunks";
|
||||
|
||||
export function DocumentsDrawer({
|
||||
open,
|
||||
onClose,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const dispatch = useAppDispatch();
|
||||
const [uploadOpen, setUploadOpen] = useState(false);
|
||||
const { current_version_id, current_legal_entity_id } = useAppSelector(
|
||||
(state) => state.consolidations,
|
||||
);
|
||||
|
||||
const { data, loading } = useDocumentsPolling(current_legal_entity_id, open);
|
||||
const handleClose = () => {
|
||||
// optional: clear on close so next open doesn't flash stale list
|
||||
// dispatch(clearDocuments());
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
destroyOnClose
|
||||
title="Документы"
|
||||
extra={
|
||||
<Button type="primary" onClick={() => setUploadOpen(true)}>
|
||||
Upload documents
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<List
|
||||
loading={loading && !data.length}
|
||||
dataSource={data}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
{item.original_name} - {item.file_type} - {item.status}
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
|
||||
{current_legal_entity_id && (
|
||||
<UploadDocumentsModal
|
||||
entityId={current_legal_entity_id}
|
||||
consolidation_version_id={current_version_id}
|
||||
open={uploadOpen}
|
||||
onClose={() => setUploadOpen(false)}
|
||||
onUploaded={() => dispatch(getFilesThunk(current_legal_entity_id))} // instant refresh, polling covers steady state
|
||||
/>
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
104
src/widgets/FilesDrawer/UploadDocumentsModal.tsx
Normal file
104
src/widgets/FilesDrawer/UploadDocumentsModal.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
// UploadDocumentsModal.tsx
|
||||
import { Modal, Upload, message } from "antd";
|
||||
import { InboxOutlined } from "@ant-design/icons";
|
||||
import { useState } from "react";
|
||||
import type { UploadFile, RcFile } from "antd/es/upload/interface";
|
||||
import { uploadOneFile } from "../../services/api/files";
|
||||
|
||||
type Props = {
|
||||
entityId: string;
|
||||
open: boolean;
|
||||
consolidation_version_id: string | null;
|
||||
onClose: () => void;
|
||||
onUploaded?: () => void; // trigger refetch after done
|
||||
};
|
||||
|
||||
export function UploadDocumentsModal({
|
||||
entityId,
|
||||
open,
|
||||
onClose,
|
||||
onUploaded,
|
||||
consolidation_version_id = null,
|
||||
}: Props) {
|
||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const handleOk = async () => {
|
||||
if (!fileList.length) {
|
||||
message.warning("No files selected");
|
||||
return;
|
||||
}
|
||||
|
||||
setUploading(true);
|
||||
const failed: string[] = [];
|
||||
|
||||
// one-by-one (sequential)
|
||||
for (const file of fileList) {
|
||||
const raw = (file.originFileObj ?? file) as RcFile;
|
||||
try {
|
||||
await uploadOneFile(entityId, raw, consolidation_version_id);
|
||||
} catch (e) {
|
||||
failed.push(file.name);
|
||||
}
|
||||
}
|
||||
|
||||
setUploading(false);
|
||||
|
||||
if (failed.length) {
|
||||
message.error(`Failed: ${failed.join(", ")}`);
|
||||
// keep only failed files so user can retry
|
||||
setFileList((prev) => prev.filter((f) => failed.includes(f.name)));
|
||||
return;
|
||||
}
|
||||
|
||||
message.success("All documents uploaded");
|
||||
setFileList([]);
|
||||
onUploaded?.(); // refetch -> polling will also catch up, but immediate feels better
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
if (uploading) return; // block closing mid-upload
|
||||
setFileList([]);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
title="Загрузить документы"
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
okText="Upload"
|
||||
confirmLoading={uploading}
|
||||
okButtonProps={{ disabled: !fileList.length }}
|
||||
closable={!uploading}
|
||||
destroyOnHidden
|
||||
>
|
||||
<Upload.Dragger
|
||||
multiple
|
||||
fileList={fileList}
|
||||
accept=".xlsx,.xls,.pdf,.jpg,.jpeg"
|
||||
beforeUpload={(file) => {
|
||||
// dedupe by uid protects from React StrictMode double invoke
|
||||
setFileList((prev) =>
|
||||
prev.some((f) => f.uid === file.uid)
|
||||
? prev
|
||||
: [...prev, file as UploadFile],
|
||||
);
|
||||
return false; // prevent antd auto-upload
|
||||
}}
|
||||
onRemove={(file) => {
|
||||
setFileList((prev) => prev.filter((f) => f.uid !== file.uid));
|
||||
}}
|
||||
// we manage fileList ourselves, so disable antd status updates
|
||||
showUploadList={{ showRemoveIcon: !uploading }}
|
||||
>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<InboxOutlined />
|
||||
</p>
|
||||
<p className="ant-upload-text">Выберите файлы</p>
|
||||
</Upload.Dragger>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
27
src/widgets/FilesDrawer/useDocumentsPolling.ts
Normal file
27
src/widgets/FilesDrawer/useDocumentsPolling.ts
Normal file
@ -0,0 +1,27 @@
|
||||
// useDocumentsPolling.ts
|
||||
import { useEffect } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "../../store/store";
|
||||
import { getFilesThunk } from "../../store/slices/files/thunks";
|
||||
|
||||
const POLL_INTERVAL = 5000;
|
||||
|
||||
export function useDocumentsPolling(rowId: string | null, enabled: boolean) {
|
||||
const dispatch = useAppDispatch();
|
||||
const data = useAppSelector((s) => s.files.currentOpenFiles);
|
||||
const loading = useAppSelector((s) => s.files.loading);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || !rowId) return;
|
||||
|
||||
// immediate fetch
|
||||
dispatch(getFilesThunk(rowId));
|
||||
|
||||
const id = window.setInterval(() => {
|
||||
dispatch(getFilesThunk(rowId));
|
||||
}, POLL_INTERVAL);
|
||||
|
||||
return () => clearInterval(id);
|
||||
}, [dispatch, rowId, enabled]);
|
||||
|
||||
return { data, loading };
|
||||
}
|
||||
@ -1,14 +1,28 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Modal, Form, Select, Row, Col } from "antd";
|
||||
import { fetchGSKByComGroup } from "../../services/api/directories/company_groups";
|
||||
import {
|
||||
Modal,
|
||||
Form,
|
||||
Select,
|
||||
Row,
|
||||
Col,
|
||||
Checkbox,
|
||||
Input,
|
||||
Empty,
|
||||
Spin,
|
||||
} from "antd";
|
||||
import {
|
||||
fetchGroupMembers,
|
||||
fetchGSKByComGroup,
|
||||
} from "../../services/api/directories/company_groups";
|
||||
import { useAppDispatch, useAppSelector } from "../../store/store";
|
||||
import { getCompanyGroupsThunk } from "../../store/slices/companyGroupsSlice/thunks";
|
||||
|
||||
type FormValues = {
|
||||
groupId: string;
|
||||
subGroupId: string;
|
||||
company_group_id: string;
|
||||
related_group_id: string;
|
||||
year: number;
|
||||
quarter: string; // example of 2nd static field
|
||||
legal_entity_ids: string[];
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@ -23,27 +37,21 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
|
||||
const { raw_rows, loading } = useAppSelector((state) => state.company_groups);
|
||||
|
||||
// Subgroups are local — only this modal needs them
|
||||
const [subgroups, setSubgroups] = useState([]);
|
||||
const [subgroups, setSubgroups] = useState<any[]>([]);
|
||||
const [loadingSubgroups, setLoadingSubgroups] = useState(false);
|
||||
// Guards against out-of-order responses when group is changed quickly
|
||||
const requestIdRef = useRef(0);
|
||||
const subgroupReqRef = useRef(0);
|
||||
|
||||
const groupId = Form.useWatch("groupId", form);
|
||||
const subGroupId = Form.useWatch("subGroupId", form);
|
||||
const [legEntities, setLegEntities] = useState<any[]>([]);
|
||||
const [loadingEntities, setLoadingEntities] = useState(false);
|
||||
const legEntReqRef = useRef(0);
|
||||
|
||||
const [legEntSearch, setLegEntSearch] = useState("");
|
||||
|
||||
const company_group_id = Form.useWatch("company_group_id", form);
|
||||
const related_group_id = Form.useWatch("related_group_id", form);
|
||||
const year = Form.useWatch("year", form);
|
||||
const quarter = Form.useWatch("quarter", form);
|
||||
|
||||
const isSubmitDisabled = !groupId || !subGroupId || !year || !quarter;
|
||||
|
||||
// Year options: current year and 3 years before
|
||||
const yearOptions = useMemo(() => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return Array.from({ length: 4 }, (_, i) => {
|
||||
const y = currentYear - i;
|
||||
return { value: y, label: String(y) };
|
||||
});
|
||||
}, []);
|
||||
const semester = Form.useWatch("quarter", form);
|
||||
const legEntIds = Form.useWatch("legal_entity_ids", form);
|
||||
|
||||
const quarterOptions = [
|
||||
{ value: "Q1", label: "1 квартал" },
|
||||
@ -51,36 +59,94 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
{ value: "Q3", label: "3 квартал" },
|
||||
{ value: "Q4", label: "4 квартал" },
|
||||
];
|
||||
|
||||
// Load groups if store is empty when modal opens
|
||||
useEffect(() => {
|
||||
if (open && raw_rows.length === 0 && !loading) {
|
||||
dispatch(getCompanyGroupsThunk());
|
||||
}
|
||||
}, [open, raw_rows.length, loading, dispatch]);
|
||||
|
||||
// When groupId changes -> reset second field, fetch subgroups locally
|
||||
const isSubmitDisabled =
|
||||
!company_group_id ||
|
||||
!related_group_id ||
|
||||
!year ||
|
||||
!semester ||
|
||||
!legEntIds?.length;
|
||||
|
||||
const yearOptions = useMemo(() => {
|
||||
const y = new Date().getFullYear();
|
||||
return Array.from({ length: 4 }, (_, i) => ({
|
||||
value: y - i,
|
||||
label: String(y - i),
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const clearLegEnts = () => {
|
||||
setLegEntities([]);
|
||||
setLoadingEntities(false);
|
||||
setLegEntSearch("");
|
||||
legEntReqRef.current++;
|
||||
};
|
||||
|
||||
// const handleGroupChange = async (value?: string) => {
|
||||
// form.setFieldsValue({
|
||||
// related_group_id: undefined as any,
|
||||
// legal_entity_ids: [],
|
||||
// });
|
||||
// setSubgroups([]);
|
||||
// clearLegEnts();
|
||||
// if (!value) return;
|
||||
// const reqId = ++subgroupReqRef.current;
|
||||
// setLoadingSubgroups(true);
|
||||
// try {
|
||||
// const data = await fetchGSKByComGroup(value);
|
||||
// if (reqId === subgroupReqRef.current) setSubgroups(data);
|
||||
// } finally {
|
||||
// if (reqId === subgroupReqRef.current) setLoadingSubgroups(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleSubGroupChange = async (value?: string) => {
|
||||
// form.setFieldsValue({ legal_entity_ids: [] });
|
||||
// clearLegEnts();
|
||||
// if (!value) return;
|
||||
// const reqId = ++legEntReqRef.current;
|
||||
// setLoadingEntities(true);
|
||||
// try {
|
||||
// const data = await fetchGroupMembers(value);
|
||||
// if (reqId === legEntReqRef.current) setLegEntities(data);
|
||||
// } finally {
|
||||
// if (reqId === legEntReqRef.current) setLoadingEntities(false);
|
||||
// }
|
||||
// };
|
||||
const handleGroupChange = async (value?: string) => {
|
||||
form.setFieldsValue({ subGroupId: undefined as any });
|
||||
form.setFieldsValue({
|
||||
related_group_id: undefined as any,
|
||||
legal_entity_ids: [],
|
||||
});
|
||||
setSubgroups([]);
|
||||
|
||||
clearLegEnts();
|
||||
if (!value) return;
|
||||
|
||||
const reqId = ++requestIdRef.current;
|
||||
const reqId = ++subgroupReqRef.current;
|
||||
const leReqId = ++legEntReqRef.current;
|
||||
setLoadingSubgroups(true);
|
||||
setLoadingEntities(true);
|
||||
try {
|
||||
const data = await fetchGSKByComGroup(value);
|
||||
// Ignore stale responses
|
||||
if (reqId === requestIdRef.current) setSubgroups(data);
|
||||
const dataSubgroups = await fetchGSKByComGroup(value);
|
||||
const dataLegEnts = await fetchGroupMembers(value);
|
||||
if (reqId === subgroupReqRef.current) setSubgroups(dataSubgroups);
|
||||
if (leReqId === legEntReqRef.current) setLegEntities(dataLegEnts);
|
||||
} finally {
|
||||
if (reqId === requestIdRef.current) setLoadingSubgroups(false);
|
||||
if (reqId === subgroupReqRef.current) setLoadingSubgroups(false);
|
||||
if (leReqId === legEntReqRef.current) setLoadingEntities(false);
|
||||
}
|
||||
};
|
||||
|
||||
const resetLocal = () => {
|
||||
setSubgroups([]);
|
||||
setLoadingSubgroups(false);
|
||||
requestIdRef.current++; // invalidate any in-flight request
|
||||
subgroupReqRef.current++;
|
||||
clearLegEnts();
|
||||
setLegEntSearch("");
|
||||
};
|
||||
|
||||
const handleOk = async () => {
|
||||
@ -89,9 +155,7 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
await onOk(values);
|
||||
form.resetFields();
|
||||
resetLocal();
|
||||
} catch {
|
||||
// validation error - keep modal open
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@ -100,6 +164,12 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const filteredLegEnts = useMemo(() => {
|
||||
if (!legEntSearch) return legEntities;
|
||||
const q = legEntSearch.toLowerCase();
|
||||
return legEntities.filter((s) => s.name.toLowerCase().includes(q));
|
||||
}, [legEntities, legEntSearch]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Новая консолидация"
|
||||
@ -107,20 +177,25 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
onCancel={handleCancel}
|
||||
onOk={handleOk}
|
||||
okButtonProps={{ disabled: isSubmitDisabled }}
|
||||
okText="+ Создать"
|
||||
cancelText="Отмена"
|
||||
okText="Submit"
|
||||
destroyOnHidden
|
||||
width={640}
|
||||
>
|
||||
<Form form={form} layout="vertical" preserve={false}>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
preserve={false}
|
||||
initialValues={{ legEntIds: [] }}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="groupId"
|
||||
label="ГК"
|
||||
rules={[{ required: true, message: "Укажите группу" }]}
|
||||
name="company_group_id"
|
||||
label="Группа компаний"
|
||||
rules={[{ required: true, message: "Укажите ГК" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Выберите группу"
|
||||
placeholder="Выберите ГК"
|
||||
loading={loading}
|
||||
showSearch
|
||||
allowClear
|
||||
@ -131,20 +206,20 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
}
|
||||
options={raw_rows.map((g) => ({ value: g.id, label: g.name }))}
|
||||
onChange={handleGroupChange}
|
||||
notFoundContent={loading ? "Загрузка" : "Нет вариантов"}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="subGroupId"
|
||||
label="ГСК/ГСЗ"
|
||||
name="related_group_id"
|
||||
label="ГСК"
|
||||
rules={[{ required: true, message: "Укажите ГСК" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder={groupId ? "Выберите ГСК" : "Сначала выберите ГК"}
|
||||
disabled={!groupId}
|
||||
placeholder={
|
||||
company_group_id ? "Выберите ГСК" : "Сначала выберите ГК"
|
||||
}
|
||||
disabled={!company_group_id}
|
||||
loading={loadingSubgroups}
|
||||
showSearch
|
||||
allowClear
|
||||
@ -154,9 +229,7 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
.includes(input.toLowerCase())
|
||||
}
|
||||
options={subgroups.map((s) => ({ value: s.id, label: s.name }))}
|
||||
notFoundContent={
|
||||
loadingSubgroups ? "Загрузка" : "Нет вариантов"
|
||||
}
|
||||
// onChange={handleSubGroupChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
@ -170,7 +243,6 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
<Select placeholder="Выберите год" options={yearOptions} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="quarter"
|
||||
@ -180,6 +252,105 @@ export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
<Select placeholder="Выберите квартал" options={quarterOptions} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
{/* ROW 3 - full width block, only after subgroup is selected */}
|
||||
{related_group_id && (
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
name="legal_entity_ids"
|
||||
label="ЮЛ консолидации"
|
||||
rules={[
|
||||
{ required: true, message: "Выберите хотя бы одно ЮЛ" },
|
||||
]}
|
||||
>
|
||||
<Checkbox.Group style={{ width: "100%" }}>
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid #d9d9d9",
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Input.Search
|
||||
placeholder="Поиск ЮЛ..."
|
||||
allowClear
|
||||
value={legEntSearch}
|
||||
onChange={(e) => setLegEntSearch(e.target.value)}
|
||||
style={{ marginBottom: 12 }}
|
||||
/>
|
||||
|
||||
{loadingEntities ? (
|
||||
<div style={{ textAlign: "center", padding: 24 }}>
|
||||
<Spin />
|
||||
</div>
|
||||
) : filteredLegEnts.length === 0 ? (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description={
|
||||
legEntities.length === 0
|
||||
? "No students"
|
||||
: "No matches"
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
maxHeight: 200,
|
||||
overflowY: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
{filteredLegEnts.map((s) => (
|
||||
<Checkbox
|
||||
key={s.id}
|
||||
value={s.id}
|
||||
style={{ width: "100%", display: "flex" }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
justifySelf: "flex-start",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
gap: "30px",
|
||||
}}
|
||||
>
|
||||
<span style={{ display: "inline-block" }}>
|
||||
{s.name}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
display: "inline-block",
|
||||
color: "rgb(102, 112, 133)",
|
||||
fontSize: 11,
|
||||
}}
|
||||
>
|
||||
{s.inn}
|
||||
</span>
|
||||
</div>
|
||||
</Checkbox>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loadingEntities && legEntities.length > 0 && (
|
||||
<div style={{ marginTop: 8, display: "flex", gap: 12 }}>
|
||||
<span style={{ marginLeft: "auto", color: "#8c8c8c" }}>
|
||||
Выбрано: {legEntIds?.length ?? 0} /{" "}
|
||||
{legEntities.length}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@ -24,6 +24,8 @@ import {
|
||||
import { USERS_ADMIN } from "../../auth/permissions";
|
||||
import { useCurrentUser } from "../../auth/MeContext";
|
||||
import { MyModal } from "../NewConsoleModal/NewConsoleModal";
|
||||
import { api } from "../../api/client";
|
||||
import { createConsolidation } from "../../services/api/consolidations";
|
||||
|
||||
type MenuItem = Required<MenuProps>["items"][number];
|
||||
|
||||
@ -245,7 +247,7 @@ export function Sidebar() {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
onOk={async (values) => {
|
||||
// await api.create(values)
|
||||
await createConsolidation(values);
|
||||
setModalOpen(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user