arm-core/web/src/widgets/ConsolidatedChessContent/ConsolidatedChessContent.tsx

289 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { DownloadOutlined, FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons';
import { Button, ConfigProvider } from 'antd';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useParams } from 'react-router-dom';
import { ApiError } from '../../api/client';
import {
exportChessTableAll,
exportChessTableManual,
fetchChessConsolidations,
fetchChessConsolidationsManual,
fetchChessConsolidationsResult,
} from '../../services/consodilation/intercompanyMatrix';
import type { IntercompanyMatrixResponse, IntercompanyMatrixResultResponse } from '../../types/chess/chess';
import type { VersionCard } from '../../types/consolidations/version_members';
import { exportFilename, saveBlob } from '../../utils/download';
import { useNotificationApi } from '../../utils/useNotification';
import { ConsolidationChessFormatTableFilter } from '../ConsolidationChessFormatTableFilter/ConsolidationChessFormatTableFilter';
import { ConsolidationChessResTable } from '../ConsolidationChessResTable/ConsolidationChessResTable';
import { ConsolidationChessTable } from '../ConsolidationChessTable/ConsolidationChessTable';
import { ConsolidationChessTableFilter } from '../ConsolidationChessTableFilter/ConsolidationChessTableFilter';
export const ChessContent = ({ card }: { card: VersionCard | null }) => {
const { version_id } = useParams<{ version_id: string }>();
const notification = useNotificationApi();
const [data, setData] = useState<IntercompanyMatrixResponse | null>(null);
const [loading, setLoading] = useState(false);
const [expoerLoading, setExportLoading] = useState(false);
const [companies, setCompanies] = useState<string[]>([]);
const [decimals, setDecimals] = useState(2);
const [units, setUnits] = useState('thousand');
const [debit, setDebit] = useState<string[]>([]);
const [credit, setCredit] = useState<string[]>([]);
const [chessResData, setChessResData] = useState<IntercompanyMatrixResultResponse | null>(null);
const [chessResUnits, setChessResUnits] = useState('thousand');
const [chessResDecimals, setChessResDecimals] = useState(2);
const [chessResLoading, setChessResLoading] = useState(false);
const chessTableRef = useRef<HTMLDivElement>(null);
const resTableRef = useRef<HTMLDivElement>(null);
const [isChessFullscreen, setIsChessFullscreen] = useState(false);
const [isResFullscreen, setIsResFullscreen] = useState(false);
const toggleFullscreen = async (ref: React.RefObject<HTMLDivElement>, setFullscreen: (val: boolean) => void) => {
if (!ref.current) return;
try {
if (!document.fullscreenElement) {
await ref.current.requestFullscreen();
setFullscreen(true);
} else {
await document.exitFullscreen();
setFullscreen(false);
}
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
}
};
useEffect(() => {
const handleFullscreenChange = () => {
if (!document.fullscreenElement) {
setIsChessFullscreen(false);
setIsResFullscreen(false);
}
};
document.addEventListener('fullscreenchange', handleFullscreenChange);
return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);
}, []);
const loadData = async (vId: string, dIds: string[], cIds: string[], isActual: () => boolean) => {
if ((dIds.length > 0 && cIds.length === 0) || (dIds.length === 0 && cIds.length > 0)) {
notification.info({ description: 'Для сравнений по Дт и Кт нужно заполнить оба фильтра' });
return;
}
setLoading(true);
try {
let res: IntercompanyMatrixResponse;
if (dIds?.length && cIds?.length) {
res = await fetchChessConsolidationsManual(vId, { debit_account_ids: dIds, credit_account_ids: cIds });
} else {
res = await fetchChessConsolidations(vId);
}
if (isActual()) {
setData(res);
}
} catch (e) {
if (isActual() && e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
if (isActual()) {
setLoading(false);
}
}
};
const loadResData = async (vid: string) => {
setChessResLoading(true);
try {
const res = await fetchChessConsolidationsResult(vid);
setChessResData(res);
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
setChessResLoading(false);
}
};
const download = async () => {
if (!version_id) {
return;
}
setExportLoading(true);
try {
if (debit?.length && credit?.length) {
const { blob, filename } = await exportChessTableManual(version_id, { debit_account_ids: debit, credit_account_ids: credit });
saveBlob(blob, filename ?? exportFilename(card?.company_group.name ?? 'ГК', card?.year ?? 0, card?.quarter ?? ''));
} else {
const { blob, filename } = await exportChessTableAll(version_id);
saveBlob(blob, filename ?? exportFilename(card?.company_group.name ?? 'ГК', card?.year ?? 0, card?.quarter ?? ''));
}
} catch (e) {
if (e instanceof ApiError) {
notification.error({ description: e.message });
}
} finally {
setExportLoading(false);
}
};
const filteredData = useMemo(() => {
if (!data) return null;
if (companies.length === 0) return data;
const filteredCompanies = data.companies.filter((c) => companies.includes(c.id));
const filteredCells = data.cells.filter((c) => companies.includes(c.company_a_id) && companies.includes(c.company_b_id));
return { companies: filteredCompanies, cells: filteredCells };
}, [data, companies]);
useEffect(() => {
if (!version_id) return;
let isMounted = true;
const isActual = () => isMounted;
loadData(version_id, debit, credit, isActual);
return () => {
isMounted = false;
};
}, [version_id, debit, credit]);
useEffect(() => {
if (!version_id) return;
loadResData(version_id);
}, [version_id]);
return (
<ConfigProvider getPopupContainer={() => (document.fullscreenElement as HTMLElement) || document.body}>
<div
style={{
display: 'flex',
flexDirection: 'column',
overflowY: 'auto',
overflowX: 'hidden',
gap: 24,
paddingBottom: 32,
width: '100%',
}}>
<div
ref={chessTableRef}
style={{
backgroundColor: isChessFullscreen ? '#fff' : 'transparent',
padding: isChessFullscreen ? 6 : 0,
height: isChessFullscreen ? '100vh' : 'auto',
overflow: isChessFullscreen ? 'hidden' : 'visible',
display: 'flex',
flexDirection: 'column',
}}>
<ConsolidationChessTable
data={filteredData}
loading={loading}
decimals={decimals}
units={units}
isFullscreen={isChessFullscreen}
toolbar={
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
flexWrap: 'wrap',
gap: 16,
}}>
<div style={{ display: 'flex', alignItems: 'flex-start', flexWrap: 'wrap', gap: 16, flex: '1 1 auto' }}>
<ConsolidationChessTableFilter
companies={companies}
onCompaniesChange={setCompanies}
debit={debit}
onDebitChange={setDebit}
credit={credit}
onCreditChange={setCredit}
allCompanies={data?.companies || []}
/>
<div
style={{
width: 1,
height: 50,
backgroundColor: '#e5e7eb',
}}
/>
<ConsolidationChessFormatTableFilter
decimals={decimals}
units={units}
onDecimalsChange={setDecimals}
onUnitsChange={setUnits}
/>
</div>
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
<Button loading={expoerLoading} onClick={() => download()} icon={<DownloadOutlined />}>
Экспорт
</Button>
<Button
onClick={() => toggleFullscreen(chessTableRef, setIsChessFullscreen)}
icon={isChessFullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}>
{isChessFullscreen ? 'Свернуть' : 'На весь экран'}
</Button>
</div>
</div>
}
/>
</div>
<div
ref={resTableRef}
style={{
backgroundColor: isResFullscreen ? '#fff' : 'transparent',
padding: isResFullscreen ? 6 : 0,
height: isResFullscreen ? '100vh' : 'auto',
overflow: isResFullscreen ? 'hidden' : 'visible',
display: 'flex',
flexDirection: 'column',
}}>
<ConsolidationChessResTable
data={chessResData}
loading={chessResLoading}
decimals={chessResDecimals}
units={chessResUnits}
isFullscreen={isResFullscreen}
toolbar={
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
flexWrap: 'wrap',
gap: 16,
}}>
<div style={{ display: 'flex', alignItems: 'flex-start', flexWrap: 'wrap', gap: 16, flex: '1 1 auto' }}>
<ConsolidationChessFormatTableFilter
decimals={chessResDecimals}
units={chessResUnits}
onDecimalsChange={setChessResDecimals}
onUnitsChange={setChessResUnits}
/>
</div>
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
<Button
onClick={() => toggleFullscreen(resTableRef, setIsResFullscreen)}
icon={isResFullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}>
{isResFullscreen ? 'Свернуть' : 'На весь экран'}
</Button>
</div>
</div>
}
/>
</div>
</div>
</ConfigProvider>
);
};