- /juil/ 경로를 /construction/으로 변경 - 컴포넌트 폴더명 juil → construction 변경 - 컴포넌트명 Juil* → Construction* 변경 - 테스트 URL 페이지 경로 업데이트 - claudedocs 문서 경로 업데이트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
601 lines
30 KiB
TypeScript
601 lines
30 KiB
TypeScript
'use client';
|
||
|
||
import React from 'react';
|
||
import { X, HelpCircle } from 'lucide-react';
|
||
import {
|
||
Tooltip,
|
||
TooltipContent,
|
||
TooltipProvider,
|
||
TooltipTrigger,
|
||
} from '@/components/ui/tooltip';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Input } from '@/components/ui/input';
|
||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue,
|
||
} from '@/components/ui/select';
|
||
import {
|
||
Table,
|
||
TableBody,
|
||
TableCell,
|
||
TableHead,
|
||
TableHeader,
|
||
TableRow,
|
||
} from '@/components/ui/table';
|
||
import type { EstimateDetailItem } from '../types';
|
||
import { formatAmount, MOCK_MATERIALS } from '../utils';
|
||
import { calculateItemValuesWithApplied, calculateTotalsWithApplied } from '../hooks/useEstimateCalculations';
|
||
|
||
// 계산식 정보
|
||
const FORMULA_INFO: Record<string, string> = {
|
||
weight: '면적 × 25',
|
||
area: '(가로 × 0.16) × (세로 × 0.5)',
|
||
steelScreen: '면적 × 47,500',
|
||
caulking: '(세로 × 4) × 조정단가',
|
||
rail: '(세로 × 0.2) × 조정단가',
|
||
bottom: '가로 × 조정단가',
|
||
boxReinforce: '가로 × 조정단가',
|
||
shaft: '가로 × 조정단가',
|
||
unitPrice: '철제스크린 + 코킹 + 레일 + 하장 + 박스보강 + 샤프트 + 도장 + 모터 + 제어기 + 가로시공비 + 세로시공비',
|
||
expense: '단가 × 공과율',
|
||
cost: '단가 + 공과',
|
||
costExecution: '원가 × 수량',
|
||
marginCost: '원가 × 마진율(1.03)',
|
||
marginCostExecution: '마진원가 × 수량',
|
||
expenseExecution: '공과 × 수량',
|
||
};
|
||
|
||
// 계산식 툴팁이 있는 헤더 컴포넌트
|
||
function FormulaHeader({ label, formulaKey, className }: { label: string; formulaKey: string; className?: string }) {
|
||
const formula = FORMULA_INFO[formulaKey];
|
||
if (!formula) {
|
||
return <span>{label}</span>;
|
||
}
|
||
return (
|
||
<TooltipProvider delayDuration={0}>
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<span className={`inline-flex items-center gap-1 cursor-help ${className || ''}`}>
|
||
{label}
|
||
<HelpCircle className="h-3.5 w-3.5 text-gray-400 hover:text-gray-600" />
|
||
</span>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="top" className="max-w-xs">
|
||
<p className="text-xs font-medium text-gray-700">{label} 계산식</p>
|
||
<p className="text-xs text-gray-500 mt-1">{formula}</p>
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
</TooltipProvider>
|
||
);
|
||
}
|
||
|
||
// appliedPrices 타입 정의
|
||
export interface AppliedPrices {
|
||
caulking: number;
|
||
rail: number;
|
||
bottom: number;
|
||
boxReinforce: number;
|
||
shaft: number;
|
||
painting: number;
|
||
motor: number;
|
||
controller: number;
|
||
}
|
||
|
||
interface EstimateDetailTableSectionProps {
|
||
detailItems: EstimateDetailItem[];
|
||
appliedPrices: AppliedPrices | null;
|
||
isViewMode: boolean;
|
||
onAddItems: (count: number) => void;
|
||
onRemoveItem: (id: string) => void;
|
||
onRemoveSelected: () => void;
|
||
onItemChange: (id: string, field: keyof EstimateDetailItem, value: string | number) => void;
|
||
onSelectItem: (id: string, selected: boolean) => void;
|
||
onSelectAll: (selected: boolean) => void;
|
||
onApplyAdjustedPrice: () => void;
|
||
onReset: () => void;
|
||
}
|
||
|
||
export function EstimateDetailTableSection({
|
||
detailItems,
|
||
appliedPrices,
|
||
isViewMode,
|
||
onAddItems,
|
||
onRemoveItem,
|
||
onRemoveSelected,
|
||
onItemChange,
|
||
onSelectItem,
|
||
onSelectAll,
|
||
onApplyAdjustedPrice,
|
||
onReset,
|
||
}: EstimateDetailTableSectionProps) {
|
||
const selectedCount = detailItems.filter((item) => (item as unknown as { selected?: boolean }).selected).length;
|
||
const allSelected = detailItems.length > 0 && detailItems.every((item) => (item as unknown as { selected?: boolean }).selected);
|
||
const totals = calculateTotalsWithApplied(detailItems, appliedPrices);
|
||
|
||
return (
|
||
<Card>
|
||
<CardHeader className="flex flex-row items-center justify-between pb-4">
|
||
<div className="flex items-center gap-4">
|
||
<CardTitle className="text-lg whitespace-nowrap">견적 상세</CardTitle>
|
||
{!isViewMode && (
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-sm text-gray-600">{selectedCount}건 선택</span>
|
||
<Button
|
||
type="button"
|
||
variant="default"
|
||
size="sm"
|
||
className="bg-gray-900 hover:bg-gray-800"
|
||
onClick={onRemoveSelected}
|
||
>
|
||
삭제
|
||
</Button>
|
||
<Button
|
||
type="button"
|
||
variant="default"
|
||
size="sm"
|
||
className="bg-blue-500 hover:bg-blue-600"
|
||
onClick={onApplyAdjustedPrice}
|
||
>
|
||
조정 단가 적용
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
{!isViewMode && (
|
||
<div className="flex items-center gap-2">
|
||
<Input
|
||
type="number"
|
||
min={1}
|
||
defaultValue={1}
|
||
className="w-16 text-center"
|
||
id="detail-add-count"
|
||
/>
|
||
<Button
|
||
type="button"
|
||
variant="default"
|
||
size="sm"
|
||
className="bg-gray-900 hover:bg-gray-800"
|
||
onClick={() => {
|
||
const countInput = document.getElementById('detail-add-count') as HTMLInputElement;
|
||
const count = Math.max(1, parseInt(countInput?.value || '1', 10));
|
||
onAddItems(count);
|
||
}}
|
||
>
|
||
추가
|
||
</Button>
|
||
{/* TODO: 견적 상세 기획서 수정 후 초기화 버튼 및 테이블 항목/데이터 재작업 필요
|
||
<Button type="button" variant="outline" size="sm" onClick={onReset}>
|
||
초기화
|
||
</Button>
|
||
*/}
|
||
</div>
|
||
)}
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="overflow-x-auto max-h-[600px]">
|
||
<Table>
|
||
<TableHeader className="sticky top-0 bg-white z-10">
|
||
<TableRow className="bg-gray-100">
|
||
{!isViewMode && (
|
||
<TableHead className="w-[40px] text-center sticky left-0 bg-gray-100 z-20">
|
||
<input
|
||
type="checkbox"
|
||
className="h-4 w-4 rounded border-gray-300"
|
||
checked={allSelected}
|
||
onChange={(e) => onSelectAll(e.target.checked)}
|
||
/>
|
||
</TableHead>
|
||
)}
|
||
<TableHead className="w-[100px] text-center">명칭</TableHead>
|
||
<TableHead className="w-[80px] text-center">제품</TableHead>
|
||
<TableHead className="w-[70px] text-right">가로</TableHead>
|
||
<TableHead className="w-[70px] text-right">세로</TableHead>
|
||
<TableHead className="w-[70px] text-right">
|
||
<FormulaHeader label="무게" formulaKey="weight" />
|
||
</TableHead>
|
||
<TableHead className="w-[70px] text-right">
|
||
<FormulaHeader label="면적" formulaKey="area" />
|
||
</TableHead>
|
||
<TableHead className="w-[90px] text-right">
|
||
<FormulaHeader label="철제,스크린" formulaKey="steelScreen" />
|
||
</TableHead>
|
||
<TableHead className="w-[80px] text-right">
|
||
<FormulaHeader label="코킹" formulaKey="caulking" />
|
||
</TableHead>
|
||
<TableHead className="w-[80px] text-right">
|
||
<FormulaHeader label="레일" formulaKey="rail" />
|
||
</TableHead>
|
||
<TableHead className="w-[80px] text-right">
|
||
<FormulaHeader label="하장" formulaKey="bottom" />
|
||
</TableHead>
|
||
<TableHead className="w-[90px] text-right">
|
||
<FormulaHeader label="박스+보강" formulaKey="boxReinforce" />
|
||
</TableHead>
|
||
<TableHead className="w-[80px] text-right">
|
||
<FormulaHeader label="샤프트" formulaKey="shaft" />
|
||
</TableHead>
|
||
<TableHead className="w-[80px] text-center">도장</TableHead>
|
||
<TableHead className="w-[80px] text-center">모터</TableHead>
|
||
<TableHead className="w-[80px] text-center">제어기</TableHead>
|
||
<TableHead className="w-[100px] text-center">가로시공비</TableHead>
|
||
<TableHead className="w-[100px] text-center">세로시공비</TableHead>
|
||
<TableHead className="w-[90px] text-right">
|
||
<FormulaHeader label="단가" formulaKey="unitPrice" />
|
||
</TableHead>
|
||
<TableHead className="w-[70px] text-right">공과율</TableHead>
|
||
<TableHead className="w-[80px] text-right">
|
||
<FormulaHeader label="공과" formulaKey="expense" />
|
||
</TableHead>
|
||
<TableHead className="w-[50px] text-right">수량</TableHead>
|
||
<TableHead className="w-[90px] text-right">
|
||
<FormulaHeader label="원가" formulaKey="cost" />
|
||
</TableHead>
|
||
<TableHead className="w-[90px] text-right">
|
||
<FormulaHeader label="원가실행" formulaKey="costExecution" />
|
||
</TableHead>
|
||
<TableHead className="w-[90px] text-right">
|
||
<FormulaHeader label="마진원가" formulaKey="marginCost" />
|
||
</TableHead>
|
||
<TableHead className="w-[100px] text-right">
|
||
<FormulaHeader label="마진원가실행" formulaKey="marginCostExecution" />
|
||
</TableHead>
|
||
<TableHead className="w-[90px] text-right">
|
||
<FormulaHeader label="공과실행" formulaKey="expenseExecution" />
|
||
</TableHead>
|
||
{!isViewMode && <TableHead className="w-[50px] text-center">삭제</TableHead>}
|
||
</TableRow>
|
||
</TableHeader>
|
||
<TableBody>
|
||
{detailItems.length === 0 ? (
|
||
<TableRow>
|
||
<TableCell
|
||
colSpan={isViewMode ? 27 : 29}
|
||
className="text-center text-gray-500 py-8"
|
||
>
|
||
등록된 항목이 없습니다.
|
||
</TableCell>
|
||
</TableRow>
|
||
) : (
|
||
<>
|
||
{detailItems.map((item) => {
|
||
const values = calculateItemValuesWithApplied(item, appliedPrices);
|
||
|
||
return (
|
||
<TableRow key={item.id}>
|
||
{!isViewMode && (
|
||
<TableCell className="text-center sticky left-0 bg-white">
|
||
<input
|
||
type="checkbox"
|
||
className="h-4 w-4 rounded border-gray-300"
|
||
checked={(item as unknown as { selected?: boolean }).selected || false}
|
||
onChange={(e) => onSelectItem(item.id, e.target.checked)}
|
||
/>
|
||
</TableCell>
|
||
)}
|
||
{/* 01: 명칭 */}
|
||
<TableCell>
|
||
<Input
|
||
value={item.name}
|
||
onChange={(e) => onItemChange(item.id, 'name', e.target.value)}
|
||
disabled={isViewMode}
|
||
className={`w-full min-w-[80px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 02: 제품 */}
|
||
<TableCell>
|
||
<Select
|
||
value={item.material}
|
||
onValueChange={(val) => onItemChange(item.id, 'material', val)}
|
||
disabled={isViewMode}
|
||
>
|
||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||
<SelectValue placeholder="선택" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{MOCK_MATERIALS.map((option) => (
|
||
<SelectItem key={option.value} value={option.value}>
|
||
{option.label}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
</TableCell>
|
||
{/* 03: 가로 */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
step="0.01"
|
||
value={item.width}
|
||
onChange={(e) => onItemChange(item.id, 'width', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[60px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 04: 세로 */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
step="0.01"
|
||
value={item.height}
|
||
onChange={(e) => onItemChange(item.id, 'height', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[60px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 05: 무게 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
step="0.01"
|
||
value={values.weight.toFixed(2)}
|
||
onChange={(e) => onItemChange(item.id, 'calcWeight', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[60px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 06: 면적 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
step="0.01"
|
||
value={values.area.toFixed(2)}
|
||
onChange={(e) => onItemChange(item.id, 'calcArea', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[60px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 07: 철제,스크린 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.steelScreen}
|
||
onChange={(e) => onItemChange(item.id, 'calcSteelScreen', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[80px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 08: 코킹 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.caulking}
|
||
onChange={(e) => onItemChange(item.id, 'calcCaulking', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 09: 레일 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.rail}
|
||
onChange={(e) => onItemChange(item.id, 'calcRail', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 10: 하장 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.bottom}
|
||
onChange={(e) => onItemChange(item.id, 'calcBottom', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 11: 박스+보강 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.boxReinforce}
|
||
onChange={(e) => onItemChange(item.id, 'calcBoxReinforce', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[80px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 12: 샤프트 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.shaft}
|
||
onChange={(e) => onItemChange(item.id, 'calcShaft', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 13: 도장 */}
|
||
<TableCell>
|
||
<Select
|
||
value={String(item.coating || '')}
|
||
onValueChange={(val) => onItemChange(item.id, 'coating', Number(val))}
|
||
disabled={isViewMode}
|
||
>
|
||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||
<SelectValue placeholder="선택" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="0">직접입력</SelectItem>
|
||
<SelectItem value="50000">도장A</SelectItem>
|
||
<SelectItem value="80000">도장B</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</TableCell>
|
||
{/* 14: 모터 */}
|
||
<TableCell>
|
||
<Select
|
||
value={String(item.mounting || '300000')}
|
||
onValueChange={(val) => onItemChange(item.id, 'mounting', Number(val))}
|
||
disabled={isViewMode}
|
||
>
|
||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||
<SelectValue placeholder="선택" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="300000">모터 300,000</SelectItem>
|
||
<SelectItem value="500000">모터 500,000</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</TableCell>
|
||
{/* 15: 제어기 */}
|
||
<TableCell>
|
||
<Select
|
||
value={String(item.controller || '')}
|
||
onValueChange={(val) => onItemChange(item.id, 'controller', Number(val))}
|
||
disabled={isViewMode}
|
||
>
|
||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||
<SelectValue placeholder="선택" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="150000">제어기 150,000</SelectItem>
|
||
<SelectItem value="250000">제어기 250,000</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</TableCell>
|
||
{/* 16: 가로시공비 */}
|
||
<TableCell>
|
||
<Select
|
||
value={String(item.widthConstruction || '')}
|
||
onValueChange={(val) => onItemChange(item.id, 'widthConstruction', Number(val))}
|
||
disabled={isViewMode}
|
||
>
|
||
<SelectTrigger className={`w-full min-w-[90px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||
<SelectValue placeholder="선택" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="300000">3.01~4.0M</SelectItem>
|
||
<SelectItem value="400000">4.01~5.0M</SelectItem>
|
||
<SelectItem value="500000">5.01~6.0M</SelectItem>
|
||
<SelectItem value="600000">6.01~7.0M</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</TableCell>
|
||
{/* 17: 세로시공비 */}
|
||
<TableCell>
|
||
<Select
|
||
value={String(item.heightConstruction || '')}
|
||
onValueChange={(val) => onItemChange(item.id, 'heightConstruction', Number(val))}
|
||
disabled={isViewMode}
|
||
>
|
||
<SelectTrigger className={`w-full min-w-[90px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||
<SelectValue placeholder="선택" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="5000">3.51~4.5M</SelectItem>
|
||
<SelectItem value="8000">4.51~5.5M</SelectItem>
|
||
<SelectItem value="10000">5.51~6.5M</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</TableCell>
|
||
{/* 18: 단가 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.unitPrice}
|
||
onChange={(e) => onItemChange(item.id, 'calcUnitPrice', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[80px] font-medium ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 19: 공과율 */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
step="0.01"
|
||
value={item.expense}
|
||
onChange={(e) => onItemChange(item.id, 'expense', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[60px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 20: 공과 (인풋, 계산값 표시 + 수정 가능) */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
value={values.expense}
|
||
onChange={(e) => onItemChange(item.id, 'calcExpense', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 21: 수량 */}
|
||
<TableCell>
|
||
<Input
|
||
type="number"
|
||
min={1}
|
||
value={item.quantity}
|
||
onChange={(e) => onItemChange(item.id, 'quantity', Number(e.target.value))}
|
||
disabled={isViewMode}
|
||
className={`text-right min-w-[40px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}
|
||
/>
|
||
</TableCell>
|
||
{/* 22: 원가 */}
|
||
<TableCell className="text-right bg-gray-50">{formatAmount(values.cost)}</TableCell>
|
||
{/* 23: 원가실행 */}
|
||
<TableCell className="text-right bg-gray-50">{formatAmount(values.costExecution)}</TableCell>
|
||
{/* 24: 마진원가 */}
|
||
<TableCell className="text-right bg-gray-50">{formatAmount(values.marginCost)}</TableCell>
|
||
{/* 25: 마진원가실행 */}
|
||
<TableCell className="text-right bg-gray-50 font-medium">{formatAmount(values.marginCostExecution)}</TableCell>
|
||
{/* 26: 공과실행 */}
|
||
<TableCell className="text-right bg-gray-50">{formatAmount(values.expenseExecution)}</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>
|
||
);
|
||
})}
|
||
{/* 합계 행 */}
|
||
{detailItems.length > 0 && (
|
||
<TableRow className="bg-orange-50 font-medium border-t-2 border-orange-300">
|
||
{!isViewMode && <TableCell className="sticky left-0 bg-orange-50"></TableCell>}
|
||
<TableCell colSpan={4} className="text-center font-bold">합계</TableCell>
|
||
<TableCell className="text-right">{totals.weight.toFixed(2)}</TableCell>
|
||
<TableCell className="text-right">{totals.area.toFixed(2)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.steelScreen)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.caulking)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.rail)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.bottom)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.boxReinforce)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.shaft)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.painting)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.motor)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.controller)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.widthConstruction)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.heightConstruction)}</TableCell>
|
||
<TableCell className="text-right font-bold">{formatAmount(totals.unitPrice)}</TableCell>
|
||
<TableCell className="text-right">-</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.expense)}</TableCell>
|
||
<TableCell className="text-right">{totals.quantity}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.cost)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.costExecution)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.marginCost)}</TableCell>
|
||
<TableCell className="text-right font-bold">{formatAmount(totals.marginCostExecution)}</TableCell>
|
||
<TableCell className="text-right">{formatAmount(totals.expenseExecution)}</TableCell>
|
||
{!isViewMode && <TableCell></TableCell>}
|
||
</TableRow>
|
||
)}
|
||
</>
|
||
)}
|
||
</TableBody>
|
||
</Table>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
} |