Compare commits

..

No commits in common. "e751c63c32153575ad9f48c57a2e05c5929533f2" and "b1b9d219a4d65907f684594cf990ff05ecd6c16e" have entirely different histories.

16 changed files with 193 additions and 306 deletions

View File

@ -39,9 +39,4 @@ export const TAG_PRESETS = {
borderColor: 'rgba(245, 158, 11, 0.25)', borderColor: 'rgba(245, 158, 11, 0.25)',
color: 'rgb(146, 64, 14)', color: 'rgb(146, 64, 14)',
}, },
gray: {
color: 'rgb(71, 84, 103)',
bgColor: 'rgb(248, 250, 252)',
borderColor: 'rgba(15, 23, 42, 0.08)',
},
} as const; } as const;

View File

@ -1,51 +0,0 @@
import { useCallback, useMemo, useRef, useState } from "react";
type Options = {
/** высота одного элемента в px (фиксированная) */
itemHeight: number;
/** высота контейнера в px (видимая область) */
containerHeight: number;
/** сколько элементов рендерить дополнительно сверху/снизу */
overscan?: number;
};
export function useVirtualList<T>(items: T[], options: Options) {
const { itemHeight, containerHeight, overscan = 5 } = options;
const [scrollTop, setScrollTop] = useState(0);
const ref = useRef<HTMLDivElement | null>(null);
const totalHeight = items.length * itemHeight;
const startIndex = Math.max(
0,
Math.floor(scrollTop / itemHeight) - overscan,
);
const endIndex = Math.min(
items.length,
Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan,
);
const visibleItems = useMemo(
() => items.slice(startIndex, endIndex).map((item, i) => ({
item,
index: startIndex + i,
})),
[items, startIndex, endIndex],
);
const offsetY = startIndex * itemHeight;
const onScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
setScrollTop(e.currentTarget.scrollTop);
}, []);
return {
ref,
onScroll,
visibleItems,
totalHeight,
offsetY,
};
}

View File

