From 83d4c2b89464c1cc336b723041e2912850bf215f Mon Sep 17 00:00:00 2001 From: Grog Date: Mon, 12 Oct 2026 17:48:14 +0300 Subject: [PATCH] added company group table --- src/layouts/CatalogueGroups.tsx | 81 +++++++- src/layouts/CatalogueJur.tsx | 18 +- src/store/slices/catalogueJurSlice/types.ts | 1 + .../companyGroupsSlice/companyGroupsSlice.ts | 60 +++--- src/store/slices/companyGroupsSlice/types.ts | 1 + src/types/catalogueJur/catalogueJur.ts | 16 +- .../CatalogueJurForm/CatalogueJurForm.tsx | 72 ++++--- .../CatalogueJurTable/CatalogueJurTable.tsx | 121 +++++++++--- src/widgets/CatalogueJurTable/config.tsx | 7 +- .../CompanyGroupsList/CompanyGroupsList.tsx | 186 ++++++++++++++++++ .../CompanyGroupsTable/CompanyGroupsTable.tsx | 2 +- 11 files changed, 464 insertions(+), 101 deletions(-) create mode 100644 src/widgets/CompanyGroupsList/CompanyGroupsList.tsx diff --git a/src/layouts/CatalogueGroups.tsx b/src/layouts/CatalogueGroups.tsx index 3915a07..9a24e1e 100644 --- a/src/layouts/CatalogueGroups.tsx +++ b/src/layouts/CatalogueGroups.tsx @@ -1,3 +1,82 @@ +import { Button, Space } from "antd"; +import PageTitle from "../components/PageTitle"; +import { CatalogueJurTable } from "../widgets/CatalogueJurTable/CatalogueJurTable"; +import { PlusOutlined } from "@ant-design/icons"; +import { useState } from "react"; +import { useAppDispatch, useAppSelector } from "../store/store"; +import { CatalogueJurForm } from "../widgets/CatalogueJurForm/CatalogueJurForm"; +import { getCatalogueJurThunk } from "../store/slices/catalogueJurSlice/thunks"; +import { CatalogueJurFormProps } from "../types/catalogueJur/catalogueJur"; +import { createCatalogueJur } from "../services/api/directories/catalogue_jur"; +import { CompanyGroupsList } from "../widgets/CompanyGroupsList/CompanyGroupsList"; + export function CatalogueGroup() { - return

ГК и организации

; + const dispatch = useAppDispatch(); + const isGettingArchive = useAppSelector( + (state) => state.catalogue_jur.isGettingArchive, + ); + + const [createFormOpen, setCreateFormOpen] = useState(false); + + async function createJurHandler(values: CatalogueJurFormProps) { + try { + await createCatalogueJur(values); + dispatch( + getCatalogueJurThunk({ closed: isGettingArchive, outside_group: true }), + ); + setCreateFormOpen(false); + } catch {} + } + + return ( +
+ + + + +
+
+ +
+ +
+ +
+
+ {createFormOpen && ( + { + setCreateFormOpen(false); + }} + onOk={(values) => { + createJurHandler(values); + }} + /> + )} +
+ ); } diff --git a/src/layouts/CatalogueJur.tsx b/src/layouts/CatalogueJur.tsx index 018ebed..eaa9468 100644 --- a/src/layouts/CatalogueJur.tsx +++ b/src/layouts/CatalogueJur.tsx @@ -14,14 +14,14 @@ export function CatalogueJur() { const isGettingArchive = useAppSelector( (state) => state.catalogue_jur.isGettingArchive, ); - + const [createFormOpen, setCreateFormOpen] = useState(false); - + const outside_group = true async function createJurHandler(values: CatalogueJurFormProps) { try { await createCatalogueJur(values); dispatch( - getCatalogueJurThunk({ closed: isGettingArchive, outside_group: true }), + getCatalogueJurThunk({ closed: isGettingArchive, outside_group: outside_group }), ); setCreateFormOpen(false); } catch {} @@ -46,17 +46,7 @@ export function CatalogueJur() { Создать ЮЛ -
- -
+ {createFormOpen && ( { - builder - .addCase(getCompanyGroupsThunk.pending, (state) => { - state.loading = true; - state.error = null; - }) - .addCase(getCompanyGroupsThunk.fulfilled, (state, action) => { - state.raw_rows = action.payload; - state.total_groups = action.payload.length; - state.loading = false; - }) - .addCase(getCompanyGroupsThunk.rejected, (state, action) => { - state.error = action.payload ?? 'Неизвестная ошибка'; - state.loading = false; - }); - }, + name: "company_groups", + initialState, + reducers: { + setActiveCompanyGroup(state, action: PayloadAction) { + state.activeCompanyGroup = action.payload; + }, + }, + extraReducers: (builder) => { + builder + .addCase(getCompanyGroupsThunk.pending, (state) => { + state.loading = true; + state.error = null; + }) + .addCase(getCompanyGroupsThunk.fulfilled, (state, action) => { + state.raw_rows = action.payload; + state.total_groups = action.payload.length; + state.loading = false; + }) + .addCase(getCompanyGroupsThunk.rejected, (state, action) => { + state.error = action.payload ?? "Неизвестная ошибка"; + state.loading = false; + }); + }, }); - +export const { setActiveCompanyGroup } = comGroupsSlice.actions; export default comGroupsSlice.reducer; diff --git a/src/store/slices/companyGroupsSlice/types.ts b/src/store/slices/companyGroupsSlice/types.ts index 5f35cfa..aff3e9d 100644 --- a/src/store/slices/companyGroupsSlice/types.ts +++ b/src/store/slices/companyGroupsSlice/types.ts @@ -5,4 +5,5 @@ export interface CompanyGroupSliceType { raw_rows: ICompanyGroup[]; loading: boolean; error: string | null; + activeCompanyGroup?:ICompanyGroup | null } diff --git a/src/types/catalogueJur/catalogueJur.ts b/src/types/catalogueJur/catalogueJur.ts index bbd6840..f291ab4 100644 --- a/src/types/catalogueJur/catalogueJur.ts +++ b/src/types/catalogueJur/catalogueJur.ts @@ -14,7 +14,7 @@ export interface ICatalogueJur { updated_at: Date; outside_group: boolean; closed: boolean | string; - memberships?: unknown[]; + memberships?: MemberShips[]; aliases?: JurAlliase[]; regional_office: { id: string; @@ -23,6 +23,16 @@ export interface ICatalogueJur { }; } +export type KindType = "gsk" | "gsz" + +export type MemberShips = { + related_group_id: string; + code: string; + kind: KindType; + name: string; + valid_from: Date; +}; + export type JurAlliase = { alias: string; created_at: Date; @@ -46,7 +56,9 @@ export type CatalogueJurFormProps = { crm_id: string; entity_type: "ЮЛ" | "ИП"; name: string; - aliases?: string[] | {value:string, label:string}[]; + aliases?: string[] | { value: string; label: string }[]; company_group_id?: string; regional_office_id?: string; + gsk_id?: string; + gsz_id?: string; }; diff --git a/src/widgets/CatalogueJurForm/CatalogueJurForm.tsx b/src/widgets/CatalogueJurForm/CatalogueJurForm.tsx index 41ff66a..aa56376 100644 --- a/src/widgets/CatalogueJurForm/CatalogueJurForm.tsx +++ b/src/widgets/CatalogueJurForm/CatalogueJurForm.tsx @@ -1,12 +1,12 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { Modal, Form, Input, Select, Row, Col, InputNumber } from "antd"; import { CatalogueJurFormProps } from "../../types/catalogueJur/catalogueJur"; import { useAppDispatch, useAppSelector } from "../../store/store"; import { getCompanyGroupsThunk } from "../../store/slices/companyGroupsSlice/thunks"; -import { api } from "../../api/client"; -import { RegionalOffice } from "../../api/types"; -import { useAsyncData } from "../Admin/hooks"; import { fetchOneCatalogueJur } from "../../services/api/directories/catalogue_jur"; +import { fetchRelatedGroups } from "../../services/api/directories/company_groups"; +import { RelatedGroupOut } from "../../types/api"; +import { useMe } from "../../auth/useMe"; type Props = { open: boolean; @@ -23,34 +23,55 @@ export const CatalogueJurForm: React.FC = ({ }) => { const [form] = Form.useForm(); const dispatch = useAppDispatch(); - const loadOffices = useCallback( - () => api.get("/directories/regional-offices"), - [], - ); - const offices = useAsyncData(loadOffices); - const type = Form.useWatch("entity_type", form); + const { me } = useMe(); - const { raw_rows, loading } = useAppSelector((state) => state.company_groups); + const type = Form.useWatch("entity_type", form); + const activeCompanyGroup = useAppSelector( + (state) => state.company_groups.activeCompanyGroup, + ); const [initialDataLoading, setInitialDataLoading] = useState(false); + const [gskData, setGskData] = useState([]); + const [gszData, setGszData] = useState([]); + const [loadingOptions, setLoadingOptions] = useState(false); async function getInitialData() { setInitialDataLoading(true); + form.setFieldsValue({ + company_group_id: jurId ? undefined : activeCompanyGroup?.id, + regional_office_id: me?.regional_office.id, + }); if (jurId) { const data = await fetchOneCatalogueJur(jurId); form.setFieldsValue({ name: data?.name, - company_group_id: data?.company_group_id, aliases: data?.aliases?.map((aliase) => aliase.alias), crm_id: data?.crm_id, entity_type: data?.entity_type, inn: data.inn, kpp: data?.kpp, regional_office_id: data?.regional_office_id, + gsk_id: data.memberships?.find((elem) => elem.kind === "gsk") + ?.related_group_id, + gsz_id: data.memberships?.find((elem) => elem.kind === "gsz") + ?.related_group_id, }); } + setInitialDataLoading(false); } + async function getRelatedGroupsHandler() { + if (activeCompanyGroup?.id) { + setLoadingOptions(true); + const res = await fetchRelatedGroups(activeCompanyGroup?.id); + const gsks = res.filter((elem) => elem.kind === "gsk"); + const gszs = res.filter((elem) => elem.kind === "gsz"); + setGskData(gsks); + setGszData(gszs); + setLoadingOptions(false); + } + } + const handleOk = async () => { try { const values = await form.validateFields(); @@ -58,7 +79,6 @@ export const CatalogueJurForm: React.FC = ({ ...values, kpp: type === "ИП" ? undefined : values.kpp?.toString(), inn: jurId ? undefined : values?.inn?.toString(), - company_group_id: jurId ? undefined : values.company_group_id, }); } catch { return; @@ -71,16 +91,19 @@ export const CatalogueJurForm: React.FC = ({ }; useEffect(() => { - if (open && raw_rows.length === 0 && !loading) { + if (open) { dispatch(getCompanyGroupsThunk()); } - }, [open, raw_rows.length, loading, dispatch]); + }, [open, dispatch]); useEffect(() => { - if (!!jurId) { - getInitialData(); - } + getInitialData(); }, [open]); + + useEffect(() => { + getRelatedGroupsHandler(); + }, [activeCompanyGroup]); + return ( = ({ - + ({ + options={gszData?.map((g) => ({ value: g.id, label: g.name, }))} showSearch optionFilterProp="label" - notFoundContent={loading ? "Загрузка" : "Нет вариантов"} + notFoundContent={loadingOptions ? "Загрузка" : "Нет вариантов"} /> diff --git a/src/widgets/CatalogueJurTable/CatalogueJurTable.tsx b/src/widgets/CatalogueJurTable/CatalogueJurTable.tsx index 9df753c..bc56d1b 100644 --- a/src/widgets/CatalogueJurTable/CatalogueJurTable.tsx +++ b/src/widgets/CatalogueJurTable/CatalogueJurTable.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { Button, Checkbox, Dropdown, Input, Space } from "antd"; +import { Button, Checkbox, Dropdown, Input, Space, Tabs } from "antd"; import { useNavigate } from "react-router-dom"; import type { ColumnsType } from "antd/es/table"; import { @@ -35,8 +35,17 @@ import { import { WithdrawCatalogueJurForm } from "../WithdrawCatalogueJurForm/WithdrawCatalogueJurForm"; import { setIsGettingArchive } from "../../store/slices/catalogueJurSlice/catalogueJurSlice"; import { CatalogueJurForm } from "../CatalogueJurForm/CatalogueJurForm"; +import Title from "antd/es/typography/Title"; +import Text from "antd/es/typography/Text"; +import { fetchRelatedGroups } from "../../services/api/directories/company_groups"; +import { RelatedGroupOut } from "../../types/api"; -export function CatalogueJurTable() { +interface CatalogueJurTableI { + outside_group: boolean; +} +type TabKey = "legal_entities" | "gsk" | "gsz"; + +export function CatalogueJurTable({ outside_group }: CatalogueJurTableI) { const navigate = useNavigate(); const dispatch = useAppDispatch(); const rawRows = useAppSelector((state) => state.catalogue_jur.raw_rows); @@ -44,12 +53,18 @@ export function CatalogueJurTable() { const isGettingArchive = useAppSelector( (state) => state.catalogue_jur.isGettingArchive, ); + const activeCompanyGroup = useAppSelector( + (state) => state.company_groups.activeCompanyGroup, + ); const [moveCatalogueFormOpen, setMoveCatalogueFormOpen] = useState(false); const [editCatalogueFormOpen, setEditCatalogueFormOpen] = useState(false); const [withdrawCatalogueFormOpen, setWithdrawCatalogueFormOpen] = useState(false); + const [activeTab, setActiveTab] = useState("legal_entities"); const [jurId, setJurId] = useState(""); + const [gskData, setGskData] = useState([]); + const [gszData, setGszData] = useState([]); const { visibleKeys, setVisibleKeys } = useVisibleColumns(catalogueJurConfig); const { rawQuery, @@ -71,9 +86,12 @@ export function CatalogueJurTable() { ); function getCatalogueJur() { - // #TODO поменять на outside_group=true dispatch( - getCatalogueJurThunk({ closed: isGettingArchive, outside_group: true }), + getCatalogueJurThunk({ + closed: isGettingArchive, + outside_group: outside_group, + company_group_id:activeCompanyGroup?.id + }), ); } @@ -117,13 +135,27 @@ export function CatalogueJurTable() { dispatch( getCatalogueJurThunk({ closed: isGettingArchive, outside_group: true }), ); - closeEditCatalogueJurHandler() + closeEditCatalogueJurHandler(); } catch {} } + async function getRelatedGroupsHandler() { + if (activeCompanyGroup?.id) { + const res = await fetchRelatedGroups(activeCompanyGroup?.id); + const gsks = res.filter((elem) => elem.kind === "gsk"); + const gszs = res.filter((elem) => elem.kind === "gsz"); + setGskData(gsks); + setGszData(gszs); + } + } + useEffect(() => { getCatalogueJur(); - }, [isGettingArchive]); + }, [isGettingArchive, activeCompanyGroup]); + + useEffect(() => { + getRelatedGroupsHandler(); + }, [activeCompanyGroup]); const extraColumns: ColumnsType = useMemo( () => [ @@ -182,23 +214,62 @@ export function CatalogueJurTable() { ); return ( -
+
+ {!outside_group && ( +
+ + {activeCompanyGroup?.name} + + + {activeCompanyGroup?.base_regional_office_name ?? ""} + {activeCompanyGroup?.legal_entities_count} организаций + +
+ )} - } - placeholder="Поиск по ГК, CRM ID или РФ" - value={rawQuery} - onChange={(e) => setRawQuery(e.target.value)} - style={{ maxWidth: 320 }} - /> + {outside_group ? ( + } + placeholder="Поиск по ГК, CRM ID или РФ" + value={rawQuery} + onChange={(e) => setRawQuery(e.target.value)} + style={{ maxWidth: 320 }} + /> + ) : ( + setActiveTab(k as TabKey)} + style={{ marginBottom: 0 }} + items={[ + { + key: "legal_entities", + label: `ЮЛ ${activeCompanyGroup?.legal_entities_count}`, + }, + { key: "gsk", label: `ГСК ${gskData?.length ?? 0}` }, + { key: "gsz", label: `ГСЗ ${gszData?.length ?? 0}` }, + ]} + /> + )} + {hasActiveFilters && (