From 2e75de33cc40a8982483ce8641b44073ab54cb25 Mon Sep 17 00:00:00 2001 From: maxim Date: Tue, 20 Oct 2026 13:14:23 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B2=D1=8B=D1=80=D0=B0=D0=B2=D0=BD?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=98=D0=9D=D0=9D=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=81=D0=BE=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D0=B8,=20+=20?= =?UTF-8?q?=D0=B8=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20=D0=B7=D0=B2?= =?UTF-8?q?=D1=91=D0=B7=D0=B4=D1=8B=20=D0=BE=D0=B1=D1=8F=D0=B7=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NewConsoleModal/CompanyOptionRow.tsx | 120 ++++++++++++++++++ .../NewConsoleModal/NewConsoleModal.tsx | 93 +------------- 2 files changed, 127 insertions(+), 86 deletions(-) create mode 100644 web/src/widgets/NewConsoleModal/CompanyOptionRow.tsx diff --git a/web/src/widgets/NewConsoleModal/CompanyOptionRow.tsx b/web/src/widgets/NewConsoleModal/CompanyOptionRow.tsx new file mode 100644 index 0000000..ab95355 --- /dev/null +++ b/web/src/widgets/NewConsoleModal/CompanyOptionRow.tsx @@ -0,0 +1,120 @@ +import { memo, useCallback, useMemo, useState } from 'react'; +import { LegalEntityBrief } from '../../types/api'; +import { Checkbox, Empty, Input, Spin } from 'antd'; + +type RowProps = { + entity: LegalEntityBrief; + checked: boolean; + onToggle: (id: string) => void; +}; +const CompanyOptionRow = memo(({ entity, checked, onToggle }: RowProps) => ( +
onToggle(entity.id)} + onKeyDown={(e) => { + if (e.key === ' ' || e.key === 'Enter') { + e.preventDefault(); + onToggle(entity.id); + } + }} + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + width: '100%', + minWidth: 0, + gap: 12, + padding: '10px 8px', + borderRadius: 4, + borderBottom: '1px solid rgba(0, 0, 0, 0.05)', + cursor: 'pointer', + boxSizing: 'border-box', + }}> + + + {entity.name} + + + {entity.inn} +
+)); + +type CompanyMultiSelectProps = { + companies: LegalEntityBrief[]; + loading?: boolean; + // injected by Form.Item: + value?: string[]; + onChange?: (value: string[]) => void; +}; + +export const CompanyMultiSelect: React.FC = ({ companies, loading, value = [], onChange }) => { + const [search, setSearch] = useState(''); + + const toggle = useCallback( + (id: string) => { + const next = value.includes(id) ? value.filter((v) => v !== id) : [...value, id]; + onChange?.(next); + }, + [value, onChange], + ); + + const filtered = useMemo(() => { + if (!search) return companies; + const q = search.toLowerCase(); + return companies.filter((s) => s.name.toLowerCase().includes(q)); + }, [companies, search]); + + return ( +
+ setSearch(e.target.value)} + style={{ marginBottom: 12 }} + /> + + {loading ? ( +
+ +
+ ) : null} + {!loading && filtered.length === 0 ? ( + + ) : ( +
+ {filtered.map((s) => ( + + ))} +
+ )} + + {!loading && companies.length > 0 && ( +
+ + Выбрано: {value.length} / {companies.length} + +
+ )} +
+ ); +}; diff --git a/web/src/widgets/NewConsoleModal/NewConsoleModal.tsx b/web/src/widgets/NewConsoleModal/NewConsoleModal.tsx index 843001a..e11c0b7 100644 --- a/web/src/widgets/NewConsoleModal/NewConsoleModal.tsx +++ b/web/src/widgets/NewConsoleModal/NewConsoleModal.tsx @@ -1,4 +1,4 @@ -import { Checkbox, Col, Empty, Form, Input, Modal, Row, Select, Spin, message } from 'antd'; +import { Col, Form, Modal, Row, Select, message } from 'antd'; import type React from 'react'; import { useEffect, useMemo, useRef, useState } from 'react'; import { ApiError } from '../../api/client'; @@ -7,6 +7,8 @@ import { getCompanyGroupsThunk } from '../../store/slices/companyGroupsSlice/thu import { useAppDispatch, useAppSelector } from '../../store/store'; import type { ConsolidationCreate, LegalEntityBrief, Quarter, RelatedGroupOut } from '../../types/api'; import { membersFirst, preselectMembers } from './preselect'; +import { PlusOutlined } from '@ant-design/icons'; +import { CompanyMultiSelect } from './CompanyOptionRow'; type Props = { open: boolean; @@ -30,13 +32,11 @@ export const NewConsolidationModal: React.FC = ({ open, onCancel, onOk }) const relatedReqRef = useRef(0); const [submitting, setSubmitting] = useState(false); - 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 semester = Form.useWatch('quarter', form); - const legEntIds = Form.useWatch('legal_entity_ids', form); + const legEntIds = Form.useWatch('legal_entity_ids', form) ?? []; const quarterOptions: { value: Quarter; label: string }[] = [ { value: 'Q1', label: '1 квартал' }, @@ -64,7 +64,6 @@ export const NewConsolidationModal: React.FC = ({ open, onCancel, onOk }) relatedReqRef.current++; // сменили ГК: ответ по старой ГСК больше не нужен setLegEntities([]); setLoadingEntities(false); - setLegEntSearch(''); legEntReqRef.current++; }; @@ -114,7 +113,6 @@ export const NewConsolidationModal: React.FC = ({ open, onCancel, onOk }) setLoadingSubgroups(false); subgroupReqRef.current++; clearLegEnts(); - setLegEntSearch(''); }; const handleOk = async () => { @@ -142,25 +140,19 @@ export const NewConsolidationModal: React.FC = ({ 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 ( }} okText='Создать' cancelText='Отмена' confirmLoading={submitting} destroyOnHidden width={640}> -
+ @@ -205,78 +197,7 @@ export const NewConsolidationModal: React.FC = ({ open, onCancel, onOk }) {related_group_id && ( - -
- setLegEntSearch(e.target.value)} - style={{ marginBottom: 12 }} - /> - - {loadingEntities ? ( -
- -
- ) : null} - {!loadingEntities && filteredLegEnts.length === 0 && ( - - )} - {!loadingEntities && filteredLegEnts.length > 0 && ( -
- {filteredLegEnts.map((s) => ( - -
- {s.name} - - {s.inn} - -
-
- ))} -
- )} - - {!loadingEntities && legEntities.length > 0 && ( -
- - Выбрано: {legEntIds?.length ?? 0} / {legEntities.length} - -
- )} -
-
+
)}