Files
sam-react-prod/src/components/business/construction/estimates/sections/ExpenseDetailSection.tsx
유병철 0db6302652 refactor(WEB): 코드 품질 개선 및 불필요 코드 제거
- 미사용 import/변수/console.log 대량 정리 (100+개 파일)
- ItemMasterContext 간소화 (미사용 로직 제거)
- IntegratedListTemplateV2 / UniversalListPage 개선
- 결재 컴포넌트(ApprovalBox, DraftBox, ReferenceBox) 정리
- HR 컴포넌트(급여/휴가/부서) 코드 간소화
- globals.css 스타일 정리 및 개선
- AuthenticatedLayout 개선
- middleware CSP 정리
- proxy route 불필요 로깅 제거

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:55:11 +09:00

197 lines
7.0 KiB
TypeScript

'use client';
import React, { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { QuantityInput } from '@/components/ui/quantity-input';
import { CurrencyInput } from '@/components/ui/currency-input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import type { ExpenseItem } from '../types';
import { formatAmount } from '../utils';
// 공과 옵션 타입
interface ExpenseOption {
value: string;
label: string;
}
interface ExpenseDetailSectionProps {
expenseItems: ExpenseItem[];
expenseOptions: ExpenseOption[]; // 공과 품목 옵션 (Items API에서 조회)
isViewMode: boolean;
onAddItems: (count: number) => void;
onRemoveSelected: () => void;
onItemChange: (id: string, field: keyof ExpenseItem, value: string | number) => void;
onSelectItem: (id: string, selected: boolean) => void;
onSelectAll: (selected: boolean) => void;
}
export function ExpenseDetailSection({
expenseItems,
expenseOptions,
isViewMode,
onAddItems,
onRemoveSelected,
onItemChange,
onSelectItem,
onSelectAll,
}: ExpenseDetailSectionProps) {
const [expenseAddCount, setExpenseAddCount] = useState<number | undefined>(1);
const selectedCount = expenseItems.filter((item) => item.selected).length;
const allSelected = expenseItems.length > 0 && expenseItems.every((item) => item.selected);
return (
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-4">
<div className="flex items-center gap-4">
<CardTitle className="text-lg"> </CardTitle>
<span className="text-sm text-muted-foreground"> {expenseItems.length}</span>
{!isViewMode && selectedCount > 0 && (
<>
<span className="text-sm text-muted-foreground">/</span>
<span className="text-sm text-primary font-medium">{selectedCount} </span>
<Button
type="button"
variant="default"
size="sm"
className="bg-gray-900 hover:bg-gray-800"
onClick={onRemoveSelected}
>
</Button>
</>
)}
</div>
{!isViewMode && (
<div className="flex items-center gap-2">
<QuantityInput
min={1}
value={expenseAddCount}
onChange={(val) => setExpenseAddCount(val)}
className="w-16 text-center"
id="expense-add-count"
/>
<Button
type="button"
variant="default"
size="sm"
className="bg-gray-900 hover:bg-gray-800"
onClick={() => {
const count = Math.max(1, expenseAddCount ?? 1);
onAddItems(count);
}}
>
</Button>
</div>
)}
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow className="bg-gray-100">
{!isViewMode && (
<TableHead className="w-[50px] text-center">
<input
type="checkbox"
className="h-4 w-4 rounded border-gray-300"
checked={allSelected}
onChange={(e) => onSelectAll(e.target.checked)}
/>
</TableHead>
)}
<TableHead className="text-center"></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{expenseItems.length === 0 ? (
<TableRow>
<TableCell
colSpan={isViewMode ? 2 : 3}
className="text-center text-gray-500 py-8"
>
.
</TableCell>
</TableRow>
) : (
expenseItems.map((item) => (
<TableRow key={item.id}>
{!isViewMode && (
<TableCell className="text-center">
<input
type="checkbox"
className="h-4 w-4 rounded border-gray-300"
checked={item.selected || false}
onChange={(e) => onSelectItem(item.id, e.target.checked)}
/>
</TableCell>
)}
<TableCell>
<Select
value={item.name}
onValueChange={(val) => onItemChange(item.id, 'name', val)}
disabled={isViewMode}
>
<SelectTrigger className={isViewMode ? 'bg-gray-50' : 'bg-white'}>
<SelectValue placeholder="공과 선택" />
</SelectTrigger>
<SelectContent>
{expenseOptions.length === 0 ? (
<SelectItem value="_empty" disabled>
</SelectItem>
) : (
expenseOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))
)}
</SelectContent>
</Select>
</TableCell>
<TableCell>
<CurrencyInput
value={item.amount}
onChange={(value) => onItemChange(item.id, 'amount', value ?? 0)}
disabled={isViewMode}
className={`text-right ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
/>
</TableCell>
</TableRow>
))
)}
{/* 합계 행 */}
{expenseItems.length > 0 && (
<TableRow className="bg-gray-50 font-medium">
<TableCell colSpan={isViewMode ? 1 : 2} className="text-center">
</TableCell>
<TableCell className="text-right">
{formatAmount(expenseItems.reduce((sum, item) => sum + item.amount, 0))}
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
);
}