summaryrefslogtreecommitdiff
path: root/lib/po/vendor-table/vendor-po-items-dialog.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-08-26 12:09:39 +0000
committerjoonhoekim <26rote@gmail.com>2025-08-26 12:09:39 +0000
commit1110427907bbe9c11a378da4c1a233b83b5ca3b1 (patch)
tree8bd7ed2ce7ec47a7f05693f5d3afcc22b1bb7e19 /lib/po/vendor-table/vendor-po-items-dialog.tsx
parent5f479f7252a7aa3328bfe186893de8b011e21b15 (diff)
(김준회) 구매정의서 구현 - PO (shi & vendor)
Diffstat (limited to 'lib/po/vendor-table/vendor-po-items-dialog.tsx')
-rw-r--r--lib/po/vendor-table/vendor-po-items-dialog.tsx199
1 files changed, 199 insertions, 0 deletions
diff --git a/lib/po/vendor-table/vendor-po-items-dialog.tsx b/lib/po/vendor-table/vendor-po-items-dialog.tsx
new file mode 100644
index 00000000..d3b33371
--- /dev/null
+++ b/lib/po/vendor-table/vendor-po-items-dialog.tsx
@@ -0,0 +1,199 @@
+"use client"
+
+import * as React from "react"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import { Badge } from "@/components/ui/badge"
+import { Skeleton } from "@/components/ui/skeleton"
+import { VendorPO, VendorPOItem } from "./types"
+import { getVendorPOItemsByContractNo } from "./service"
+
+interface VendorPOItemsDialogProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ po: VendorPO | null
+}
+
+export function VendorPOItemsDialog({ open, onOpenChange, po }: VendorPOItemsDialogProps) {
+ const [items, setItems] = React.useState<VendorPOItem[]>([])
+ const [loading, setLoading] = React.useState(false)
+ const [error, setError] = React.useState<string | null>(null)
+
+ // 상세품목 데이터 로드
+ React.useEffect(() => {
+ if (!open || !po) {
+ setItems([])
+ setError(null)
+ return
+ }
+
+ const loadItems = async () => {
+ setLoading(true)
+ setError(null)
+ try {
+ const vendorPOItems = await getVendorPOItemsByContractNo(po.contractNo)
+ setItems(vendorPOItems)
+ } catch (err) {
+ console.error("Failed to load vendor PO items:", err)
+ setError("상세품목을 불러오는 중 오류가 발생했습니다.")
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ loadItems()
+ }, [open, po])
+
+ if (!po) return null
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-[95vw] max-h-[90vh] w-full flex flex-col">
+ <DialogHeader className="flex-shrink-0">
+ <DialogTitle className="text-lg font-semibold">
+ 상세품목 현황 - {po.contractNo}
+ </DialogTitle>
+ <DialogDescription>
+ {po.contractName} ({items.length}개 품목)
+ </DialogDescription>
+ </DialogHeader>
+
+ <div className="flex-1 overflow-hidden">
+ {loading ? (
+ <div className="space-y-4">
+ <div className="text-center py-4 text-muted-foreground">
+ 상세품목을 불러오는 중...
+ </div>
+ <div className="space-y-2">
+ {Array.from({ length: 3 }).map((_, i) => (
+ <Skeleton key={i} className="h-12 w-full" />
+ ))}
+ </div>
+ </div>
+ ) : error ? (
+ <div className="text-center py-8 text-destructive">
+ {error}
+ </div>
+ ) : items.length === 0 ? (
+ <div className="text-center py-8 text-muted-foreground">
+ 등록된 상세품목이 없습니다.
+ </div>
+ ) : (
+ <div className="overflow-auto max-h-[60vh]">
+ <Table>
+ <TableHeader className="sticky top-0 bg-background z-10">
+ <TableRow>
+ <TableHead className="min-w-[120px] whitespace-nowrap">PO/계약번호</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">품번</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">P/R번호</TableHead>
+ <TableHead className="min-w-[120px] whitespace-nowrap">자재그룹(명)</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">단가기준</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">자재번호</TableHead>
+ <TableHead className="min-w-[200px] whitespace-nowrap">품목/자재내역</TableHead>
+ <TableHead className="min-w-[200px] whitespace-nowrap">자재내역사양</TableHead>
+ <TableHead className="min-w-[120px] whitespace-nowrap">설계자재번호</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">Fitting No.</TableHead>
+ <TableHead className="min-w-[80px] whitespace-nowrap">Cert.</TableHead>
+ <TableHead className="min-w-[80px] whitespace-nowrap">재질</TableHead>
+ <TableHead className="min-w-[150px] whitespace-nowrap">규격</TableHead>
+ <TableHead className="min-w-[80px] text-right whitespace-nowrap">수량</TableHead>
+ <TableHead className="min-w-[80px] whitespace-nowrap">수량단위</TableHead>
+ <TableHead className="min-w-[80px] text-right whitespace-nowrap">중량</TableHead>
+ <TableHead className="min-w-[80px] whitespace-nowrap">중량단위</TableHead>
+ <TableHead className="min-w-[100px] text-right whitespace-nowrap">총중량</TableHead>
+ <TableHead className="min-w-[100px] text-right whitespace-nowrap">단가기준</TableHead>
+ <TableHead className="min-w-[80px] whitespace-nowrap">단가단위</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">가격단위값</TableHead>
+ <TableHead className="min-w-[120px] text-right whitespace-nowrap">PO계약금액</TableHead>
+ <TableHead className="min-w-[100px] text-right whitespace-nowrap">조정금액</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">납기일자</TableHead>
+ <TableHead className="min-w-[80px] whitespace-nowrap">VAT구분</TableHead>
+ <TableHead className="min-w-[120px] whitespace-nowrap">철의장 SPEC</TableHead>
+ <TableHead className="min-w-[100px] whitespace-nowrap">P/R 담당자</TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {items.map((item, index) => (
+ <TableRow key={`${item.contractNo}-${item.itemNo}-${index}`}>
+ <TableCell className="font-medium">{item.contractNo || '-'}</TableCell>
+ <TableCell>{item.itemNo || '-'}</TableCell>
+ <TableCell>{item.prNo || '-'}</TableCell>
+ <TableCell>{item.materialGroup || '-'}</TableCell>
+ <TableCell>{item.priceStandard || '-'}</TableCell>
+ <TableCell className="font-mono text-sm">{item.materialNo || '-'}</TableCell>
+ <TableCell className="max-w-[200px]">
+ <div className="truncate" title={item.itemDescription || ''}>
+ {item.itemDescription || '-'}
+ </div>
+ </TableCell>
+ <TableCell className="max-w-[200px]">
+ <div className="truncate" title={item.materialSpec || ''}>
+ {item.materialSpec || '-'}
+ </div>
+ </TableCell>
+ <TableCell>{item.designMaterialNo || '-'}</TableCell>
+ <TableCell>{item.fittingNo || '-'}</TableCell>
+ <TableCell>{item.cert || '-'}</TableCell>
+ <TableCell>{item.material || '-'}</TableCell>
+ <TableCell>{item.specification || '-'}</TableCell>
+ <TableCell className="text-right font-mono">
+ {item.quantity?.toLocaleString() || '-'}
+ </TableCell>
+ <TableCell>{item.quantityUnit || '-'}</TableCell>
+ <TableCell className="text-right font-mono">
+ {item.weight ? item.weight.toLocaleString() : '-'}
+ </TableCell>
+ <TableCell>{item.weightUnit || '-'}</TableCell>
+ <TableCell className="text-right font-mono">
+ {item.totalWeight ? item.totalWeight.toLocaleString() : '-'}
+ </TableCell>
+ <TableCell className="text-right font-mono">
+ {item.unitPrice?.toLocaleString() || '-'}
+ </TableCell>
+ <TableCell>{item.priceUnit || '-'}</TableCell>
+ <TableCell>{item.priceUnitValue || '-'}</TableCell>
+ <TableCell className="text-right font-mono font-semibold">
+ {item.contractAmount?.toLocaleString() || '-'}
+ </TableCell>
+ <TableCell className="text-right font-mono">
+ {item.adjustmentAmount ? item.adjustmentAmount.toLocaleString() : '-'}
+ </TableCell>
+ <TableCell>{item.deliveryDate || '-'}</TableCell>
+ <TableCell>{item.vatType || '-'}</TableCell>
+ <TableCell>{item.steelSpec || '-'}</TableCell>
+ <TableCell>{item.prManager || '-'}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </div>
+ )}
+ </div>
+
+ {items.length > 0 && (
+ <div className="flex justify-between items-center pt-4 border-t flex-shrink-0">
+ <div className="text-sm text-muted-foreground">
+ 총 {items.length}개 품목
+ </div>
+ <div className="text-sm font-medium">
+ 총 계약금액: {items.reduce((sum, item) => sum + item.contractAmount, 0).toLocaleString()} 원
+ </div>
+ </div>
+ )}
+ </DialogContent>
+ </Dialog>
+ )
+}