296 lines
9.9 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 { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { ApiError } from '../../../api/client';
import { ConsolidationFormatTableFilter } from '../../../components/ConsolidationFormatTableFilter/ConsolidationFormatTableFilter';
import {
exportChessConsolidationsResult,
exportChessTableAll,
exportChessTableManual,
fetchChessConsolidations,
fetchChessConsolidationsManual,
fetchChessConsolidationsResult,
} from '../../../services/api/intercompanyMatrix';
import type { IntercompanyMatrixResponse, IntercompanyMatrixResultResponse } from '../../../types/api';
import type { VersionCard } from '../../../types/consolidations/version_members';
import { chessMatrixMode } from '../../../utils/chessMatrixMode';
import { saveBlob, xlsxFilename } from '../../../utils/download';
import { useNotificationApi } from '../../../utils/useNotification';
import { ConsolidationChessResTable } from '../ConsolidationChessResTable/ConsolidationChessResTable';
import { ConsolidationChessTable } from '../ConsolidationChessTable/ConsolidationChessTable';
import { ConsolidationChessTableFilter } from '../ConsolidationChessTableFilter/ConsolidationChessTableFilter';
import { useFullscreen } from '../../../hooks/useFullScreen';
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 [matrixExportLoading, setMatrixExportLoading] = useState(false);
const [resExportLoading, setResExportLoading] = 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 {
ref: chessTableRef,
isFullscreen: isChessFullscreen,
toggle: toggleChessFullscreen,
} = useFullscreen<HTMLDivElement>();
const {
ref: resTableRef,
isFullscreen: isResFullscreen,
toggle: toggleResFullscreen,
} = useFullscreen<HTMLDivElement>();
const matrixMode = chessMatrixMode(debit, credit);
const isHalfFiltered = matrixMode === 'half';
const loadData = useCallback(
async (vId: string, dIds: string[], cIds: string[], isActual: () => boolean) => {
setLoading(true);
try {
let res: IntercompanyMatrixResponse;
if (chessMatrixMode(dIds, cIds) === 'manual') {
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);
}
}
},
[notification],
);
const loadResData = useCallback(
async (vid: string) => {
setChessResLoading(true);
try {
const res = await fetchChessConsolidationsResult(vid);
setChessResData(res);
} catch (e) {
notification.error({ description: e instanceof Error ? e.message : 'Ошибка при получении данных' });
} finally {
setChessResLoading(false);
}
},
[notification],
);
const versionInfo = card ? { groupName: card.company_group.name, year: card.year, quarter: card.quarter } : null;
const download = async () => {
if (!version_id) {
return;
}
setMatrixExportLoading(true);
try {
if (matrixMode === 'manual') {
const { blob, filename } = await exportChessTableManual(version_id, { debit_account_ids: debit, credit_account_ids: credit });
saveBlob(blob, filename ?? xlsxFilename('Шахматка', versionInfo));
} else {
const { blob, filename } = await exportChessTableAll(version_id);
saveBlob(blob, filename ?? xlsxFilename('Шахматка', versionInfo));
}
} catch (e) {
notification.error({ description: e instanceof Error ? e.message : 'Ошибка при экспорте данных' });
} finally {
setMatrixExportLoading(false);
}
};
const downloadResTable = async () => {
if (!version_id) {
return;
}
setResExportLoading(true);
try {
const { blob, filename } = await exportChessConsolidationsResult(version_id);
saveBlob(blob, filename ?? xlsxFilename('Результирующая_таблица_ВГО', versionInfo));
} catch (e) {
notification.error({ description: e instanceof Error ? e.message : 'Ошибка при экспорте данных' });
} finally {
setResExportLoading(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 || isHalfFiltered) return;
let isMounted = true;
const isActual = () => isMounted;
loadData(version_id, debit, credit, isActual);
return () => {
isMounted = false;
setLoading(false);
};
}, [version_id, debit, credit, isHalfFiltered, loadData]);
useEffect(() => {
if (!version_id) return;
loadResData(version_id);
}, [version_id, loadResData]);
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}
isHalfFiltered={isHalfFiltered}
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 || []}
year={card?.year}
quarter={card?.quarter}
/>
<div
style={{
width: 1,
height: 50,
backgroundColor: '#e5e7eb',
}}
/>
<ConsolidationFormatTableFilter
decimals={decimals}
units={units}
onDecimalsChange={setDecimals}
onUnitsChange={setUnits}
/>
</div>
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
<Button disabled={isHalfFiltered} loading={matrixExportLoading} onClick={() => download()} icon={<DownloadOutlined />}>
Экспорт
</Button>
<Button
onClick={() => toggleChessFullscreen()}
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' }}>
<ConsolidationFormatTableFilter
decimals={chessResDecimals}
units={chessResUnits}
onDecimalsChange={setChessResDecimals}
onUnitsChange={setChessResUnits}
/>
</div>
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
<Button loading={resExportLoading} onClick={() => downloadResTable()} icon={<DownloadOutlined />}>
Экспорт
</Button>
<Button
onClick={() => toggleResFullscreen()}
icon={isResFullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}>
{isResFullscreen ? 'Свернуть' : 'На весь экран'}
</Button>
</div>
</div>
}
/>
</div>
</div>
</ConfigProvider>
);
};