diff options
Diffstat (limited to 'components/ship-vendor-document/user-vendor-document-table-container.tsx')
| -rw-r--r-- | components/ship-vendor-document/user-vendor-document-table-container.tsx | 615 |
1 files changed, 466 insertions, 149 deletions
diff --git a/components/ship-vendor-document/user-vendor-document-table-container.tsx b/components/ship-vendor-document/user-vendor-document-table-container.tsx index 4e133696..61d52c28 100644 --- a/components/ship-vendor-document/user-vendor-document-table-container.tsx +++ b/components/ship-vendor-document/user-vendor-document-table-container.tsx @@ -27,7 +27,7 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog" -import { Building, FileText, AlertCircle, Eye, Download, Loader2, Plus } from "lucide-react" +import { Building, FileText, AlertCircle, Eye, Download, Loader2, Plus, Trash2, Edit } from "lucide-react" import { SimplifiedDocumentsTable } from "@/lib/vendor-document-list/ship/enhanced-documents-table" import { getUserVendorDocuments, @@ -38,6 +38,7 @@ import { WebViewerInstance } from "@pdftron/webviewer" import { NewRevisionDialog } from "./new-revision-dialog" import { useRouter } from 'next/navigation' import { AddAttachmentDialog } from "./add-attachment-dialog" // ✅ import 추가 +import { EditRevisionDialog } from "./edit-revision-dialog" // ✅ 추가 /* ------------------------------------------------------------------------------------------------- * Types & Constants @@ -91,6 +92,7 @@ interface AttachmentInfo { revisionId: number fileName: string filePath: string + dolceFilePath: string | null fileSize: number | null fileType: string | null createdAt: Date @@ -129,7 +131,7 @@ export const DocumentSelectionContext = React.createContext<DocumentSelectionCon function getUsageTypeDisplay(usageType: string | null): string { if (!usageType) return '-' - + // B3 용도 타입 축약 표시 const abbreviations: Record<string, string> = { 'Approval Submission Full': 'AS-F', @@ -143,18 +145,20 @@ function getUsageTypeDisplay(usageType: string | null): string { 'Reference Series Full': 'RS-F', 'Reference Series Partial': 'RS-P', } - + return abbreviations[usageType] || usageType } -function RevisionTable({ - revisions, +function RevisionTable({ + revisions, onViewRevision, - onNewRevision -}: { + onNewRevision, + onEditRevision, // ✅ 수정 함수 prop 추가 +}: { revisions: RevisionInfo[] onViewRevision: (revision: RevisionInfo) => void onNewRevision: () => void + onEditRevision: (revision: RevisionInfo) => void // ✅ 수정 함수 타입 추가 }) { const { selectedRevisionId, setSelectedRevisionId } = React.useContext(DocumentSelectionContext) @@ -163,6 +167,38 @@ function RevisionTable({ setSelectedRevisionId(revisionId === selectedRevisionId ? null : revisionId) } + // ✅ 리비전 수정 가능 여부 확인 함수 + const canEditRevision = React.useCallback((revision: RevisionInfo) => { + // 첨부파일이 없으면 수정 가능 + if ((!revision.attachments || revision.attachments.length === 0)&&revision.uploaderType ==="vendor") { + return true + } + + // 모든 첨부파일의 dolceFilePath가 null이거나 빈값이어야 수정 가능 + return revision.attachments.every(attachment => + !attachment.dolceFilePath || attachment.dolceFilePath.trim() === '' + ) + }, []) + + // ✅ 리비전 상태 표시 함수 (처리된 파일이 있는지 확인) + const getRevisionProcessStatus = React.useCallback((revision: RevisionInfo) => { + if (!revision.attachments || revision.attachments.length === 0) { + return 'no-files' + } + + const processedCount = revision.attachments.filter(attachment => + attachment.dolceFilePath && attachment.dolceFilePath.trim() !== '' + ).length + + if (processedCount === 0) { + return 'not-processed' + } else if (processedCount === revision.attachments.length) { + return 'fully-processed' + } else { + return 'partially-processed' + } + }, []) + return ( <Card className="flex-1"> <CardHeader> @@ -182,14 +218,14 @@ function RevisionTable({ </CardHeader> <CardContent> <div className="overflow-x-auto"> - <Table className="tbl-compact"> + <Table className="tbl-compact"> <TableHeader> <TableRow> <TableHead className="w-12">Select</TableHead> <TableHead>Revision</TableHead> <TableHead>Category</TableHead> <TableHead>Usage</TableHead> - <TableHead>Type</TableHead> {/* ✅ usageType 컬럼 */} + <TableHead>Type</TableHead> <TableHead>Status</TableHead> <TableHead>Uploader</TableHead> <TableHead>Comment</TableHead> @@ -199,94 +235,144 @@ function RevisionTable({ </TableRow> </TableHeader> <TableBody> - {revisions.map((revision) => ( - <TableRow - key={revision.id} - className={`revision-table-row ${ - selectedRevisionId === revision.id ? 'selected' : '' - }`} - > - <TableCell> - <input - type="checkbox" - checked={selectedRevisionId === revision.id} - onChange={() => toggleSelect(revision.id)} - className="h-4 w-4 cursor-pointer" - /> - </TableCell> - <TableCell className="font-mono font-medium"> - {revision.revision} - </TableCell> - <TableCell className="text-sm"> - {revision.uploaderType === "vendor" ? "To SHI" : "From SHI"} - </TableCell> - <TableCell> - <span className="text-sm"> - {revision.usage || '-'} - </span> - </TableCell> - {/* ✅ usageType 표시 */} - <TableCell> - <span className="text-sm"> - {revision.usageType ? - + {revisions.map((revision) => { + const canEdit = canEditRevision(revision) + const processStatus = getRevisionProcessStatus(revision) + + return ( + <TableRow + key={revision.id} + className={`revision-table-row ${selectedRevisionId === revision.id ? 'selected' : '' + }`} + > + <TableCell> + <input + type="checkbox" + checked={selectedRevisionId === revision.id} + onChange={() => toggleSelect(revision.id)} + className="h-4 w-4 cursor-pointer" + /> + </TableCell> + <TableCell className="font-mono font-medium"> + <div className="flex items-center gap-2"> + {revision.revision} + {/* ✅ 처리 상태 인디케이터 */} + {processStatus === 'fully-processed' && ( + <div + className="w-2 h-2 bg-blue-500 rounded-full" + title="All files processed" + /> + )} + {processStatus === 'partially-processed' && ( + <div + className="w-2 h-2 bg-yellow-500 rounded-full" + title="Some files processed" + /> + )} + </div> + </TableCell> + <TableCell className="text-sm"> + {revision.uploaderType === "vendor" ? "To SHI" : "From SHI"} + </TableCell> + <TableCell> + <span className="text-sm"> + {revision.usage || '-'} + </span> + </TableCell> + <TableCell> + <span className="text-sm"> + {revision.usageType ? ( revision.usageType - - : ( + ) : ( + <span className="text-gray-400 text-xs">-</span> + )} + </span> + </TableCell> + <TableCell> + <Badge + variant={ + revision.revisionStatus === 'APPROVED' + ? 'default' + : 'secondary' + } + className="text-xs" + > + {revision.revisionStatus} + </Badge> + </TableCell> + <TableCell> + <span className="text-sm">{revision.uploaderName || '-'}</span> + </TableCell> + <TableCell className="py-1 px-2"> + {revision.comment ? ( + <div className="max-w-24"> + <p className="text-xs text-gray-700 bg-gray-50 p-1 rounded truncate" title={revision.comment}> + {revision.comment} + </p> + </div> + ) : ( <span className="text-gray-400 text-xs">-</span> )} - </span> - </TableCell> - <TableCell> - <Badge - variant={ - revision.revisionStatus === 'APPROVED' - ? 'default' - : 'secondary' - } - className="text-xs" - > - {revision.revisionStatus} - </Badge> - </TableCell> - <TableCell> - <span className="text-sm">{revision.uploaderName || '-'}</span> - </TableCell> - <TableCell className="py-1 px-2"> - {revision.comment ? ( - <div className="max-w-24"> - <p className="text-xs text-gray-700 bg-gray-50 p-1 rounded truncate" title={revision.comment}> - {revision.comment} - </p> + </TableCell> + <TableCell> + <span className="text-sm"> + {revision.uploadedAt + ? new Date(revision.uploadedAt).toLocaleDateString() + : '-'} + </span> + </TableCell> + <TableCell className="text-center"> + <div className="flex items-center justify-center gap-1"> + <span>{revision.attachments.length}</span> + {/* ✅ 처리된 파일 수 표시 */} + {processStatus === 'partially-processed' && ( + <span className="text-xs text-gray-500"> + ({revision.attachments.filter(att => + att.dolceFilePath && att.dolceFilePath.trim() !== '' + ).length} processed) + </span> + )} </div> - ) : ( - <span className="text-gray-400 text-xs">-</span> - )} - </TableCell> - <TableCell> - <span className="text-sm"> - {revision.uploadedAt - ? new Date(revision.uploadedAt).toLocaleDateString() - : '-'} - </span> - </TableCell> - <TableCell className="text-center"> - {revision.attachments.length} - </TableCell> - <TableCell> - {revision.attachments.length > 0 && ( - <Button - variant="ghost" - size="sm" - onClick={() => onViewRevision(revision)} - className="h-8 px-2" - > - <Eye className="h-4 w-4" /> - </Button> - )} - </TableCell> - </TableRow> - ))} + </TableCell> + <TableCell> + <div className="flex items-center gap-1"> + {/* 보기 버튼 */} + {revision.attachments.length > 0 && ( + <Button + variant="ghost" + size="sm" + onClick={() => onViewRevision(revision)} + className="h-8 px-2" + title="View attachments" + > + <Eye className="h-4 w-4" /> + </Button> + )} + + {/* ✅ 수정 버튼 */} + <Button + variant="ghost" + size="sm" + onClick={() => onEditRevision(revision)} + className={`h-8 px-2 ${ + canEdit + ? 'text-blue-600 hover:text-blue-700 hover:bg-blue-50' + : 'text-gray-400 cursor-not-allowed' + }`} + disabled={!canEdit} + title={ + canEdit + ? 'Edit revision' + : 'Cannot edit - some files have been processed' + } + > + <Edit className="h-4 w-4" /> + </Button> + </div> + </TableCell> + </TableRow> + ) + })} </TableBody> </Table> </div> @@ -295,21 +381,24 @@ function RevisionTable({ ) } -function AttachmentTable({ - attachments, - onDownloadFile -}: { +function AttachmentTable({ + attachments, + onDownloadFile, + onDeleteFile, // ✅ 삭제 함수 prop 추가 +}: { attachments: AttachmentInfo[] onDownloadFile: (attachment: AttachmentInfo) => void + onDeleteFile: (attachment: AttachmentInfo) => Promise<void> // ✅ 삭제 함수 추가 }) { const { selectedRevisionId, allData, setAllData } = React.useContext(DocumentSelectionContext) - const [addAttachmentDialogOpen, setAddAttachmentDialogOpen] = React.useState(false) // ✅ 추가 - const router = useRouter() // ✅ 추가 + const [addAttachmentDialogOpen, setAddAttachmentDialogOpen] = React.useState(false) + const [deletingFileId, setDeletingFileId] = React.useState<number | null>(null) // ✅ 삭제 중인 파일 ID + const router = useRouter() - // ✅ 선택된 리비전 정보 가져오기 + // 선택된 리비전 정보 가져오기 const selectedRevisionInfo = React.useMemo(() => { if (!selectedRevisionId || !allData) return null - + for (const doc of allData) { if (doc.allStages) { for (const stage of doc.allStages as StageInfo[]) { @@ -321,14 +410,43 @@ function AttachmentTable({ return null }, [selectedRevisionId, allData]) - // ✅ 첨부파일 추가 핸들러 + // 첨부파일 추가 핸들러 const handleAddAttachment = React.useCallback(() => { if (selectedRevisionInfo) { setAddAttachmentDialogOpen(true) } }, [selectedRevisionInfo]) - // ✅ 첨부파일 업로드 성공 핸들러 + // ✅ 삭제 가능 여부 확인 함수 + const canDeleteFile = React.useCallback((attachment: AttachmentInfo) => { + return !attachment.dolceFilePath || attachment.dolceFilePath.trim() === '' + }, []) + + // ✅ 파일 삭제 핸들러 + const handleDeleteFile = React.useCallback(async (attachment: AttachmentInfo) => { + if (!canDeleteFile(attachment)) { + alert('This file cannot be deleted because it has been processed by the system.') + return + } + + const confirmDelete = window.confirm( + `Are you sure you want to delete "${attachment.fileName}"?\nThis action cannot be undone.` + ) + + if (!confirmDelete) return + + try { + setDeletingFileId(attachment.id) + await onDeleteFile(attachment) + } catch (error) { + console.error('Delete file error:', error) + alert(`Failed to delete file: ${error instanceof Error ? error.message : 'Unknown error'}`) + } finally { + setDeletingFileId(null) + } + }, [canDeleteFile, onDeleteFile]) + + // 첨부파일 업로드 성공 핸들러 const handleAttachmentUploadSuccess = React.useCallback((uploadResult?: any) => { if (!selectedRevisionId || !allData || !uploadResult?.data) { console.log('🔄 Full refresh') @@ -343,6 +461,7 @@ function AttachmentTable({ revisionId: selectedRevisionId, fileName: file.fileName, filePath: file.filePath, + dolceFilePath: null, // ✅ 새 파일은 dolceFilePath가 없음 fileSize: file.fileSize, fileType: file.fileType || null, createdAt: new Date(), @@ -352,10 +471,10 @@ function AttachmentTable({ // allData에서 해당 리비전을 찾아서 첨부파일 추가 const updatedData = allData.map(doc => { const updatedDoc = { ...doc } - + if (updatedDoc.allStages) { const stages = [...updatedDoc.allStages as StageInfo[]] - + for (const stage of stages) { const revisionIndex = stage.revisions.findIndex(r => r.id === selectedRevisionId) if (revisionIndex !== -1) { @@ -369,7 +488,7 @@ function AttachmentTable({ } } } - + return updatedDoc }) @@ -393,7 +512,7 @@ function AttachmentTable({ <CardHeader> <div className="flex items-center justify-between"> <CardTitle className="text-lg">Attachments</CardTitle> - {/* ✅ + 버튼 추가 */} + {/* + 버튼 */} {selectedRevisionId && selectedRevisionInfo && ( <Button onClick={handleAddAttachment} @@ -408,7 +527,7 @@ function AttachmentTable({ </div> </CardHeader> <CardContent> - <Table className="tbl-compact"> + <Table className="tbl-compact"> <TableHeader> <TableRow> <TableHead>File Name</TableHead> @@ -426,7 +545,7 @@ function AttachmentTable({ ? 'Please select a revision' : 'No attached files'} </span> - {/* ✅ 리비전이 선택된 경우 추가 버튼 표시 */} + {/* 리비전이 선택된 경우 추가 버튼 표시 */} {selectedRevisionId && selectedRevisionInfo && ( <Button onClick={handleAddAttachment} @@ -456,17 +575,51 @@ function AttachmentTable({ : `${(file.fileSize / 1024).toFixed(1)}KB` : '-'} </div> + {/* ✅ dolceFilePath 상태 표시 */} + {file.dolceFilePath && file.dolceFilePath.trim() !== '' && ( + <div className="text-xs text-blue-600 font-medium"> + Processed + </div> + )} </div> </TableCell> <TableCell> - <Button - variant="ghost" - size="sm" - onClick={() => onDownloadFile(file)} - className="h-8 px-2" - > - <Download className="h-4 w-4" /> - </Button> + <div className="flex items-center gap-1"> + {/* 다운로드 버튼 */} + <Button + variant="ghost" + size="sm" + onClick={() => onDownloadFile(file)} + className="h-8 px-2" + title="Download file" + > + <Download className="h-4 w-4" /> + </Button> + + {/* ✅ 삭제 버튼 */} + <Button + variant="ghost" + size="sm" + onClick={() => handleDeleteFile(file)} + className={`h-8 px-2 ${ + canDeleteFile(file) + ? 'text-red-600 hover:text-red-700 hover:bg-red-50' + : 'text-gray-400 cursor-not-allowed' + }`} + disabled={!canDeleteFile(file) || deletingFileId === file.id} + title={ + canDeleteFile(file) + ? 'Delete file' + : 'Cannot delete processed file' + } + > + {deletingFileId === file.id ? ( + <Loader2 className="h-4 w-4 animate-spin" /> + ) : ( + <Trash2 className="h-4 w-4" /> + )} + </Button> + </div> </TableCell> </TableRow> )) @@ -476,7 +629,7 @@ function AttachmentTable({ </CardContent> </Card> - {/* ✅ AddAttachmentDialog 추가 */} + {/* AddAttachmentDialog */} {selectedRevisionInfo && ( <AddAttachmentDialog open={addAttachmentDialogOpen} @@ -490,12 +643,10 @@ function AttachmentTable({ ) } -/* ------------------------------------------------------------------------------------------------- - * Derived Sub Tables Wrapper - * -----------------------------------------------------------------------------------------------*/ +// SubTables 컴포넌트 - 중복 정의 제거 및 통합 function SubTables() { const router = useRouter() - const { selectedDocumentId, selectedRevisionId, allData, setAllData } = // ✅ setAllData 추가 + const { selectedDocumentId, selectedRevisionId, setSelectedRevisionId, allData, setAllData } = React.useContext(DocumentSelectionContext) // PDF 뷰어 상태 관리 @@ -509,11 +660,166 @@ function SubTables() { const isCancelled = React.useRef(false) const [newRevisionDialogOpen, setNewRevisionDialogOpen] = React.useState(false) + + // ✅ 리비전 수정 다이얼로그 상태 + const [editRevisionDialogOpen, setEditRevisionDialogOpen] = React.useState(false) + const [editingRevision, setEditingRevision] = React.useState<RevisionInfo | null>(null) const handleNewRevision = React.useCallback(() => { setNewRevisionDialogOpen(true) }, []) + // ✅ 리비전 수정 핸들러 + const handleEditRevision = React.useCallback((revision: RevisionInfo) => { + setEditingRevision(revision) + setEditRevisionDialogOpen(true) + }, []) + + // ✅ 리비전 수정 성공 핸들러 + const handleRevisionEditSuccess = React.useCallback((action: 'update' | 'delete', result?: any) => { + if (!allData || !editingRevision) { + // fallback: 전체 새로고침 + setTimeout(() => router.refresh(), 500) + return + } + + try { + if (action === 'delete') { + // 리비전 삭제: allData에서 해당 리비전 제거 + const updatedData = allData.map(doc => { + const updatedDoc = { ...doc } + + if (updatedDoc.allStages) { + const stages = [...updatedDoc.allStages as StageInfo[]] + + for (const stage of stages) { + const revisionIndex = stage.revisions.findIndex(r => r.id === editingRevision.id) + if (revisionIndex !== -1) { + // 해당 리비전 제거 + stage.revisions.splice(revisionIndex, 1) + updatedDoc.allStages = stages + break + } + } + } + + return updatedDoc + }) + + setAllData(updatedData) + + // 삭제된 리비전이 선택되어 있었으면 선택 해제 + if (selectedRevisionId === editingRevision.id) { + setSelectedRevisionId(null) + } + + console.log('✅ Revision deleted and state updated') + + } else if (action === 'update') { + // 리비전 업데이트: allData에서 해당 리비전 정보 수정 + const updatedData = allData.map(doc => { + const updatedDoc = { ...doc } + + if (updatedDoc.allStages) { + const stages = [...updatedDoc.allStages as StageInfo[]] + + for (const stage of stages) { + const revisionIndex = stage.revisions.findIndex(r => r.id === editingRevision.id) + if (revisionIndex !== -1) { + // 해당 리비전 업데이트 + stage.revisions[revisionIndex] = { + ...stage.revisions[revisionIndex], + comment: result?.updatedRevision?.comment || stage.revisions[revisionIndex].comment, + usage: result?.updatedRevision?.usage || stage.revisions[revisionIndex].usage, + usageType: result?.updatedRevision?.usageType || stage.revisions[revisionIndex].usageType, + updatedAt: new Date(), + } + updatedDoc.allStages = stages + break + } + } + } + + return updatedDoc + }) + + setAllData(updatedData) + console.log('✅ Revision updated and state updated') + } + + // 약간의 지연 후 서버 데이터 새로고침 + setTimeout(() => { + router.refresh() + }, 1000) + + } catch (error) { + console.error('❌ Revision edit state update failed:', error) + // 실패 시 전체 새로고침 + setTimeout(() => router.refresh(), 500) + } + }, [allData, editingRevision, setAllData, selectedRevisionId, setSelectedRevisionId, router]) + + // 파일 삭제 함수 + const handleDeleteFile = React.useCallback(async (attachment: AttachmentInfo) => { + try { + const response = await fetch(`/api/attachment-delete`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + attachmentId: attachment.id, + revisionId: attachment.revisionId, + }), + }) + + if (!response.ok) { + const errorData = await response.json() + throw new Error(errorData.error || 'Failed to delete file.') + } + + // 성공시 로컬 상태 업데이트 + if (allData && selectedRevisionId) { + const updatedData = allData.map(doc => { + const updatedDoc = { ...doc } + + if (updatedDoc.allStages) { + const stages = [...updatedDoc.allStages as StageInfo[]] + + for (const stage of stages) { + const revisionIndex = stage.revisions.findIndex(r => r.id === selectedRevisionId) + if (revisionIndex !== -1) { + // 해당 리비전에서 첨부파일 제거 + stage.revisions[revisionIndex] = { + ...stage.revisions[revisionIndex], + attachments: stage.revisions[revisionIndex].attachments.filter( + att => att.id !== attachment.id + ) + } + updatedDoc.allStages = stages + break + } + } + } + + return updatedDoc + }) + + setAllData(updatedData) + console.log('✅ File deleted and state updated') + } + + // 약간의 지연 후 서버 데이터 새로고침 + setTimeout(() => { + router.refresh() + }, 1000) + + } catch (error) { + console.error('Delete file error:', error) + throw error // AttachmentTable에서 에러 핸들링 + } + }, [allData, selectedRevisionId, setAllData, router]) + const handleRevisionUploadSuccess = React.useCallback(async (uploadResult?: any) => { if (!selectedDocumentId || !allData || !uploadResult?.data) { // fallback: 전체 새로고침 @@ -530,7 +836,7 @@ function SubTables() { uploaderType: "vendor", uploaderId: null, uploaderName: uploadResult.data.uploaderName || null, - comment: uploadResult.data.comment || null, // ✅ comment도 포함 + comment: uploadResult.data.comment || null, usage: uploadResult.data.usage, usageType: uploadResult.data.usageType || null, revisionStatus: "UPLOADED", @@ -550,6 +856,7 @@ function SubTables() { revisionId: uploadResult.data.revisionId, fileName: file.fileName, filePath: file.filePath, + dolceFilePath: null, fileSize: file.fileSize, fileType: file.fileType || null, createdAt: new Date(), @@ -561,26 +868,26 @@ function SubTables() { const updatedData = allData.map(doc => { if (doc.documentId === selectedDocumentId) { const updatedDoc = { ...doc } - + // allStages가 있으면 해당 stage에 새 revision 추가 if (updatedDoc.allStages) { - const stages = [...updatedDoc.allStages as StageInfo[]] // ✅ 배열 복사 - const targetStage = stages.find(stage => - stage.stageName === uploadResult.data.stage || + const stages = [...updatedDoc.allStages as StageInfo[]] + const targetStage = stages.find(stage => + stage.stageName === uploadResult.data.stage || stage.stageName === uploadResult.data.usage ) - + if (targetStage) { // 기존 revision과 중복 체크 (같은 revision, usage, usageType) - const isDuplicate = targetStage.revisions.some(rev => + const isDuplicate = targetStage.revisions.some(rev => rev.revision === newRevision.revision && rev.usage === newRevision.usage && rev.usageType === newRevision.usageType ) - + if (!isDuplicate) { targetStage.revisions = [newRevision, ...targetStage.revisions] - updatedDoc.allStages = stages // ✅ 업데이트된 stages 할당 + updatedDoc.allStages = stages } } else { // 첫 번째 stage에 추가 (fallback) @@ -590,7 +897,7 @@ function SubTables() { } } } - + return updatedDoc } return doc @@ -598,9 +905,9 @@ function SubTables() { // State 업데이트 setAllData(updatedData) - + console.log('✅ RevisionTable data update complete') - + } catch (error) { console.error('❌ RevisionTable update failed:', error) // 실패 시 전체 새로고침 @@ -611,7 +918,7 @@ function SubTables() { router.refresh() // 서버 컴포넌트 재렌더링으로 최신 데이터 가져오기 }, 1500) // 1.5초 후 새로고침 (사용자가 업데이트를 확인할 시간) - }, [selectedDocumentId, allData, setAllData]) + }, [selectedDocumentId, allData, setAllData, router]) const selectedDocument = React.useMemo(() => { if (!selectedDocumentId || !allData) return null @@ -738,7 +1045,7 @@ function SubTables() { } setTimeout(() => cleanupHtmlStyle(), 500) } - }, [viewerOpen, cleanupHtmlStyle]) + }, [viewerOpen, cleanupHtmlStyle, instance]) // 문서 로드 React.useEffect(() => { @@ -803,14 +1110,16 @@ function SubTables() { return ( <> <div className="flex gap-4"> - <RevisionTable - revisions={allRevisions} + <RevisionTable + revisions={allRevisions} onViewRevision={handleViewRevision} onNewRevision={handleNewRevision} + onEditRevision={handleEditRevision} // ✅ 수정 함수 전달 /> - <AttachmentTable - attachments={selectedRevisionData?.attachments || []} + <AttachmentTable + attachments={selectedRevisionData?.attachments || []} onDownloadFile={handleDownloadFile} + onDeleteFile={handleDeleteFile} /> </div> @@ -847,6 +1156,14 @@ function SubTables() { drawingKind={selectedDocument.drawingKind || 'B4'} onSuccess={handleRevisionUploadSuccess} /> + + {/* ✅ 리비전 수정 다이얼로그 */} + <EditRevisionDialog + open={editRevisionDialogOpen} + onOpenChange={setEditRevisionDialogOpen} + revision={editingRevision} + onSuccess={handleRevisionEditSuccess} + /> </> ) } @@ -970,15 +1287,15 @@ export function UserVendorDocumentDisplay({ return ( <DocumentSelectionContext.Provider value={ctx}> <div className="space-y-4"> - <Card> - <CardContent className="flex items-center justify-center py-8"> - <SimplifiedDocumentsTable - allPromises={allPromises} - onDataLoaded={setAllData} - onDocumentSelect={handleDocumentSelect} - /> - </CardContent> - </Card> + <Card> + <CardContent className="flex items-center justify-center py-8"> + <SimplifiedDocumentsTable + allPromises={allPromises} + onDataLoaded={setAllData} + onDocumentSelect={handleDocumentSelect} + /> + </CardContent> + </Card> <SelectedDocumentInfo /> <SubTables /> |
