- /juil/ 경로를 /construction/으로 변경 - 컴포넌트 폴더명 juil → construction 변경 - 컴포넌트명 Juil* → Construction* 변경 - 테스트 URL 페이지 경로 업데이트 - claudedocs 문서 경로 업데이트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
183 lines
7.3 KiB
TypeScript
183 lines
7.3 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { Plus, X } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import type { EstimateSummaryItem } from '../types';
|
|
import { formatAmount } from '../utils';
|
|
|
|
interface EstimateSummarySectionProps {
|
|
summaryItems: EstimateSummaryItem[];
|
|
summaryMemo: string;
|
|
isViewMode: boolean;
|
|
onAddItem: () => void;
|
|
onRemoveItem: (id: string) => void;
|
|
onItemChange: (id: string, field: keyof EstimateSummaryItem, value: string | number) => void;
|
|
onMemoChange: (memo: string) => void;
|
|
}
|
|
|
|
export function EstimateSummarySection({
|
|
summaryItems,
|
|
summaryMemo,
|
|
isViewMode,
|
|
onAddItem,
|
|
onRemoveItem,
|
|
onItemChange,
|
|
onMemoChange,
|
|
}: EstimateSummarySectionProps) {
|
|
return (
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
<CardTitle className="text-lg">견적 요약 정보</CardTitle>
|
|
{!isViewMode && (
|
|
<Button type="button" variant="outline" size="sm" onClick={onAddItem}>
|
|
<Plus className="h-4 w-4 mr-1" />
|
|
추가
|
|
</Button>
|
|
)}
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="overflow-x-auto">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead className="w-[200px]">명칭</TableHead>
|
|
<TableHead className="w-[80px] text-center">수량</TableHead>
|
|
<TableHead className="w-[80px] text-center">단위</TableHead>
|
|
<TableHead className="w-[120px] text-right">재료비</TableHead>
|
|
<TableHead className="w-[120px] text-right">노무비</TableHead>
|
|
<TableHead className="w-[120px] text-right">합계</TableHead>
|
|
<TableHead className="w-[150px]">비고</TableHead>
|
|
{!isViewMode && <TableHead className="w-[60px] text-center">삭제</TableHead>}
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{summaryItems.length === 0 ? (
|
|
<TableRow>
|
|
<TableCell colSpan={isViewMode ? 7 : 8} className="text-center text-gray-500 py-8">
|
|
등록된 항목이 없습니다.
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
summaryItems.map((item) => (
|
|
<TableRow key={item.id}>
|
|
<TableCell>
|
|
<Input
|
|
value={item.name}
|
|
onChange={(e) => onItemChange(item.id, 'name', e.target.value)}
|
|
disabled={isViewMode}
|
|
className={isViewMode ? 'bg-gray-50' : 'bg-white'}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
type="number"
|
|
value={item.quantity}
|
|
onChange={(e) => onItemChange(item.id, 'quantity', Number(e.target.value))}
|
|
disabled={isViewMode}
|
|
className={`text-center ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
value={item.unit}
|
|
onChange={(e) => onItemChange(item.id, 'unit', e.target.value)}
|
|
disabled={isViewMode}
|
|
className={`text-center ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
type="number"
|
|
value={item.materialCost}
|
|
onChange={(e) => onItemChange(item.id, 'materialCost', Number(e.target.value))}
|
|
disabled={isViewMode}
|
|
className={`text-right ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
type="number"
|
|
value={item.laborCost}
|
|
onChange={(e) => onItemChange(item.id, 'laborCost', Number(e.target.value))}
|
|
disabled={isViewMode}
|
|
className={`text-right ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
|
/>
|
|
</TableCell>
|
|
<TableCell className="text-right font-medium">
|
|
{formatAmount(item.totalCost)}
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
value={item.remarks}
|
|
onChange={(e) => onItemChange(item.id, 'remarks', e.target.value)}
|
|
disabled={isViewMode}
|
|
className={isViewMode ? 'bg-gray-50' : 'bg-white'}
|
|
/>
|
|
</TableCell>
|
|
{!isViewMode && (
|
|
<TableCell className="text-center">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="h-8 w-8 text-red-500 hover:text-red-600"
|
|
onClick={() => onRemoveItem(item.id)}
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</Button>
|
|
</TableCell>
|
|
)}
|
|
</TableRow>
|
|
))
|
|
)}
|
|
{/* 합계 행 */}
|
|
{summaryItems.length > 0 && (
|
|
<TableRow className="bg-gray-50 font-medium">
|
|
<TableCell colSpan={3} className="text-center">
|
|
합계
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{formatAmount(summaryItems.reduce((sum, item) => sum + item.materialCost, 0))}
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{formatAmount(summaryItems.reduce((sum, item) => sum + item.laborCost, 0))}
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{formatAmount(summaryItems.reduce((sum, item) => sum + item.totalCost, 0))}
|
|
</TableCell>
|
|
<TableCell colSpan={isViewMode ? 1 : 2}></TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
|
|
{/* 메모 입력 */}
|
|
<div className="mt-4 space-y-2">
|
|
<Label className="text-sm font-medium text-gray-700">메모</Label>
|
|
<Textarea
|
|
value={summaryMemo}
|
|
onChange={(e) => onMemoChange(e.target.value)}
|
|
placeholder="견적 관련 메모를 입력하세요"
|
|
disabled={isViewMode}
|
|
className={isViewMode ? 'bg-gray-50' : 'bg-white'}
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
} |