"use client" import * as React from "react" import { type Row } from "@tanstack/react-table" import { Loader, Mail, AlertCircle, XCircle } from "lucide-react" import { toast } from "sonner" 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 { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert" import { VendorCandidates } from "@/db/schema/vendors" import { bulkUpdateVendorCandidateStatus } from "../service" import { useSession } from "next-auth/react" // next-auth 세션 훅 interface InviteCandidatesDialogProps extends React.ComponentPropsWithoutRef { candidates: Row["original"][] showTrigger?: boolean onSuccess?: () => void } export function InviteCandidatesDialog({ candidates, showTrigger = true, onSuccess, ...props }: InviteCandidatesDialogProps) { const [isInvitePending, startInviteTransition] = React.useTransition() const isDesktop = useMediaQuery("(min-width: 640px)") const { data: session, status } = useSession() // 후보자를 상태별로 분류 const discardedCandidates = candidates.filter(candidate => candidate.status === "DISCARDED") const nonDiscardedCandidates = candidates.filter(candidate => candidate.status !== "DISCARDED") // 이메일 유무에 따라 초대 가능한 후보자 분류 (DISCARDED가 아닌 후보자 중에서) const candidatesWithEmail = nonDiscardedCandidates.filter(candidate => candidate.contactEmail) const candidatesWithoutEmail = nonDiscardedCandidates.filter(candidate => !candidate.contactEmail) // 각 카테고리 수 const invitableCount = candidatesWithEmail.length const hasUninvitableCandidates = candidatesWithoutEmail.length > 0 const hasDiscardedCandidates = discardedCandidates.length > 0 function onInvite() { startInviteTransition(async () => { // 이메일이 있고 DISCARDED가 아닌 후보자만 상태 업데이트 if (!session?.user?.id) { toast.error("인증 오류. 로그인 정보를 찾을 수 없습니다.") return } const { error } = await bulkUpdateVendorCandidateStatus({ ids: candidatesWithEmail.map((candidate) => candidate.id), status: "INVITED", }) if (error) { toast.error(error) return } props.onOpenChange?.(false) if (invitableCount === 0) { toast.warning("No invitation sent - no eligible candidates with email addresses") } else { let skipMessage = "" if (hasUninvitableCandidates && hasDiscardedCandidates) { skipMessage = ` ${candidatesWithoutEmail.length} candidates without email and ${discardedCandidates.length} discarded candidates were skipped.` } else if (hasUninvitableCandidates) { skipMessage = ` ${candidatesWithoutEmail.length} candidates without email were skipped.` } else if (hasDiscardedCandidates) { skipMessage = ` ${discardedCandidates.length} discarded candidates were skipped.` } toast.success(`Invitation emails sent to ${invitableCount} candidates.${skipMessage}`) } onSuccess?.() }) } // 초대 버튼 비활성화 조건 const disableInviteButton = isInvitePending || invitableCount === 0 const DialogComponent = ( <>
{/* 이메일 없는 후보자 알림 */} {hasUninvitableCandidates && ( Missing Email Addresses {candidatesWithoutEmail.length} candidate{candidatesWithoutEmail.length > 1 ? 's' : ''} {candidatesWithoutEmail.length > 1 ? 'don't' : 'doesn't'} have email addresses and won't receive invitations. )} {/* 폐기된 후보자 알림 */} {hasDiscardedCandidates && ( Discarded Candidates {discardedCandidates.length} candidate{discardedCandidates.length > 1 ? 's have' : ' has'} been discarded and won't receive invitations. )} {invitableCount > 0 ? ( <> This will send invitation emails to{" "} {invitableCount} {invitableCount === 1 ? " candidate" : " candidates"} and change their status to INVITED. ) : ( <> No candidates can be invited because none of the selected candidates have valid email addresses or they have been discarded. )}
) if (isDesktop) { return ( {showTrigger ? ( ) : null} Send invitations? {DialogComponent} ) } return ( {showTrigger ? ( ) : null} Send invitations? {DialogComponent} ) }