2026-03-20 09:51:18 +09:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
Table,
|
|
|
|
|
TableBody,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableHead,
|
|
|
|
|
TableHeader,
|
|
|
|
|
TableRow,
|
|
|
|
|
} from '@/components/ui/table';
|
|
|
|
|
import { formatNumber } from '@/lib/utils/amount';
|
|
|
|
|
import type { IncomeStatementData } from './types';
|
|
|
|
|
import { HIGHLIGHT_CODES } from './types';
|
|
|
|
|
|
|
|
|
|
interface PeriodViewProps {
|
|
|
|
|
data: IncomeStatementData;
|
|
|
|
|
showPrevious: boolean;
|
|
|
|
|
unitLabel: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function PeriodView({ data, showPrevious, unitLabel }: PeriodViewProps) {
|
|
|
|
|
const { period, sections } = data;
|
|
|
|
|
const colSpan = showPrevious ? 5 : 3; // 과목 + (금액+소계) * 기수
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<div className="rounded-md border overflow-x-auto">
|
|
|
|
|
<div className="min-w-[400px]">
|
|
|
|
|
<Table>
|
|
|
|
|
<TableHeader>
|
|
|
|
|
{/* 1행: 과목 + 기수 헤더 (녹색 배경) */}
|
2026-03-20 10:02:37 +09:00
|
|
|
<TableRow className="bg-green-700 hover:bg-green-700">
|
2026-03-20 09:51:18 +09:00
|
|
|
<TableHead
|
|
|
|
|
rowSpan={2}
|
|
|
|
|
className="font-semibold text-xs md:text-sm text-white w-[40%] border-r border-green-600"
|
|
|
|
|
>
|
|
|
|
|
과 목
|
|
|
|
|
</TableHead>
|
|
|
|
|
<TableHead
|
|
|
|
|
colSpan={2}
|
|
|
|
|
className="font-semibold text-center text-xs md:text-sm text-white border-b border-green-600"
|
|
|
|
|
>
|
|
|
|
|
{period.current.label}
|
|
|
|
|
</TableHead>
|
|
|
|
|
{showPrevious && (
|
|
|
|
|
<TableHead
|
|
|
|
|
colSpan={2}
|
|
|
|
|
className="font-semibold text-center text-xs md:text-sm text-white border-b border-green-600 border-l border-green-600"
|
|
|
|
|
>
|
|
|
|
|
{period.previous.label}
|
|
|
|
|
</TableHead>
|
|
|
|
|
)}
|
|
|
|
|
</TableRow>
|
|
|
|
|
{/* 2행: 금 액 서브헤더 */}
|
2026-03-20 10:02:37 +09:00
|
|
|
<TableRow className="bg-green-700 hover:bg-green-700">
|
2026-03-20 09:51:18 +09:00
|
|
|
<TableHead
|
|
|
|
|
colSpan={2}
|
|
|
|
|
className="font-semibold text-center text-xs md:text-sm text-white"
|
|
|
|
|
>
|
|
|
|
|
금 액
|
|
|
|
|
</TableHead>
|
|
|
|
|
{showPrevious && (
|
|
|
|
|
<TableHead
|
|
|
|
|
colSpan={2}
|
|
|
|
|
className="font-semibold text-center text-xs md:text-sm text-white border-l border-green-600"
|
|
|
|
|
>
|
|
|
|
|
금 액
|
|
|
|
|
</TableHead>
|
|
|
|
|
)}
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
<TableBody>
|
|
|
|
|
{sections.map((section) => {
|
|
|
|
|
const isHighlight = HIGHLIGHT_CODES.includes(section.code);
|
|
|
|
|
const hasItems = section.items.length > 0;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<SectionRows
|
|
|
|
|
key={section.code}
|
|
|
|
|
code={section.code}
|
|
|
|
|
name={section.name}
|
|
|
|
|
currentAmount={section.currentAmount}
|
|
|
|
|
previousAmount={section.previousAmount}
|
|
|
|
|
items={section.items}
|
|
|
|
|
isHighlight={isHighlight}
|
|
|
|
|
hasItems={hasItems}
|
|
|
|
|
showPrevious={showPrevious}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/* 하단 단위 표기 */}
|
|
|
|
|
<div className="text-xs text-muted-foreground text-right px-1">
|
|
|
|
|
(단위: {unitLabel})
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface SectionRowsProps {
|
|
|
|
|
code: string;
|
|
|
|
|
name: string;
|
|
|
|
|
currentAmount: number;
|
|
|
|
|
previousAmount: number;
|
|
|
|
|
items: { code: string; name: string; current: number; previous: number }[];
|
|
|
|
|
isHighlight: boolean;
|
|
|
|
|
hasItems: boolean;
|
|
|
|
|
showPrevious: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function SectionRows({
|
|
|
|
|
code,
|
|
|
|
|
name,
|
|
|
|
|
currentAmount,
|
|
|
|
|
previousAmount,
|
|
|
|
|
items,
|
|
|
|
|
isHighlight,
|
|
|
|
|
hasItems,
|
|
|
|
|
showPrevious,
|
|
|
|
|
}: SectionRowsProps) {
|
|
|
|
|
const highlightClass = isHighlight ? 'bg-green-50' : '';
|
|
|
|
|
|
|
|
|
|
// 계산 항목 (III, V, VIII, X 등) — 소계열에만 금액 표시
|
|
|
|
|
if (!hasItems && isHighlight) {
|
|
|
|
|
return (
|
|
|
|
|
<TableRow className={highlightClass}>
|
|
|
|
|
<TableCell className="text-xs md:text-sm font-semibold">
|
|
|
|
|
{code}. {name}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell />
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm font-bold whitespace-nowrap">
|
|
|
|
|
{formatNumber(currentAmount)}
|
|
|
|
|
</TableCell>
|
|
|
|
|
{showPrevious && (
|
|
|
|
|
<>
|
|
|
|
|
<TableCell />
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm font-bold whitespace-nowrap">
|
|
|
|
|
{formatNumber(previousAmount)}
|
|
|
|
|
</TableCell>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</TableRow>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 세부과목 없는 합계 항목 (VIII 등) — 소계열에만 금액
|
|
|
|
|
if (!hasItems) {
|
|
|
|
|
return (
|
|
|
|
|
<TableRow className={highlightClass}>
|
|
|
|
|
<TableCell className="text-xs md:text-sm font-semibold">
|
|
|
|
|
{code}. {name}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell />
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm font-semibold whitespace-nowrap">
|
|
|
|
|
{currentAmount !== 0 ? formatNumber(currentAmount) : ''}
|
|
|
|
|
</TableCell>
|
|
|
|
|
{showPrevious && (
|
|
|
|
|
<>
|
|
|
|
|
<TableCell />
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm font-semibold whitespace-nowrap">
|
|
|
|
|
{previousAmount !== 0 ? formatNumber(previousAmount) : ''}
|
|
|
|
|
</TableCell>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</TableRow>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 세부 과목이 있는 항목 (I, II, IV, VI, VII, IX)
|
|
|
|
|
const lastIdx = items.length - 1;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{/* 섹션 헤더 — 금액 표시 안 함 */}
|
|
|
|
|
<TableRow>
|
|
|
|
|
<TableCell className="text-xs md:text-sm font-semibold">
|
|
|
|
|
{code}. {name}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell />
|
|
|
|
|
<TableCell />
|
|
|
|
|
{showPrevious && (
|
|
|
|
|
<>
|
|
|
|
|
<TableCell />
|
|
|
|
|
<TableCell />
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</TableRow>
|
|
|
|
|
{/* 세부 과목 행 — 들여쓰기 */}
|
|
|
|
|
{items.map((item, idx) => {
|
|
|
|
|
const isLast = idx === lastIdx;
|
|
|
|
|
return (
|
|
|
|
|
<TableRow key={item.code}>
|
|
|
|
|
<TableCell className="text-xs md:text-sm pl-8 text-muted-foreground">
|
|
|
|
|
{item.name}
|
|
|
|
|
</TableCell>
|
|
|
|
|
{/* 금액 열 */}
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm whitespace-nowrap">
|
|
|
|
|
{item.current !== 0 ? formatNumber(item.current) : ''}
|
|
|
|
|
</TableCell>
|
|
|
|
|
{/* 소계 열 — 마지막 세부항목에만 섹션 합계 표시 */}
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm whitespace-nowrap font-semibold">
|
|
|
|
|
{isLast ? formatNumber(currentAmount) : ''}
|
|
|
|
|
</TableCell>
|
|
|
|
|
{showPrevious && (
|
|
|
|
|
<>
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm whitespace-nowrap">
|
|
|
|
|
{item.previous !== 0 ? formatNumber(item.previous) : ''}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-right text-xs md:text-sm whitespace-nowrap font-semibold">
|
|
|
|
|
{isLast ? formatNumber(previousAmount) : ''}
|
|
|
|
|
</TableCell>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</TableRow>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|