From 0fddf148402fd6b99a1b3800d73679899bcb2ed3 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 13 Jun 2025 07:11:18 +0000 Subject: (대표님) 20250613 16시 10분 global css, b-rfq, document 등 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/vendor-document-list/import-service.ts | 4 +- .../ship/enhanced-doc-table-columns.tsx | 616 +++++++------- .../ship/enhanced-doc-table-toolbar-actions.tsx | 73 +- .../ship/enhanced-document-sheet.tsx | 939 --------------------- .../ship/enhanced-documents-table.tsx | 135 ++- .../ship/import-from-dolce-button.tsx | 258 ++++-- .../ship/revision-upload-dialog.tsx | 629 -------------- .../ship/simplified-document-edit-dialog.tsx | 287 ------- .../ship/stage-revision-expanded-content.tsx | 752 ----------------- .../ship/stage-revision-sheet.tsx | 86 -- .../ship/swp-workflow-panel.tsx | 370 -------- lib/vendor-document-list/ship/update-doc-sheet.tsx | 267 ------ 12 files changed, 612 insertions(+), 3804 deletions(-) delete mode 100644 lib/vendor-document-list/ship/enhanced-document-sheet.tsx delete mode 100644 lib/vendor-document-list/ship/revision-upload-dialog.tsx delete mode 100644 lib/vendor-document-list/ship/simplified-document-edit-dialog.tsx delete mode 100644 lib/vendor-document-list/ship/stage-revision-expanded-content.tsx delete mode 100644 lib/vendor-document-list/ship/stage-revision-sheet.tsx delete mode 100644 lib/vendor-document-list/ship/swp-workflow-panel.tsx delete mode 100644 lib/vendor-document-list/ship/update-doc-sheet.tsx (limited to 'lib/vendor-document-list') diff --git a/lib/vendor-document-list/import-service.ts b/lib/vendor-document-list/import-service.ts index d2a14980..344597fa 100644 --- a/lib/vendor-document-list/import-service.ts +++ b/lib/vendor-document-list/import-service.ts @@ -183,7 +183,6 @@ class ImportService { .where(eq(contracts.id, contractId)) .limit(1) - return result?.projectCode && result?.vendorCode ? { projectCode: result.projectCode, vendorCode: result.vendorCode } : null @@ -608,6 +607,9 @@ class ImportService { eq(documents.externalSystemType, sourceSystem) )) + console.log(contractId, "contractId") + + // 프로젝트 코드와 벤더 코드 조회 const contractInfo = await this.getContractInfoById(contractId) diff --git a/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx b/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx index b80c0869..ad184378 100644 --- a/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx +++ b/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx @@ -16,123 +16,36 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Button } from "@/components/ui/button" -import { Badge } from "@/components/ui/badge" import { Ellipsis, - Calendar, - CalendarClock, - User, FileText, Eye, Edit, Trash2, - Building, - Code, - Settings } from "lucide-react" import { cn } from "@/lib/utils" import { SimplifiedDocumentsView } from "@/db/schema" +// DocumentSelectionContext를 import (실제 파일 경로에 맞게 수정 필요) +// 예: import { DocumentSelectionContext } from "../user-vendor-document-display" +// 또는: import { DocumentSelectionContext } from "./user-vendor-document-display" +import { DocumentSelectionContext } from "@/components/ship-vendor-document/user-vendor-document-table-container" + interface GetColumnsProps { setRowAction: React.Dispatch | null>> } -// 유틸리티 함수들 -const getDrawingKindText = (drawingKind: string) => { - switch (drawingKind) { - case 'B3': return 'B3 도면' - case 'B4': return 'B4 도면' - case 'B5': return 'B5 도면' - default: return drawingKind - } -} - -const getDrawingKindColor = (drawingKind: string) => { - switch (drawingKind) { - case 'B3': return 'bg-blue-100 text-blue-800' - case 'B4': return 'bg-green-100 text-green-800' - case 'B5': return 'bg-purple-100 text-purple-800' - default: return 'bg-gray-100 text-gray-800' - } -} - -// 스테이지별 이름 표시 컴포넌트 -const StageNameDisplay = ({ - stageName, - drawingKind, - isFirst = true -}: { - stageName: string | null, - drawingKind: string | null, - isFirst?: boolean -}) => { - if (!stageName) return - - - const stageType = isFirst ? "1차" : "2차" - const getExpectedStage = () => { - if (drawingKind === 'B4') return isFirst ? 'Pre' : 'Work' - if (drawingKind === 'B3') return isFirst ? 'Approval' : 'Work' - if (drawingKind === 'B5') return isFirst ? 'First' : 'Second' - return '' - } - - return ( -
-
{stageType} 스테이지
-
{stageName}
- {getExpectedStage() && ( -
({getExpectedStage()})
- )} -
- ) -} - -// 날짜 정보 표시 컴포넌트 -const StageDateInfo = ({ - planDate, - actualDate, - stageName -}: { - planDate: string | null - actualDate: string | null - stageName: string | null -}) => { - if (!planDate && !actualDate) { - return 날짜 미설정 - } - - const isCompleted = !!actualDate - const isLate = actualDate && planDate && new Date(actualDate) > new Date(planDate) - +// 날짜 표시 컴포넌트 (간단 버전) +const DateDisplay = ({ date, isSelected = false }: { date: string | null, isSelected?: boolean }) => { + if (!date) return - + return ( -
- {planDate && ( -
- 계획: - {formatDate(planDate)} -
- )} - {actualDate && ( -
- 실제: - - {formatDate(actualDate)} - -
- )} - {!actualDate && planDate && ( -
- 진행중 -
- )} - {isCompleted && ( -
- ✓ 완료 -
- )} -
+ + {formatDate(date)} + ) } @@ -140,36 +53,28 @@ export function getSimplifiedDocumentColumns({ setRowAction, }: GetColumnsProps): ColumnDef[] { - // 기본 컬럼들 - const baseColumns: ColumnDef[] = [ - // 체크박스 선택 + const columns: ColumnDef[] = [ + // 라디오 버튼 같은 체크박스 선택 { id: "select", header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - className="translate-y-0.5" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - className="translate-y-0.5" - /> +
+ 선택 +
), + cell: ({ row }) => { + const doc = row.original + + return ( + + ) + }, size: 40, enableSorting: false, enableHiding: false, }, - // 문서번호 + Drawing Kind + // 문서번호 (선택된 행 하이라이트 적용) { accessorKey: "docNumber", header: ({ column }) => ( @@ -177,33 +82,19 @@ export function getSimplifiedDocumentColumns({ ), cell: ({ row }) => { const doc = row.original + return ( -
- {doc.docNumber} - {doc.vendorDocNumber && ( - - 벤더: {doc.vendorDocNumber} - - )} - {doc.drawingKind && ( - - {getDrawingKindText(doc.drawingKind)} - - )} -
+ ) }, - size: 140, + size: 120, enableResizing: true, meta: { excelHeader: "문서번호" }, }, - // 문서명 + 프로젝트/벤더 정보 + // 문서명 (선택된 행 하이라이트 적용) { accessorKey: "title", header: ({ column }) => ( @@ -211,148 +102,136 @@ export function getSimplifiedDocumentColumns({ ), cell: ({ row }) => { const doc = row.original + return ( -
-
- {doc.title} -
-
- {doc.pic && ( - - PIC: {doc.pic} - - )} - {doc.projectCode && ( -
- - {doc.projectCode} -
- )} - {doc.vendorName && ( -
- - {doc.vendorName} -
- )} -
-
+ ) }, - size: 200, enableResizing: true, meta: { excelHeader: "문서명" }, }, - // 첫 번째 스테이지 정보 + // 프로젝트 코드 { - accessorKey: "firstStageName", + accessorKey: "projectCode", header: ({ column }) => ( - + ), cell: ({ row }) => { - const doc = row.original + const projectCode = row.original.projectCode + return ( - + ) }, - size: 130, enableResizing: true, meta: { - excelHeader: "1차 스테이지" + excelHeader: "프로젝트" }, }, - // 첫 번째 스테이지 날짜 + // 1차 스테이지 그룹 { - accessorKey: "firstStagePlanDate", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const doc = row.original + id: "firstStageGroup", + header: ({ table }) => { + // 첫 번째 행의 firstStageName을 그룹 헤더로 사용 + const firstRow = table.getRowModel().rows[0]?.original + const stageName = firstRow?.firstStageName || "1차 스테이지" return ( - - ) - }, - size: 140, - enableResizing: true, - meta: { - excelHeader: "1차 일정" - }, - }, - - // 두 번째 스테이지 정보 - { - accessorKey: "secondStageName", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const doc = row.original - return ( - +
+ {stageName} +
) }, - size: 130, - enableResizing: true, - meta: { - excelHeader: "2차 스테이지" - }, + columns: [ + { + accessorKey: "firstStagePlanDate", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return + }, + enableResizing: true, + meta: { + excelHeader: "1차 계획일" + }, + }, + { + accessorKey: "firstStageActualDate", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return + }, + enableResizing: true, + meta: { + excelHeader: "1차 실제일" + }, + }, + ], }, - // 두 번째 스테이지 날짜 + // 2차 스테이지 그룹 { - accessorKey: "secondStagePlanDate", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const doc = row.original + id: "secondStageGroup", + header: ({ table }) => { + // 첫 번째 행의 secondStageName을 그룹 헤더로 사용 + const firstRow = table.getRowModel().rows[0]?.original + const stageName = firstRow?.secondStageName || "2차 스테이지" return ( - +
+ {stageName} +
) }, - size: 140, - enableResizing: true, - meta: { - excelHeader: "2차 일정" - }, + columns: [ + { + accessorKey: "secondStagePlanDate", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return + }, + enableResizing: true, + meta: { + excelHeader: "2차 계획일" + }, + }, + { + accessorKey: "secondStageActualDate", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return + }, + enableResizing: true, + meta: { + excelHeader: "2차 실제일" + }, + }, + ], }, // 첨부파일 수 { accessorKey: "attachmentCount", header: ({ column }) => ( - + ), cell: ({ row }) => { const count = row.original.attachmentCount || 0 + return ( -
- - {count} -
+ ) }, - size: 80, + size: 60, enableResizing: true, meta: { excelHeader: "첨부파일" @@ -365,12 +244,11 @@ export function getSimplifiedDocumentColumns({ header: ({ column }) => ( ), - cell: ({ cell }) => ( - - {formatDateTime(cell.getValue() as Date)} - - ), - size: 140, + cell: ({ cell, row }) => { + return ( + + ) + }, enableResizing: true, meta: { excelHeader: "업데이트" @@ -378,50 +256,208 @@ export function getSimplifiedDocumentColumns({ }, // 액션 버튼 - { - id: "actions", - header: () => Actions, - cell: ({ row }) => { - const doc = row.original - return ( - - - - - - setRowAction({ type: "view", row: doc })} - > - - 보기 - - setRowAction({ type: "edit", row: doc })} - > - - 편집 - - - setRowAction({ type: "delete", row: doc })} - className="text-red-600" - > - - 삭제 - - - - - ) - }, - size: 50, - enableSorting: false, - enableHiding: false, - }, + // { + // id: "actions", + // header: () => Actions, + // cell: ({ row }) => { + // const doc = row.original + // return ( + // + // + // + // + // + // setRowAction({ type: "view", row: doc })} + // > + // + // 보기 + // + // setRowAction({ type: "edit", row: doc })} + // > + // + // 편집 + // + // + // setRowAction({ type: "delete", row: doc })} + // className="text-red-600" + // > + // + // 삭제 + // + // + // + // + // ) + // }, + // size: 50, + // enableSorting: false, + // enableHiding: false, + // }, ] - return baseColumns + return columns +} + +// 개별 셀 컴포넌트들 (Context 사용) +function SelectCell({ documentId }: { documentId: number }) { + const { selectedDocumentId, setSelectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === documentId; + + return ( +
+ { + const newSelection = isSelected ? null : documentId; + setSelectedDocumentId(newSelection); + }} + className="cursor-pointer w-4 h-4" + /> +
+ ); +} + +function DocNumberCell({ doc }: { doc: SimplifiedDocumentsView }) { + const { selectedDocumentId, setSelectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === doc.documentId; + + return ( +
{ + const newSelection = isSelected ? null : doc.documentId; + setSelectedDocumentId(newSelection); + }} + > + {doc.docNumber} +
+ ); +} + +function TitleCell({ doc }: { doc: SimplifiedDocumentsView }) { + const { selectedDocumentId, setSelectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === doc.documentId; + + return ( +
{ + const newSelection = isSelected ? null : doc.documentId; + setSelectedDocumentId(newSelection); + }} + > + {doc.title} +
+ ); +} + +function ProjectCodeCell({ projectCode, documentId }: { projectCode: string | null, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === documentId; + + if (!projectCode) return -; + + return ( + + {projectCode} + + ); +} + +function FirstStagePlanDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === row.original.documentId; + + return ; +} + +function FirstStageActualDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === row.original.documentId; + const date = row.original.firstStageActualDate; + + return ( +
+ + {date && ✓ 완료} +
+ ); +} + +function SecondStagePlanDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === row.original.documentId; + + return ; +} + +function SecondStageActualDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === row.original.documentId; + const date = row.original.secondStageActualDate; + + return ( +
+ + {date && ✓ 완료} +
+ ); +} + +function AttachmentCountCell({ count, documentId }: { count: number, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === documentId; + + return ( +
+ + + {count} + +
+ ); +} + +function UpdatedAtCell({ updatedAt, documentId }: { updatedAt: Date, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContext); + const isSelected = selectedDocumentId === documentId; + + return ( + + {formatDateTime(updatedAt)} + + ); } \ No newline at end of file diff --git a/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx b/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx index 3960bbce..508d8c91 100644 --- a/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx +++ b/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx @@ -6,29 +6,18 @@ import { toast } from "sonner" import { exportTableToExcel } from "@/lib/export" import { Button } from "@/components/ui/button" -import { EnhancedDocumentsView } from "@/db/schema/vendorDocu" -import { AddDocumentListDialog } from "./add-doc-dialog" -import { DeleteDocumentsDialog } from "./delete-docs-dialog" -import { BulkUploadDialog } from "./bulk-upload-dialog" -import type { EnhancedDocument } from "@/types/enhanced-documents" +import { SimplifiedDocumentsView } from "@/db/schema/vendorDocu" import { SendToSHIButton } from "./send-to-shi-button" import { ImportFromDOLCEButton } from "./import-from-dolce-button" -import { SWPWorkflowPanel } from "./swp-workflow-panel" interface EnhancedDocTableToolbarActionsProps { - table: Table + table: Table projectType: "ship" | "plant" - selectedPackageId: number - onNewDocument: () => void - onBulkAction: (action: string, selectedRows: any[]) => Promise } export function EnhancedDocTableToolbarActions({ table, projectType, - selectedPackageId, - onNewDocument, - onBulkAction }: EnhancedDocTableToolbarActionsProps) { const [bulkUploadDialogOpen, setBulkUploadDialogOpen] = React.useState(false) @@ -61,45 +50,15 @@ export function EnhancedDocTableToolbarActions({ return (
- {/* 삭제 버튼 */} - {table.getFilteredSelectedRowModel().rows.length > 0 ? ( - row.original)} - onSuccess={() => table.toggleAllRowsSelected(false)} - /> - ) : null} - - {/* projectType에 따른 조건부 렌더링 */} - {projectType === "ship" ? ( + <> {/* SHIP: DOLCE에서 목록 가져오기 */} - ) : ( - <> - {/* PLANT: 수동 문서 추가 */} - - - )} - - {/* 일괄 업로드 버튼 (공통) */} - + {/* Export 버튼 (공통) */}
) } \ No newline at end of file diff --git a/lib/vendor-document-list/ship/enhanced-document-sheet.tsx b/lib/vendor-document-list/ship/enhanced-document-sheet.tsx deleted file mode 100644 index 88e342c8..00000000 --- a/lib/vendor-document-list/ship/enhanced-document-sheet.tsx +++ /dev/null @@ -1,939 +0,0 @@ -// enhanced-document-sheet.tsx -"use client" - -import * as React from "react" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { toast } from "sonner" -import { z } from "zod" -import { useRouter } from "next/navigation" -import { - Loader, - Save, - Upload, - Calendar, - User, - FileText, - AlertTriangle, - CheckCircle, - Clock, - Plus, - X -} from "lucide-react" - -import { Button } from "@/components/ui/button" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Sheet, - SheetClose, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetTitle, -} from "@/components/ui/sheet" -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, -} from "@/components/ui/tabs" -import { Input } from "@/components/ui/input" -import { Textarea } from "@/components/ui/textarea" -import { Badge } from "@/components/ui/badge" -import { Separator } from "@/components/ui/separator" -import { ScrollArea } from "@/components/ui/scroll-area" -import { Calendar as CalendarComponent } from "@/components/ui/calendar" -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" -import { cn } from "@/lib/utils" -import { format } from "date-fns" -import { ko } from "date-fns/locale" -import { EnhancedDocumentsView } from "@/db/schema/vendorDocu" - -// 드롭존과 파일 관련 컴포넌트들 -import { - Dropzone, - DropzoneDescription, - DropzoneInput, - DropzoneTitle, - DropzoneUploadIcon, - DropzoneZone, -} from "@/components/ui/dropzone" -import { - FileList, - FileListAction, - FileListDescription, - FileListHeader, - FileListIcon, - FileListInfo, - FileListItem, - FileListName, - FileListSize, -} from "@/components/ui/file-list" -import prettyBytes from "pretty-bytes" - -// 스키마 정의 -const enhancedDocumentSchema = z.object({ - // 기본 문서 정보 - docNumber: z.string().min(1, "문서번호는 필수입니다"), - title: z.string().min(1, "제목은 필수입니다"), - pic: z.string().optional(), - status: z.string().min(1, "상태는 필수입니다"), - issuedDate: z.date().optional(), - - // 스테이지 관리 (plant 타입에서만 수정 가능) - stages: z.array(z.object({ - id: z.number().optional(), - stageName: z.string().min(1, "스테이지명은 필수입니다"), - stageOrder: z.number(), - priority: z.enum(["HIGH", "MEDIUM", "LOW"]).default("MEDIUM"), - planDate: z.date().optional(), - assigneeName: z.string().optional(), - description: z.string().optional(), - })).optional(), - - // 리비전 업로드 (현재 스테이지에 대한) - newRevision: z.object({ - stage: z.string().optional(), - revision: z.string().optional(), - uploaderType: z.enum(["vendor", "client", "shi"]).default("vendor"), - uploaderName: z.string().optional(), - comment: z.string().optional(), - attachments: z.array(z.instanceof(File)).optional(), - }).optional(), -}) - -type EnhancedDocumentSchema = z.infer - -// 상태 옵션 정의 -const statusOptions = [ - { value: "ACTIVE", label: "활성" }, - { value: "INACTIVE", label: "비활성" }, - { value: "COMPLETED", label: "완료" }, - { value: "CANCELLED", label: "취소" }, -] - -const priorityOptions = [ - { value: "HIGH", label: "높음" }, - { value: "MEDIUM", label: "보통" }, - { value: "LOW", label: "낮음" }, -] - -const stageStatusOptions = [ - { value: "PLANNED", label: "계획됨" }, - { value: "IN_PROGRESS", label: "진행중" }, - { value: "SUBMITTED", label: "제출됨" }, - { value: "APPROVED", label: "승인됨" }, - { value: "REJECTED", label: "반려됨" }, - { value: "COMPLETED", label: "완료됨" }, -] - -interface EnhancedDocumentSheetProps - extends React.ComponentPropsWithRef { - document: EnhancedDocumentsView | null - projectType: "ship" | "plant" - mode: "view" | "edit" | "upload" | "schedule" | "approve" -} - -export function EnhancedDocumentSheet({ - document, - projectType, - mode = "view", - ...props -}: EnhancedDocumentSheetProps) { - const [isUpdatePending, startUpdateTransition] = React.useTransition() - const [selectedFiles, setSelectedFiles] = React.useState([]) - const [uploadProgress, setUploadProgress] = React.useState(0) - const [activeTab, setActiveTab] = React.useState("info") - const router = useRouter() - - // 권한 계산 - const permissions = React.useMemo(() => { - const canEdit = projectType === "plant" || mode === "edit" - const canUpload = mode === "upload" || mode === "edit" - const canApprove = mode === "approve" && projectType === "ship" - const canSchedule = mode === "schedule" || (projectType === "plant" && mode === "edit") - - return { canEdit, canUpload, canApprove, canSchedule } - }, [projectType, mode]) - - const form = useForm({ - resolver: zodResolver(enhancedDocumentSchema), - defaultValues: { - docNumber: "", - title: "", - pic: "", - status: "ACTIVE", - issuedDate: undefined, - stages: [], - newRevision: { - stage: "", - revision: "", - uploaderType: "vendor", - uploaderName: "", - comment: "", - attachments: [], - }, - }, - }) - - // 폼 초기화 - React.useEffect(() => { - if (document) { - form.reset({ - docNumber: document.docNumber, - title: document.title, - pic: document.pic || "", - status: document.status, - issuedDate: document.issuedDate ? new Date(document.issuedDate) : undefined, - stages: document.allStages?.map((stage, index) => ({ - id: stage.id, - stageName: stage.stageName, - stageOrder: stage.stageOrder || index, - priority: stage.priority as "HIGH" | "MEDIUM" | "LOW" || "MEDIUM", - planDate: stage.planDate ? new Date(stage.planDate) : undefined, - assigneeName: stage.assigneeName || "", - description: "", - })) || [], - newRevision: { - stage: document.currentStageName || "", - revision: "", - uploaderType: "vendor", - uploaderName: "", - comment: "", - attachments: [], - }, - }) - - // 모드에 따른 기본 탭 설정 - if (mode === "upload") { - setActiveTab("upload") - } else if (mode === "schedule") { - setActiveTab("schedule") - } else if (mode === "approve") { - setActiveTab("approve") - } - } - }, [document, form, mode]) - - // 파일 처리 - const handleDropAccepted = (acceptedFiles: File[]) => { - const newFiles = [...selectedFiles, ...acceptedFiles] - setSelectedFiles(newFiles) - form.setValue('newRevision.attachments', newFiles) - } - - const removeFile = (index: number) => { - const updatedFiles = [...selectedFiles] - updatedFiles.splice(index, 1) - setSelectedFiles(updatedFiles) - form.setValue('newRevision.attachments', updatedFiles) - } - - // 스테이지 추가/제거 - const addStage = () => { - const currentStages = form.getValues("stages") || [] - const newStage = { - stageName: "", - stageOrder: currentStages.length, - priority: "MEDIUM" as const, - planDate: undefined, - assigneeName: "", - description: "", - } - form.setValue("stages", [...currentStages, newStage]) - } - - const removeStage = (index: number) => { - const currentStages = form.getValues("stages") || [] - const updatedStages = currentStages.filter((_, i) => i !== index) - form.setValue("stages", updatedStages) - } - - // 제출 처리 - function onSubmit(input: EnhancedDocumentSchema) { - startUpdateTransition(async () => { - if (!document) return - - try { - // 모드에 따른 다른 처리 - switch (mode) { - case "edit": - // 문서 정보 업데이트 + 스테이지 관리 - await updateDocumentInfo(input) - break - case "upload": - // 리비전 업로드 - await uploadRevision(input) - break - case "approve": - // 승인 처리 - await approveRevision(input) - break - case "schedule": - // 스케줄 관리 - await updateSchedule(input) - break - } - - form.reset() - setSelectedFiles([]) - props.onOpenChange?.(false) - toast.success("성공적으로 처리되었습니다") - router.refresh() - } catch (error) { - toast.error("처리 중 오류가 발생했습니다") - console.error(error) - } - }) - } - - // 개별 처리 함수들 - const updateDocumentInfo = async (input: EnhancedDocumentSchema) => { - // 문서 기본 정보 업데이트 API 호출 - console.log("문서 정보 업데이트:", input) - } - - const uploadRevision = async (input: EnhancedDocumentSchema) => { - if (!input.newRevision?.attachments?.length) { - throw new Error("파일을 선택해주세요") - } - - // 파일 업로드 처리 - const formData = new FormData() - formData.append("documentId", String(document?.documentId)) - formData.append("stage", input.newRevision.stage || "") - formData.append("revision", input.newRevision.revision || "") - formData.append("uploaderType", input.newRevision.uploaderType) - - input.newRevision.attachments.forEach((file) => { - formData.append("attachments", file) - }) - - // API 호출 - console.log("리비전 업로드:", formData) - } - - const approveRevision = async (input: EnhancedDocumentSchema) => { - // 승인 처리 API 호출 - console.log("리비전 승인:", input) - } - - const updateSchedule = async (input: EnhancedDocumentSchema) => { - // 스케줄 업데이트 API 호출 - console.log("스케줄 업데이트:", input) - } - - // 제목 및 설명 생성 - const getSheetTitle = () => { - switch (mode) { - case "edit": return "문서 정보 수정" - case "upload": return "리비전 업로드" - case "approve": return "문서 승인" - case "schedule": return "일정 관리" - default: return "문서 상세" - } - } - - const getSheetDescription = () => { - const docInfo = document ? `${document.docNumber} - ${document.title}` : "" - switch (mode) { - case "edit": return `문서 정보를 수정합니다. ${docInfo}` - case "upload": return `새 리비전을 업로드합니다. ${docInfo}` - case "approve": return `문서를 검토하고 승인 처리합니다. ${docInfo}` - case "schedule": return `문서의 일정을 관리합니다. ${docInfo}` - default: return docInfo - } - } - - return ( - - - - - {mode === "upload" && } - {mode === "approve" && } - {mode === "schedule" && } - {mode === "edit" && } - {getSheetTitle()} - - - {getSheetDescription()} - - - {/* 프로젝트 타입 및 권한 표시 */} -
- - {projectType === "ship" ? "조선 프로젝트" : "플랜트 프로젝트"} - - {document?.isOverdue && ( - - - 지연 - - )} - {document?.currentStagePriority === "HIGH" && ( - 높은 우선순위 - )} -
-
- -
- - - - 기본정보 - - 일정관리 - - - 리비전업로드 - - - 승인처리 - - - - {/* 기본 정보 탭 */} - - -
- ( - - 문서번호 - - - - - - )} - /> - - ( - - 제목 - - - - - - )} - /> - -
- ( - - 담당자 (PIC) - - - - - - )} - /> - - ( - - 상태 - - - - )} - /> -
- - ( - - 발행일 - - - - - - - - date > new Date()} - initialFocus - /> - - - - - )} - /> - - {/* 현재 상태 정보 표시 */} - {document && ( -
-

- - 현재 진행 상황 -

-
-
- 현재 스테이지: -

{document.currentStageName || "-"}

-
-
- 진행률: -

{document.progressPercentage || 0}%

-
-
- 최신 리비전: -

{document.latestRevision || "-"}

-
-
- 담당자: -

{document.currentStageAssigneeName || "-"}

-
-
-
- )} -
-
-
- - {/* 일정 관리 탭 */} - - -
-
-

스테이지 일정 관리

- {projectType === "plant" && ( - - )} -
- - {form.watch("stages")?.map((stage, index) => ( -
-
-
스테이지 {index + 1}
- {projectType === "plant" && ( - - )} -
- -
- ( - - 스테이지명 - - - - - - )} - /> - - ( - - 우선순위 - - - - )} - /> - - ( - - 계획일 - - - - - - - - - - - - - )} - /> - - ( - - 담당자 - - - - - - )} - /> -
-
- ))} -
-
-
- - {/* 리비전 업로드 탭 */} - - -
-
- ( - - 스테이지 - - - - - - )} - /> - - ( - - 리비전 - - - - - - )} - /> -
- - ( - - 업로더명 (선택) - - - - - - )} - /> - - ( - - 코멘트 (선택) - -