diff --git a/src/App.tsx b/src/App.tsx
index 4244953..418b7c9 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -10,6 +10,7 @@ import {
CatalogueGroup,
CatalogueJur,
ConsolidationPage,
+ ConsolidationView,
DocStorage,
GroupsLayout,
Main,
@@ -41,6 +42,10 @@ function App() {
} />
} />
} />
+ }
+ />
diff --git a/src/layouts/ConsolidationView.tsx b/src/layouts/ConsolidationView.tsx
new file mode 100644
index 0000000..97ea209
--- /dev/null
+++ b/src/layouts/ConsolidationView.tsx
@@ -0,0 +1,97 @@
+import { useNavigate, useParams, useSearchParams } from "react-router-dom";
+import { useAppSelector, useAppDispatch } from "../store/store";
+import PageTitle from "../components/PageTitle";
+import { useEffect, useMemo, useState } from "react";
+import { api } from "../api/client";
+import { Button, Tag } from "antd";
+import { TableTag } from "../components/TableTag/TableTag";
+import { TAG_PRESETS } from "../components/TableTag/presets";
+
+export function ConsolidationView() {
+ const [searchParams, setSearchParams] = useSearchParams();
+ const { cons_id } = useParams();
+ // const files = useAppSelector((state) => state.files.currentOpenFiles);
+ const navigate = useNavigate();
+ const [versionData, setVersionData] = useState(null);
+ const [loading, setLoading] = useState(false);
+ useEffect(() => {
+ async function fetchVersionCard() {
+ if (!cons_id) return;
+ setLoading(true);
+ const res = await api.get(`/consolidations/versions/${cons_id}`);
+ const another_res = await api.get(
+ `/consolidations/versions/${cons_id}/osv`,
+ );
+ setVersionData(res);
+ setLoading(false);
+ }
+ fetchVersionCard();
+ }, [cons_id]);
+ const dispatch = useAppDispatch();
+
+ const desc = useMemo(() => {
+ return "";
+ }, []);
+ const headerContent = useMemo(() => {
+ if (!versionData) return { location: "", header: "" };
+ const year = versionData.year;
+ const quarter = versionData.quarter;
+ const company_name = versionData.company_group.name;
+ let header = `${company_name} - ${year} ${quarter}`;
+ let location = `Отчётность > ${company_name} > ${year} · ${quarter}`;
+ console.log({ header, location });
+ return { header, location };
+ }, [versionData]);
+ return (
+
+ {!loading && versionData !== null && (
+ <>
+
+
+
+
Статус отчета
+
+
+
+
+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/layouts/index.ts b/src/layouts/index.ts
index 25eb9e0..cd337a3 100644
--- a/src/layouts/index.ts
+++ b/src/layouts/index.ts
@@ -1,3 +1,4 @@
+export { ConsolidationView } from "./ConsolidationView";
export { Main } from "./Main";
export { CatalogueJur } from "./CatalogueJur";
export { ConsolidationPage } from "./ConsolidationsList";
diff --git a/src/services/api/directories/company_groups.ts b/src/services/api/directories/company_groups.ts
index 6cf1837..aefbd34 100644
--- a/src/services/api/directories/company_groups.ts
+++ b/src/services/api/directories/company_groups.ts
@@ -21,21 +21,16 @@ 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;
-// }
+export async function fetchGroupMembers(related_group_id: string) {
+ const res = await api.get(`/directories/related-groups/${related_group_id}`);
+ const members = res?.members ?? [];
+ return members;
+}
-// legal-entities?company_group_id=<ГК>&closed=false
-
-export async function fetchGroupMembers(company_group_id: string) {
+export async function fetchLegEntsByComGroup(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;
}
diff --git a/src/widgets/ConsoleTable/ConsoleTable.tsx b/src/widgets/ConsoleTable/ConsoleTable.tsx
index ff911da..fae440e 100644
--- a/src/widgets/ConsoleTable/ConsoleTable.tsx
+++ b/src/widgets/ConsoleTable/ConsoleTable.tsx
@@ -169,7 +169,7 @@ export function ConsoleTableWidget() {
diff --git a/src/widgets/FilesDrawer/FilesTable.tsx b/src/widgets/FilesDrawer/FilesTable.tsx
new file mode 100644
index 0000000..74305f5
--- /dev/null
+++ b/src/widgets/FilesDrawer/FilesTable.tsx
@@ -0,0 +1,165 @@
+import { Table, Tag } from "antd";
+import {
+ CheckOutlined,
+ CloseOutlined,
+ QuestionOutlined,
+} from "@ant-design/icons";
+import type { ColumnsType } from "antd/es/table";
+import { TableTag } from "../../components/TableTag/TableTag";
+import { TAG_PRESETS } from "../../components/TableTag/presets";
+import styles from "./styles.module.css";
+
+type Status = "MATCH" | "MISMATCH" | "UNKNOWN";
+type OverallStatus = "OK" | "ERROR" | "CHECK";
+
+interface Row {
+ name: string;
+ check_status: Status;
+ file_status: Status;
+ overall_status: OverallStatus;
+}
+
+// Map a Status to its icon + color. Single source of truth.
+const STATUS_CONFIG: Record =
+ {
+ MATCH: { color: "#52c41a", icon: }, // green, 'v'
+ MISMATCH: { color: "#ff4d4f", icon: }, // red, '*'
+ UNKNOWN: { color: "#1677ff", icon: }, // blue, '?'
+ };
+
+const OVERALL_COLOR: Record = {
+ OK: TAG_PRESETS.green,
+ ERROR: TAG_PRESETS.red,
+ CHECK: TAG_PRESETS.orange,
+};
+
+// One stacked line: icon + label, colored by its own status.
+function StatusLine({ label, status }: { label: string; status: Status }) {
+ const { color, icon } = STATUS_CONFIG[status];
+ return (
+
+ {icon}
+ {label}
+
+ );
+}
+
+const columns: ColumnsType = [
+ {
+ title: "Name",
+ dataIndex: "name",
+ key: "name",
+ },
+ {
+ title: "Overall",
+ dataIndex: "overall_status",
+ key: "overall_status",
+ render: (value: OverallStatus) => (
+
+ ),
+ },
+ {
+ title: "Statuses",
+ key: "simple_statuses",
+ // No dataIndex — we read the whole record from the second arg.
+ render: (_, record) => (
+
+
+
+
+ ),
+ },
+];
+
+// Red background when any sub-status is MISMATCH or UNKNOWN.
+function rowNeedsHighlight(row: Row): boolean {
+ const flagged: (Status | OverallStatus)[] = ["MISMATCH", "UNKNOWN", "ERROR"];
+ return (
+ flagged.includes(row.check_status) ||
+ flagged.includes(row.file_status) ||
+ flagged.includes(row.overall_status)
+ );
+}
+
+const mockData = [
+ {
+ key: "1",
+ name: "document_a.pdf",
+ check_status: "MATCH",
+ file_status: "MATCH",
+ overall_status: "OK",
+ },
+ {
+ key: "2",
+ name: "document_b.pdf",
+ check_status: "MISMATCH",
+ file_status: "MATCH",
+ overall_status: "ERROR",
+ },
+ {
+ key: "3",
+ name: "document_c.xlsx",
+ check_status: "MISMATCH",
+ file_status: "MISMATCH",
+ overall_status: "ERROR",
+ },
+ {
+ key: "4",
+ name: "document_d.docx",
+ check_status: "MATCH",
+ file_status: "MISMATCH",
+ overall_status: "ERROR",
+ },
+ {
+ key: "5",
+ name: "document_e.pdf",
+ check_status: "UNKNOWN",
+ file_status: "MATCH",
+ overall_status: "CHECK",
+ },
+ {
+ key: "6",
+ name: "document_f.pdf",
+ check_status: "MATCH",
+ file_status: "UNKNOWN",
+ overall_status: "CHECK",
+ },
+ {
+ key: "7",
+ name: "document_g.pdf",
+ check_status: "UNKNOWN",
+ file_status: "UNKNOWN",
+ overall_status: "CHECK",
+ },
+ {
+ key: "8",
+ name: "document_h.pdf",
+ check_status: "MISMATCH",
+ file_status: "UNKNOWN",
+ overall_status: "ERROR",
+ },
+ {
+ key: "9",
+ name: "document_i.pdf",
+ check_status: "UNKNOWN",
+ file_status: "MISMATCH",
+ overall_status: "ERROR",
+ },
+];
+
+export function FilesTable() {
+ return (
+
+ columns={columns}
+ dataSource={mockData}
+ pagination={false}
+ rowKey="name"
+ rowHoverable={false}
+ rowClassName={(record) =>
+ rowNeedsHighlight(record) ? styles.row_highlight : ""
+ }
+ />
+ );
+}
diff --git a/src/widgets/FilesDrawer/styles.module.css b/src/widgets/FilesDrawer/styles.module.css
new file mode 100644
index 0000000..d575e0c
--- /dev/null
+++ b/src/widgets/FilesDrawer/styles.module.css
@@ -0,0 +1,8 @@
+.row_highlight > td {
+ background-color: #fff1f0; /* soft red; use #ffccc7 for stronger */
+}
+
+/* Preserve the highlight on hover instead of Antd's default grey */
+row_highlight:hover > td {
+ background-color: #ffe0dd !important;
+}
\ No newline at end of file
diff --git a/src/widgets/NewConsoleModal/NewConsoleModal.tsx b/src/widgets/NewConsoleModal/NewConsoleModal.tsx
index 6dd76b2..040f827 100644
--- a/src/widgets/NewConsoleModal/NewConsoleModal.tsx
+++ b/src/widgets/NewConsoleModal/NewConsoleModal.tsx
@@ -11,8 +11,9 @@ import {
Spin,
} from "antd";
import {
- fetchGroupMembers,
+ fetchLegEntsByComGroup,
fetchGSKByComGroup,
+ fetchGroupMembers,
} from "../../services/api/directories/company_groups";
import { useAppDispatch, useAppSelector } from "../../store/store";
import { getCompanyGroupsThunk } from "../../store/slices/companyGroupsSlice/thunks";
@@ -46,6 +47,7 @@ export const MyModal: React.FC = ({ open, onCancel, onOk }) => {
const legEntReqRef = useRef(0);
const [legEntSearch, setLegEntSearch] = useState("");
+ const gskEntReqRef = useRef(0);
const company_group_id = Form.useWatch("company_group_id", form);
const related_group_id = Form.useWatch("related_group_id", form);
@@ -105,19 +107,28 @@ export const MyModal: React.FC = ({ open, onCancel, onOk }) => {
// }
// };
- // 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 handleSubGroupChange = async (value?: string) => {
+ form.setFieldsValue({ legal_entity_ids: [] });
+ if (!value) return;
+ const reqId = ++gskEntReqRef.current;
+ setLoadingEntities(true);
+ try {
+ const data = await fetchGroupMembers(value);
+
+ if (reqId !== gskEntReqRef.current) return;
+ const gskEntitiesIds = new Set(data.map((c) => c.legal_entity_id));
+ const sorted = [...legEntities].sort((a, b) => {
+ const a_gsk = gskEntitiesIds.has(a.id);
+ const b_gsk = gskEntitiesIds.has(b.id);
+ if (a_gsk !== b_gsk) return a_gsk ? -1 : 1;
+ return a.name.localeCompare(b.name);
+ });
+ setLegEntities(sorted);
+ form.setFieldsValue({ legal_entity_ids: [...gskEntitiesIds] });
+ } finally {
+ if (reqId === gskEntReqRef.current) setLoadingEntities(false);
+ }
+ };
const handleGroupChange = async (value?: string) => {
form.setFieldsValue({
related_group_id: undefined as any,
@@ -132,7 +143,7 @@ export const MyModal: React.FC = ({ open, onCancel, onOk }) => {
setLoadingEntities(true);
try {
const dataSubgroups = await fetchGSKByComGroup(value);
- const dataLegEnts = await fetchGroupMembers(value);
+ const dataLegEnts = await fetchLegEntsByComGroup(value);
if (reqId === subgroupReqRef.current) setSubgroups(dataSubgroups);
if (leReqId === legEntReqRef.current) setLegEntities(dataLegEnts);
} finally {
@@ -181,12 +192,7 @@ export const MyModal: React.FC = ({ open, onCancel, onOk }) => {
destroyOnHidden
width={640}
>
-