summaryrefslogtreecommitdiff
path: root/lib/general-contracts/detail/general-contract-items-table.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/general-contracts/detail/general-contract-items-table.tsx')
-rw-r--r--lib/general-contracts/detail/general-contract-items-table.tsx117
1 files changed, 94 insertions, 23 deletions
diff --git a/lib/general-contracts/detail/general-contract-items-table.tsx b/lib/general-contracts/detail/general-contract-items-table.tsx
index e5fc6cf2..4f74cfbb 100644
--- a/lib/general-contracts/detail/general-contract-items-table.tsx
+++ b/lib/general-contracts/detail/general-contract-items-table.tsx
@@ -32,6 +32,7 @@ import { MaterialGroupSelectorDialogSingle } from '@/components/common/material/
import { MaterialSearchItem } from '@/lib/material/material-group-service'
import { ProcurementItemSelectorDialogSingle } from '@/components/common/selectors/procurement-item/procurement-item-selector-dialog-single'
import { ProcurementSearchItem } from '@/components/common/selectors/procurement-item/procurement-item-service'
+import { cn } from '@/lib/utils'
interface ContractItem {
id?: number
@@ -43,12 +44,12 @@ interface ContractItem {
materialGroupCode?: string
materialGroupDescription?: string
specification: string
- quantity: number
+ quantity: number | string // number | string으로 변경하여 입력 중 포맷팅 지원
quantityUnit: string
- totalWeight: number
+ totalWeight: number | string // number | string으로 변경하여 입력 중 포맷팅 지원
weightUnit: string
contractDeliveryDate: string
- contractUnitPrice: number
+ contractUnitPrice: number | string // number | string으로 변경하여 입력 중 포맷팅 지원
contractAmount: number
contractCurrency: string
isSelected?: boolean
@@ -105,6 +106,34 @@ export function ContractItemsTable({
contractUnitPrice: ''
})
+ // 천단위 콤마 포맷팅 헬퍼 함수들
+ const formatNumberWithCommas = (value: string | number | null | undefined): string => {
+ if (value === null || value === undefined || value === '') return ''
+ const str = value.toString()
+ const parts = str.split('.')
+ const integerPart = parts[0].replace(/,/g, '')
+
+ // 정수부가 비어있거나 '-' 만 있는 경우 처리
+ if (integerPart === '' || integerPart === '-') {
+ return str
+ }
+
+ const num = parseFloat(integerPart)
+ if (isNaN(num)) return str
+
+ const formattedInt = num.toLocaleString()
+
+ if (parts.length > 1) {
+ return `${formattedInt}.${parts[1]}`
+ }
+
+ return formattedInt
+ }
+
+ const parseNumberFromCommas = (value: string): string => {
+ return value.replace(/,/g, '')
+ }
+
// 초기 데이터 로드
React.useEffect(() => {
const loadItems = async () => {
@@ -125,6 +154,8 @@ export function ContractItemsTable({
}
}
+ // number 타입을 string으로 변환하지 않고 일단 그대로 둠 (렌더링 시 포맷팅)
+ // 단, 입력 중 편의를 위해 string이 들어올 수 있으므로 ContractItem 타입 변경함
return {
id: item.id,
projectId: item.projectId || null,
@@ -174,8 +205,17 @@ export function ContractItemsTable({
// validation 체크
const errors: string[] = []
- for (let index = 0; index < localItems.length; index++) {
- const item = localItems[index]
+ // 저장 시 number로 변환된 데이터 준비
+ const itemsToSave = localItems.map(item => ({
+ ...item,
+ quantity: parseFloat(item.quantity.toString().replace(/,/g, '')) || 0,
+ totalWeight: parseFloat(item.totalWeight.toString().replace(/,/g, '')) || 0,
+ contractUnitPrice: parseFloat(item.contractUnitPrice.toString().replace(/,/g, '')) || 0,
+ contractAmount: parseFloat(item.contractAmount.toString().replace(/,/g, '')) || 0,
+ }));
+
+ for (let index = 0; index < itemsToSave.length; index++) {
+ const item = itemsToSave[index]
// if (!item.itemCode) errors.push(`${index + 1}번째 품목의 품목코드`)
if (!item.itemInfo) errors.push(`${index + 1}번째 품목의 Item 정보`)
if (!item.quantity || item.quantity <= 0) errors.push(`${index + 1}번째 품목의 수량`)
@@ -188,7 +228,7 @@ export function ContractItemsTable({
return
}
- await updateContractItems(contractId, localItems as any)
+ await updateContractItems(contractId, itemsToSave as any)
toast.success('품목정보가 저장되었습니다.')
} catch (error) {
console.error('Error saving contract items:', error)
@@ -199,9 +239,18 @@ export function ContractItemsTable({
}
// 총 금액 계산
- const totalAmount = localItems.reduce((sum, item) => sum + item.contractAmount, 0)
- const totalQuantity = localItems.reduce((sum, item) => sum + item.quantity, 0)
- const totalUnitPrice = localItems.reduce((sum, item) => sum + item.contractUnitPrice, 0)
+ const totalAmount = localItems.reduce((sum, item) => {
+ const amount = parseFloat(item.contractAmount.toString().replace(/,/g, '')) || 0
+ return sum + amount
+ }, 0)
+ const totalQuantity = localItems.reduce((sum, item) => {
+ const quantity = parseFloat(item.quantity.toString().replace(/,/g, '')) || 0
+ return sum + quantity
+ }, 0)
+ const totalUnitPrice = localItems.reduce((sum, item) => {
+ const unitPrice = parseFloat(item.contractUnitPrice.toString().replace(/,/g, '')) || 0
+ return sum + unitPrice
+ }, 0)
const amountDifference = availableBudget - totalAmount
const budgetRatio = availableBudget > 0 ? (totalAmount / availableBudget) * 100 : 0
@@ -213,12 +262,14 @@ export function ContractItemsTable({
// 아이템 업데이트
const updateItem = (index: number, field: keyof ContractItem, value: string | number | boolean | undefined) => {
const updatedItems = [...localItems]
- updatedItems[index] = { ...updatedItems[index], [field]: value }
+ const updatedItem = { ...updatedItems[index], [field]: value }
+ updatedItems[index] = updatedItem
// 단가나 수량이 변경되면 금액 자동 계산
if (field === 'contractUnitPrice' || field === 'quantity') {
- const item = updatedItems[index]
- updatedItems[index].contractAmount = item.contractUnitPrice * item.quantity
+ const quantity = parseFloat(updatedItem.quantity.toString().replace(/,/g, '')) || 0
+ const unitPrice = parseFloat(updatedItem.contractUnitPrice.toString().replace(/,/g, '')) || 0
+ updatedItem.contractAmount = unitPrice * quantity
}
setLocalItems(updatedItems)
@@ -326,7 +377,8 @@ export function ContractItemsTable({
if (batchInputData.contractUnitPrice) {
updatedItem.contractUnitPrice = parseFloat(batchInputData.contractUnitPrice) || 0
// 단가가 변경되면 계약금액도 재계산
- updatedItem.contractAmount = updatedItem.contractUnitPrice * updatedItem.quantity
+ const quantity = parseFloat(updatedItem.quantity.toString().replace(/,/g, '')) || 0
+ updatedItem.contractAmount = (parseFloat(batchInputData.contractUnitPrice) || 0) * quantity
}
return updatedItem
@@ -712,14 +764,23 @@ export function ContractItemsTable({
)}
</TableCell> */}
<TableCell className="px-3 py-3">
+ {readOnly ? (
+ <span className="text-sm text-right">{item.quantity.toLocaleString()}</span>
+ ) : (
<Input
- type="number"
- value={item.quantity}
- onChange={(e) => updateItem(index, 'quantity', parseFloat(e.target.value) || 0)}
+ type="text"
+ value={formatNumberWithCommas(item.quantity)}
+ onChange={(e) => {
+ const val = parseNumberFromCommas(e.target.value)
+ if (val === '' || /^-?\d*\.?\d*$/.test(val)) {
+ updateItem(index, 'quantity', val)
+ }
+ }}
className="h-8 text-sm text-right"
placeholder="0"
- disabled={!isEnabled}
+ disabled={!isEnabled || isQuantityDisabled}
/>
+ )}
</TableCell>
<TableCell className="px-3 py-3">
{readOnly ? (
@@ -748,9 +809,14 @@ export function ContractItemsTable({
<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)}
+ type="text"
+ value={formatNumberWithCommas(item.totalWeight)}
+ onChange={(e) => {
+ const val = parseNumberFromCommas(e.target.value)
+ if (val === '' || /^-?\d*\.?\d*$/.test(val)) {
+ updateItem(index, 'totalWeight', val)
+ }
+ }}
className="h-8 text-sm text-right"
placeholder="0"
disabled={!isEnabled || isQuantityDisabled}
@@ -797,9 +863,14 @@ export function ContractItemsTable({
<span className="text-sm text-right">{item.contractUnitPrice.toLocaleString()}</span>
) : (
<Input
- type="number"
- value={item.contractUnitPrice}
- onChange={(e) => updateItem(index, 'contractUnitPrice', parseFloat(e.target.value) || 0)}
+ type="text"
+ value={formatNumberWithCommas(item.contractUnitPrice)}
+ onChange={(e) => {
+ const val = parseNumberFromCommas(e.target.value)
+ if (val === '' || /^-?\d*\.?\d*$/.test(val)) {
+ updateItem(index, 'contractUnitPrice', val)
+ }
+ }}
className="h-8 text-sm text-right"
placeholder="0"
disabled={!isEnabled}