161 lines
4.9 KiB
TypeScript
161 lines
4.9 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import { Input, List, Spin, Empty, Typography, Tag } from 'antd';
|
|
import { FolderOpenOutlined, SearchOutlined } from '@ant-design/icons';
|
|
import { useAppDispatch, useAppSelector } from '../../store/store';
|
|
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 = () => {
|
|
const dispatch = useAppDispatch();
|
|
|
|
const rawRows = useAppSelector((state) => state.company_groups.raw_rows);
|
|
const loading = useAppSelector((state) => state.company_groups.loading);
|
|
const activeCompanyGroup = useAppSelector((state) => state.company_groups.activeCompanyGroup);
|
|
|
|
const [query, setQuery] = useState('');
|
|
|
|
const filtered = useMemo(() => {
|
|
const q = query.trim().toLowerCase();
|
|
if (!q) return rawRows;
|
|
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={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
height: '100%',
|
|
background: '#fff',
|
|
borderRight: '1px solid #f0f0f0',
|
|
borderRadius: 12,
|
|
}}>
|
|
<div style={{ padding: 12 }}>
|
|
<Input
|
|
allowClear
|
|
prefix={<SearchOutlined style={{ color: '#bfbfbf' }} />}
|
|
placeholder='Поиск по ГК или CRM ID'
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div ref={ref} onScroll={onScroll} style={{ flex: 1, overflowY: 'auto', padding: '0 8px 8px' }}>
|
|
{loading ? (
|
|
<div style={{ textAlign: 'center', padding: 24 }}>
|
|
<Spin />
|
|
</div>
|
|
) : filtered.length === 0 ? (
|
|
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description='Компании и организации не найдены' style={{ marginTop: 32 }} />
|
|
) : (
|
|
<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,
|
|
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',
|
|
}}
|
|
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,
|
|
}}>
|
|
<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>
|
|
);
|
|
};
|