added company group table #31
@ -39,4 +39,9 @@ export const TAG_PRESETS = {
|
||||
borderColor: 'rgba(245, 158, 11, 0.25)',
|
||||
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;
|
||||
|
||||
51
src/hooks/useVirtualList.tsx
Normal file
51
src/hooks/useVirtualList.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
@ -95,15 +95,24 @@ export function CatalogueGroup() {
|
||||
}
|
||||
|
||||
async function getRelatedGroupsHandler() {
|
||||
setRelatedGroupLoading(true);
|
||||
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);
|
||||
setRelatedGroupLoading(true);
|
||||
try {
|
||||
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);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({
|
||||
description: e.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setRelatedGroupLoading(false);
|
||||
}
|
||||
}
|
||||
setRelatedGroupLoading(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -7,9 +7,10 @@ import type {
|
||||
MoveCatalogueJurProps,
|
||||
WithdrawCatalogueJurProps,
|
||||
} from '../../../types/catalogueJur/catalogueJur';
|
||||
import { LE_PAGE } from './company_groups';
|
||||
|
||||
export async function fetchCatalogueJur(params: GetCatalogueJurPropsI) {
|
||||
const res = await api.get<Page<ICatalogueJur>>('/directories/legal-entities', params);
|
||||
const res = await api.get<Page<ICatalogueJur>>('/directories/legal-entities', {limit:LE_PAGE, offset:0, ...params});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import type { LegalEntityBrief, Page, RelatedGroupDetail, RelatedGroupOut } from
|
||||
import type { CreateCompanyGroupProps, ICompanyGroupOut } from '../../../types/companyGroups/companyGroups';
|
||||
|
||||
const CG_PAGE = 200;
|
||||
const LE_PAGE = 200;
|
||||
export const LE_PAGE = 200;
|
||||
|
||||
/** Все видимые ГК — дочитываем страницы, пока не получим total. */
|
||||
export async function fetchCompanyGroups(): Promise<ICompanyGroupOut[]> {
|
||||
|
||||
@ -52,13 +52,13 @@ export type WithdrawCatalogueJurProps = {
|
||||
|
||||
export type CatalogueJurFormProps = {
|
||||
inn?: string;
|
||||
kpp?: string;
|
||||
kpp?: string | null;
|
||||
crm_id: string;
|
||||
entity_type: 'ЮЛ' | 'ИП';
|
||||
name: string;
|
||||
aliases?: string[] | { value: string; label: string }[];
|
||||
company_group_id?: string;
|
||||
regional_office_id?: string;
|
||||
gsk_id?: string;
|
||||
gsz_id?: string;
|
||||
regional_office_id?: string | null;
|
||||
gsk_id?: string | null;
|
||||
gsz_id?: string | null;
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Col, Form, Input, InputNumber, Modal, Row, Select } from 'antd';
|
||||
import { Col, Form, Input, Modal, Row, Select } from 'antd';
|
||||
import type React from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { ApiError } from '../../api/client';
|
||||
@ -37,40 +37,62 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
async function getInitialData() {
|
||||
if (jurId) {
|
||||
setInitialDataLoading(true);
|
||||
const data = await fetchOneCatalogueJur(jurId);
|
||||
form.setFieldsValue({
|
||||
name: data?.name,
|
||||
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,
|
||||
company_group_id: data?.company_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,
|
||||
});
|
||||
|
||||
setInitialDataLoading(false);
|
||||
try {
|
||||
const data = await fetchOneCatalogueJur(jurId);
|
||||
form.setFieldsValue({
|
||||
name: data?.name,
|
||||
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,
|
||||
company_group_id: data?.company_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,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({ description: e.message });
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
} finally {
|
||||
setInitialDataLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getOfficesOptionsHandler() {
|
||||
setLoadingOfficesOptions(true);
|
||||
const res = await loadOffices();
|
||||
const options = res.map((elem) => elem && { value: elem.id, label: elem.name });
|
||||
setOfficesOptions(options);
|
||||
setLoadingOfficesOptions(false);
|
||||
try {
|
||||
const res = await loadOffices();
|
||||
const options = res.map((elem) => elem && { value: elem.id, label: elem.name });
|
||||
setOfficesOptions(options);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({ description: e.message });
|
||||
}
|
||||
} finally {
|
||||
setLoadingOfficesOptions(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function getRelatedGroupsHandler() {
|
||||
if (companyGroupId) {
|
||||
setLoadingRelatedGroupsOptions(true);
|
||||
if (!companyGroupId) return;
|
||||
|
||||
setLoadingRelatedGroupsOptions(true);
|
||||
try {
|
||||
const res = await fetchRelatedGroups(companyGroupId);
|
||||
const gsks = res.filter((elem) => elem.kind === 'gsk');
|
||||
const gszs = res.filter((elem) => elem.kind === 'gsz');
|
||||
setGskData(gsks);
|
||||
setGszData(gszs);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({ description: e.message });
|
||||
}
|
||||
} finally {
|
||||
setLoadingRelatedGroupsOptions(false);
|
||||
}
|
||||
}
|
||||
@ -81,15 +103,20 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
const values = await form.validateFields();
|
||||
await onOk({
|
||||
...values,
|
||||
kpp: type === 'ИП' ? undefined : values.kpp?.toString(),
|
||||
kpp: type === 'ИП' ? null : values.kpp?.toString(),
|
||||
inn: jurId ? undefined : values?.inn?.toString(),
|
||||
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) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({
|
||||
description: e.message,
|
||||
});
|
||||
} else if (e instanceof Error && e.name !== 'ValidationError') {
|
||||
console.error(e);
|
||||
}
|
||||
} finally {
|
||||
setConfirmLoading(false);
|
||||
@ -112,7 +139,12 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
useEffect(() => {
|
||||
getOfficesOptionsHandler();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
gsk_id: undefined,
|
||||
gsz_id: undefined,
|
||||
});
|
||||
getRelatedGroupsHandler();
|
||||
}, [companyGroupId]);
|
||||
|
||||
@ -122,7 +154,7 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
open={open}
|
||||
onCancel={handleCancel}
|
||||
onOk={handleOk}
|
||||
okText={jurId ? 'Сохраить' : 'Добавить'}
|
||||
okText={jurId ? 'Сохранить' : 'Добавить'}
|
||||
cancelText='Отмена'
|
||||
destroyOnHidden
|
||||
width={720}
|
||||
@ -145,7 +177,7 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
{ pattern: /^\d{10}$/, message: '10 цифр' },
|
||||
]
|
||||
}>
|
||||
<InputNumber
|
||||
<Input
|
||||
disabled={!!jurId}
|
||||
style={{ width: '100%' }}
|
||||
placeholder={type === 'ИП' ? '000000000000' : '0000000000'}
|
||||
@ -154,13 +186,13 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name='kpp' label='КПП' rules={[{ pattern: /^\d{9}$/, message: '9 цифр' }]}>
|
||||
<InputNumber placeholder='000000000' maxLength={9} disabled={type === 'ИП'} style={{ width: '100%' }} />
|
||||
<Form.Item name='kpp' label='КПП' rules={[{ pattern: /^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$/, message: 'Некорректный формат' }]}>
|
||||
<Input placeholder='000000000' maxLength={9} disabled={type === 'ИП'} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name='crm_id' label='ID ЮЛ в CRM' rules={[{ len: 11, message: 'Должно быть 11 символов' }]}>
|
||||
<Input maxLength={11} />
|
||||
<Form.Item name='crm_id' label='ID ЮЛ в CRM'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
|
||||
@ -54,7 +54,7 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
|
||||
const [jurId, setJurId] = useState('');
|
||||
|
||||
const filterdData = useMemo(() => {
|
||||
if (relatedGroupFilter?.id) {
|
||||
if (relatedGroupFilter?.id && !outside_group) {
|
||||
return rawRows?.filter((row) => {
|
||||
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} />
|
||||
</Space>
|
||||
</Space>
|
||||
{relatedGroupFilter?.id && (
|
||||
{(relatedGroupFilter?.id && !outside_group) && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
@ -269,7 +269,7 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
|
||||
<span style={{ color: '#874d00' }}>Фильтр по:</span>
|
||||
<span style={{ fontWeight: 600, color: '#ad4e00' }}>{relatedGroupFilter.code}</span>
|
||||
<span style={{ color: '#874d00' }}>
|
||||
— {filterdData?.length}
|
||||
— {filterdData?.length}
|
||||
{pluralRu(filterdData?.length ?? 0, ['запись', 'записи', 'записей'])}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -12,8 +12,10 @@ export const catalogueJurConfig: TableConfig<ICatalogueJur> = {
|
||||
title: 'Статус',
|
||||
features: { sort: true },
|
||||
render: (_: unknown, row) => {
|
||||
// #TODO поменять логику если запись не активна
|
||||
return <TableTag text={!row.closed ? 'Активно' : 'Не активно'} {...TAG_PRESETS.green} />;
|
||||
const preset = !row.closed ? TAG_PRESETS.green : TAG_PRESETS.gray;
|
||||
const text = !row.closed ? 'Активно' : 'Не активно';
|
||||
|
||||
return <TableTag text={text} {...preset} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -26,10 +26,17 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
|
||||
async function getOfficesOptionsHandler() {
|
||||
setLoadingOptions(true);
|
||||
const res = await loadOffices();
|
||||
const options = res.map((elem) => elem && { value: elem.id, label: elem.name });
|
||||
setOfficesOptions(options);
|
||||
setLoadingOptions(false);
|
||||
try {
|
||||
const res = await loadOffices();
|
||||
const options = res.map((elem) => elem && { value: elem.id, label: elem.name });
|
||||
setOfficesOptions(options);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({ description: e.message });
|
||||
}
|
||||
} finally {
|
||||
setLoadingOptions(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = async () => {
|
||||
@ -45,6 +52,8 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
notification.error({
|
||||
description: e.message,
|
||||
});
|
||||
} else if (e instanceof Error && e.name !== 'ValidationError') {
|
||||
console.error(e);
|
||||
}
|
||||
} finally {
|
||||
setConfirmLoading(false);
|
||||
@ -78,8 +87,8 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name='crm_id' label='ID ГК в CRM' rules={[{ len: 11, message: 'Должно быть 11 символов' }]}>
|
||||
<Input maxLength={11} placeholder='Генерируется автоматически' />
|
||||
<Form.Item name='crm_id' label='ID ГК в CRM'>
|
||||
<Input placeholder='Генерируется автоматически' />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { FolderOpenOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
import { Empty, Input, Listy, Spin, Tag, Typography } from 'antd';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { setActiveCompanyGroup } from '../../store/slices/companyGroupsSlice/companyGroupsSlice';
|
||||
import { getCompanyGroupsThunk } from '../../store/slices/companyGroupsSlice/thunks';
|
||||
import { Input, List, Spin, Empty, Typography, Tag } from 'antd';
|
||||
import { FolderOpenOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
import { useAppDispatch, useAppSelector } from '../../store/store';
|
||||
import './CompanyGroupsList.css';
|
||||
import { getCompanyGroupsThunk } from '../../store/slices/companyGroupsSlice/thunks';
|
||||
import { setActiveCompanyGroup } from '../../store/slices/companyGroupsSlice/companyGroupsSlice';
|
||||
import { useVirtualList } from '../../hooks/useVirtualList';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export const CompanyGroupsList = () => {
|
||||
@ -22,10 +23,22 @@ export const CompanyGroupsList = () => {
|
||||
return rawRows.filter((g) => g.name.toLowerCase().includes(q) || g.crm_id?.toLowerCase().includes(q));
|
||||
}, [rawRows, query]);
|
||||
|
||||
const { ref, onScroll, visibleItems, totalHeight, offsetY } = useVirtualList(filtered, {
|
||||
itemHeight: 56,
|
||||
containerHeight: 900,
|
||||
overscan: 5,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getCompanyGroupsThunk());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeCompanyGroup && rawRows?.length) {
|
||||
dispatch(setActiveCompanyGroup(rawRows?.[0]));
|
||||
}
|
||||
}, [rawRows, activeCompanyGroup, dispatch]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@ -36,7 +49,7 @@ export const CompanyGroupsList = () => {
|
||||
borderRight: '1px solid #f0f0f0',
|
||||
borderRadius: 12,
|
||||
}}>
|
||||
<div style={{ padding: '12px 16px', borderBottom: '1px solid #0f172a0f' }}>
|
||||
<div style={{ padding: 12 }}>
|
||||
<Input
|
||||
allowClear
|
||||
prefix={<SearchOutlined style={{ color: '#bfbfbf' }} />}
|
||||
@ -46,124 +59,100 @@ export const CompanyGroupsList = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: '10px' }}>
|
||||
{loading && (
|
||||
<div ref={ref} onScroll={onScroll} style={{ flex: 1, overflowY: 'auto', padding: '0 8px 8px' }}>
|
||||
{loading ? (
|
||||
<div style={{ textAlign: 'center', padding: 24 }}>
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
{!loading && filtered.length === 0 && (
|
||||
) : filtered.length === 0 ? (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description='Компании и организации не найдены' style={{ marginTop: 32 }} />
|
||||
)}
|
||||
{!loading && filtered.length > 0 && (
|
||||
<Listy
|
||||
virtual
|
||||
rowKey='id'
|
||||
items={filtered}
|
||||
height={900}
|
||||
itemRender={(item) => {
|
||||
const active = item.id === activeCompanyGroup?.id;
|
||||
return (
|
||||
<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
|
||||
) : (
|
||||
<div style={{ height: totalHeight, position: 'relative' }}>
|
||||
<div style={{ transform: `translateY(${offsetY}px)` }}>
|
||||
<List
|
||||
dataSource={visibleItems}
|
||||
split={false}
|
||||
renderItem={({item}) => {
|
||||
const active = item.id === activeCompanyGroup?.id;
|
||||
return (
|
||||
<List.Item
|
||||
onClick={() => dispatch(setActiveCompanyGroup(item))}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
borderRadius: 8,
|
||||
width: 28,
|
||||
height: 28,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
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',
|
||||
}}
|
||||
color='success'>
|
||||
<FolderOpenOutlined
|
||||
style={{
|
||||
fontSize: 18,
|
||||
color: 'green',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
</Tag>
|
||||
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text
|
||||
strong
|
||||
ellipsis
|
||||
style={{
|
||||
display: 'block',
|
||||
fontSize: 14,
|
||||
lineHeight: '20px',
|
||||
}}>
|
||||
{item.name}
|
||||
</Text>
|
||||
<Text
|
||||
type='secondary'
|
||||
ellipsis
|
||||
style={{
|
||||
display: 'block',
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
}}>
|
||||
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,
|
||||
onMouseEnter={(e) => {
|
||||
if (!active) {
|
||||
(e.currentTarget as HTMLElement).style.background = '#fafafa';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!active) {
|
||||
(e.currentTarget as HTMLElement).style.background = 'transparent';
|
||||
}
|
||||
}}>
|
||||
{item.legal_entities_count}
|
||||
</Tag>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
gap: 12,
|
||||
minWidth: 0,
|
||||
}}>
|
||||
<FolderOpenOutlined style={{ fontSize: 18, color: '#8c8c8c', flexShrink: 0 }} />
|
||||
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text
|
||||
strong
|
||||
ellipsis
|
||||
style={{
|
||||
display: 'block',
|
||||
fontSize: 14,
|
||||
lineHeight: '20px',
|
||||
}}>
|
||||
{item.name}
|
||||
</Text>
|
||||
<Text
|
||||
type='secondary'
|
||||
ellipsis
|
||||
style={{
|
||||
display: 'block',
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
}}>
|
||||
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>
|
||||
|
||||
@ -5,9 +5,10 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
onOk: () => void | Promise<void>;
|
||||
loading?: boolean;
|
||||
kind: 'gsk' | 'gsz';
|
||||
};
|
||||
|
||||
export const DeleteRelatedGroupModal: React.FC<Props> = ({ open, onCancel, onOk, loading }) => {
|
||||
export const DeleteRelatedGroupModal: React.FC<Props> = ({ open, onCancel, onOk, loading, kind }) => {
|
||||
return (
|
||||
<Modal
|
||||
title='Удалить?'
|
||||
@ -19,7 +20,7 @@ export const DeleteRelatedGroupModal: React.FC<Props> = ({ open, onCancel, onOk,
|
||||
okButtonProps={{ danger: true }}
|
||||
confirmLoading={loading}
|
||||
centered>
|
||||
<p style={{ margin: 0, color: '#595959' }}>Группа связанных компаний будет удалена.</p>
|
||||
<p style={{ margin: 0, color: '#595959' }}>Группа связанных {kind === 'gsk' ? 'компаний' : 'заёмщиков'} будет удалена.</p>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
@ -36,15 +36,22 @@ export const MoveCatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, ju
|
||||
|
||||
async function getInitialData() {
|
||||
setInitialDataLoading(true);
|
||||
const data = await fetchOneCatalogueJur(jurId);
|
||||
form.setFieldsValue({
|
||||
name: data?.name,
|
||||
company_group_name: data?.company_group_name ?? '',
|
||||
old_company_group_id: data?.company_group_id ?? '',
|
||||
company_group_id: undefined,
|
||||
effective_date: '',
|
||||
});
|
||||
setInitialDataLoading(false);
|
||||
try {
|
||||
const data = await fetchOneCatalogueJur(jurId);
|
||||
form.setFieldsValue({
|
||||
name: data?.name,
|
||||
company_group_name: data?.company_group_name ?? '',
|
||||
old_company_group_id: data?.company_group_id ?? '',
|
||||
company_group_id: undefined,
|
||||
effective_date: '',
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({ description: e.message });
|
||||
}
|
||||
} finally {
|
||||
setInitialDataLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = async () => {
|
||||
|
||||
@ -36,7 +36,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
|
||||
form.setFieldsValue({
|
||||
crm_id: data?.crm_id,
|
||||
name: data?.name,
|
||||
created_date: data?.created_date,
|
||||
created_date: data?.created_date ?? dayjs().format('YYYY-MM-DD'),
|
||||
company_group_id: data?.company_group_id,
|
||||
});
|
||||
} catch (e) {
|
||||
@ -53,7 +53,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
|
||||
try {
|
||||
const { created_date, name, crm_id, company_group_id } = await form.validateFields();
|
||||
await onOk({
|
||||
created_date,
|
||||
created_date: created_date ?? dayjs().format('YYYY-MM-DD'),
|
||||
name,
|
||||
crm_id,
|
||||
company_group_id: relatedGroupId ? undefined : company_group_id,
|
||||
@ -85,7 +85,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={`${relatedGroupId ? 'Изменить' : 'Создать'} группу связанных ${kind === 'gsk' ? 'компаний' : 'заемщииков'}`}
|
||||
title={`${relatedGroupId ? 'Изменить' : 'Создать'} группу связанных ${kind === 'gsk' ? 'компаний' : 'заемщиков'}`}
|
||||
open={open}
|
||||
onCancel={handleCancel}
|
||||
onOk={handleOk}
|
||||
@ -95,8 +95,8 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
|
||||
loading={initialDataLoading}
|
||||
confirmLoading={confirmLoading}>
|
||||
<Form form={form} layout='vertical'>
|
||||
<FormItem name='crm_id' label='ID в CRM' rules={[{ len: 11, message: 'Должно быть 11 символов' }]}>
|
||||
<Input maxLength={11} placeholder='Генерируется автоматически' />
|
||||
<FormItem name='crm_id' label='ID в CRM'>
|
||||
<Input placeholder='Генерируется автоматически' />
|
||||
</FormItem>
|
||||
<FormItem name='name' label='Наименование' rules={[{ required: true, message: 'Укажите наименование' }]}>
|
||||
<Input />
|
||||
@ -115,7 +115,6 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
|
||||
<Form.Item
|
||||
name='created_date'
|
||||
label='Дата создания'
|
||||
rules={[{ required: true, message: 'Укажите дату' }]}
|
||||
getValueProps={(value: string | undefined) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
|
||||
@ -180,6 +180,7 @@ export function RelatedGroupTable({ tabsComponent, data, loading, getData, activ
|
||||
loading={deletingLoading}
|
||||
onCancel={() => closeDeleteRelatedGroupHandler()}
|
||||
onOk={deleteRelatedGroupHandler}
|
||||
kind={activeTab}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -34,14 +34,21 @@ export const WithdrawCatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk
|
||||
|
||||
async function getInitialData() {
|
||||
setInitialDataLoading(true);
|
||||
const data = await fetchOneCatalogueJur(jurId);
|
||||
form.setFieldsValue({
|
||||
name: data?.name,
|
||||
event: undefined,
|
||||
effective_date: '',
|
||||
comment: '',
|
||||
});
|
||||
setInitialDataLoading(false);
|
||||
try {
|
||||
const data = await fetchOneCatalogueJur(jurId);
|
||||
form.setFieldsValue({
|
||||
name: data?.name,
|
||||
event: undefined,
|
||||
effective_date: '',
|
||||
comment: '',
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({ description: e.message });
|
||||
}
|
||||
} finally {
|
||||
setInitialDataLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = async () => {
|
||||
@ -100,7 +107,7 @@ export const WithdrawCatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk
|
||||
<DatePicker minDate={dayjs()} style={{ width: '100%' }} format='DD.MM.YYYY' placeholder='ДД.ММ.ГГГГ' />
|
||||
</Form.Item>
|
||||
<FormItem name='comment' label='Комментарий'>
|
||||
<TextArea maxLength={6} rows={4} placeholder='Необязательно' />
|
||||
<TextArea rows={4} placeholder='Необязательно' />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user