summaryrefslogtreecommitdiff
path: root/lib/general-contracts/detail/general-contract-storage-info.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/general-contracts/detail/general-contract-storage-info.tsx')
-rw-r--r--lib/general-contracts/detail/general-contract-storage-info.tsx249
1 files changed, 249 insertions, 0 deletions
diff --git a/lib/general-contracts/detail/general-contract-storage-info.tsx b/lib/general-contracts/detail/general-contract-storage-info.tsx
new file mode 100644
index 00000000..2c9b230c
--- /dev/null
+++ b/lib/general-contracts/detail/general-contract-storage-info.tsx
@@ -0,0 +1,249 @@
+'use client'
+
+import React, { useState, useEffect } from 'react'
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+import { Input } from '@/components/ui/input'
+import { Button } from '@/components/ui/button'
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
+import { Save, Plus, Trash2, LoaderIcon } from 'lucide-react'
+import { toast } from 'sonner'
+import { useSession } from 'next-auth/react'
+import { getStorageInfo, saveStorageInfo } from '../service'
+
+interface StorageInfoItem {
+ id?: number
+ poNumber: string
+ hullNumber: string
+ remainingAmount: number
+}
+
+interface ContractStorageInfoProps {
+ contractId: number
+ readOnly?: boolean
+}
+
+export function ContractStorageInfo({ contractId, readOnly = false }: ContractStorageInfoProps) {
+ const session = useSession()
+ const userId = session.data?.user?.id ? Number(session.data.user.id) : null
+ const [isLoading, setIsLoading] = useState(false)
+ const [isSaving, setIsSaving] = useState(false)
+ const [items, setItems] = useState<StorageInfoItem[]>([])
+
+ // 데이터 로드
+ useEffect(() => {
+ const loadStorageInfo = async () => {
+ setIsLoading(true)
+ try {
+ const data = await getStorageInfo(contractId)
+ setItems(data || [])
+ } catch (error) {
+ console.error('Error loading storage info:', error)
+ toast.error('임치계약 정보를 불러오는 중 오류가 발생했습니다.')
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ if (contractId) {
+ loadStorageInfo()
+ }
+ }, [contractId])
+
+ // 행 추가
+ const addRow = () => {
+ setItems(prev => [...prev, {
+ poNumber: '',
+ hullNumber: '',
+ remainingAmount: 0
+ }])
+ }
+
+ // 행 삭제
+ const deleteRow = (index: number) => {
+ setItems(prev => prev.filter((_, i) => i !== index))
+ }
+
+ // 항목 업데이트
+ const updateItem = (index: number, field: keyof StorageInfoItem, value: string | number) => {
+ setItems(prev => prev.map((item, i) =>
+ i === index ? { ...item, [field]: value } : item
+ ))
+ }
+
+ // 저장
+ const handleSave = async () => {
+ if (!userId) {
+ toast.error('사용자 정보를 찾을 수 없습니다.')
+ return
+ }
+
+ // 유효성 검사
+ const errors: string[] = []
+ items.forEach((item, index) => {
+ if (!item.poNumber.trim()) errors.push(`${index + 1}번째 행의 PO No.`)
+ if (!item.hullNumber.trim()) errors.push(`${index + 1}번째 행의 호선`)
+ if (item.remainingAmount < 0) errors.push(`${index + 1}번째 행의 미입고 잔여금액`)
+ })
+
+ if (errors.length > 0) {
+ toast.error(`다음 항목을 확인해주세요: ${errors.join(', ')}`)
+ return
+ }
+
+ setIsSaving(true)
+ try {
+ await saveStorageInfo(contractId, items, userId)
+ toast.success('임치계약 정보가 저장되었습니다.')
+ } catch (error) {
+ console.error('Error saving storage info:', error)
+ toast.error('임치계약 정보 저장 중 오류가 발생했습니다.')
+ } finally {
+ setIsSaving(false)
+ }
+ }
+
+ if (isLoading) {
+ return (
+ <Card>
+ <CardHeader>
+ <CardTitle>임치(물품보관)계약 상세 정보</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <div className="flex items-center justify-center py-8">
+ <LoaderIcon className="w-6 h-6 animate-spin mr-2" />
+ <span>로딩 중...</span>
+ </div>
+ </CardContent>
+ </Card>
+ )
+ }
+
+ return (
+ <Card>
+ <CardHeader>
+ <div className="flex items-center justify-between">
+ <CardTitle>임치(물품보관)계약 상세 정보</CardTitle>
+ {!readOnly && (
+ <div className="flex items-center gap-2">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={addRow}
+ >
+ <Plus className="w-4 h-4 mr-2" />
+ 행 추가
+ </Button>
+ <Button
+ onClick={handleSave}
+ disabled={isSaving}
+ >
+ {isSaving ? (
+ <>
+ <LoaderIcon className="w-4 h-4 mr-2 animate-spin" />
+ 저장 중...
+ </>
+ ) : (
+ <>
+ <Save className="w-4 h-4 mr-2" />
+ 저장
+ </>
+ )}
+ </Button>
+ </div>
+ )}
+ </div>
+ </CardHeader>
+ <CardContent>
+ {items.length === 0 ? (
+ <div className="text-center py-8 text-muted-foreground">
+ <p>등록된 정보가 없습니다.</p>
+ {!readOnly && (
+ <Button
+ variant="outline"
+ className="mt-4"
+ onClick={addRow}
+ >
+ <Plus className="w-4 h-4 mr-2" />
+ 정보 추가
+ </Button>
+ )}
+ </div>
+ ) : (
+ <div className="space-y-4">
+ <Table>
+ <TableHeader>
+ <TableRow>
+ <TableHead className="w-12">번호</TableHead>
+ <TableHead>PO No.</TableHead>
+ <TableHead>호선</TableHead>
+ <TableHead className="text-right">미입고 잔여금액</TableHead>
+ {!readOnly && <TableHead className="w-20">삭제</TableHead>}
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {items.map((item, index) => (
+ <TableRow key={index}>
+ <TableCell>{index + 1}</TableCell>
+ <TableCell>
+ {readOnly ? (
+ <span className="text-sm">{item.poNumber || '-'}</span>
+ ) : (
+ <Input
+ value={item.poNumber}
+ onChange={(e) => updateItem(index, 'poNumber', e.target.value)}
+ placeholder="PO No. 입력"
+ className="h-8 text-sm"
+ />
+ )}
+ </TableCell>
+ <TableCell>
+ {readOnly ? (
+ <span className="text-sm">{item.hullNumber || '-'}</span>
+ ) : (
+ <Input
+ value={item.hullNumber}
+ onChange={(e) => updateItem(index, 'hullNumber', e.target.value)}
+ placeholder="호선 입력"
+ className="h-8 text-sm"
+ />
+ )}
+ </TableCell>
+ <TableCell>
+ {readOnly ? (
+ <span className="text-sm text-right block">
+ {item.remainingAmount.toLocaleString()}
+ </span>
+ ) : (
+ <Input
+ type="number"
+ value={item.remainingAmount}
+ onChange={(e) => updateItem(index, 'remainingAmount', parseFloat(e.target.value) || 0)}
+ placeholder="0"
+ className="h-8 text-sm text-right"
+ min="0"
+ />
+ )}
+ </TableCell>
+ {!readOnly && (
+ <TableCell>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => deleteRow(index)}
+ className="text-red-600 hover:text-red-700"
+ >
+ <Trash2 className="w-4 h-4" />
+ </Button>
+ </TableCell>
+ )}
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </div>
+ )}
+ </CardContent>
+ </Card>
+ )
+}
+