diff options
Diffstat (limited to 'lib/site-visit/site-visit-detail-dialog.tsx')
| -rw-r--r-- | lib/site-visit/site-visit-detail-dialog.tsx | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/lib/site-visit/site-visit-detail-dialog.tsx b/lib/site-visit/site-visit-detail-dialog.tsx new file mode 100644 index 00000000..714ca3e3 --- /dev/null +++ b/lib/site-visit/site-visit-detail-dialog.tsx @@ -0,0 +1,266 @@ +"use client"
+
+import * as React from "react"
+import { format } from "date-fns"
+import { ko } from "date-fns/locale"
+import { FileText, Download } from "lucide-react"
+import { toast } from "sonner"
+
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import { Separator } from "@/components/ui/separator"
+
+interface SiteVisitRequest {
+ id: number
+ investigationId: number
+ requesterId: number | null
+ inspectionDuration: string | null
+ requestedStartDate: Date | null
+ requestedEndDate: Date | null
+ shiAttendees: Record<string, unknown> | null
+ shiAttendeeDetails?: string | null
+ vendorRequests: Record<string, unknown> | null
+ additionalRequests: string | null
+ status: string
+ sentAt: Date | null
+ createdAt: Date
+ updatedAt: Date
+
+ // 실사 정보
+ evaluationType: string | null //구매담당자가 작성한 실사방법
+ investigationMethod: string | null // QM담당자가 작성한 실사방법
+ investigationAddress: string | null
+ investigationNotes: string | null
+ forecastedAt: Date | null
+ actualAt: Date | null
+ result: string | null
+ resultNotes: string | null
+
+ // PQ 정보
+ pqItems: string | null
+
+ // 요청자 정보
+ requesterName: string | null
+ requesterEmail: string | null
+ requesterTitle: string | null
+
+ // QM 매니저 정보
+ qmManagerName: string | null
+ qmManagerEmail: string | null
+ qmManagerTitle: string | null
+
+ // 협력업체 정보
+ vendorInfo?: {
+ id: number
+ siteVisitRequestId: number
+ factoryName: string
+ factoryLocation: string
+ factoryAddress: string
+ factoryPicName: string
+ factoryPicPhone: string
+ factoryPicEmail: string
+ factoryDirections: string | null
+ accessProcedure: string | null
+ hasAttachments: boolean
+ otherInfo: string | null
+ submittedAt: Date
+ submittedBy: number
+ createdAt: Date
+ updatedAt: Date
+ } | null
+
+ // SHI 첨부파일
+ shiAttachments?: Array<{
+ id: number
+ siteVisitRequestId: number
+ vendorSiteVisitInfoId: number | null
+ fileName: string
+ originalFileName: string
+ filePath: string
+ fileSize: number
+ mimeType: string
+ createdAt: Date
+ updatedAt: Date
+ }> | null
+}
+
+interface SiteVisitDetailDialogProps {
+ isOpen: boolean
+ onOpenChange: (open: boolean) => void
+ selectedRequest: SiteVisitRequest | null
+}
+
+export function SiteVisitDetailDialog({
+ isOpen,
+ onOpenChange,
+ selectedRequest,
+}: SiteVisitDetailDialogProps) {
+
+ const formatDate = (date: Date | null) => {
+ if (!date) return "-"
+ return format(date, "yyyy.MM.dd", { locale: ko })
+ }
+
+ return (
+ <Dialog open={isOpen} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
+ <DialogHeader>
+ <DialogTitle>방문실사 상세 정보</DialogTitle>
+ <DialogDescription>
+ 작성한 방문실사 정보의 상세 내용입니다.
+ </DialogDescription>
+ </DialogHeader>
+
+ {selectedRequest && (
+ <div className="space-y-6">
+ {/* 기본 정보 */}
+
+ {/* 협력업체 정보 */}
+ {selectedRequest.vendorInfo && (
+ <>
+ <Separator />
+ <div>
+ <h3 className="font-semibold mb-2">작성한 협력업체 정보</h3>
+ <div className="bg-muted p-4 rounded-md">
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
+ <div className="space-y-4">
+ <div>
+ <h4 className="font-semibold mb-2">공장 기본 정보</h4>
+ <div className="space-y-2 text-sm">
+ <div><span className="font-medium">공장명:</span> {selectedRequest.vendorInfo.factoryName}</div>
+ <div><span className="font-medium">공장위치:</span> {selectedRequest.vendorInfo.factoryLocation}</div>
+ <div><span className="font-medium">공장주소:</span> {selectedRequest.vendorInfo.factoryAddress}</div>
+ </div>
+ </div>
+
+ <div>
+ <h4 className="font-semibold mb-2">공장 PIC 정보</h4>
+ <div className="space-y-2 text-sm">
+ <div><span className="font-medium">이름:</span> {selectedRequest.vendorInfo.factoryPicName}</div>
+ <div><span className="font-medium">전화번호:</span> {selectedRequest.vendorInfo.factoryPicPhone}</div>
+ <div><span className="font-medium">이메일:</span> {selectedRequest.vendorInfo.factoryPicEmail}</div>
+ </div>
+ </div>
+ </div>
+
+ <div className="space-y-4">
+ {selectedRequest.vendorInfo.factoryDirections && (
+ <div>
+ <h4 className="font-semibold mb-2">공장 가는 법</h4>
+ <div className="bg-background p-3 rounded-md">
+ <p className="text-sm whitespace-pre-wrap">{selectedRequest.vendorInfo.factoryDirections}</p>
+ </div>
+ </div>
+ )}
+
+ {selectedRequest.vendorInfo.accessProcedure && (
+ <div>
+ <h4 className="font-semibold mb-2">공장 출입절차</h4>
+ <div className="bg-background p-3 rounded-md">
+ <p className="text-sm whitespace-pre-wrap">{selectedRequest.vendorInfo.accessProcedure}</p>
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+
+ {/* 기타 정보 */}
+ {selectedRequest.vendorInfo.otherInfo && (
+ <div className="mt-6">
+ <h4 className="font-semibold mb-2">기타 정보</h4>
+ <div className="bg-background p-3 rounded-md">
+ <p className="text-sm whitespace-pre-wrap">{selectedRequest.vendorInfo.otherInfo}</p>
+ </div>
+ </div>
+ )}
+
+ {/* 제출 정보 */}
+ <div className="mt-6">
+ <h4 className="font-semibold mb-2">제출 정보</h4>
+ <div className="space-y-2 text-sm">
+ <div><span className="font-medium">제출일:</span> {formatDate(selectedRequest.vendorInfo.submittedAt)}</div>
+ <div><span className="font-medium">첨부파일:</span> {selectedRequest.vendorInfo.hasAttachments ? "있음" : "없음"}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </>
+ )}
+
+ <Separator />
+
+ {/* SHI 첨부파일 */}
+ {selectedRequest.shiAttachments && selectedRequest.shiAttachments.length > 0 && (
+ <>
+ <div>
+ <h3 className="font-semibold mb-2">SHI 첨부파일 ({selectedRequest.shiAttachments.length}개)</h3>
+ <div className="bg-muted p-4 rounded-md">
+ <div className="space-y-2">
+ {selectedRequest.shiAttachments.map((attachment) => (
+ <div key={attachment.id} className="flex items-center justify-between p-2 border rounded-md">
+ <div className="flex items-center space-x-2 flex-1 min-w-0">
+ <FileText className="h-4 w-4 text-muted-foreground" />
+ <span className="text-sm truncate">{attachment.originalFileName}</span>
+ <span className="text-xs text-muted-foreground">
+ ({Math.round((attachment.fileSize || 0) / 1024)}KB)
+ </span>
+ </div>
+ <Button
+ type="button"
+ variant="outline"
+ size="sm"
+ className="p-2"
+ onClick={async () => {
+ try {
+ const { downloadFile } = await import('@/lib/file-download')
+ await downloadFile(attachment.filePath, attachment.originalFileName || '', {
+ showToast: true,
+ onError: (error) => {
+ console.error('다운로드 오류:', error)
+ toast.error("다운로드 실패: " + error)
+ },
+ onSuccess: (fileName, fileSize) => {
+ console.log(`다운로드 성공: ${fileName} (${fileSize} bytes)`)
+ }
+ })
+ } catch (error) {
+ console.error('다운로드 오류:', error)
+ toast.error("파일 다운로드 중 오류가 발생했습니다.")
+ }
+ }}
+ aria-label="파일 다운로드"
+ >
+ <Download className="h-4 w-4" />
+ </Button>
+ </div>
+ ))}
+ </div>
+ </div>
+ </div>
+ </>
+ )}
+
+ {/* 추가 요청사항 */}
+ {selectedRequest.additionalRequests && (
+ <>
+ <div>
+ <h3 className="font-semibold mb-2">SHI 추가 요청사항</h3>
+ <div className="bg-muted p-4 rounded-md">
+ <p className="text-sm whitespace-pre-wrap">{selectedRequest.additionalRequests}</p>
+ </div>
+ </div>
+ <Separator />
+ </>
+ )}
+ </div>
+ )}
+ </DialogContent>
+ </Dialog>
+ )
+}
\ No newline at end of file |
