summaryrefslogtreecommitdiff
path: root/lib/techsales-rfq/table/rfq-items-view-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/techsales-rfq/table/rfq-items-view-dialog.tsx')
-rw-r--r--lib/techsales-rfq/table/rfq-items-view-dialog.tsx198
1 files changed, 198 insertions, 0 deletions
diff --git a/lib/techsales-rfq/table/rfq-items-view-dialog.tsx b/lib/techsales-rfq/table/rfq-items-view-dialog.tsx
new file mode 100644
index 00000000..10bc9f1f
--- /dev/null
+++ b/lib/techsales-rfq/table/rfq-items-view-dialog.tsx
@@ -0,0 +1,198 @@
+"use client"
+
+import * as React from "react"
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogDescription,
+ DialogFooter,
+} from "@/components/ui/dialog"
+import { Button } from "@/components/ui/button"
+import { Badge } from "@/components/ui/badge"
+import { Package, FileText, X } from "lucide-react"
+import { getTechSalesRfqItems } from "../service"
+
+interface RfqItem {
+ id: number;
+ rfqId: number;
+ itemType: "SHIP" | "TOP" | "HULL";
+ itemCode: string;
+ itemList: string;
+ workType: string;
+ shipType?: string; // 조선용
+ subItemName?: string; // 해양용
+}
+
+interface RfqItemsViewDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ rfq: {
+ id: number;
+ rfqCode?: string;
+ status?: string;
+ description?: string;
+ rfqType?: "SHIP" | "TOP" | "HULL";
+ } | null;
+}
+
+export function RfqItemsViewDialog({
+ open,
+ onOpenChange,
+ rfq,
+}: RfqItemsViewDialogProps) {
+ const [items, setItems] = React.useState<RfqItem[]>([]);
+ const [loading, setLoading] = React.useState(false);
+
+ console.log("RfqItemsViewDialog render:", { open, rfq });
+
+ React.useEffect(() => {
+ console.log("RfqItemsViewDialog useEffect:", { open, rfqId: rfq?.id });
+ if (open && rfq?.id) {
+ loadItems();
+ }
+ }, [open, rfq?.id]);
+
+ const loadItems = async () => {
+ if (!rfq?.id) return;
+
+ console.log("Loading items for RFQ:", rfq.id);
+ setLoading(true);
+ try {
+ const result = await getTechSalesRfqItems(rfq.id);
+ console.log("Items loaded:", result);
+ if (result.data) {
+ setItems(result.data);
+ }
+ } catch (error) {
+ console.error("Failed to load items:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const getTypeLabel = (type: string) => {
+ switch (type) {
+ case "SHIP":
+ return "조선";
+ case "TOP":
+ return "해양TOP";
+ case "HULL":
+ return "해양HULL";
+ default:
+ return type;
+ }
+ };
+
+ const getTypeColor = (type: string) => {
+ switch (type) {
+ case "SHIP":
+ return "bg-blue-100 text-blue-800";
+ case "TOP":
+ return "bg-green-100 text-green-800";
+ case "HULL":
+ return "bg-purple-100 text-purple-800";
+ default:
+ return "bg-gray-100 text-gray-800";
+ }
+ };
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-none w-[1200px]">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2">
+ RFQ 아이템 조회
+ <Badge variant="outline" className="ml-2">
+ {rfq?.rfqCode || `RFQ #${rfq?.id}`}
+ </Badge>
+ </DialogTitle>
+ <DialogDescription>
+ RFQ에 등록된 아이템 목록을 확인할 수 있습니다.
+ </DialogDescription>
+ </DialogHeader>
+
+ <div className="overflow-x-auto w-full">
+ <div className="space-y-4">
+ {loading ? (
+ <div className="flex items-center justify-center py-8">
+ <div className="text-center space-y-2">
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
+ <p className="text-sm text-muted-foreground">아이템을 불러오는 중...</p>
+ </div>
+ </div>
+ ) : items.length === 0 ? (
+ <div className="flex flex-col items-center justify-center py-12 text-center">
+ <FileText className="h-12 w-12 text-muted-foreground mb-3" />
+ <h3 className="text-lg font-medium mb-1">아이템이 없습니다</h3>
+ <p className="text-sm text-muted-foreground">
+ 이 RFQ에 등록된 아이템이 없습니다.
+ </p>
+ </div>
+ ) : (
+ <>
+ {/* 헤더 행 (라벨) */}
+ <div className="flex items-center gap-2 border-b pb-2 font-medium text-sm">
+ <div className="w-[50px] text-center">No.</div>
+ <div className="w-[120px] pl-2">타입</div>
+ <div className="w-[200px] ">자재 그룹</div>
+ <div className="w-[150px] ">공종</div>
+ <div className="w-[300px] ">자재명</div>
+ <div className="w-[150px] ">선종/자재명(상세)</div>
+ </div>
+
+ {/* 아이템 행들 */}
+ <div className="max-h-[50vh] overflow-y-auto pr-1 space-y-2">
+ {items.map((item, index) => (
+ <div
+ key={item.id}
+ className="flex items-center gap-2 group hover:bg-gray-50 p-2 rounded-md transition-colors border"
+ >
+ <div className="w-[50px] text-center text-sm font-medium text-muted-foreground">
+ {index + 1}
+ </div>
+ <div className="w-[120px] pl-2">
+ <Badge variant="secondary" className={`text-xs ${getTypeColor(item.itemType)}`}>
+ {getTypeLabel(item.itemType)}
+ </Badge>
+ </div>
+ <div className="w-[200px] pl-2 font-mono text-sm">
+ {item.itemCode}
+ </div>
+ <div className="w-[150px] pl-2 text-sm">
+ {item.workType}
+ </div>
+ <div className="w-[300px] pl-2 font-medium">
+ {item.itemList}
+ </div>
+ <div className="w-[150px] pl-2 text-sm">
+ {item.itemType === 'SHIP' ? item.shipType : item.subItemName}
+ </div>
+ </div>
+ ))}
+ </div>
+
+ <div className="flex justify-between items-center pt-2 border-t">
+ <div className="flex items-center gap-2">
+ <Package className="h-4 w-4 text-muted-foreground" />
+ <span className="text-sm text-muted-foreground">
+ 총 {items.length}개 아이템
+ </span>
+ </div>
+ </div>
+ </>
+ )}
+ </div>
+ </div>
+
+ <DialogFooter className="mt-6">
+ <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
+ <X className="mr-2 h-4 w-4" />
+ 닫기
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+} \ No newline at end of file