diff options
Diffstat (limited to 'lib/dolce/dialogs/detail-drawing-dialog.tsx')
| -rw-r--r-- | lib/dolce/dialogs/detail-drawing-dialog.tsx | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/lib/dolce/dialogs/detail-drawing-dialog.tsx b/lib/dolce/dialogs/detail-drawing-dialog.tsx new file mode 100644 index 00000000..a06c9688 --- /dev/null +++ b/lib/dolce/dialogs/detail-drawing-dialog.tsx @@ -0,0 +1,311 @@ +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Plus, RefreshCw, Upload, Loader2 } from "lucide-react"; +import { toast } from "sonner"; +import { + UnifiedDwgReceiptItem, + DetailDwgReceiptItem, + FileInfoItem, + fetchDetailDwgReceiptList, + fetchFileInfoList, +} from "../actions"; +import { DrawingListTable } from "../table/drawing-list-table"; +import { detailDrawingColumns } from "../table/detail-drawing-columns"; +import { createFileListColumns } from "../table/file-list-columns"; +import { AddDetailDrawingDialog } from "./add-detail-drawing-dialog"; +import { UploadFilesToDetailDialog } from "./upload-files-to-detail-dialog"; + +interface DetailDrawingDialogProps { + drawing: UnifiedDwgReceiptItem | null; + open: boolean; + onOpenChange: (open: boolean) => void; + vendorCode: string; + userId: string; + userName: string; + userEmail: string; + drawingKind: "B3" | "B4"; +} + +export function DetailDrawingDialog({ + drawing, + open, + onOpenChange, + vendorCode, + userId, + userName, + userEmail, + drawingKind, +}: DetailDrawingDialogProps) { + const [detailDrawings, setDetailDrawings] = useState<DetailDwgReceiptItem[]>([]); + const [selectedDetail, setSelectedDetail] = useState<DetailDwgReceiptItem | null>(null); + const [files, setFiles] = useState<FileInfoItem[]>([]); + const [isLoading, setIsLoading] = useState(false); + const [isLoadingFiles, setIsLoadingFiles] = useState(false); + const [addDialogOpen, setAddDialogOpen] = useState(false); + const [uploadFilesDialogOpen, setUploadFilesDialogOpen] = useState(false); + + // 상세도면 목록 로드 + const loadDetailDrawings = useCallback(async () => { + if (!drawing) return; + + try { + setIsLoading(true); + const data = await fetchDetailDwgReceiptList({ + project: drawing.ProjectNo, + drawingNo: drawing.DrawingNo, + discipline: drawing.Discipline, + drawingKind: drawing.DrawingKind, + userId: "", // 조회 시 모든 사용자의 상세도면을 보기 위해 빈 문자열 전달 + }); + setDetailDrawings(data); + + // 첫 번째 상세도면 자동 선택 + if (data.length > 0 && !selectedDetail) { + setSelectedDetail(data[0]); + } + } catch (error) { + console.error("상세도면 로드 실패:", error); + toast.error("상세도면 로드에 실패했습니다"); + } finally { + setIsLoading(false); + } + }, [drawing, selectedDetail]); + + // 파일 목록 로드 + const loadFiles = useCallback(async () => { + if (!selectedDetail) { + setFiles([]); + return; + } + + try { + setIsLoadingFiles(true); + const data = await fetchFileInfoList(selectedDetail.UploadId); + setFiles(data); + } catch (error) { + console.error("파일 목록 로드 실패:", error); + toast.error("파일 목록 로드에 실패했습니다"); + } finally { + setIsLoadingFiles(false); + } + }, [selectedDetail]); + + // 다이얼로그 열릴 때 데이터 로드 + useEffect(() => { + if (open && drawing) { + loadDetailDrawings(); + } else { + setDetailDrawings([]); + setSelectedDetail(null); + setFiles([]); + } + }, [open, drawing, loadDetailDrawings]); + + // 선택된 상세도면 변경 시 파일 목록 로드 + useEffect(() => { + if (selectedDetail) { + loadFiles(); + } + }, [selectedDetail, loadFiles]); + + const handleDownload = async (file: FileInfoItem) => { + try { + toast.info("파일 다운로드를 준비 중입니다..."); + + // 파일 생성자의 userId를 사용하여 다운로드 + const response = await fetch("/api/dolce/download", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + fileId: file.FileId, + userId: file.CreateUserId, // 파일 생성자의 ID 사용 + fileName: file.FileName, + }), + }); + + if (!response.ok) { + throw new Error("파일 다운로드 실패"); + } + + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = file.FileName; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + document.body.removeChild(a); + + toast.success("파일 다운로드가 완료되었습니다"); + } catch (error) { + console.error("파일 다운로드 실패:", error); + toast.error("파일 다운로드에 실패했습니다"); + } + }; + + const handleRefresh = () => { + loadDetailDrawings(); + }; + + const handleAddComplete = () => { + setAddDialogOpen(false); + loadDetailDrawings(); + }; + + const handleUploadComplete = () => { + setUploadFilesDialogOpen(false); + loadFiles(); + }; + + const fileColumns = createFileListColumns({ onDownload: handleDownload }); + + // RegisterId + UploadId 조합으로 고유 ID 생성 + const getDetailDrawingId = (detail: DetailDwgReceiptItem) => { + return `${detail.RegisterId}_${detail.UploadId}`; + }; + + // B4인 경우 "도면입수"인 건만 상세도면 추가 및 파일 첨부 가능 + // B3인 경우 모든 건에 대해 가능 + const canAddDetailDrawing = drawingKind === "B3" || + (drawingKind === "B4" && drawing && 'DrawingMoveGbn' in drawing && drawing.DrawingMoveGbn === "도면입수"); + + return ( + <> + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="max-w-[95vw] h-[90vh] flex flex-col"> + <DialogHeader> + <DialogTitle className="flex flex-col gap-1"> + <span>상세도면 정보</span> + {drawing && ( + <span className="text-sm font-normal text-muted-foreground"> + {drawing.DrawingNo} | 프로젝트: {drawing.ProjectNo} | Discipline: {drawing.Discipline} | 종류: {drawing.DrawingKind} + </span> + )} + </DialogTitle> + </DialogHeader> + + <div className="flex-1 overflow-hidden flex flex-col gap-4"> + {/* 상단: 상세도면 리스트 */} + <Card className="flex-1 overflow-hidden flex flex-col"> + <CardHeader className="flex-row items-center justify-between py-3"> + <CardTitle className="text-base">상세도면 목록</CardTitle> + <div className="flex gap-2"> + <Button + variant="outline" + size="sm" + onClick={handleRefresh} + disabled={isLoading} + > + <RefreshCw className={`h-4 w-4 mr-2 ${isLoading ? "animate-spin" : ""}`} /> + 새로고침 + </Button> + {canAddDetailDrawing && ( + <Button + variant="default" + size="sm" + onClick={() => setAddDialogOpen(true)} + > + <Plus className="h-4 w-4 mr-2" /> + 상세도면 추가 + </Button> + )} + </div> + </CardHeader> + <CardContent className="flex-1 overflow-y-auto p-4"> + <DrawingListTable<DetailDwgReceiptItem, unknown> + columns={detailDrawingColumns} + data={detailDrawings} + onRowClick={setSelectedDetail} + selectedRow={selectedDetail || undefined} + getRowId={(row) => getDetailDrawingId(row)} + /> + </CardContent> + </Card> + + {/* 하단: 첨부파일 리스트 */} + <Card className="flex-1 overflow-hidden flex flex-col"> + <CardHeader className="flex-row items-center justify-between py-3"> + <CardTitle className="text-base"> + 첨부파일 목록 + {selectedDetail && ` - Rev. ${selectedDetail.DrawingRevNo}`} + </CardTitle> + {selectedDetail && canAddDetailDrawing && ( + <Button + variant="default" + size="sm" + onClick={() => setUploadFilesDialogOpen(true)} + > + <Upload className="h-4 w-4 mr-2" /> + 파일 업로드 + </Button> + )} + </CardHeader> + <CardContent className="flex-1 overflow-y-auto p-4"> + {!selectedDetail ? ( + <div className="h-full flex items-center justify-center text-muted-foreground"> + 상세도면을 선택하세요 + </div> + ) : isLoadingFiles ? ( + <div className="space-y-4"> + <div className="flex items-center justify-center gap-2 text-muted-foreground py-8"> + <Loader2 className="h-5 w-5 animate-spin" /> + <span>Loading files...</span> + </div> + <div className="space-y-2"> + <Skeleton className="h-10 w-full" /> + <Skeleton className="h-10 w-full" /> + <Skeleton className="h-10 w-full" /> + </div> + </div> + ) : ( + <DrawingListTable + columns={fileColumns} + data={files} + /> + )} + </CardContent> + </Card> + </div> + </DialogContent> + </Dialog> + + <AddDetailDrawingDialog + open={addDialogOpen} + onOpenChange={setAddDialogOpen} + drawing={drawing} + vendorCode={vendorCode} + userId={userId} + userName={userName} + userEmail={userEmail} + onComplete={handleAddComplete} + drawingKind={drawingKind} + /> + + {selectedDetail && ( + <UploadFilesToDetailDialog + open={uploadFilesDialogOpen} + onOpenChange={setUploadFilesDialogOpen} + uploadId={selectedDetail.UploadId} + drawingNo={selectedDetail.DrawingNo} + revNo={selectedDetail.DrawingRevNo} + userId={userId} + onUploadComplete={handleUploadComplete} + /> + )} + </> + ); +} + |
