diff options
Diffstat (limited to 'lib/pq')
| -rw-r--r-- | lib/pq/pq-review-table-new/edit-investigation-dialog.tsx | 124 | ||||
| -rw-r--r-- | lib/pq/pq-review-table-new/site-visit-dialog.tsx | 16 | ||||
| -rw-r--r-- | lib/pq/service.ts | 57 |
3 files changed, 193 insertions, 4 deletions
diff --git a/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx b/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx index 7fd1c3f8..c5470e47 100644 --- a/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx +++ b/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx @@ -40,6 +40,9 @@ import { PopoverTrigger,
} from "@/components/ui/popover"
import { z } from "zod"
+import { getInvestigationAttachments, deleteInvestigationAttachment } from "../service"
+import { downloadFile } from "@/lib/file-download"
+import { Download } from "lucide-react"
// Validation schema for editing investigation
const editInvestigationSchema = z.object({
@@ -74,6 +77,8 @@ export function EditInvestigationDialog({ }: EditInvestigationDialogProps) {
const [isPending, startTransition] = React.useTransition()
const [selectedFiles, setSelectedFiles] = React.useState<File[]>([])
+ const [existingAttachments, setExistingAttachments] = React.useState<any[]>([])
+ const [loadingAttachments, setLoadingAttachments] = React.useState(false)
const fileInputRef = React.useRef<HTMLInputElement>(null)
const form = useForm<EditInvestigationSchema>({
@@ -96,9 +101,67 @@ export function EditInvestigationDialog({ attachments: [],
})
setSelectedFiles([])
+
+ // 기존 첨부파일 로드
+ loadExistingAttachments(investigation.id)
}
}, [investigation, form])
+ // 기존 첨부파일 로드 함수
+ const loadExistingAttachments = async (investigationId: number) => {
+ setLoadingAttachments(true)
+ try {
+ const result = await getInvestigationAttachments(investigationId)
+ if (result.success) {
+ setExistingAttachments(result.attachments || [])
+ } else {
+ toast.error("첨부파일 목록을 불러오는데 실패했습니다.")
+ }
+ } catch (error) {
+ console.error("첨부파일 로드 실패:", error)
+ toast.error("첨부파일 목록을 불러오는 중 오류가 발생했습니다.")
+ } finally {
+ setLoadingAttachments(false)
+ }
+ }
+
+ // 첨부파일 삭제 함수
+ const handleDeleteAttachment = async (attachmentId: number) => {
+ if (!investigation) return
+
+ try {
+ const result = await deleteInvestigationAttachment(attachmentId)
+ if (result.success) {
+ toast.success("첨부파일이 삭제되었습니다.")
+ // 목록 새로고침
+ loadExistingAttachments(investigation.id)
+ } else {
+ toast.error(result.error || "첨부파일 삭제에 실패했습니다.")
+ }
+ } catch (error) {
+ console.error("첨부파일 삭제 오류:", error)
+ toast.error("첨부파일 삭제 중 오류가 발생했습니다.")
+ }
+ }
+
+ // 첨부파일 다운로드 함수
+ const handleDownloadAttachment = async (attachment: any) => {
+ if (!attachment.filePath || !attachment.fileName) {
+ toast.error("첨부파일 정보가 올바르지 않습니다.")
+ return
+ }
+
+ try {
+ await downloadFile(attachment.filePath, attachment.fileName, {
+ showToast: true,
+ action: 'download'
+ })
+ } catch (error) {
+ console.error("첨부파일 다운로드 오류:", error)
+ toast.error("첨부파일 다운로드 중 오류가 발생했습니다.")
+ }
+ }
+
// 파일 선택 핸들러
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(event.target.files || [])
@@ -145,7 +208,7 @@ export function EditInvestigationDialog({ return (
<Dialog open={isOpen} onOpenChange={onClose}>
- <DialogContent className="sm:max-w-[425px]">
+ <DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>실사 정보 수정</DialogTitle>
<DialogDescription>
@@ -246,6 +309,65 @@ export function EditInvestigationDialog({ <FormLabel>첨부파일</FormLabel>
<FormControl>
<div className="space-y-4">
+ {/* 기존 첨부파일 목록 */}
+ {(existingAttachments.length > 0 || loadingAttachments) && (
+ <div className="space-y-2">
+ <div className="text-sm font-medium text-muted-foreground">기존 첨부파일</div>
+ <div className="border rounded-md p-3 space-y-2 max-h-32 overflow-y-auto">
+ {loadingAttachments ? (
+ <div className="flex items-center justify-center py-4">
+ <Loader className="h-4 w-4 animate-spin" />
+ <span className="ml-2 text-sm text-muted-foreground">
+ 첨부파일 로딩 중...
+ </span>
+ </div>
+ ) : existingAttachments.length > 0 ? (
+ existingAttachments.map((attachment) => (
+ <div key={attachment.id} className="flex items-center justify-between text-sm">
+ <div className="flex items-center space-x-2 flex-1 min-w-0">
+ <span className="text-xs px-2 py-1 bg-muted rounded">
+ {attachment.attachmentType || 'FILE'}
+ </span>
+ <span className="truncate">{attachment.fileName}</span>
+ <span className="text-muted-foreground">
+ ({attachment.fileSize ? Math.round(attachment.fileSize / 1024) : 0}KB)
+ </span>
+ </div>
+ <div className="flex items-center gap-1">
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => handleDownloadAttachment(attachment)}
+ className="text-blue-600 hover:text-blue-700"
+ disabled={isPending}
+ title="파일 다운로드"
+ >
+ <Download className="h-4 w-4" />
+ </Button>
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => handleDeleteAttachment(attachment.id)}
+ className="text-destructive hover:text-destructive"
+ disabled={isPending}
+ title="파일 삭제"
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </div>
+ </div>
+ ))
+ ) : (
+ <div className="text-sm text-muted-foreground text-center py-2">
+ 첨부된 파일이 없습니다.
+ </div>
+ )}
+ </div>
+ </div>
+ )}
+
{/* 파일 선택 영역 */}
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">
<input
diff --git a/lib/pq/pq-review-table-new/site-visit-dialog.tsx b/lib/pq/pq-review-table-new/site-visit-dialog.tsx index 172aed98..2b65d03e 100644 --- a/lib/pq/pq-review-table-new/site-visit-dialog.tsx +++ b/lib/pq/pq-review-table-new/site-visit-dialog.tsx @@ -138,7 +138,7 @@ interface SiteVisitDialogProps { vendorCode: string
projectName?: string
projectCode?: string
- pqItems?: string | null
+ pqItems?: Array<{itemCode: string, itemName: string}> | null
}
}
@@ -315,7 +315,19 @@ export function SiteVisitDialog({ <div>
<FormLabel className="text-sm font-medium">대상품목</FormLabel>
<div className="mt-1 p-3 bg-muted rounded-md">
- <div className="font-medium">{investigation.pqItems || "-"}</div>
+ <div className="font-medium">
+ {investigation.pqItems && investigation.pqItems.length > 0
+ ? investigation.pqItems.map((item, index) => (
+ <div key={index} className="flex items-center gap-2">
+ <span className="text-xs px-2 py-1 bg-primary/10 rounded">
+ {item.itemCode}
+ </span>
+ <span>{item.itemName}</span>
+ </div>
+ ))
+ : "-"
+ }
+ </div>
</div>
</div>
</div>
diff --git a/lib/pq/service.ts b/lib/pq/service.ts index f15790eb..989f8d5c 100644 --- a/lib/pq/service.ts +++ b/lib/pq/service.ts @@ -2710,7 +2710,7 @@ export async function sendInvestigationResultsAction(input: { await tx.insert(vendorRegularRegistrations).values({
vendorId: investigation.vendorId,
- status: "audit_pass", // 실사 통과 상태로 시작
+ status: "under_review", // 실사 통과 상태로 시작
majorItems: majorItemsJson,
registrationRequestDate: new Date().toISOString().split('T')[0], // date 타입으로 변환
remarks: `PQ 실사 통과로 자동 생성 (PQ번호: ${investigation.pqNumber || 'N/A'})`,
@@ -3772,6 +3772,61 @@ export async function updateInvestigationDetailsAction(input: { }
}
+// 구매자체평가 첨부파일 조회
+export async function getInvestigationAttachments(investigationId: number) {
+ try {
+ const attachments = await db
+ .select({
+ id: vendorInvestigationAttachments.id,
+ fileName: vendorInvestigationAttachments.fileName,
+ originalFileName: vendorInvestigationAttachments.originalFileName,
+ filePath: vendorInvestigationAttachments.filePath,
+ fileSize: vendorInvestigationAttachments.fileSize,
+ mimeType: vendorInvestigationAttachments.mimeType,
+ attachmentType: vendorInvestigationAttachments.attachmentType,
+ createdAt: vendorInvestigationAttachments.createdAt,
+ })
+ .from(vendorInvestigationAttachments)
+ .where(eq(vendorInvestigationAttachments.investigationId, investigationId))
+ .orderBy(desc(vendorInvestigationAttachments.createdAt));
+
+ return {
+ success: true,
+ attachments,
+ };
+ } catch (error) {
+ console.error("첨부파일 조회 오류:", error);
+ return {
+ success: false,
+ error: "첨부파일 조회 중 오류가 발생했습니다.",
+ attachments: [],
+ };
+ }
+}
+
+// 구매자체평가 첨부파일 삭제
+export async function deleteInvestigationAttachment(attachmentId: number) {
+ try {
+ await db
+ .delete(vendorInvestigationAttachments)
+ .where(eq(vendorInvestigationAttachments.id, attachmentId));
+
+ revalidateTag("pq-submissions");
+ revalidatePath("/evcp/pq_new");
+
+ return {
+ success: true,
+ message: "첨부파일이 성공적으로 삭제되었습니다.",
+ };
+ } catch (error) {
+ console.error("첨부파일 삭제 오류:", error);
+ return {
+ success: false,
+ error: "첨부파일 삭제 중 오류가 발생했습니다.",
+ };
+ }
+}
+
export async function autoDeactivateExpiredPQLists() {
try {
const now = new Date();
|
