summaryrefslogtreecommitdiff
path: root/lib/techsales-rfq/table/detail-table
diff options
context:
space:
mode:
Diffstat (limited to 'lib/techsales-rfq/table/detail-table')
-rw-r--r--lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx17
-rw-r--r--lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx91
-rw-r--r--lib/techsales-rfq/table/detail-table/update-vendor-sheet.tsx449
-rw-r--r--lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx22
4 files changed, 32 insertions, 547 deletions
diff --git a/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx b/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx
index c4a7edde..cfae0bd7 100644
--- a/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx
+++ b/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx
@@ -14,10 +14,11 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Ellipsis, MessageCircle } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
+import { useRouter } from "next/navigation";
export interface DataTableRowAction<TData> {
row: Row<TData>;
- type: "delete" | "update" | "communicate";
+ type: "delete" | "communicate";
}
// 벤더 견적 데이터 타입 정의
@@ -232,6 +233,13 @@ export function getRfqDetailColumns({
cell: function Cell({ row }) {
const vendorId = row.original.vendorId;
const unreadCount = vendorId ? unreadMessages[vendorId] || 0 : 0;
+ const router = useRouter();
+
+ const handleViewDetails = () => {
+ if (vendorId) {
+ router.push(`/ko/evcp/vendors/${vendorId}/info`);
+ }
+ };
return (
<div className="text-right flex items-center justify-end gap-1">
@@ -269,9 +277,12 @@ export function getRfqDetailColumns({
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[160px]">
<DropdownMenuItem
- onClick={() => setRowAction({ row, type: "update" })}
+ onClick={handleViewDetails}
+ disabled={!vendorId}
+ className="gap-2"
>
- 벤더 수정
+ {/* <Eye className="h-4 w-4" /> */}
+ 벤더 상세정보
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setRowAction({ row, type: "delete" })}
diff --git a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
index 4f8ac37b..a2f012ad 100644
--- a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
+++ b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
@@ -12,11 +12,10 @@ import { toast } from "sonner"
import { Skeleton } from "@/components/ui/skeleton"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
-import { Loader2, UserPlus, BarChart2, Send, Trash2, MessageCircle } from "lucide-react"
+import { Loader2, UserPlus, BarChart2, Send, Trash2 } from "lucide-react"
import { ClientDataTable } from "@/components/client-data-table/data-table"
import { AddVendorDialog } from "./add-vendor-dialog"
import { DeleteVendorDialog } from "./delete-vendor-dialog"
-import { UpdateVendorSheet } from "./update-vendor-sheet"
import { VendorCommunicationDrawer } from "./vendor-communication-drawer"
import { VendorQuotationComparisonDialog } from "./vendor-quotation-comparison-dialog"
@@ -41,28 +40,6 @@ interface RfqDetailTablesProps {
maxHeight?: string | number
}
-// 데이터 타입 정의
-interface Vendor {
- id: number;
- vendorName: string;
- vendorCode: string;
- // 기타 필요한 벤더 속성들
-}
-
-interface Currency {
- code: string;
- name: string;
-}
-
-interface PaymentTerm {
- code: string;
- description: string;
-}
-
-interface Incoterm {
- code: string;
- description: string;
-}
export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps) {
// console.log("selectedRfq", selectedRfq)
@@ -71,14 +48,9 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
const [isLoading, setIsLoading] = useState(false)
const [details, setDetails] = useState<RfqDetailView[]>([])
const [vendorDialogOpen, setVendorDialogOpen] = React.useState(false)
- const [updateSheetOpen, setUpdateSheetOpen] = React.useState(false)
const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false)
const [selectedDetail, setSelectedDetail] = React.useState<RfqDetailView | null>(null)
- const [vendors, setVendors] = React.useState<Vendor[]>([])
- const [currencies, setCurrencies] = React.useState<Currency[]>([])
- const [paymentTerms, setPaymentTerms] = React.useState<PaymentTerm[]>([])
- const [incoterms, setIncoterms] = React.useState<Incoterm[]>([])
const [isAdddialogLoading, setIsAdddialogLoading] = useState(false)
const [rowAction, setRowAction] = React.useState<DataTableRowAction<RfqDetailView> | null>(null)
@@ -159,21 +131,6 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
const handleAddVendor = useCallback(async () => {
try {
setIsAdddialogLoading(true)
-
- // TODO: 기술영업용 벤더, 통화, 지불조건, 인코텀즈 데이터 로드 함수 구현 필요
- // const [vendorsData, currenciesData, paymentTermsData, incotermsData] = await Promise.all([
- // fetchVendors(),
- // fetchCurrencies(),
- // fetchPaymentTerms(),
- // fetchIncoterms()
- // ])
-
- // 임시 데이터
- setVendors([])
- setCurrencies([])
- setPaymentTerms([])
- setIncoterms([])
-
setVendorDialogOpen(true)
} catch (error) {
console.error("데이터 로드 오류:", error)
@@ -417,39 +374,16 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
return;
}
- // 다른 액션들은 기존과 동일하게 처리
- setIsAdddialogLoading(true);
-
- // TODO: 필요한 데이터 로드 (벤더, 통화, 지불조건, 인코텀즈)
- // const [vendorsData, currenciesData, paymentTermsData, incotermsData] = await Promise.all([
- // fetchVendors(),
- // fetchCurrencies(),
- // fetchPaymentTerms(),
- // fetchIncoterms()
- // ]);
-
- // 임시 데이터
- setVendors([]);
- setCurrencies([]);
- setPaymentTerms([]);
- setIncoterms([]);
-
- // 이제 데이터가 로드되었으므로 필요한 작업 수행
- if (rowAction.type === "update") {
- setSelectedDetail(rowAction.row.original);
- setUpdateSheetOpen(true);
- } else if (rowAction.type === "delete") {
+ // 삭제 액션인 경우
+ if (rowAction.type === "delete") {
setSelectedDetail(rowAction.row.original);
setDeleteDialogOpen(true);
+ setRowAction(null);
+ return;
}
} catch (error) {
- console.error("데이터 로드 오류:", error);
- toast.error("데이터를 불러오는 중 오류가 발생했습니다");
- } finally {
- // communicate 타입이 아닌 경우에만 로딩 상태 변경
- if (rowAction && rowAction.type !== "communicate") {
- setIsAdddialogLoading(false);
- }
+ console.error("액션 처리 오류:", error);
+ toast.error("작업을 처리하는 중 오류가 발생했습니다");
}
};
@@ -615,17 +549,6 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
onSuccess={handleRefreshData}
/>
- <UpdateVendorSheet
- open={updateSheetOpen}
- onOpenChange={setUpdateSheetOpen}
- detail={selectedDetail}
- vendors={vendors}
- currencies={currencies}
- paymentTerms={paymentTerms}
- incoterms={incoterms}
- onSuccess={handleRefreshData}
- />
-
<DeleteVendorDialog
open={deleteDialogOpen}
onOpenChange={setDeleteDialogOpen}
diff --git a/lib/techsales-rfq/table/detail-table/update-vendor-sheet.tsx b/lib/techsales-rfq/table/detail-table/update-vendor-sheet.tsx
deleted file mode 100644
index 0399f4df..00000000
--- a/lib/techsales-rfq/table/detail-table/update-vendor-sheet.tsx
+++ /dev/null
@@ -1,449 +0,0 @@
-"use client"
-
-import * as React from "react"
-import { zodResolver } from "@hookform/resolvers/zod"
-import { Check, ChevronsUpDown, Loader } from "lucide-react"
-import { useForm } from "react-hook-form"
-import { toast } from "sonner"
-import { z } from "zod"
-
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
-} from "@/components/ui/command"
-import {
- Form,
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form"
-import { Input } from "@/components/ui/input"
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover"
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
-import {
- Sheet,
- SheetClose,
- SheetContent,
- SheetDescription,
- SheetFooter,
- SheetHeader,
- SheetTitle,
-} from "@/components/ui/sheet"
-import { Checkbox } from "@/components/ui/checkbox"
-import { ScrollArea } from "@/components/ui/scroll-area"
-
-import { RfqDetailView } from "./rfq-detail-column"
-import { updateRfqDetail } from "@/lib/procurement-rfqs/services"
-
-// 폼 유효성 검증 스키마
-const updateRfqDetailSchema = z.object({
- vendorId: z.string().min(1, "벤더를 선택해주세요"),
- currency: z.string().min(1, "통화를 선택해주세요"),
- paymentTermsCode: z.string().min(1, "지불 조건을 선택해주세요"),
- incotermsCode: z.string().min(1, "인코텀즈를 선택해주세요"),
- incotermsDetail: z.string().optional(),
- deliveryDate: z.string().optional(),
- taxCode: z.string().optional(),
- placeOfShipping: z.string().optional(),
- placeOfDestination: z.string().optional(),
- materialPriceRelatedYn: z.boolean().default(false),
-})
-
-type UpdateRfqDetailFormValues = z.infer<typeof updateRfqDetailSchema>
-
-// 데이터 타입 정의
-interface Vendor {
- id: number;
- vendorName: string;
- vendorCode: string;
-}
-
-interface Currency {
- code: string;
- name: string;
-}
-
-interface PaymentTerm {
- code: string;
- description: string;
-}
-
-interface Incoterm {
- code: string;
- description: string;
-}
-
-interface UpdateRfqDetailSheetProps
- extends React.ComponentPropsWithRef<typeof Sheet> {
- detail: RfqDetailView | null;
- vendors: Vendor[];
- currencies: Currency[];
- paymentTerms: PaymentTerm[];
- incoterms: Incoterm[];
- onSuccess?: () => void;
-}
-
-export function UpdateVendorSheet({
- detail,
- vendors,
- currencies,
- paymentTerms,
- incoterms,
- onSuccess,
- ...props
-}: UpdateRfqDetailSheetProps) {
- const [isUpdatePending, startUpdateTransition] = React.useTransition()
- const [vendorOpen, setVendorOpen] = React.useState(false)
-
- const form = useForm<UpdateRfqDetailFormValues>({
- resolver: zodResolver(updateRfqDetailSchema),
- defaultValues: {
- vendorId: detail?.vendorName ? String(vendors.find(v => v.vendorName === detail.vendorName)?.id || "") : "",
- currency: detail?.currency || "",
- paymentTermsCode: detail?.paymentTermsCode || "",
- incotermsCode: detail?.incotermsCode || "",
- incotermsDetail: detail?.incotermsDetail || "",
- deliveryDate: detail?.deliveryDate ? new Date(detail.deliveryDate).toISOString().split('T')[0] : "",
- taxCode: detail?.taxCode || "",
- placeOfShipping: detail?.placeOfShipping || "",
- placeOfDestination: detail?.placeOfDestination || "",
- materialPriceRelatedYn: detail?.materialPriceRelatedYn || false,
- },
- })
-
- // detail이 변경될 때 form 값 업데이트
- React.useEffect(() => {
- if (detail) {
- const vendorId = vendors.find(v => v.vendorName === detail.vendorName)?.id
-
- form.reset({
- vendorId: vendorId ? String(vendorId) : "",
- currency: detail.currency || "",
- paymentTermsCode: detail.paymentTermsCode || "",
- incotermsCode: detail.incotermsCode || "",
- incotermsDetail: detail.incotermsDetail || "",
- deliveryDate: detail.deliveryDate ? new Date(detail.deliveryDate).toISOString().split('T')[0] : "",
- taxCode: detail.taxCode || "",
- placeOfShipping: detail.placeOfShipping || "",
- placeOfDestination: detail.placeOfDestination || "",
- materialPriceRelatedYn: detail.materialPriceRelatedYn || false,
- })
- }
- }, [detail, form, vendors])
-
- function onSubmit(values: UpdateRfqDetailFormValues) {
- if (!detail) return
-
- startUpdateTransition(async () => {
- try {
- const result = await updateRfqDetail(detail.detailId, values)
-
- if (!result.success) {
- toast.error(result.message || "수정 중 오류가 발생했습니다")
- return
- }
-
- props.onOpenChange?.(false)
- toast.success("RFQ 벤더 정보가 수정되었습니다")
- onSuccess?.()
- } catch (error) {
- console.error("RFQ 벤더 수정 오류:", error)
- toast.error("수정 중 오류가 발생했습니다")
- }
- })
- }
-
- return (
- <Sheet {...props}>
- <SheetContent className="flex w-full flex-col gap-6 sm:max-w-xl">
- <SheetHeader className="text-left">
- <SheetTitle>RFQ 벤더 정보 수정</SheetTitle>
- <SheetDescription>
- 벤더 정보를 수정하고 저장하세요
- </SheetDescription>
- </SheetHeader>
- <ScrollArea className="flex-1 pr-4">
- <Form {...form}>
- <form
- id="update-rfq-detail-form"
- onSubmit={form.handleSubmit(onSubmit)}
- className="flex flex-col gap-4"
- >
- {/* 검색 가능한 벤더 선택 필드 */}
- <FormField
- control={form.control}
- name="vendorId"
- render={({ field }) => (
- <FormItem className="flex flex-col">
- <FormLabel>벤더 <span className="text-red-500">*</span></FormLabel>
- <Popover open={vendorOpen} onOpenChange={setVendorOpen}>
- <PopoverTrigger asChild>
- <FormControl>
- <Button
- variant="outline"
- role="combobox"
- aria-expanded={vendorOpen}
- className={cn(
- "w-full justify-between",
- !field.value && "text-muted-foreground"
- )}
- >
- {field.value
- ? vendors.find((vendor) => String(vendor.id) === field.value)
- ? `${vendors.find((vendor) => String(vendor.id) === field.value)?.vendorName} (${vendors.find((vendor) => String(vendor.id) === field.value)?.vendorCode})`
- : "벤더를 선택하세요"
- : "벤더를 선택하세요"}
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
- </Button>
- </FormControl>
- </PopoverTrigger>
- <PopoverContent className="w-[400px] p-0">
- <Command>
- <CommandInput placeholder="벤더 검색..." />
- <CommandEmpty>검색 결과가 없습니다</CommandEmpty>
- <ScrollArea className="h-60">
- <CommandGroup>
- {vendors.map((vendor) => (
- <CommandItem
- key={vendor.id}
- value={`${vendor.vendorName} ${vendor.vendorCode}`}
- onSelect={() => {
- form.setValue("vendorId", String(vendor.id), {
- shouldValidate: true,
- })
- setVendorOpen(false)
- }}
- >
- <Check
- className={cn(
- "mr-2 h-4 w-4",
- String(vendor.id) === field.value
- ? "opacity-100"
- : "opacity-0"
- )}
- />
- {vendor.vendorName} ({vendor.vendorCode})
- </CommandItem>
- ))}
- </CommandGroup>
- </ScrollArea>
- </Command>
- </PopoverContent>
- </Popover>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="currency"
- render={({ field }) => (
- <FormItem>
- <FormLabel>통화 <span className="text-red-500">*</span></FormLabel>
- <Select onValueChange={field.onChange} value={field.value}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="통화를 선택하세요" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {currencies.map((currency) => (
- <SelectItem key={currency.code} value={currency.code}>
- {currency.name} ({currency.code})
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <div className="grid grid-cols-2 gap-4">
- <FormField
- control={form.control}
- name="paymentTermsCode"
- render={({ field }) => (
- <FormItem>
- <FormLabel>지불 조건 <span className="text-red-500">*</span></FormLabel>
- <Select onValueChange={field.onChange} value={field.value}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="지불 조건 선택" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {paymentTerms.map((term) => (
- <SelectItem key={term.code} value={term.code}>
- {term.description}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="incotermsCode"
- render={({ field }) => (
- <FormItem>
- <FormLabel>인코텀즈 <span className="text-red-500">*</span></FormLabel>
- <Select onValueChange={field.onChange} value={field.value}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="인코텀즈 선택" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {incoterms.map((incoterm) => (
- <SelectItem key={incoterm.code} value={incoterm.code}>
- {incoterm.description}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- <FormMessage />
- </FormItem>
- )}
- />
- </div>
-
- <FormField
- control={form.control}
- name="incotermsDetail"
- render={({ field }) => (
- <FormItem>
- <FormLabel>인코텀즈 세부사항</FormLabel>
- <FormControl>
- <Input {...field} placeholder="인코텀즈 세부사항" />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <div className="grid grid-cols-2 gap-4">
- <FormField
- control={form.control}
- name="deliveryDate"
- render={({ field }) => (
- <FormItem>
- <FormLabel>납품 예정일</FormLabel>
- <FormControl>
- <Input {...field} type="date" />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="taxCode"
- render={({ field }) => (
- <FormItem>
- <FormLabel>세금 코드</FormLabel>
- <FormControl>
- <Input {...field} placeholder="세금 코드" />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- </div>
-
- <div className="grid grid-cols-2 gap-4">
- <FormField
- control={form.control}
- name="placeOfShipping"
- render={({ field }) => (
- <FormItem>
- <FormLabel>선적지</FormLabel>
- <FormControl>
- <Input {...field} placeholder="선적지" />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="placeOfDestination"
- render={({ field }) => (
- <FormItem>
- <FormLabel>도착지</FormLabel>
- <FormControl>
- <Input {...field} placeholder="도착지" />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- </div>
-
- <FormField
- control={form.control}
- name="materialPriceRelatedYn"
- render={({ field }) => (
- <FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4">
- <FormControl>
- <Checkbox
- checked={field.value}
- onCheckedChange={field.onChange}
- />
- </FormControl>
- <div className="space-y-1 leading-none">
- <FormLabel>자재 가격 관련 여부</FormLabel>
- </div>
- </FormItem>
- )}
- />
- </form>
- </Form>
- </ScrollArea>
- <SheetFooter className="gap-2 pt-2 sm:space-x-0">
- <SheetClose asChild>
- <Button type="button" variant="outline">
- 취소
- </Button>
- </SheetClose>
- <Button
- type="submit"
- form="update-rfq-detail-form"
- disabled={isUpdatePending}
- >
- {isUpdatePending && (
- <Loader
- className="mr-2 size-4 animate-spin"
- aria-hidden="true"
- />
- )}
- 저장
- </Button>
- </SheetFooter>
- </SheetContent>
- </Sheet>
- )
-} \ No newline at end of file
diff --git a/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx b/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx
index 51ef7b38..958cc8d1 100644
--- a/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx
+++ b/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx
@@ -37,7 +37,7 @@ import {
} from "@/components/ui/dialog"
import { formatDateTime } from "@/lib/utils"
import { formatFileSize } from "@/lib/utils" // formatFileSize 유틸리티 임포트
-import { fetchVendorComments, markMessagesAsRead } from "@/lib/procurement-rfqs/services"
+import { fetchTechSalesVendorComments, markTechSalesMessagesAsRead } from "@/lib/techsales-rfq/service"
// 타입 정의
interface Comment {
@@ -59,7 +59,7 @@ interface Attachment {
id: number;
fileName: string;
fileSize: number;
- fileType: string;
+ fileType: string | null;
filePath: string;
uploadedAt: Date;
}
@@ -99,8 +99,8 @@ async function sendComment(params: {
});
}
- // API 엔드포인트 구성
- const url = `/api/procurement-rfqs/${params.rfqId}/vendors/${params.vendorId}/comments`;
+ // API 엔드포인트 구성 - techSales용으로 변경
+ const url = `/api/tech-sales-rfqs/${params.rfqId}/vendors/${params.vendorId}/comments`;
// API 호출
const response = await fetch(url, {
@@ -169,11 +169,11 @@ export function VendorCommunicationDrawer({
setIsLoading(true);
// Server Action을 사용하여 코멘트 데이터 가져오기
- const commentsData = await fetchVendorComments(selectedRfq.id, selectedVendor.vendorId || 0);
+ const commentsData = await fetchTechSalesVendorComments(selectedRfq.id, selectedVendor.vendorId || 0);
setComments(commentsData as Comment[]); // 구체적인 타입으로 캐스팅
// Server Action을 사용하여 읽지 않은 메시지를 읽음 상태로 변경
- await markMessagesAsRead(selectedRfq.id, selectedVendor.vendorId || 0);
+ await markTechSalesMessagesAsRead(selectedRfq.id, selectedVendor.vendorId || 0);
} catch (error) {
console.error("코멘트 로드 오류:", error);
toast.error("메시지를 불러오는 중 오류가 발생했습니다");
@@ -269,15 +269,15 @@ export function VendorCommunicationDrawer({
const renderAttachmentPreviewDialog = () => {
if (!selectedAttachment) return null;
- const isImage = selectedAttachment.fileType.startsWith("image/");
- const isPdf = selectedAttachment.fileType.includes("pdf");
+ const isImage = selectedAttachment.fileType?.startsWith("image/");
+ const isPdf = selectedAttachment.fileType?.includes("pdf");
return (
<Dialog open={previewDialogOpen} onOpenChange={setPreviewDialogOpen}>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
- {getFileIcon(selectedAttachment.fileType)}
+ {getFileIcon(selectedAttachment.fileType || '')}
{selectedAttachment.fileName}
</DialogTitle>
<DialogDescription>
@@ -300,7 +300,7 @@ export function VendorCommunicationDrawer({
/>
) : (
<div className="flex flex-col items-center gap-4 p-8">
- {getFileIcon(selectedAttachment.fileType)}
+ {getFileIcon(selectedAttachment.fileType || '')}
<p className="text-muted-foreground text-sm">미리보기를 지원하지 않는 파일 형식입니다.</p>
<Button
variant="outline"
@@ -398,7 +398,7 @@ export function VendorCommunicationDrawer({
className="flex items-center text-xs gap-2 mb-1 p-1 rounded hover:bg-black/5 cursor-pointer"
onClick={() => handleAttachmentPreview(attachment)}
>
- {getFileIcon(attachment.fileType)}
+ {getFileIcon(attachment.fileType || '')}
<span className="flex-1 truncate">{attachment.fileName}</span>
<span className="text-xs opacity-70">
{formatFileSize(attachment.fileSize)}