summaryrefslogtreecommitdiff
path: root/lib/itb/table/view-purchase-request-sheet.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/itb/table/view-purchase-request-sheet.tsx')
-rw-r--r--lib/itb/table/view-purchase-request-sheet.tsx809
1 files changed, 809 insertions, 0 deletions
diff --git a/lib/itb/table/view-purchase-request-sheet.tsx b/lib/itb/table/view-purchase-request-sheet.tsx
new file mode 100644
index 00000000..c4ff9416
--- /dev/null
+++ b/lib/itb/table/view-purchase-request-sheet.tsx
@@ -0,0 +1,809 @@
+// components/purchase-requests/view-purchase-request-sheet.tsx
+"use client";
+
+import * as React from "react";
+import {
+ Sheet,
+ SheetContent,
+ SheetDescription,
+ SheetHeader,
+ SheetTitle,
+ SheetFooter,
+} from "@/components/ui/sheet";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from "@/components/ui/tabs";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+ TableFooter,
+} from "@/components/ui/table";
+import { Separator } from "@/components/ui/separator";
+import {
+ FileList,
+ FileListDescription,
+ FileListHeader,
+ FileListIcon,
+ FileListInfo,
+ FileListItem,
+ FileListName,
+ FileListSize,
+} from "@/components/ui/file-list";
+import {
+ FileText,
+ Package,
+ Edit,
+ Download,
+ Calendar,
+ User,
+ Building,
+ MapPin,
+ Hash,
+ DollarSign,
+ Clock,
+ CheckCircle,
+ XCircle,
+ AlertCircle,
+ Layers,
+ Tag,
+ Paperclip,
+ FileIcon,
+ Eye
+} from "lucide-react";
+import { format } from "date-fns";
+import { ko } from "date-fns/locale";
+import type { PurchaseRequestView } from "@/db/schema";
+import { useRouter } from "next/navigation";
+import { getPurchaseRequestAttachments } from "../service";
+import { downloadFile, quickPreview, formatFileSize, getFileInfo } from "@/lib/file-download";
+
+interface ViewPurchaseRequestSheetProps {
+ request: PurchaseRequestView;
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ onEditClick?: () => void;
+}
+
+const statusConfig = {
+ "작성중": {
+ variant: "secondary" as const,
+ color: "text-gray-500",
+ icon: Edit,
+ bgColor: "bg-gray-100"
+ },
+ "요청완료": {
+ variant: "default" as const,
+ color: "text-blue-500",
+ icon: CheckCircle,
+ bgColor: "bg-blue-50"
+ },
+ "검토중": {
+ variant: "warning" as const,
+ color: "text-yellow-500",
+ icon: Clock,
+ bgColor: "bg-yellow-50"
+ },
+ "승인": {
+ variant: "success" as const,
+ color: "text-green-500",
+ icon: CheckCircle,
+ bgColor: "bg-green-50"
+ },
+ "반려": {
+ variant: "destructive" as const,
+ color: "text-red-500",
+ icon: XCircle,
+ bgColor: "bg-red-50"
+ },
+ "RFQ생성완료": {
+ variant: "outline" as const,
+ color: "text-purple-500",
+ icon: Package,
+ bgColor: "bg-purple-50"
+ },
+};
+
+export function ViewPurchaseRequestSheet({
+ request,
+ open,
+ onOpenChange,
+ onEditClick,
+}: ViewPurchaseRequestSheetProps) {
+ const router = useRouter();
+ const [activeTab, setActiveTab] = React.useState("overview");
+ const [attachments, setAttachments] = React.useState<any[]>([]);
+ const [isLoadingFiles, setIsLoadingFiles] = React.useState(false);
+
+ // 첨부파일 로드
+ React.useEffect(() => {
+ async function loadAttachments() {
+ if (open && request.id) {
+ setIsLoadingFiles(true);
+ try {
+ const result = await getPurchaseRequestAttachments(request.id);
+ if (result.success && result.data) {
+ setAttachments(result.data);
+ } else {
+ console.error("Failed to load attachments:", result.error);
+ setAttachments([]);
+ }
+ } catch (error) {
+ console.error("Error loading attachments:", error);
+ setAttachments([]);
+ } finally {
+ setIsLoadingFiles(false);
+ }
+ }
+ }
+
+ loadAttachments();
+ }, [open, request.id]);
+
+ // 파일 다운로드 핸들러
+ const handleFileDownload = async (file: any) => {
+ const result = await downloadFile(file.filePath, file.originalFileName, {
+ action: 'download',
+ showToast: true,
+ onError: (error) => {
+ console.error("Download failed:", error);
+ },
+ onSuccess: (fileName, fileSize) => {
+ console.log(`Successfully downloaded: ${fileName} (${fileSize} bytes)`);
+ }
+ });
+
+ return result;
+ };
+
+ // 파일 미리보기 핸들러
+ const handleFilePreview = async (file: any) => {
+ const fileInfo = getFileInfo(file.originalFileName);
+
+ if (!fileInfo.canPreview) {
+ // 미리보기가 지원되지 않는 파일은 다운로드
+ return handleFileDownload(file);
+ }
+
+ const result = await quickPreview(file.filePath, file.originalFileName);
+ return result;
+ };
+
+ // 전체 다운로드 핸들러
+ const handleDownloadAll = async () => {
+ for (const file of attachments) {
+ await handleFileDownload(file);
+ // 여러 파일 다운로드 시 간격 두기
+ await new Promise(resolve => setTimeout(resolve, 500));
+ }
+ };
+
+ // 아이템 총액 계산
+ const totalAmount = React.useMemo(() => {
+ if (!request.items || !Array.isArray(request.items)) return 0;
+ return request.items.reduce((sum, item) => {
+ const subtotal = (item.quantity || 0) * (item.estimatedUnitPrice || 0);
+ return sum + subtotal;
+ }, 0);
+ }, [request.items]);
+
+ const handleEdit = () => {
+ if (onEditClick) {
+ onEditClick();
+ } else {
+ onOpenChange(false);
+ }
+ };
+
+ const handleExport = () => {
+ // Export to Excel 기능
+ console.log("Export to Excel");
+ };
+
+ const StatusIcon = statusConfig[request.status]?.icon || AlertCircle;
+ const statusBgColor = statusConfig[request.status]?.bgColor || "bg-gray-50";
+
+ return (
+ <Sheet open={open} onOpenChange={onOpenChange}>
+ <SheetContent className="w-[900px] max-w-[900px] overflow-hidden flex flex-col min-h-0" style={{width:900 , maxWidth:900}}>
+ <SheetHeader className="flex-shrink-0">
+ <div className="flex items-center justify-between">
+ <SheetTitle>구매요청 상세</SheetTitle>
+ <div className={`flex items-center gap-2 px-3 py-1.5 rounded-full ${statusBgColor}`}>
+ <StatusIcon className={`h-4 w-4 ${statusConfig[request.status]?.color}`} />
+ <span className={`text-sm font-medium ${statusConfig[request.status]?.color}`}>
+ {request.status}
+ </span>
+ </div>
+ </div>
+ <SheetDescription>
+ 요청번호: <span className="font-mono font-medium">{request.requestCode}</span> |
+ 작성일: {request.createdAt && format(new Date(request.createdAt), "yyyy-MM-dd")}
+ </SheetDescription>
+ </SheetHeader>
+
+ <Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col min-h-0">
+ <TabsList className="grid w-full grid-cols-4 flex-shrink-0">
+ <TabsTrigger value="overview">
+ <FileText className="mr-2 h-4 w-4" />
+ 개요
+ </TabsTrigger>
+ <TabsTrigger value="items">
+ <Package className="mr-2 h-4 w-4" />
+ 품목 정보
+ {request.itemCount > 0 && (
+ <span className="ml-2 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground">
+ {request.itemCount}
+ </span>
+ )}
+ </TabsTrigger>
+ <TabsTrigger value="files">
+ <Paperclip className="mr-2 h-4 w-4" />
+ 첨부파일
+ {attachments.length > 0 && (
+ <span className="ml-2 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground">
+ {attachments.length}
+ </span>
+ )}
+ </TabsTrigger>
+ <TabsTrigger value="history">
+ <Clock className="mr-2 h-4 w-4" />
+ 처리 이력
+ </TabsTrigger>
+ </TabsList>
+
+ <div className="flex-1 overflow-y-auto px-1 min-h-0">
+ <TabsContent value="overview" className="space-y-6 mt-6">
+ {/* 기본 정보 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-base">기본 정보</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ <div>
+ <p className="text-sm text-muted-foreground mb-1">요청 제목</p>
+ <p className="text-lg font-semibold">{request.requestTitle}</p>
+ </div>
+
+ {request.requestDescription && (
+ <div>
+ <p className="text-sm text-muted-foreground mb-1">요청 설명</p>
+ <p className="text-sm bg-muted/30 p-3 rounded-lg">{request.requestDescription}</p>
+ </div>
+ )}
+
+ <Separator />
+
+ <div className="grid grid-cols-2 gap-4">
+ <div className="flex items-center gap-3">
+ <Calendar className="h-4 w-4 text-muted-foreground" />
+ <div>
+ <p className="text-sm text-muted-foreground">희망 납기일</p>
+ <p className="font-medium">
+ {request.requestedDeliveryDate
+ ? format(new Date(request.requestedDeliveryDate), "yyyy-MM-dd")
+ : "-"}
+ </p>
+ </div>
+ </div>
+
+ <div className="flex items-center gap-3">
+ <DollarSign className="h-4 w-4 text-muted-foreground" />
+ <div>
+ <p className="text-sm text-muted-foreground">예상 예산</p>
+ <p className="font-medium">{request.estimatedBudget || "-"}</p>
+ </div>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+
+ {/* 프로젝트 & 패키지 정보 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-base">프로젝트 정보</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ <div className="grid grid-cols-2 gap-4">
+ <div className="flex items-start gap-3">
+ <Hash className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">프로젝트 코드</p>
+ <p className="font-medium">{request.projectCode || "-"}</p>
+ </div>
+ </div>
+
+ <div className="flex items-start gap-3">
+ <FileText className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">프로젝트명</p>
+ <p className="font-medium">{request.projectName || "-"}</p>
+ </div>
+ </div>
+
+ <div className="flex items-start gap-3">
+ <Building className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">발주처</p>
+ <p className="font-medium">{request.projectCompany || "-"}</p>
+ </div>
+ </div>
+
+ <div className="flex items-start gap-3">
+ <MapPin className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">현장</p>
+ <p className="font-medium">{request.projectSite || "-"}</p>
+ </div>
+ </div>
+ </div>
+
+ <Separator />
+
+ <div className="grid grid-cols-2 gap-4">
+ <div className="flex items-start gap-3">
+ <Package className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">패키지</p>
+ <p className="font-medium">{request.packageNo} - {request.packageName || "-"}</p>
+ </div>
+ </div>
+
+ <div className="flex items-start gap-3">
+ <Tag className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">SM 코드</p>
+ <p className="font-medium">{request.smCode || "-"}</p>
+ </div>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+
+ {/* 자재 정보 */}
+ {(request.majorItemMaterialCategory || request.majorItemMaterialDescription) && (
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-base">자재 정보</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <div className="grid grid-cols-2 gap-4">
+ <div className="flex items-start gap-3">
+ <Layers className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">자재 그룹</p>
+ <p className="font-medium">{request.majorItemMaterialCategory || "-"}</p>
+ </div>
+ </div>
+
+ <div className="flex items-start gap-3">
+ <FileText className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">자재 설명</p>
+ <p className="font-medium">{request.majorItemMaterialDescription || "-"}</p>
+ </div>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+ )}
+
+ {/* 담당자 정보 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-base">담당자 정보</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <div className="grid grid-cols-2 gap-4">
+ <div className="flex items-start gap-3">
+ <User className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">설계 담당자</p>
+ <p className="font-medium">{request.engPicName || "-"}</p>
+ {request.engPicEmail && (
+ <p className="text-sm text-muted-foreground">{request.engPicEmail}</p>
+ )}
+ </div>
+ </div>
+
+ <div className="flex items-start gap-3">
+ <User className="h-4 w-4 text-muted-foreground mt-0.5" />
+ <div>
+ <p className="text-sm text-muted-foreground">구매 담당자</p>
+ <p className="font-medium">{request.purchasePicName || "미배정"}</p>
+ {request.purchasePicEmail && (
+ <p className="text-sm text-muted-foreground">{request.purchasePicEmail}</p>
+ )}
+ </div>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+
+ {/* 반려 사유 */}
+ {request.status === "반려" && request.rejectReason && (
+ <Card className="border-destructive">
+ <CardHeader className="bg-destructive/5">
+ <CardTitle className="text-base text-destructive flex items-center gap-2">
+ <XCircle className="h-4 w-4" />
+ 반려 사유
+ </CardTitle>
+ </CardHeader>
+ <CardContent className="pt-4">
+ <p className="text-sm">{request.rejectReason}</p>
+ </CardContent>
+ </Card>
+ )}
+ </TabsContent>
+
+ <TabsContent value="items" className="mt-6">
+ <Card>
+ <CardHeader>
+ <div className="flex items-center justify-between">
+ <div>
+ <CardTitle className="text-base">품목 목록</CardTitle>
+ <CardDescription className="mt-1">
+ 총 {request.itemCount || 0}개 품목 | 총 수량 {request.totalQuantity || 0}개
+ </CardDescription>
+ </div>
+ <Button variant="outline" size="sm" onClick={handleExport}>
+ <Download className="mr-2 h-4 w-4" />
+ Excel 다운로드
+ </Button>
+ </div>
+ </CardHeader>
+ <CardContent>
+ {(!request.items || request.items.length === 0) ? (
+ <div className="flex flex-col items-center justify-center h-32 text-muted-foreground">
+ <Package className="h-8 w-8 mb-2" />
+ <p>등록된 품목이 없습니다</p>
+ </div>
+ ) : (
+ <div className="border rounded-lg">
+ <Table>
+ <TableHeader>
+ <TableRow>
+ <TableHead className="w-[50px] text-center">번호</TableHead>
+ <TableHead>아이템 코드</TableHead>
+ <TableHead>아이템명</TableHead>
+ <TableHead>사양</TableHead>
+ <TableHead className="text-right">수량</TableHead>
+ <TableHead className="text-center">단위</TableHead>
+ <TableHead className="text-right">예상 단가</TableHead>
+ <TableHead className="text-right">예상 금액</TableHead>
+ {request.items[0]?.remarks && <TableHead>비고</TableHead>}
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {request.items.map((item: any, index: number) => {
+ const subtotal = (item.quantity || 0) * (item.estimatedUnitPrice || 0);
+ return (
+ <TableRow key={item.id || index}>
+ <TableCell className="text-center">{index + 1}</TableCell>
+ <TableCell className="font-mono text-sm">{item.itemCode || "-"}</TableCell>
+ <TableCell className="font-medium">{item.itemName}</TableCell>
+ <TableCell className="text-sm">{item.specification || "-"}</TableCell>
+ <TableCell className="text-right font-medium">
+ {item.quantity?.toLocaleString('ko-KR')}
+ </TableCell>
+ <TableCell className="text-center">{item.unit}</TableCell>
+ <TableCell className="text-right">
+ {item.estimatedUnitPrice
+ ? item.estimatedUnitPrice.toLocaleString('ko-KR') + "원"
+ : "-"}
+ </TableCell>
+ <TableCell className="text-right font-medium">
+ {subtotal > 0
+ ? subtotal.toLocaleString('ko-KR') + "원"
+ : "-"}
+ </TableCell>
+ {request.items[0]?.remarks && (
+ <TableCell className="text-sm">{item.remarks || "-"}</TableCell>
+ )}
+ </TableRow>
+ );
+ })}
+ </TableBody>
+ <TableFooter>
+ <TableRow>
+ <TableCell colSpan={6} className="text-right font-medium">
+ 총 합계
+ </TableCell>
+ <TableCell colSpan={2} className="text-right">
+ <div className="flex flex-col">
+ <span className="text-2xl font-bold text-primary">
+ {new Intl.NumberFormat('ko-KR', {
+ style: 'currency',
+ currency: 'KRW'
+ }).format(totalAmount)}
+ </span>
+ </div>
+ </TableCell>
+ {request.items[0]?.remarks && <TableCell />}
+ </TableRow>
+ </TableFooter>
+ </Table>
+ </div>
+ )}
+ </CardContent>
+ </Card>
+ </TabsContent>
+
+ <TabsContent value="files" className="mt-6">
+ <Card>
+ <CardHeader>
+ <div className="flex items-center justify-between">
+ <div>
+ <CardTitle className="text-base">첨부파일</CardTitle>
+ <CardDescription className="mt-1">
+ 구매 요청 관련 문서 및 파일
+ </CardDescription>
+ </div>
+ </div>
+ </CardHeader>
+ <CardContent>
+ {isLoadingFiles ? (
+ <div className="flex items-center justify-center h-32">
+ <p className="text-muted-foreground">파일 목록을 불러오는 중...</p>
+ </div>
+ ) : attachments.length === 0 ? (
+ <div className="flex flex-col items-center justify-center h-32 text-muted-foreground">
+ <Paperclip className="h-8 w-8 mb-2" />
+ <p>첨부된 파일이 없습니다</p>
+ </div>
+ ) : (
+ <div className="space-y-4">
+ <div className="flex items-center justify-between mb-4">
+ <p className="text-sm text-muted-foreground">
+ 총 {attachments.length}개의 파일이 첨부되어 있습니다
+ </p>
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleDownloadAll}
+ disabled={attachments.length === 0}
+ >
+ <Download className="mr-2 h-4 w-4" />
+ 전체 다운로드
+ </Button>
+ </div>
+
+ <FileList>
+ {attachments.map((file, index) => {
+ const fileInfo = getFileInfo(file.originalFileName || file.fileName);
+ return (
+ <FileListItem key={file.id || file.fileName || index}>
+ <FileListIcon>
+ <FileIcon className="h-4 w-4" />
+ </FileListIcon>
+ <FileListInfo>
+ <FileListHeader>
+ <FileListName>
+ {file.originalFileName || file.fileName}
+ </FileListName>
+ <FileListSize>
+ {file.fileSize}
+ </FileListSize>
+ </FileListHeader>
+ <FileListDescription className="flex items-center gap-4">
+ {file.createdAt && (
+ <span className="text-xs">
+ {format(new Date(file.createdAt), "yyyy-MM-dd HH:mm")}
+ </span>
+ )}
+ {file.category && (
+ <Badge variant="secondary" className="text-xs">
+ {file.category}
+ </Badge>
+ )}
+ </FileListDescription>
+ </FileListInfo>
+ <div className="flex items-center gap-1">
+ {fileInfo.canPreview && (
+ <Button
+ variant="ghost"
+ size="icon"
+ className="h-8 w-8"
+ onClick={() => handleFilePreview(file)}
+ title="미리보기"
+ >
+ <Eye className="h-4 w-4" />
+ </Button>
+ )}
+ <Button
+ variant="ghost"
+ size="icon"
+ className="h-8 w-8"
+ onClick={() => handleFileDownload(file)}
+ title="다운로드"
+ >
+ <Download className="h-4 w-4" />
+ </Button>
+ </div>
+ </FileListItem>
+ );
+ })}
+ </FileList>
+
+ {/* 파일 종류별 요약 */}
+ {attachments.length > 0 && (
+ <div className="mt-6 p-4 bg-muted/30 rounded-lg">
+ <h4 className="text-sm font-medium mb-3">파일 요약</h4>
+ <div className="grid grid-cols-3 gap-4 text-sm">
+ <div>
+ <p className="text-muted-foreground">총 파일 수</p>
+ <p className="font-medium">{attachments.length}개</p>
+ </div>
+ <div>
+ <p className="text-muted-foreground">총 용량</p>
+ <p className="font-medium">
+ {formatFileSize(
+ attachments.reduce((sum, file) => sum + (file.fileSize || 0), 0)
+ )}
+ </p>
+ </div>
+ <div>
+ <p className="text-muted-foreground">최근 업로드</p>
+ <p className="font-medium">
+ {attachments[0]?.createdAt
+ ? format(new Date(attachments[0].createdAt), "yyyy-MM-dd")
+ : "-"}
+ </p>
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ )}
+ </CardContent>
+ </Card>
+ </TabsContent>
+
+ <TabsContent value="history" className="mt-6">
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-base">처리 이력</CardTitle>
+ <CardDescription>요청서의 처리 현황을 시간순으로 확인할 수 있습니다</CardDescription>
+ </CardHeader>
+ <CardContent>
+ <div className="relative">
+ <div className="absolute left-3 top-0 bottom-0 w-0.5 bg-border" />
+
+ <div className="space-y-6">
+ {/* 생성 */}
+ <div className="flex gap-4">
+ <div className="relative">
+ <div className="h-6 w-6 rounded-full bg-primary flex items-center justify-center">
+ <div className="h-2 w-2 rounded-full bg-white" />
+ </div>
+ </div>
+ <div className="flex-1 -mt-0.5">
+ <p className="font-medium">요청서 작성</p>
+ <p className="text-sm text-muted-foreground">
+ {request.createdByName} ({request.createdByEmail})
+ </p>
+ <p className="text-xs text-muted-foreground mt-1">
+ {request.createdAt && format(new Date(request.createdAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}
+ </p>
+ </div>
+ </div>
+
+ {/* 확정 */}
+ {request.confirmedAt && (
+ <div className="flex gap-4">
+ <div className="relative">
+ <div className="h-6 w-6 rounded-full bg-blue-500 flex items-center justify-center">
+ <CheckCircle className="h-3 w-3 text-white" />
+ </div>
+ </div>
+ <div className="flex-1 -mt-0.5">
+ <p className="font-medium">요청 확정</p>
+ <p className="text-sm text-muted-foreground">
+ {request.confirmedByName}
+ </p>
+ <p className="text-xs text-muted-foreground mt-1">
+ {format(new Date(request.confirmedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}
+ </p>
+ </div>
+ </div>
+ )}
+
+ {/* 반려 */}
+ {request.status === "반려" && request.rejectReason && (
+ <div className="flex gap-4">
+ <div className="relative">
+ <div className="h-6 w-6 rounded-full bg-destructive flex items-center justify-center">
+ <XCircle className="h-3 w-3 text-white" />
+ </div>
+ </div>
+ <div className="flex-1 -mt-0.5">
+ <p className="font-medium text-destructive">반려됨</p>
+ <p className="text-sm text-muted-foreground">
+ {request.rejectReason}
+ </p>
+ <p className="text-xs text-muted-foreground mt-1">
+ {request.updatedAt && format(new Date(request.updatedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}
+ </p>
+ </div>
+ </div>
+ )}
+
+ {/* RFQ 생성 */}
+ {request.rfqCreatedAt && (
+ <div className="flex gap-4">
+ <div className="relative">
+ <div className="h-6 w-6 rounded-full bg-green-500 flex items-center justify-center">
+ <Package className="h-3 w-3 text-white" />
+ </div>
+ </div>
+ <div className="flex-1 -mt-0.5">
+ <p className="font-medium">RFQ 생성 완료</p>
+ <p className="text-sm text-muted-foreground">
+ RFQ 번호: <span className="font-mono font-medium">{request.rfqCode}</span>
+ </p>
+ <p className="text-xs text-muted-foreground mt-1">
+ {format(new Date(request.rfqCreatedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}
+ </p>
+ </div>
+ </div>
+ )}
+
+ {/* 최종 수정 */}
+ {request.updatedAt && request.updatedAt !== request.createdAt && !request.rfqCreatedAt && request.status !== "반려" && (
+ <div className="flex gap-4">
+ <div className="relative">
+ <div className="h-6 w-6 rounded-full bg-muted flex items-center justify-center">
+ <Edit className="h-3 w-3 text-muted-foreground" />
+ </div>
+ </div>
+ <div className="flex-1 -mt-0.5">
+ <p className="font-medium">최종 수정</p>
+ <p className="text-sm text-muted-foreground">
+ {request.updatedByName} ({request.updatedByEmail})
+ </p>
+ <p className="text-xs text-muted-foreground mt-1">
+ {format(new Date(request.updatedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}
+ </p>
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+ </TabsContent>
+ </div>
+ </Tabs>
+
+ <SheetFooter className="mt-6 flex-shrink-0">
+ <div className="flex w-full justify-between">
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
+ 닫기
+ </Button>
+ {request.status === "작성중" && (
+ <Button onClick={handleEdit}>
+ <Edit className="mr-2 h-4 w-4" />
+ 수정하기
+ </Button>
+ )}
+ </div>
+ </SheetFooter>
+ </SheetContent>
+ </Sheet>
+ );
+} \ No newline at end of file