summaryrefslogtreecommitdiff
path: root/lib/dolce/dialogs/detail-drawing-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce/dialogs/detail-drawing-dialog.tsx')
-rw-r--r--lib/dolce/dialogs/detail-drawing-dialog.tsx311
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}
+ />
+ )}
+ </>
+ );
+}
+