diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-19 07:51:27 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-19 07:51:27 +0000 |
| commit | 9ecdfb23fe3df6a5df86782385002c562dfc1198 (patch) | |
| tree | 4188cb7e6bf2c862d9c86a59d79946bd41217227 /lib/general-contracts/detail | |
| parent | b67861fbb424c7ad47ad1538f75e2945bd8890c5 (diff) | |
(대표님) rfq 히스토리, swp 등
Diffstat (limited to 'lib/general-contracts/detail')
| -rw-r--r-- | lib/general-contracts/detail/general-contract-detail.tsx | 1 | ||||
| -rw-r--r-- | lib/general-contracts/detail/general-contract-items-table.tsx | 139 |
2 files changed, 94 insertions, 46 deletions
diff --git a/lib/general-contracts/detail/general-contract-detail.tsx b/lib/general-contracts/detail/general-contract-detail.tsx index 9d9f35bd..8e7a7aff 100644 --- a/lib/general-contracts/detail/general-contract-detail.tsx +++ b/lib/general-contracts/detail/general-contract-detail.tsx @@ -149,7 +149,6 @@ export default function ContractDetailPage() { items={[]} onItemsChange={() => {}} onTotalAmountChange={() => {}} - currency="USD" availableBudget={0} readOnly={contract?.contractScope === '단가' || contract?.contractScope === '물량(실적)'} /> diff --git a/lib/general-contracts/detail/general-contract-items-table.tsx b/lib/general-contracts/detail/general-contract-items-table.tsx index 5176c6ce..1b9a1a06 100644 --- a/lib/general-contracts/detail/general-contract-items-table.tsx +++ b/lib/general-contracts/detail/general-contract-items-table.tsx @@ -7,6 +7,7 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Table, TableBody, @@ -26,12 +27,13 @@ import { Save, LoaderIcon } from 'lucide-react' interface ContractItem { id?: number - project: string itemCode: string itemInfo: string specification: string quantity: number quantityUnit: string + totalWeight: number + weightUnit: string contractDeliveryDate: string contractUnitPrice: number contractAmount: number @@ -45,22 +47,27 @@ interface ContractItemsTableProps { items: ContractItem[] onItemsChange: (items: ContractItem[]) => void onTotalAmountChange: (total: number) => void - currency?: string availableBudget?: number readOnly?: boolean } +// 통화 목록 +const CURRENCIES = ["USD", "EUR", "KRW", "JPY", "CNY"]; + +// 수량 단위 목록 +const QUANTITY_UNITS = ["KG", "TON", "EA", "M", "M2", "M3", "L", "ML", "G", "SET", "PCS"]; + +// 중량 단위 목록 +const WEIGHT_UNITS = ["KG", "TON", "G", "LB", "OZ"]; + export function ContractItemsTable({ contractId, items, onItemsChange, onTotalAmountChange, - currency = 'USD', availableBudget = 0, readOnly = false }: ContractItemsTableProps) { - // 통화 코드가 null이거나 undefined일 때 기본값 설정 - const safeCurrency = currency || 'USD' const [localItems, setLocalItems] = React.useState<ContractItem[]>(items) const [isSaving, setIsSaving] = React.useState(false) const [isLoading, setIsLoading] = React.useState(false) @@ -74,16 +81,17 @@ export function ContractItemsTable({ const fetchedItems = await getContractItems(contractId) const formattedItems = fetchedItems.map(item => ({ id: item.id, - project: item.project || '', itemCode: item.itemCode || '', itemInfo: item.itemInfo || '', specification: item.specification || '', quantity: Number(item.quantity) || 0, - quantityUnit: item.quantityUnit || 'KG', + quantityUnit: item.quantityUnit || 'EA', + totalWeight: Number(item.totalWeight) || 0, + weightUnit: item.weightUnit || 'KG', contractDeliveryDate: item.contractDeliveryDate || '', contractUnitPrice: Number(item.contractUnitPrice) || 0, contractAmount: Number(item.contractAmount) || 0, - contractCurrency: item.contractCurrency || safeCurrency, + contractCurrency: item.contractCurrency || 'KRW', isSelected: false })) as ContractItem[] setLocalItems(formattedItems as ContractItem[]) @@ -99,7 +107,7 @@ export function ContractItemsTable({ } loadItems() - }, [contractId, currency, onItemsChange]) + }, [contractId, onItemsChange]) // 로컬 상태와 부모 상태 동기화 (초기 로드 후에는 부모 상태 우선) React.useEffect(() => { @@ -116,10 +124,8 @@ export function ContractItemsTable({ const errors: string[] = [] for (let index = 0; index < localItems.length; index++) { const item = localItems[index] - if (!item.project) errors.push(`${index + 1}번째 품목의 프로젝트`) if (!item.itemCode) errors.push(`${index + 1}번째 품목의 품목코드`) if (!item.itemInfo) errors.push(`${index + 1}번째 품목의 Item 정보`) - if (!item.specification) errors.push(`${index + 1}번째 품목의 사양`) if (!item.quantity || item.quantity <= 0) errors.push(`${index + 1}번째 품목의 수량`) if (!item.contractUnitPrice || item.contractUnitPrice <= 0) errors.push(`${index + 1}번째 품목의 단가`) if (!item.contractDeliveryDate) errors.push(`${index + 1}번째 품목의 납기일`) @@ -170,16 +176,17 @@ export function ContractItemsTable({ // 행 추가 const addRow = () => { const newItem: ContractItem = { - project: '', itemCode: '', itemInfo: '', specification: '', quantity: 0, - quantityUnit: 'KG', + quantityUnit: 'EA', // 기본 수량 단위 + totalWeight: 0, + weightUnit: 'KG', // 기본 중량 단위 contractDeliveryDate: '', contractUnitPrice: 0, contractAmount: 0, - contractCurrency: safeCurrency, + contractCurrency: 'KRW', // 기본 통화 isSelected: false } const updatedItems = [...localItems, newItem] @@ -213,10 +220,10 @@ export function ContractItemsTable({ // 통화 포맷팅 - const formatCurrency = (amount: number) => { + const formatCurrency = (amount: number, currency: string = 'KRW') => { return new Intl.NumberFormat('ko-KR', { style: 'currency', - currency: safeCurrency, + currency: currency, }).format(amount) } @@ -270,7 +277,7 @@ export function ContractItemsTable({ <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> - <span className="text-sm text-gray-600">총 금액: {totalAmount.toLocaleString()} {currency}</span> + <span className="text-sm text-gray-600">총 금액: {formatCurrency(totalAmount, localItems[0]?.contractCurrency || 'KRW')}</span> <span className="text-sm text-gray-600">총 수량: {totalQuantity.toLocaleString()}</span> </div> {!readOnly && ( @@ -316,19 +323,19 @@ export function ContractItemsTable({ <div className="space-y-1"> <Label className="text-sm font-medium">총 계약금액</Label> <div className="text-lg font-bold text-primary"> - {formatCurrency(totalAmount)} + {formatCurrency(totalAmount, localItems[0]?.contractCurrency || 'KRW')} </div> </div> <div className="space-y-1"> <Label className="text-sm font-medium">가용예산</Label> <div className="text-lg font-bold"> - {formatCurrency(availableBudget)} + {formatCurrency(availableBudget, localItems[0]?.contractCurrency || 'KRW')} </div> </div> <div className="space-y-1"> <Label className="text-sm font-medium">가용예산 比 (금액차)</Label> <div className={`text-lg font-bold ${amountDifference >= 0 ? 'text-green-600' : 'text-red-600'}`}> - {formatCurrency(amountDifference)} + {formatCurrency(amountDifference, localItems[0]?.contractCurrency || 'KRW')} </div> </div> <div className="space-y-1"> @@ -357,12 +364,13 @@ export function ContractItemsTable({ /> )} </TableHead> - <TableHead className="px-3 py-3 font-semibold">프로젝트</TableHead> <TableHead className="px-3 py-3 font-semibold">품목코드 (PKG No.)</TableHead> <TableHead className="px-3 py-3 font-semibold">Item 정보 (자재그룹 / 자재코드)</TableHead> <TableHead className="px-3 py-3 font-semibold">규격</TableHead> <TableHead className="px-3 py-3 font-semibold text-right">수량</TableHead> <TableHead className="px-3 py-3 font-semibold">수량단위</TableHead> + <TableHead className="px-3 py-3 font-semibold text-right">총 중량</TableHead> + <TableHead className="px-3 py-3 font-semibold">중량단위</TableHead> <TableHead className="px-3 py-3 font-semibold">계약납기일</TableHead> <TableHead className="px-3 py-3 font-semibold text-right">계약단가</TableHead> <TableHead className="px-3 py-3 font-semibold text-right">계약금액</TableHead> @@ -385,19 +393,6 @@ export function ContractItemsTable({ </TableCell> <TableCell className="px-3 py-3"> {readOnly ? ( - <span className="text-sm">{item.project || '-'}</span> - ) : ( - <Input - value={item.project} - onChange={(e) => updateItem(index, 'project', e.target.value)} - placeholder="프로젝트" - className="h-8 text-sm" - disabled={!isEnabled} - /> - )} - </TableCell> - <TableCell className="px-3 py-3"> - {readOnly ? ( <span className="text-sm">{item.itemCode || '-'}</span> ) : ( <Input @@ -453,17 +448,62 @@ export function ContractItemsTable({ {readOnly ? ( <span className="text-sm">{item.quantityUnit || '-'}</span> ) : ( - <Input + <Select value={item.quantityUnit} - onChange={(e) => updateItem(index, 'quantityUnit', e.target.value)} - placeholder="단위" - className="h-8 text-sm w-16" + onValueChange={(value) => updateItem(index, 'quantityUnit', value)} + disabled={!isEnabled} + > + <SelectTrigger className="h-8 text-sm w-20"> + <SelectValue /> + </SelectTrigger> + <SelectContent> + {QUANTITY_UNITS.map((unit) => ( + <SelectItem key={unit} value={unit}> + {unit} + </SelectItem> + ))} + </SelectContent> + </Select> + )} + </TableCell> + <TableCell className="px-3 py-3"> + {readOnly ? ( + <span className="text-sm text-right">{item.totalWeight.toLocaleString()}</span> + ) : ( + <Input + type="number" + value={item.totalWeight} + onChange={(e) => updateItem(index, 'totalWeight', parseFloat(e.target.value) || 0)} + className="h-8 text-sm text-right" + placeholder="0" disabled={!isEnabled} /> )} </TableCell> <TableCell className="px-3 py-3"> {readOnly ? ( + <span className="text-sm">{item.weightUnit || '-'}</span> + ) : ( + <Select + value={item.weightUnit} + onValueChange={(value) => updateItem(index, 'weightUnit', value)} + disabled={!isEnabled} + > + <SelectTrigger className="h-8 text-sm w-20"> + <SelectValue /> + </SelectTrigger> + <SelectContent> + {WEIGHT_UNITS.map((unit) => ( + <SelectItem key={unit} value={unit}> + {unit} + </SelectItem> + ))} + </SelectContent> + </Select> + )} + </TableCell> + <TableCell className="px-3 py-3"> + {readOnly ? ( <span className="text-sm">{item.contractDeliveryDate || '-'}</span> ) : ( <Input @@ -498,13 +538,22 @@ export function ContractItemsTable({ {readOnly ? ( <span className="text-sm">{item.contractCurrency || '-'}</span> ) : ( - <Input + <Select value={item.contractCurrency} - onChange={(e) => updateItem(index, 'contractCurrency', e.target.value)} - placeholder="통화" - className="h-8 text-sm w-16" + onValueChange={(value) => updateItem(index, 'contractCurrency', value)} disabled={!isEnabled} - /> + > + <SelectTrigger className="h-8 text-sm w-20"> + <SelectValue /> + </SelectTrigger> + <SelectContent> + {CURRENCIES.map((currency) => ( + <SelectItem key={currency} value={currency}> + {currency} + </SelectItem> + ))} + </SelectContent> + </Select> )} </TableCell> </TableRow> @@ -528,14 +577,14 @@ export function ContractItemsTable({ <div className="flex items-center justify-between"> <span className="text-sm font-medium text-muted-foreground">총 단가</span> <span className="text-lg font-semibold"> - {totalUnitPrice.toLocaleString()} {currency} + {formatCurrency(totalUnitPrice, localItems[0]?.contractCurrency || 'KRW')} </span> </div> <div className="border-t pt-4"> <div className="flex items-center justify-between"> <span className="text-xl font-bold text-primary">합계 금액</span> <span className="text-2xl font-bold text-primary"> - {formatCurrency(totalAmount)} + {formatCurrency(totalAmount, localItems[0]?.contractCurrency || 'KRW')} </span> </div> </div> |
