summaryrefslogtreecommitdiff
path: root/lib/rfq-last/vendor-response/editor/attachments-upload.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-29 11:33:37 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-29 11:33:37 +0000
commit8438c05efc7a141e349c5d6416ad08156b4c0775 (patch)
treed90080c294140db8082d0861c649845ec36c4cea /lib/rfq-last/vendor-response/editor/attachments-upload.tsx
parentc17b495c700dcfa040abc93a210727cbe72785f1 (diff)
(최겸) 구매 견적 이메일 추가, 미리보기, 첨부삭제, 기타 수정 등
Diffstat (limited to 'lib/rfq-last/vendor-response/editor/attachments-upload.tsx')
-rw-r--r--lib/rfq-last/vendor-response/editor/attachments-upload.tsx134
1 files changed, 123 insertions, 11 deletions
diff --git a/lib/rfq-last/vendor-response/editor/attachments-upload.tsx b/lib/rfq-last/vendor-response/editor/attachments-upload.tsx
index a2967767..ea7bb9c9 100644
--- a/lib/rfq-last/vendor-response/editor/attachments-upload.tsx
+++ b/lib/rfq-last/vendor-response/editor/attachments-upload.tsx
@@ -1,11 +1,20 @@
"use client"
import { useState, useRef } from "react"
+import { useSession } from "next-auth/react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Alert, AlertDescription } from "@/components/ui/alert"
import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import {
Table,
TableBody,
TableCell,
@@ -23,10 +32,13 @@ import {
Paperclip,
FileCheck,
Calculator,
- Wrench
+ Wrench,
+ X
} from "lucide-react"
import { formatBytes } from "@/lib/utils"
import { cn } from "@/lib/utils"
+import { toast } from "sonner"
+import { deleteVendorResponseAttachment } from "../../service"
interface FileWithType extends File {
attachmentType?: "구매" | "설계"
@@ -37,6 +49,9 @@ interface AttachmentsUploadProps {
attachments: FileWithType[]
onAttachmentsChange: (files: FileWithType[]) => void
existingAttachments?: any[]
+ onExistingAttachmentsChange?: (files: any[]) => void
+ responseId?: number
+ userId?: number
}
const acceptedFileTypes = {
@@ -49,13 +64,18 @@ const acceptedFileTypes = {
export default function AttachmentsUpload({
attachments,
onAttachmentsChange,
- existingAttachments = []
+ existingAttachments = [],
+ onExistingAttachmentsChange,
+ responseId,
+ userId
}: AttachmentsUploadProps) {
const purchaseInputRef = useRef<HTMLInputElement>(null)
const designInputRef = useRef<HTMLInputElement>(null)
const [purchaseDragActive, setPurchaseDragActive] = useState(false)
const [designDragActive, setDesignDragActive] = useState(false)
const [uploadErrors, setUploadErrors] = useState<string[]>([])
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
+ const [fileToDelete, setFileToDelete] = useState<{file: any, isExisting: boolean, index: number} | null>(null)
// 파일 유효성 검사
const validateFile = (file: File): string | null => {
@@ -158,6 +178,57 @@ export default function AttachmentsUpload({
newFiles[index].attachmentType = newType
onAttachmentsChange(newFiles)
}
+
+ // 파일 삭제 확인
+ const handleDeleteClick = (file: any, isExisting: boolean, index: number) => {
+ setFileToDelete({ file, isExisting, index })
+ setDeleteDialogOpen(true)
+ }
+
+ // 파일 삭제 실행
+ const handleDeleteConfirm = async () => {
+ if (!fileToDelete) return
+
+ const { isExisting, index } = fileToDelete
+
+ if (isExisting) {
+ // 기존 첨부파일 삭제 - 서버액션 호출
+ if (responseId && userId && fileToDelete.file.id) {
+ try {
+ const result = await deleteVendorResponseAttachment({
+ attachmentId: fileToDelete.file.id,
+ responseId,
+ userId
+ })
+ if (result.success) {
+ // 클라이언트 상태 업데이트
+ const newExistingAttachments = existingAttachments.filter((_, i) => i !== index)
+ onExistingAttachmentsChange?.(newExistingAttachments)
+ } else {
+ toast.error(`삭제 실패: ${result.error}`)
+ return
+ }
+ } catch (error) {
+ console.error('삭제 API 호출 실패:', error)
+ toast.error('삭제 중 오류가 발생했습니다.')
+ return
+ }
+ }
+ } else {
+ // 새 첨부파일 삭제 (클라이언트에서만)
+ const newFiles = attachments.filter((_, i) => i !== index)
+ onAttachmentsChange(newFiles)
+ }
+
+ setDeleteDialogOpen(false)
+ setFileToDelete(null)
+ }
+
+ // 파일 삭제 취소
+ const handleDeleteCancel = () => {
+ setDeleteDialogOpen(false)
+ setFileToDelete(null)
+ }
// 파일 아이콘 가져오기
const getFileIcon = (fileName: string) => {
@@ -388,14 +459,24 @@ export default function AttachmentsUpload({
</Badge>
</TableCell>
<TableCell>
- <Button
- type="button"
- variant="ghost"
- size="sm"
- onClick={() => window.open(file.filePath, '_blank')}
- >
- <Download className="h-4 w-4" />
- </Button>
+ <div className="flex items-center gap-1">
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => window.open(file.filePath, '_blank')}
+ >
+ <Download className="h-4 w-4" />
+ </Button>
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => handleDeleteClick(file, true, index)}
+ >
+ <Trash2 className="h-4 w-4 text-red-500" />
+ </Button>
+ </div>
</TableCell>
</TableRow>
))}
@@ -449,7 +530,7 @@ export default function AttachmentsUpload({
type="button"
variant="ghost"
size="sm"
- onClick={() => handleFileRemove(index)}
+ onClick={() => handleDeleteClick(file, false, index)}
>
<Trash2 className="h-4 w-4 text-red-500" />
</Button>
@@ -461,6 +542,37 @@ export default function AttachmentsUpload({
</CardContent>
</Card>
)}
+
+ {/* 파일 삭제 확인 다이얼로그 */}
+ <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
+ <DialogContent>
+ <DialogHeader>
+ <DialogTitle>파일 삭제</DialogTitle>
+ <DialogDescription>
+ {fileToDelete?.isExisting ? '기존 첨부파일' : '새로 업로드한 파일'} "{fileToDelete?.file.originalFileName || fileToDelete?.file.name}"을(를) 삭제하시겠습니까?
+ <br />
+ <strong>삭제된 파일은 복구할 수 없습니다.</strong>
+ </DialogDescription>
+ </DialogHeader>
+ <DialogFooter>
+ <Button
+ type="button"
+ variant="outline"
+ onClick={handleDeleteCancel}
+ >
+ 취소
+ </Button>
+ <Button
+ type="button"
+ variant="destructive"
+ onClick={handleDeleteConfirm}
+ >
+ <Trash2 className="h-4 w-4 mr-2" />
+ 삭제
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
</div>
)
} \ No newline at end of file