summaryrefslogtreecommitdiff
path: root/lib/cbe/table/invite-vendors-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cbe/table/invite-vendors-dialog.tsx')
-rw-r--r--lib/cbe/table/invite-vendors-dialog.tsx428
1 files changed, 0 insertions, 428 deletions
diff --git a/lib/cbe/table/invite-vendors-dialog.tsx b/lib/cbe/table/invite-vendors-dialog.tsx
deleted file mode 100644
index 38edddc1..00000000
--- a/lib/cbe/table/invite-vendors-dialog.tsx
+++ /dev/null
@@ -1,428 +0,0 @@
-"use client"
-
-import * as React from "react"
-import { useForm } from "react-hook-form"
-import { zodResolver } from "@hookform/resolvers/zod"
-import { Loader, Send, User } from "lucide-react"
-import { toast } from "sonner"
-import { z } from "zod"
-
-import { useMediaQuery } from "@/hooks/use-media-query"
-import { Button } from "@/components/ui/button"
-import {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@/components/ui/dialog"
-import {
- Drawer,
- DrawerClose,
- DrawerContent,
- DrawerDescription,
- DrawerFooter,
- DrawerHeader,
- DrawerTitle,
- DrawerTrigger,
-} from "@/components/ui/drawer"
-import { Input } from "@/components/ui/input"
-import { Textarea } from "@/components/ui/textarea"
-import {
- Form,
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
- FormDescription,
-} from "@/components/ui/form"
-import { type Row } from "@tanstack/react-table"
-import { Badge } from "@/components/ui/badge"
-import { ScrollArea } from "@/components/ui/scroll-area"
-
-import { VendorWithCbeFields } from "@/config/vendorCbeColumnsConfig"
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
-import { createCbeEvaluation } from "@/lib/rfqs/service"
-
-// 컴포넌트 내부에서 사용할 폼 스키마 정의
-const formSchema = z.object({
- paymentTerms: z.string().min(1, "지급 조건을 입력하세요"),
- incoterms: z.string().min(1, "Incoterms를 입력하세요"),
- deliverySchedule: z.string().min(1, "배송 일정을 입력하세요"),
- notes: z.string().optional(),
-})
-
-type FormValues = z.infer<typeof formSchema>
-
-interface InviteVendorsDialogProps
- extends React.ComponentPropsWithoutRef<typeof Dialog> {
- rfqId: number
- vendors: Row<VendorWithCbeFields>["original"][]
- currentUserId?: number
- currentUser?: {
- id: string
- name?: string | null
- email?: string | null
- image?: string | null
- companyId?: number | null
- domain?: string | null
- }
- showTrigger?: boolean
- onSuccess?: () => void
- hasMultipleRfqIds?: boolean
-}
-
-export function InviteVendorsDialog({
- rfqId,
- vendors,
- currentUserId,
- currentUser,
- showTrigger = true,
- onSuccess,
- hasMultipleRfqIds,
- ...props
-}: InviteVendorsDialogProps) {
- const [files, setFiles] = React.useState<FileList | null>(null)
- const isDesktop = useMediaQuery("(min-width: 640px)")
- const [isSubmitting, setIsSubmitting] = React.useState(false)
-
- // 로컬 스키마와 폼 값을 사용하도록 수정
- const form = useForm<FormValues>({
- resolver: zodResolver(formSchema),
- defaultValues: {
- paymentTerms: "",
- incoterms: "",
- deliverySchedule: "",
- notes: "",
- },
- mode: "onChange",
- })
-
- // 폼 상태 감시
- const { formState } = form
- const isValid = formState.isValid &&
- !!form.getValues("paymentTerms") &&
- !!form.getValues("incoterms") &&
- !!form.getValues("deliverySchedule")
-
- // 디버깅용 상태 트래킹
- React.useEffect(() => {
- const subscription = form.watch((value) => {
- // 폼 값이 변경될 때마다 실행되는 콜백
- console.log("Form values changed:", value);
- });
-
- return () => subscription.unsubscribe();
- }, [form]);
-
- async function onSubmit(data: FormValues) {
- try {
- setIsSubmitting(true)
-
- // 기본 FormData 생성
- const formData = new FormData()
-
- // rfqId 추가
- formData.append("rfqId", String(rfqId))
-
- // 폼 데이터 추가
- Object.entries(data).forEach(([key, value]) => {
- if (value !== undefined && value !== null) {
- formData.append(key, String(value))
- }
- })
-
- // 현재 사용자 ID 추가
- if (currentUserId) {
- formData.append("evaluatedBy", String(currentUserId))
- }
-
- // 협력업체 ID만 추가 (서버에서 연락처 정보를 조회)
- vendors.forEach((vendor) => {
- formData.append("vendorIds[]", String(vendor.vendorId))
- })
-
- // 파일 추가 (있는 경우에만)
- if (files && files.length > 0) {
- for (let i = 0; i < files.length; i++) {
- formData.append("files", files[i])
- }
- }
-
- // 서버 액션 호출
- const response = await createCbeEvaluation(formData)
-
- if (response.error) {
- toast.error(response.error)
- return
- }
-
- // 성공 처리
- toast.success(`${vendors.length}개 협력업체에 CBE 평가가 성공적으로 전송되었습니다!`)
- form.reset()
- setFiles(null)
- props.onOpenChange?.(false)
- onSuccess?.()
- } catch (error) {
- console.error(error)
- toast.error("CBE 평가 생성 중 오류가 발생했습니다.")
- } finally {
- setIsSubmitting(false)
- }
- }
-
- function handleDialogOpenChange(nextOpen: boolean) {
- if (!nextOpen) {
- form.reset()
- setFiles(null)
- }
- props.onOpenChange?.(nextOpen)
- }
-
- // 필수 필드 라벨에 추가할 요소
- const RequiredLabel = (
- <span className="text-destructive ml-1 font-medium">*</span>
- )
-
- const formContent = (
- <Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
- {/* 선택된 협력업체 정보 표시 */}
- <div className="space-y-2">
- <FormLabel>선택된 협력업체 ({vendors.length})</FormLabel>
- <ScrollArea className="h-20 border rounded-md p-2">
- <div className="flex flex-wrap gap-2">
- {vendors.map((vendor, index) => (
- <Badge key={index} variant="secondary" className="py-1">
- {vendor.vendorName || `협력업체 #${vendor.vendorCode}`}
- </Badge>
- ))}
- </div>
- </ScrollArea>
- <FormDescription>
- 선택된 모든 협력업체의 등록된 연락처에게 CBE 평가 알림이 전송됩니다.
- </FormDescription>
- </div>
-
- {/* 작성자 정보 (읽기 전용) */}
- {currentUser && (
- <div className="border rounded-md p-3 space-y-2">
- <FormLabel>작성자</FormLabel>
- <div className="flex items-center gap-3">
- {currentUser.image ? (
- <Avatar className="h-8 w-8">
- <AvatarImage src={currentUser.image} alt={currentUser.name || ""} />
- <AvatarFallback>
- {currentUser.name?.charAt(0) || <User className="h-4 w-4" />}
- </AvatarFallback>
- </Avatar>
- ) : (
- <Avatar className="h-8 w-8">
- <AvatarFallback>
- {currentUser.name?.charAt(0) || <User className="h-4 w-4" />}
- </AvatarFallback>
- </Avatar>
- )}
- <div>
- <p className="text-sm font-medium">{currentUser.name || "Unknown User"}</p>
- <p className="text-xs text-muted-foreground">{currentUser.email || ""}</p>
- </div>
- </div>
- </div>
- )}
-
- {/* 지급 조건 - 필수 필드 */}
- <FormField
- control={form.control}
- name="paymentTerms"
- render={({ field }) => (
- <FormItem>
- <FormLabel>
- 지급 조건{RequiredLabel}
- </FormLabel>
- <FormControl>
- <Input {...field} placeholder="예: Net 30" />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- {/* Incoterms - 필수 필드 */}
- <FormField
- control={form.control}
- name="incoterms"
- render={({ field }) => (
- <FormItem>
- <FormLabel>
- Incoterms{RequiredLabel}
- </FormLabel>
- <FormControl>
- <Input {...field} placeholder="예: FOB, CIF" />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- {/* 배송 일정 - 필수 필드 */}
- <FormField
- control={form.control}
- name="deliverySchedule"
- render={({ field }) => (
- <FormItem>
- <FormLabel>
- 배송 일정{RequiredLabel}
- </FormLabel>
- <FormControl>
- <Textarea
- {...field}
- placeholder="배송 일정 세부사항을 입력하세요"
- rows={3}
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- {/* 비고 - 선택적 필드 */}
- <FormField
- control={form.control}
- name="notes"
- render={({ field }) => (
- <FormItem>
- <FormLabel>비고</FormLabel>
- <FormControl>
- <Textarea
- {...field}
- placeholder="추가 비고 사항을 입력하세요"
- rows={3}
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- {/* 파일 첨부 (옵션) */}
- <div className="space-y-2">
- <FormLabel htmlFor="files">첨부 파일 (선택사항)</FormLabel>
- <Input
- id="files"
- type="file"
- multiple
- onChange={(e) => setFiles(e.target.files)}
- />
- {files && files.length > 0 && (
- <p className="text-sm text-muted-foreground">
- {files.length}개 파일이 첨부되었습니다
- </p>
- )}
- </div>
-
- {/* 필수 입력 항목 안내 */}
- <div className="text-sm text-muted-foreground">
- <span className="text-destructive">*</span> 표시는 필수 입력 항목입니다.
- </div>
-
- {/* 모바일에서는 Drawer 내부에서 버튼이 렌더링되므로 여기서는 숨김 */}
- {isDesktop && (
- <DialogFooter className="gap-2 pt-4">
- <DialogClose asChild>
- <Button
- type="button"
- variant="outline"
- >
- 취소
- </Button>
- </DialogClose>
- <Button
- type="submit"
- disabled={isSubmitting || !isValid}
- >
- {isSubmitting && (
- <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
- )}
- {vendors.length > 1 ? `${vendors.length}개 협력업체에 전송` : "전송"}
- </Button>
- </DialogFooter>
- )}
- </form>
- </Form>
- )
- if (hasMultipleRfqIds) {
- toast.error("동일한 RFQ에 대해 선택해주세요");
- return;
- }
- // Desktop Dialog
- if (isDesktop) {
- return (
- <Dialog {...props} onOpenChange={handleDialogOpenChange}>
- {showTrigger ? (
- <DialogTrigger asChild>
- <Button variant="outline" size="sm">
- <Send className="mr-2 size-4" aria-hidden="true" />
- CBE 평가 전송 ({vendors.length})
- </Button>
- </DialogTrigger>
- ) : null}
- <DialogContent className="sm:max-w-[600px]">
- <DialogHeader>
- <DialogTitle>CBE 평가 생성 및 전송</DialogTitle>
- <DialogDescription>
- 선택한 {vendors.length}개 협력업체에 대한 상업 입찰 평가를 생성하고 알림을 전송합니다.
- </DialogDescription>
- </DialogHeader>
-
- {formContent}
- </DialogContent>
- </Dialog>
- )
- }
-
- // Mobile Drawer
- return (
- <Drawer {...props} onOpenChange={handleDialogOpenChange}>
- {showTrigger ? (
- <DrawerTrigger asChild>
- <Button variant="outline" size="sm">
- <Send className="mr-2 size-4" aria-hidden="true" />
- CBE 평가 전송 ({vendors.length})
- </Button>
- </DrawerTrigger>
- ) : null}
- <DrawerContent>
- <DrawerHeader>
- <DrawerTitle>CBE 평가 생성 및 전송</DrawerTitle>
- <DrawerDescription>
- 선택한 {vendors.length}개 협력업체에 대한 상업 입찰 평가를 생성하고 알림을 전송합니다.
- </DrawerDescription>
- </DrawerHeader>
-
- <div className="px-4">
- {formContent}
- </div>
-
- <DrawerFooter className="gap-2 sm:space-x-0">
- <DrawerClose asChild>
- <Button variant="outline">취소</Button>
- </DrawerClose>
- <Button
- onClick={form.handleSubmit(onSubmit)}
- disabled={isSubmitting || !isValid}
- >
- {isSubmitting && (
- <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
- )}
- {vendors.length > 1 ? `${vendors.length}개 협력업체에 전송` : "전송"}
- </Button>
- </DrawerFooter>
- </DrawerContent>
- </Drawer>
- )
-} \ No newline at end of file