@ -95,24 +95,15 @@ export function CatalogueGroup() {
} }
async function getRelatedGroupsHandler() { async function getRelatedGroupsHandler() {
setRelatedGroupLoading(true);
if (activeCompanyGroup?.id) { if (activeCompanyGroup?.id) {
setRelatedGroupLoading(true); const res = await fetchRelatedGroups(activeCompanyGroup?.id);
try { const gsks = res.filter((elem) => elem.kind === 'gsk');
const res = await fetchRelatedGroups(activeCompanyGroup?.id); const gszs = res.filter((elem) => elem.kind === 'gsz');
const gsks = res.filter((elem) => elem.kind === 'gsk'); setGskData(gsks);
const gszs = res.filter((elem) => elem.kind === 'gsz'); setGszData(gszs);
setGskData(gsks);
setGszData(gszs);
} catch (e) {
if (e instanceof ApiError) {
notification.error({
description: e.message,
});
}
} finally {
setRelatedGroupLoading(false);
}
} }
setRelatedGroupLoading(false);
} }
useEffect(() => { useEffect(() => {

View File

@ -7,10 +7,9 @@ import type {
MoveCatalogueJurProps, MoveCatalogueJurProps,
WithdrawCatalogueJurProps, WithdrawCatalogueJurProps,
} from '../../../types/catalogueJur/catalogueJur'; } from '../../../types/catalogueJur/catalogueJur';
import { LE_PAGE } from './company_groups';
export async function fetchCatalogueJur(params: GetCatalogueJurPropsI) { export async function fetchCatalogueJur(params: GetCatalogueJurPropsI) {
const res = await api.get<Page<ICatalogueJur>>('/directories/legal-entities', {limit:LE_PAGE, offset:0, ...params}); const res = await api.get<Page<ICatalogueJur>>('/directories/legal-entities', params);
return res; return res;
} }

View File

@ -3,7 +3,7 @@ import type { LegalEntityBrief, Page, RelatedGroupDetail, RelatedGroupOut } from
import type { CreateCompanyGroupProps, ICompanyGroupOut } from '../../../types/companyGroups/companyGroups'; import type { CreateCompanyGroupProps, ICompanyGroupOut } from '../../../types/companyGroups/companyGroups';
const CG_PAGE = 200; const CG_PAGE = 200;
export const LE_PAGE = 200; const LE_PAGE = 200;
/** Все видимые ГК — дочитываем страницы, пока не получим total. */ /** Все видимые ГК — дочитываем страницы, пока не получим total. */
export async function fetchCompanyGroups(): Promise<ICompanyGroupOut[]> { export async function fetchCompanyGroups(): Promise<ICompanyGroupOut[]> {

View File

@ -52,13 +52,13 @@ export type WithdrawCatalogueJurProps = {
export type CatalogueJurFormProps = { export type CatalogueJurFormProps = {
inn?: string; inn?: string;
kpp?: string | null; kpp?: string;
crm_id: string; crm_id: string;
entity_type: 'ЮЛ' | 'ИП'; entity_type: 'ЮЛ' | 'ИП';
name: string; name: string;
aliases?: string[] | { value: string; label: string }[]; aliases?: string[] | { value: string; label: string }[];
company_group_id?: string; company_group_id?: string;
regional_office_id?: string | null; regional_office_id?: string;
gsk_id?: string | null; gsk_id?: string;
gsz_id?: string | null; gsz_id?: string;
}; };

View File

@ -1,4 +1,4 @@
import { Col, Form, Input, Modal, Row, Select } from 'antd'; import { Col, Form, Input, InputNumber, Modal, Row, Select } from 'antd';
import type React from 'react'; import type React from 'react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { ApiError } from '../../api/client'; import { ApiError } from '../../api/client';
@ -37,62 +37,40 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
async function getInitialData() { async function getInitialData() {
if (jurId) { if (jurId) {
setInitialDataLoading(true); setInitialDataLoading(true);
try { const data = await fetchOneCatalogueJur(jurId);
const data = await fetchOneCatalogueJur(jurId); form.setFieldsValue({
form.setFieldsValue({ name: data?.name,
name: data?.name, aliases: data?.aliases?.map((aliase) => aliase.alias),
aliases: data?.aliases?.map((aliase) => aliase.alias), crm_id: data?.crm_id,
crm_id: data?.crm_id, entity_type: data?.entity_type,
entity_type: data?.entity_type, inn: data.inn,
inn: data.inn, kpp: data?.kpp,
kpp: data?.kpp, regional_office_id: data?.regional_office_id,
regional_office_id: data?.regional_office_id, company_group_id: data?.company_group_id,
company_group_id: data?.company_group_id, gsk_id: data.memberships?.find((elem) => elem.kind === 'gsk')?.related_group_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,
gsz_id: data.memberships?.find((elem) => elem.kind === 'gsz')?.related_group_id, });
});
} catch (e) { setInitialDataLoading(false);
if (e instanceof ApiError) {
notification.error({ description: e.message });
} else {
console.error(e);
}
} finally {
setInitialDataLoading(false);
}
} }
} }
async function getOfficesOptionsHandler() { async function getOfficesOptionsHandler() {
setLoadingOfficesOptions(true); setLoadingOfficesOptions(true);
try { const res = await loadOffices();
const res = await loadOffices(); const options = res.map((elem) => elem && { value: elem.id, label: elem.name });
const options = res.map((elem) => elem && { value: elem.id, label: elem.name }); setOfficesOptions(options);
setOfficesOptions(options); setLoadingOfficesOptions(false);
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
setLoadingOfficesOptions(false);
}
} }
async function getRelatedGroupsHandler() { async function getRelatedGroupsHandler() {
if (!companyGroupId) return; if (companyGroupId) {
setLoadingRelatedGroupsOptions(true);
setLoadingRelatedGroupsOptions(true);
try {
const res = await fetchRelatedGroups(companyGroupId); const res = await fetchRelatedGroups(companyGroupId);
const gsks = res.filter((elem) => elem.kind === 'gsk'); const gsks = res.filter((elem) => elem.kind === 'gsk');
const gszs = res.filter((elem) => elem.kind === 'gsz'); const gszs = res.filter((elem) => elem.kind === 'gsz');
setGskData(gsks); setGskData(gsks);
setGszData(gszs); setGszData(gszs);
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
setLoadingRelatedGroupsOptions(false); setLoadingRelatedGroupsOptions(false);
} }
} }
@ -103,20 +81,15 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
const values = await form.validateFields(); const values = await form.validateFields();
await onOk({ await onOk({
...values, ...values,
kpp: type === 'ИП' ? null : values.kpp?.toString(), kpp: type === 'ИП' ? undefined : values.kpp?.toString(),
inn: jurId ? undefined : values?.inn?.toString(), inn: jurId ? undefined : values?.inn?.toString(),
company_group_id: jurId ? undefined : values.company_group_id, company_group_id: jurId ? undefined : values.company_group_id,
gsz_id: values.gsz_id ? values?.gsz_id : null,
gsk_id: values.gsk_id ? values?.gsk_id : null,
regional_office_id: values.regional_office_id ? values?.regional_office_id : null,
}); });
} catch (e) { } catch (e) {
if (e instanceof ApiError) { if (e instanceof ApiError) {
notification.error({ notification.error({
description: e.message, description: e.message,
}); });
} else if (e instanceof Error && e.name !== 'ValidationError') {
console.error(e);
} }
} finally { } finally {
setConfirmLoading(false); setConfirmLoading(false);
@ -139,12 +112,7 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
useEffect(() => { useEffect(() => {
getOfficesOptionsHandler(); getOfficesOptionsHandler();
}, []); }, []);
useEffect(() => { useEffect(() => {
form.setFieldsValue({
gsk_id: undefined,
gsz_id: undefined,
});
getRelatedGroupsHandler(); getRelatedGroupsHandler();
}, [companyGroupId]); }, [companyGroupId]);
@ -154,7 +122,7 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
open={open} open={open}
onCancel={handleCancel} onCancel={handleCancel}
onOk={handleOk} onOk={handleOk}
okText={jurId ? 'Сохранить' : 'Добавить'} okText={jurId ? 'Сохраить' : 'Добавить'}
cancelText='Отмена' cancelText='Отмена'
destroyOnHidden destroyOnHidden
width={720} width={720}
@ -177,7 +145,7 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
{ pattern: /^\d{10}$/, message: '10 цифр' }, { pattern: /^\d{10}$/, message: '10 цифр' },
] ]
}> }>
<Input <InputNumber
disabled={!!jurId} disabled={!!jurId}
style={{ width: '100%' }} style={{ width: '100%' }}
placeholder={type === 'ИП' ? '000000000000' : '0000000000'} placeholder={type === 'ИП' ? '000000000000' : '0000000000'}
@ -186,13 +154,13 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item name='kpp' label='КПП' rules={[{ pattern: /^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$/, message: 'Некорректный формат' }]}> <Form.Item name='kpp' label='КПП' rules={[{ pattern: /^\d{9}$/, message: '9 цифр' }]}>
<Input placeholder='000000000' maxLength={9} disabled={type === 'ИП'} style={{ width: '100%' }} /> <InputNumber placeholder='000000000' maxLength={9} disabled={type === 'ИП'} style={{ width: '100%' }} />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item name='crm_id' label='ID ЮЛ в CRM'> <Form.Item name='crm_id' label='ID ЮЛ в CRM' rules={[{ len: 11, message: 'Должно быть 11 символов' }]}>
<Input /> <Input maxLength={11} />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={6}> <Col span={6}>

View File

@ -54,7 +54,7 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
const [jurId, setJurId] = useState(''); const [jurId, setJurId] = useState('');
const filterdData = useMemo(() => { const filterdData = useMemo(() => {
if (relatedGroupFilter?.id && !outside_group) { if (relatedGroupFilter?.id) {
return rawRows?.filter((row) => { return rawRows?.filter((row) => {
return row?.memberships?.some((member) => member.related_group_id === relatedGroupFilter.id); return row?.memberships?.some((member) => member.related_group_id === relatedGroupFilter.id);
}); });
@ -243,7 +243,7 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
<ColumnSettings defs={catalogueJurConfig.columns} visibleKeys={visibleKeys} onChange={setVisibleKeys} /> <ColumnSettings defs={catalogueJurConfig.columns} visibleKeys={visibleKeys} onChange={setVisibleKeys} />
</Space> </Space>
</Space> </Space>
{(relatedGroupFilter?.id && !outside_group) && ( {relatedGroupFilter?.id && (
<div <div
style={{ style={{
display: 'flex', display: 'flex',
@ -269,7 +269,7 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
<span style={{ color: '#874d00' }}>Фильтр по:</span> <span style={{ color: '#874d00' }}>Фильтр по:</span>
<span style={{ fontWeight: 600, color: '#ad4e00' }}>{relatedGroupFilter.code}</span> <span style={{ fontWeight: 600, color: '#ad4e00' }}>{relatedGroupFilter.code}</span>
<span style={{ color: '#874d00' }}> <span style={{ color: '#874d00' }}>
— {filterdData?.length} &nbsp; — {filterdData?.length}
{pluralRu(filterdData?.length ?? 0, ['запись', 'записи', 'записей'])} {pluralRu(filterdData?.length ?? 0, ['запись', 'записи', 'записей'])}
</span> </span>
</div> </div>

View File

@ -12,10 +12,8 @@ export const catalogueJurConfig: TableConfig<ICatalogueJur> = {
title: 'Статус', title: 'Статус',
features: { sort: true }, features: { sort: true },
render: (_: unknown, row) => { render: (_: unknown, row) => {
const preset = !row.closed ? TAG_PRESETS.green : TAG_PRESETS.gray; // #TODO поменять логику если запись не активна
const text = !row.closed ? 'Активно' : 'Не активно'; return <TableTag text={!row.closed ? 'Активно' : 'Не активно'} {...TAG_PRESETS.green} />;
return <TableTag text={text} {...preset} />;
}, },
}, },
{ {

View File

@ -26,17 +26,10 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
async function getOfficesOptionsHandler() { async function getOfficesOptionsHandler() {
setLoadingOptions(true); setLoadingOptions(true);
try { const res = await loadOffices();
const res = await loadOffices(); const options = res.map((elem) => elem && { value: elem.id, label: elem.name });
const options = res.map((elem) => elem && { value: elem.id, label: elem.name }); setOfficesOptions(options);
setOfficesOptions(options); setLoadingOptions(false);
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
setLoadingOptions(false);
}
} }
const handleOk = async () => { const handleOk = async () => {
@ -52,8 +45,6 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
notification.error({ notification.error({
description: e.message, description: e.message,
}); });
} else if (e instanceof Error && e.name !== 'ValidationError') {
console.error(e);
} }
} finally { } finally {
setConfirmLoading(false); setConfirmLoading(false);
@ -87,8 +78,8 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item name='crm_id' label='ID ГК в CRM'> <Form.Item name='crm_id' label='ID ГК в CRM' rules={[{ len: 11, message: 'Должно быть 11 символов' }]}>
<Input placeholder='Генерируется автоматически' /> <Input maxLength={11} placeholder='Генерируется автоматически' />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>

View File

@ -1,11 +1,10 @@
import { useEffect, useMemo, useState } from 'react';
import { Input, List, Spin, Empty, Typography, Tag } from 'antd';
import { FolderOpenOutlined, SearchOutlined } from '@ant-design/icons'; import { FolderOpenOutlined, SearchOutlined } from '@ant-design/icons';
import { useAppDispatch, useAppSelector } from '../../store/store'; import { Empty, Input, Listy, Spin, Tag, Typography } from 'antd';
import { getCompanyGroupsThunk } from '../../store/slices/companyGroupsSlice/thunks'; import { useEffect, useMemo, useState } from 'react';
import { setActiveCompanyGroup } from '../../store/slices/companyGroupsSlice/companyGroupsSlice'; import { setActiveCompanyGroup } from '../../store/slices/companyGroupsSlice/companyGroupsSlice';
import { useVirtualList } from '../../hooks/useVirtualList'; import { getCompanyGroupsThunk } from '../../store/slices/companyGroupsSlice/thunks';
import { useAppDispatch, useAppSelector } from '../../store/store';
import './CompanyGroupsList.css';
const { Text } = Typography; const { Text } = Typography;
export const CompanyGroupsList = () => { export const CompanyGroupsList = () => {
@ -23,22 +22,10 @@ export const CompanyGroupsList = () => {
return rawRows.filter((g) => g.name.toLowerCase().includes(q) || g.crm_id?.toLowerCase().includes(q)); return rawRows.filter((g) => g.name.toLowerCase().includes(q) || g.crm_id?.toLowerCase().includes(q));
}, [rawRows, query]); }, [rawRows, query]);
const { ref, onScroll, visibleItems, totalHeight, offsetY } = useVirtualList(filtered, {
itemHeight: 56,
containerHeight: 900,
overscan: 5,
});
useEffect(() => { useEffect(() => {
dispatch(getCompanyGroupsThunk()); dispatch(getCompanyGroupsThunk());
}, [dispatch]); }, [dispatch]);
useEffect(() => {
if (!activeCompanyGroup && rawRows?.length) {
dispatch(setActiveCompanyGroup(rawRows?.[0]));
}
}, [rawRows, activeCompanyGroup, dispatch]);
return ( return (
<div <div
style={{ style={{
@ -49,7 +36,7 @@ export const CompanyGroupsList = () => {
borderRight: '1px solid #f0f0f0', borderRight: '1px solid #f0f0f0',
borderRadius: 12, borderRadius: 12,
}}> }}>
<div style={{ padding: 12 }}> <div style={{ padding: '12px 16px', borderBottom: '1px solid #0f172a0f' }}>
<Input <Input
allowClear allowClear
prefix={<SearchOutlined style={{ color: '#bfbfbf' }} />} prefix={<SearchOutlined style={{ color: '#bfbfbf' }} />}
@ -59,100 +46,124 @@ export const CompanyGroupsList = () => {
/> />
</div> </div>
<div ref={ref} onScroll={onScroll} style={{ flex: 1, overflowY: 'auto', padding: '0 8px 8px' }}> <div style={{ flex: 1, overflowY: 'auto', padding: '10px' }}>
{loading ? ( {loading && (
<div style={{ textAlign: 'center', padding: 24 }}> <div style={{ textAlign: 'center', padding: 24 }}>
<Spin /> <Spin />
</div> </div>
) : filtered.length === 0 ? ( )}
{!loading && filtered.length === 0 && (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description='Компании и организации не найдены' style={{ marginTop: 32 }} /> <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description='Компании и организации не найдены' style={{ marginTop: 32 }} />
) : ( )}
<div style={{ height: totalHeight, position: 'relative' }}> {!loading && filtered.length > 0 && (
<div style={{ transform: `translateY(${offsetY}px)` }}> <Listy
<List virtual
dataSource={visibleItems} rowKey='id'
split={false} items={filtered}
renderItem={({item}) => { height={900}
const active = item.id === activeCompanyGroup?.id; itemRender={(item) => {
return ( const active = item.id === activeCompanyGroup?.id;
<List.Item return (
onClick={() => dispatch(setActiveCompanyGroup(item))} <button
type='button'
onClick={() => dispatch(setActiveCompanyGroup(item))}
style={{
display: 'block',
width: '100%',
border: 0,
font: 'inherit',
textAlign: 'left',
cursor: 'pointer',
borderRadius: 8,
padding: '10px 12px',
marginBottom: 4,
background: active ? '#e6f4ea' : 'transparent',
boxShadow: active ? 'inset 0 0 0 1px #2ea64b3d, inset 3px 0 0 #258141' : 'inset 0 0 0 1px #0f172a14',
transition: 'background 0.15s',
borderBottom: 0,
}}
onMouseEnter={(e) => {
if (!active) {
(e.currentTarget as HTMLElement).style.background = '#fafafa';
}
}}
onMouseLeave={(e) => {
if (!active) {
(e.currentTarget as HTMLElement).style.background = 'transparent';
}
}}>
<div
style={{
display: 'flex',
alignItems: 'center',
width: '100%',
gap: 12,
minWidth: 0,
}}>
<Tag
style={{ style={{
cursor: 'pointer',
borderRadius: 8, borderRadius: 8,
padding: '10px 12px', width: 28,
marginBottom: 4, height: 28,
background: active ? '#e6f4ea' : 'transparent', display: 'flex',
boxShadow: active ? 'inset 0 0 0 1px #2ea64b3d, inset 3px 0 0 #258141' : 'inset 0 0 0 1px #0f172a14', alignItems: 'center',
transition: 'background 0.15s', justifyContent: 'center',
}} }}
onMouseEnter={(e) => { color='success'>
if (!active) { <FolderOpenOutlined
(e.currentTarget as HTMLElement).style.background = '#fafafa';
}
}}
onMouseLeave={(e) => {
if (!active) {
(e.currentTarget as HTMLElement).style.background = 'transparent';
}
}}>
<div
style={{ style={{
display: 'flex', fontSize: 18,
alignItems: 'center', color: 'green',
width: '100%', flexShrink: 0,
gap: 12, }}
minWidth: 0, />
</Tag>
<div style={{ flex: 1, minWidth: 0 }}>
<Text
strong
ellipsis
style={{
display: 'block',
fontSize: 14,
lineHeight: '20px',
}}> }}>
<FolderOpenOutlined style={{ fontSize: 18, color: '#8c8c8c', flexShrink: 0 }} /> {item.name}
</Text>
<Text
type='secondary'
ellipsis
style={{
display: 'block',
fontSize: 12,
lineHeight: '16px',
}}>
CRM ID: {item.crm_id ?? '-'}
</Text>
</div>
<div style={{ flex: 1, minWidth: 0 }}> <Tag
<Text style={{
strong margin: 0,
ellipsis borderRadius: 10,
style={{ background: '#f5f5f5',
display: 'block', border: 'none',
fontSize: 14, color: '#595959',
lineHeight: '20px', fontWeight: 500,
}}> minWidth: 24,
{item.name} height: 20,
</Text> lineHeight: '20px',
<Text textAlign: 'center',
type='secondary' padding: '0 8px',
ellipsis flexShrink: 0,
style={{ }}>
display: 'block', {item.legal_entities_count}
fontSize: 12, </Tag>
lineHeight: '16px', </div>
}}> </button>
CRM ID: {item.crm_id ?? '-'} );
</Text> }}
</div> />
<Tag
style={{
margin: 0,
borderRadius: 10,
background: '#f5f5f5',
border: 'none',
color: '#595959',
fontWeight: 500,
minWidth: 24,
height: 20,
lineHeight: '20px',
textAlign: 'center',
padding: '0 8px',
flexShrink: 0,
}}>
{item.legal_entities_count}
</Tag>
</div>
</List.Item>
);
}}
/>
</div>
</div>
)} )}
</div> </div>
</div> </div>

View File

@ -5,10 +5,9 @@ type Props = {
onCancel: () => void; onCancel: () => void;
onOk: () => void | Promise<void>; onOk: () => void | Promise<void>;
loading?: boolean; loading?: boolean;
kind: 'gsk' | 'gsz';
}; };
export const DeleteRelatedGroupModal: React.FC<Props> = ({ open, onCancel, onOk, loading, kind }) => { export const DeleteRelatedGroupModal: React.FC<Props> = ({ open, onCancel, onOk, loading }) => {
return ( return (
<Modal <Modal
title='Удалить?' title='Удалить?'
@ -20,7 +19,7 @@ export const DeleteRelatedGroupModal: React.FC<Props> = ({ open, onCancel, onOk,
okButtonProps={{ danger: true }} okButtonProps={{ danger: true }}
confirmLoading={loading} confirmLoading={loading}
centered> centered>
<p style={{ margin: 0, color: '#595959' }}>Группа связанных {kind === 'gsk' ? 'компаний' : 'заёмщиков'} будет удалена.</p> <p style={{ margin: 0, color: '#595959' }}>Группа связанных компаний будет удалена.</p>
</Modal> </Modal>
); );
}; };

View File

@ -36,22 +36,15 @@ export const MoveCatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, ju
async function getInitialData() { async function getInitialData() {
setInitialDataLoading(true); setInitialDataLoading(true);
try { const data = await fetchOneCatalogueJur(jurId);
const data = await fetchOneCatalogueJur(jurId); form.setFieldsValue({
form.setFieldsValue({ name: data?.name,
name: data?.name, company_group_name: data?.company_group_name ?? '',
company_group_name: data?.company_group_name ?? '', old_company_group_id: data?.company_group_id ?? '',
old_company_group_id: data?.company_group_id ?? '', company_group_id: undefined,
company_group_id: undefined, effective_date: '',
effective_date: '', });
}); setInitialDataLoading(false);
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
setInitialDataLoading(false);
}
} }
const handleOk = async () => { const handleOk = async () => {

View File

@ -36,7 +36,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
form.setFieldsValue({ form.setFieldsValue({
crm_id: data?.crm_id, crm_id: data?.crm_id,
name: data?.name, name: data?.name,
created_date: data?.created_date ?? dayjs().format('YYYY-MM-DD'), created_date: data?.created_date,
company_group_id: data?.company_group_id, company_group_id: data?.company_group_id,
}); });
} catch (e) { } catch (e) {
@ -53,7 +53,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
try { try {
const { created_date, name, crm_id, company_group_id } = await form.validateFields(); const { created_date, name, crm_id, company_group_id } = await form.validateFields();
await onOk({ await onOk({
created_date: created_date ?? dayjs().format('YYYY-MM-DD'), created_date,
name, name,
crm_id, crm_id,
company_group_id: relatedGroupId ? undefined : company_group_id, company_group_id: relatedGroupId ? undefined : company_group_id,
@ -85,7 +85,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
return ( return (
<Modal <Modal
title={`${relatedGroupId ? 'Изменить' : 'Создать'} группу связанных ${kind === 'gsk' ? 'компаний' : 'заемщиков'}`} title={`${relatedGroupId ? 'Изменить' : 'Создать'} группу связанных ${kind === 'gsk' ? 'компаний' : 'заемщииков'}`}
open={open} open={open}
onCancel={handleCancel} onCancel={handleCancel}
onOk={handleOk} onOk={handleOk}
@ -95,8 +95,8 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
loading={initialDataLoading} loading={initialDataLoading}
confirmLoading={confirmLoading}> confirmLoading={confirmLoading}>
<Form form={form} layout='vertical'> <Form form={form} layout='vertical'>
<FormItem name='crm_id' label='ID в CRM'> <FormItem name='crm_id' label='ID в CRM' rules={[{ len: 11, message: 'Должно быть 11 символов' }]}>
<Input placeholder='Генерируется автоматически' /> <Input maxLength={11} placeholder='Генерируется автоматически' />
</FormItem> </FormItem>
<FormItem name='name' label='Наименование' rules={[{ required: true, message: 'Укажите наименование' }]}> <FormItem name='name' label='Наименование' rules={[{ required: true, message: 'Укажите наименование' }]}>
<Input /> <Input />
@ -115,6 +115,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
<Form.Item <Form.Item
name='created_date' name='created_date'
label='Дата создания' label='Дата создания'
rules={[{ required: true, message: 'Укажите дату' }]}
getValueProps={(value: string | undefined) => ({ getValueProps={(value: string | undefined) => ({
value: value ? dayjs(value) : undefined, value: value ? dayjs(value) : undefined,
})} })}

View File

@ -180,7 +180,6 @@ export function RelatedGroupTable({ tabsComponent, data, loading, getData, activ
loading={deletingLoading} loading={deletingLoading}
onCancel={() => closeDeleteRelatedGroupHandler()} onCancel={() => closeDeleteRelatedGroupHandler()}
onOk={deleteRelatedGroupHandler} onOk={deleteRelatedGroupHandler}
kind={activeTab}
/> />
)} )}
</div> </div>

View File

@ -34,21 +34,14 @@ export const WithdrawCatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk
async function getInitialData() { async function getInitialData() {
setInitialDataLoading(true); setInitialDataLoading(true);
try { const data = await fetchOneCatalogueJur(jurId);
const data = await fetchOneCatalogueJur(jurId); form.setFieldsValue({
form.setFieldsValue({ name: data?.name,
name: data?.name, event: undefined,
event: undefined, effective_date: '',
effective_date: '', comment: '',
comment: '', });
}); setInitialDataLoading(false);
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
setInitialDataLoading(false);
}
} }
const handleOk = async () => { const handleOk = async () => {
@ -107,7 +100,7 @@ export const WithdrawCatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk
<DatePicker minDate={dayjs()} style={{ width: '100%' }} format='DD.MM.YYYY' placeholder='ДД.ММ.ГГГГ' /> <DatePicker minDate={dayjs()} style={{ width: '100%' }} format='DD.MM.YYYY' placeholder='ДД.ММ.ГГГГ' />
</Form.Item> </Form.Item>
<FormItem name='comment' label='Комментарий'> <FormItem name='comment' label='Комментарий'>
<TextArea rows={4} placeholder='Необязательно' /> <TextArea maxLength={6} rows={4} placeholder='Необязательно' />
</FormItem> </FormItem>
</Form> </Form>
</Modal> </Modal>