diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-15 14:41:01 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-15 14:41:01 +0000 |
| commit | 4ee8b24cfadf47452807fa2af801385ed60ab47c (patch) | |
| tree | e1d1fb029f0cf5519c517494bf9a545505c35700 /lib/tbe-last/vendor/vendor-pr-items-dialog.tsx | |
| parent | 265859d691a01cdcaaf9154f93c38765bc34df06 (diff) | |
(대표님) 작업사항 - rfqLast, tbeLast, pdfTron, userAuth
Diffstat (limited to 'lib/tbe-last/vendor/vendor-pr-items-dialog.tsx')
| -rw-r--r-- | lib/tbe-last/vendor/vendor-pr-items-dialog.tsx | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/lib/tbe-last/vendor/vendor-pr-items-dialog.tsx b/lib/tbe-last/vendor/vendor-pr-items-dialog.tsx new file mode 100644 index 00000000..e4b03e6d --- /dev/null +++ b/lib/tbe-last/vendor/vendor-pr-items-dialog.tsx @@ -0,0 +1,253 @@ +// lib/vendor-rfq-response/vendor-tbe-table/vendor-pr-items-dialog.tsx + +"use client" + +import * as React from "react" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription +} from "@/components/ui/dialog" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { ScrollArea } from "@/components/ui/scroll-area" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { formatDate } from "@/lib/utils" +import { Download, Package, AlertCircle } from "lucide-react" +import { toast } from "sonner" +import { exportDataToExcel } from "@/lib/export-to-excel" +import { getVendorPrItems } from "../vendor-tbe-service" + +interface VendorPrItemsDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + rfqId: number | null +} + +interface PrItem { + id: number + prNo: string + prItem: string + materialCode: string + materialDescription: string + size?: string + quantity: number + uom: string + deliveryDate?: string + majorYn: boolean + specifications?: string + remarks?: string +} + +export function VendorPrItemsDialog({ + open, + onOpenChange, + rfqId +}: VendorPrItemsDialogProps) { + + const [prItems, setPrItems] = React.useState<PrItem[]>([]) + const [isLoading, setIsLoading] = React.useState(false) + + // Load PR items when dialog opens + React.useEffect(() => { + if (open && rfqId) { + loadPrItems() + } + }, [open, rfqId]) + + const loadPrItems = async () => { + if (!rfqId) return + + setIsLoading(true) + try { + const data = await getVendorPrItems(rfqId) + + setPrItems(data) + + } catch (error) { + console.error("Failed to load PR items:", error) + toast.error("Error loading PR items") + } finally { + setIsLoading(false) + } + } + + // Export to Excel + const handleExport = async () => { + if (prItems.length === 0) { + toast.error("No items to export") + return + } + + try { + // Prepare data for export + const exportData = prItems.map(item => ({ + "PR No": item.prNo || "-", + "PR Item": item.prItem || "-", + "Material Code": item.materialCode || "-", + "Description": item.materialDescription || "-", + "Size": item.size || "-", + "Quantity": item.quantity, + "Unit": item.uom || "-", + "Delivery Date": item.deliveryDate ? formatDate(item.deliveryDate, "KR") : "-", + "Major Item": item.majorYn ? "Yes" : "No", + "Specifications": item.specifications || "-", + "Remarks": item.remarks || "-" + })) + + // Export using new utility + await exportDataToExcel(exportData, { + filename: `pr-items-${rfqId}`, + sheetName: "PR Items", + autoFilter: true, + freezeHeader: true + }) + + toast.success("Excel file exported successfully") + } catch (error) { + console.error("Export error:", error) + toast.error("Failed to export Excel file") + } + } + + // Statistics + const statistics = React.useMemo(() => { + const totalItems = prItems.length + const majorItems = prItems.filter(item => item.majorYn).length + const minorItems = totalItems - majorItems + + return { totalItems, majorItems, minorItems } + }, [prItems]) + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="max-w-6xl max-h-[80vh]"> + <DialogHeader> + <div className="flex items-center justify-between"> + <div> + <DialogTitle>Purchase Request Items</DialogTitle> + <DialogDescription> + RFQ에 포함된 구매 요청 아이템 목록 + </DialogDescription> + </div> + <Button + variant="outline" + size="sm" + onClick={handleExport} + disabled={prItems.length === 0} + > + <Download className="h-4 w-4 mr-2" /> + Export + </Button> + </div> + </DialogHeader> + + {/* Statistics */} + <div className="flex items-center gap-4 py-2"> + <Badge variant="outline" className="flex items-center gap-1"> + <Package className="h-3 w-3" /> + Total: {statistics.totalItems} + </Badge> + <Badge variant="default" className="flex items-center gap-1"> + <AlertCircle className="h-3 w-3" /> + Major: {statistics.majorItems} + </Badge> + <Badge variant="secondary"> + Minor: {statistics.minorItems} + </Badge> + </div> + + {/* PR Items Table */} + {isLoading ? ( + <div className="p-8 text-center">Loading PR items...</div> + ) : prItems.length === 0 ? ( + <div className="p-8 text-center text-muted-foreground"> + No PR items available + </div> + ) : ( + <ScrollArea className="h-[400px]"> + <Table> + <TableHeader> + <TableRow> + <TableHead className="w-[100px]">PR No</TableHead> + <TableHead className="w-[80px]">Item</TableHead> + <TableHead className="w-[120px]">Material Code</TableHead> + <TableHead>Description</TableHead> + <TableHead className="w-[80px]">Size</TableHead> + <TableHead className="w-[80px] text-right">Qty</TableHead> + <TableHead className="w-[60px]">Unit</TableHead> + <TableHead className="w-[100px]">Delivery</TableHead> + <TableHead className="w-[80px] text-center">Major</TableHead> + </TableRow> + </TableHeader> + <TableBody> + {prItems.map((item) => ( + <TableRow key={item.id}> + <TableCell className="font-medium">{item.prNo || "-"}</TableCell> + <TableCell>{item.prItem || "-"}</TableCell> + <TableCell> + <span className="font-mono text-xs">{item.materialCode || "-"}</span> + </TableCell> + <TableCell> + <div> + <p className="text-sm">{item.materialDescription || "-"}</p> + {item.remarks && ( + <p className="text-xs text-muted-foreground mt-1"> + {item.remarks} + </p> + )} + </div> + </TableCell> + <TableCell>{item.size || "-"}</TableCell> + <TableCell className="text-right font-medium"> + {item.quantity.toLocaleString()} + </TableCell> + <TableCell>{item.uom || "-"}</TableCell> + <TableCell> + {item.deliveryDate ? ( + <span className="text-sm"> + {formatDate(item.deliveryDate, "KR")} + </span> + ) : ( + <span className="text-muted-foreground">-</span> + )} + </TableCell> + <TableCell className="text-center"> + {item.majorYn ? ( + <Badge variant="default" className="text-xs"> + Major + </Badge> + ) : ( + <Badge variant="outline" className="text-xs"> + Minor + </Badge> + )} + </TableCell> + </TableRow> + ))} + </TableBody> + </Table> + </ScrollArea> + )} + + {/* Footer Note */} + <div className="mt-4 p-3 bg-muted/50 rounded-lg"> + <p className="text-xs text-muted-foreground"> + <strong>Note:</strong> Major items은 기술 평가의 주요 대상이며, + 모든 기술 요구사항을 충족해야 합니다. + 각 아이템의 세부 사양은 RFQ 문서를 참조해주세요. + </p> + </div> + </DialogContent> + </Dialog> + ) +}
\ No newline at end of file |
