summaryrefslogtreecommitdiff
path: root/components/ship-vendor-document/user-vendor-document-table-container.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-10-01 10:31:23 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-10-01 10:31:23 +0000
commit74843fe598702a9a55f914f2d2d291368a5abb13 (patch)
treea88abdaf039f51dd843e0416321f08877b17ea75 /components/ship-vendor-document/user-vendor-document-table-container.tsx
parent33e8452331c301430191b3506825ebaf3edac93a (diff)
(대표님) dolce 수정, spreadjs 수정 등
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.tsx168
1 files changed, 116 insertions, 52 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 7fac34a9..775dac47 100644
--- a/components/ship-vendor-document/user-vendor-document-table-container.tsx
+++ b/components/ship-vendor-document/user-vendor-document-table-container.tsx
@@ -40,6 +40,16 @@ import { useRouter } from 'next/navigation'
import { AddAttachmentDialog } from "./add-attachment-dialog" // ✅ import 추가
import { EditRevisionDialog } from "./edit-revision-dialog" // ✅ 추가
import { downloadFile } from "@/lib/file-download" // ✅ 공용 다운로드 함수 import
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog"
/* -------------------------------------------------------------------------------------------------
* Types & Constants
@@ -172,12 +182,12 @@ function RevisionTable({
// ✅ 리비전 수정 가능 여부 확인 함수
const canEditRevision = React.useCallback((revision: RevisionInfo) => {
// 첨부파일이 없으면 수정 가능
- if ((!revision.attachments || revision.attachments.length === 0)&&revision.uploaderType ==="vendor") {
+ if ((!revision.attachments || revision.attachments.length === 0) && revision.uploaderType === "vendor") {
return true
}
// 모든 첨부파일의 dolceFilePath가 null이거나 빈값이어야 수정 가능
- return revision.attachments.every(attachment =>
+ return revision.attachments.every(attachment =>
!attachment.dolceFilePath || attachment.dolceFilePath.trim() === ''
)
}, [])
@@ -188,7 +198,7 @@ function RevisionTable({
return 'no-files'
}
- const processedCount = revision.attachments.filter(attachment =>
+ const processedCount = revision.attachments.filter(attachment =>
attachment.dolceFilePath && attachment.dolceFilePath.trim() !== ''
).length
@@ -241,7 +251,7 @@ function RevisionTable({
{revisions.map((revision) => {
const canEdit = canEditRevision(revision)
const processStatus = getRevisionProcessStatus(revision)
-
+
return (
<TableRow
key={revision.id}
@@ -264,14 +274,14 @@ function RevisionTable({
{revision.revision}
{/* ✅ 처리 상태 인디케이터 */}
{processStatus === 'fully-processed' && (
- <div
- className="w-2 h-2 bg-blue-500 rounded-full"
+ <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"
+ <div
+ className="w-2 h-2 bg-yellow-500 rounded-full"
title="Some files processed"
/>
)}
@@ -333,7 +343,7 @@ function RevisionTable({
{/* ✅ 처리된 파일 수 표시 */}
{processStatus === 'partially-processed' && (
<span className="text-xs text-muted-foreground">
- ({revision.attachments.filter(att =>
+ ({revision.attachments.filter(att =>
att.dolceFilePath && att.dolceFilePath.trim() !== ''
).length} processed)
</span>
@@ -354,21 +364,20 @@ function RevisionTable({
<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'
+ className={`h-8 px-2 ${canEdit
+ ? 'text-blue-600 hover:text-blue-700 hover:bg-blue-50'
: 'text-muted-foreground cursor-not-allowed'
- }`}
+ }`}
disabled={!canEdit}
title={
- canEdit
- ? 'Edit revision'
+ canEdit
+ ? 'Edit revision'
: 'Cannot edit - some files have been processed'
}
>
@@ -390,17 +399,23 @@ function RevisionTable({
function AttachmentTable({
attachments,
onDownloadFile,
- onDeleteFile, // ✅ 삭제 함수 prop 추가
+ onDeleteFile,
}: {
attachments: AttachmentInfo[]
onDownloadFile: (attachment: AttachmentInfo) => void
- onDeleteFile: (attachment: AttachmentInfo) => Promise<void> // ✅ 삭제 함수 추가
+ onDeleteFile: (attachment: AttachmentInfo) => Promise<void>
}) {
const { selectedRevisionId, allData, setAllData } = React.useContext(DocumentSelectionContext)
const [addAttachmentDialogOpen, setAddAttachmentDialogOpen] = React.useState(false)
- const [deletingFileId, setDeletingFileId] = React.useState<number | null>(null) // ✅ 삭제 중인 파일 ID
+ const [deletingFileId, setDeletingFileId] = React.useState<number | null>(null)
const router = useRouter()
+ // ✅ AlertDialog 상태 추가
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false)
+ const [fileToDelete, setFileToDelete] = React.useState<AttachmentInfo | null>(null)
+ const [errorAlertOpen, setErrorAlertOpen] = React.useState(false)
+ const [errorMessage, setErrorMessage] = React.useState('')
+
// 선택된 리비전 정보 가져오기
const selectedRevisionInfo = React.useMemo(() => {
if (!selectedRevisionId || !allData) return null
@@ -425,34 +440,48 @@ function AttachmentTable({
// ✅ 삭제 가능 여부 확인 함수
const canDeleteFile = React.useCallback((attachment: AttachmentInfo) => {
+ // rejected 상태의 리비전에 속한 첨부파일은 무조건 삭제 가능
+ if (selectedRevisionInfo &&
+ selectedRevisionInfo.revisionStatus &&
+ selectedRevisionInfo.revisionStatus.toLowerCase() === 'rejected') {
+ return true
+ }
+
+ // 그 외의 경우는 기존 로직대로: dolceFilePath가 없거나 빈값인 경우만 삭제 가능
return !attachment.dolceFilePath || attachment.dolceFilePath.trim() === ''
- }, [])
+ }, [selectedRevisionInfo])
- // ✅ 파일 삭제 핸들러
- const handleDeleteFile = React.useCallback(async (attachment: AttachmentInfo) => {
+ // ✅ 삭제 요청 핸들러 (확인 다이얼로그 표시)
+ const handleDeleteRequest = React.useCallback((attachment: AttachmentInfo) => {
if (!canDeleteFile(attachment)) {
- alert('This file cannot be deleted because it has been processed by the system.')
+ setErrorMessage('This file cannot be deleted because it has been processed by the system.')
+ setErrorAlertOpen(true)
return
}
- const confirmDelete = window.confirm(
- `Are you sure you want to delete "${attachment.fileName}"?\nThis action cannot be undone.`
- )
-
- if (!confirmDelete) return
+ setFileToDelete(attachment)
+ setDeleteConfirmOpen(true)
+ }, [canDeleteFile])
+
+ // ✅ 실제 삭제 수행 핸들러
+ const handleConfirmDelete = React.useCallback(async () => {
+ if (!fileToDelete) return
try {
- setDeletingFileId(attachment.id)
- await onDeleteFile(attachment)
+ setDeletingFileId(fileToDelete.id)
+ setDeleteConfirmOpen(false)
+ await onDeleteFile(fileToDelete)
} catch (error) {
console.error('Delete file error:', error)
- alert(`Failed to delete file: ${error instanceof Error ? error.message : 'Unknown error'}`)
+ setErrorMessage(`Failed to delete file: ${error instanceof Error ? error.message : 'Unknown error'}`)
+ setErrorAlertOpen(true)
} finally {
setDeletingFileId(null)
+ setFileToDelete(null)
}
- }, [canDeleteFile, onDeleteFile])
+ }, [fileToDelete, onDeleteFile])
- // 첨부파일 업로드 성공 핸들러
+ // 첨부파일 업로드 성공 핸들러 (기존 코드 유지)
const handleAttachmentUploadSuccess = React.useCallback((uploadResult?: any) => {
if (!selectedRevisionId || !allData || !uploadResult?.data) {
console.log('🔄 Full refresh')
@@ -467,7 +496,7 @@ function AttachmentTable({
revisionId: selectedRevisionId,
fileName: file.fileName,
filePath: file.filePath,
- dolceFilePath: null, // ✅ 새 파일은 dolceFilePath가 없음
+ dolceFilePath: null,
fileSize: file.fileSize,
fileType: file.fileType || null,
createdAt: new Date(),
@@ -484,7 +513,6 @@ function AttachmentTable({
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, ...newAttachments]
@@ -501,7 +529,6 @@ function AttachmentTable({
setAllData(updatedData)
console.log('✅ AttachmentTable update complete')
- // 메인 테이블도 업데이트 (약간의 지연 후)
setTimeout(() => {
router.refresh()
}, 1500)
@@ -518,7 +545,6 @@ function AttachmentTable({
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="text-lg">Attachments</CardTitle>
- {/* + 버튼 */}
{selectedRevisionId && selectedRevisionInfo && (
<Button
onClick={handleAddAttachment}
@@ -551,7 +577,6 @@ function AttachmentTable({
? 'Please select a revision'
: 'No attached files'}
</span>
- {/* 리비전이 선택된 경우 추가 버튼 표시 */}
{selectedRevisionId && selectedRevisionInfo && (
<Button
onClick={handleAddAttachment}
@@ -581,7 +606,6 @@ 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
@@ -591,7 +615,6 @@ function AttachmentTable({
</TableCell>
<TableCell>
<div className="flex items-center gap-1">
- {/* 다운로드 버튼 */}
<Button
variant="ghost"
size="sm"
@@ -601,21 +624,21 @@ function AttachmentTable({
>
<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'
+ onClick={() => handleDeleteRequest(file)}
+ className={`h-8 px-2 ${canDeleteFile(file)
+ ? 'text-red-600 hover:text-red-700 hover:bg-red-50'
: 'text-muted-foreground cursor-not-allowed'
- }`}
+ }`}
disabled={!canDeleteFile(file) || deletingFileId === file.id}
title={
- canDeleteFile(file)
- ? 'Delete file'
+ canDeleteFile(file)
+ ? selectedRevisionInfo?.revisionStatus?.toLowerCase() === 'rejected'
+ ? 'Delete file (rejected revision)'
+ : 'Delete file'
: 'Cannot delete processed file'
}
>
@@ -635,6 +658,47 @@ function AttachmentTable({
</CardContent>
</Card>
+ {/* ✅ 삭제 확인 다이얼로그 */}
+ <AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
+ <AlertDialogContent>
+ <AlertDialogHeader>
+ <AlertDialogTitle>Delete File</AlertDialogTitle>
+ <AlertDialogDescription>
+ Are you sure you want to delete "{fileToDelete?.fileName}"?
+ This action cannot be undone.
+ </AlertDialogDescription>
+ </AlertDialogHeader>
+ <AlertDialogFooter>
+ <AlertDialogCancel onClick={() => setFileToDelete(null)}>
+ Cancel
+ </AlertDialogCancel>
+ <AlertDialogAction
+ onClick={handleConfirmDelete}
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ >
+ Delete
+ </AlertDialogAction>
+ </AlertDialogFooter>
+ </AlertDialogContent>
+ </AlertDialog>
+
+ {/* ✅ 에러 메시지 다이얼로그 */}
+ <AlertDialog open={errorAlertOpen} onOpenChange={setErrorAlertOpen}>
+ <AlertDialogContent>
+ <AlertDialogHeader>
+ <AlertDialogTitle>Error</AlertDialogTitle>
+ <AlertDialogDescription>
+ {errorMessage}
+ </AlertDialogDescription>
+ </AlertDialogHeader>
+ <AlertDialogFooter>
+ <AlertDialogAction onClick={() => setErrorMessage('')}>
+ OK
+ </AlertDialogAction>
+ </AlertDialogFooter>
+ </AlertDialogContent>
+ </AlertDialog>
+
{/* AddAttachmentDialog */}
{selectedRevisionInfo && (
<AddAttachmentDialog
@@ -666,7 +730,7 @@ 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)
@@ -770,7 +834,7 @@ function SubTables() {
try {
// 파일 경로 처리
let downloadPath = attachment.filePath
-
+
// 공용 다운로드 함수 사용 (보안 검증, 파일 체크 모두 포함)
const result = await downloadFile(downloadPath, attachment.fileName, {
action: 'download',
@@ -784,7 +848,7 @@ function SubTables() {
} catch (error) {
console.error('File download error:', error)
-
+
// fallback: API 엔드포인트를 통한 다운로드 시도
try {
const queryParam = attachment.id