From ba8cd44a0ed2c613a5f2cee06bfc9bd0f61f21c7 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 7 Nov 2025 08:39:04 +0000
Subject: (최겸) 입찰/견적 수정사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/bidding-pre-quote-invitation-dialog.tsx  | 770 ---------------------
 1 file changed, 770 deletions(-)
 delete mode 100644 lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx

(limited to 'lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx')

diff --git a/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx b/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx
deleted file mode 100644
index 3205df08..00000000
--- a/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx
+++ /dev/null
@@ -1,770 +0,0 @@
-'use client'
-
-import * as React from 'react'
-import { Button } from '@/components/ui/button'
-import { Checkbox } from '@/components/ui/checkbox'
-import { Input } from '@/components/ui/input'
-import { Label } from '@/components/ui/label'
-import {
-  Dialog,
-  DialogContent,
-  DialogDescription,
-  DialogFooter,
-  DialogHeader,
-  DialogTitle,
-} from '@/components/ui/dialog'
-import { Badge } from '@/components/ui/badge'
-import { BiddingCompany } from './bidding-pre-quote-vendor-columns'
-import { sendPreQuoteInvitations, sendBiddingBasicContracts, getExistingBasicContractsForBidding } from '../service'
-import { getActiveContractTemplates } from '../../service'
-import { useToast } from '@/hooks/use-toast'
-import { useTransition } from 'react'
-import { Mail, Building2, Calendar, FileText, CheckCircle, Info, RefreshCw } from 'lucide-react'
-import { Progress } from '@/components/ui/progress'
-import { Separator } from '@/components/ui/separator'
-import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
-import { cn } from '@/lib/utils'
-
-interface BiddingPreQuoteInvitationDialogProps {
-  open: boolean
-  onOpenChange: (open: boolean) => void
-  companies: BiddingCompany[]
-  biddingId: number
-  biddingTitle: string
-  projectName?: string
-  onSuccess: () => void
-}
-
-interface BasicContractTemplate {
-  id: number
-  templateName: string
-  revision: number
-  status: string
-  filePath: string | null
-  validityPeriod: number | null
-  legalReviewRequired: boolean
-  createdAt: Date | null
-}
-
-interface SelectedContract {
-  templateId: number
-  templateName: string
-  contractType: string // templateName을 contractType으로 사용
-  checked: boolean
-}
-
-// PDF 생성 유틸리티 함수
-const generateBasicContractPdf = async (
-  template: BasicContractTemplate,
-  vendorId: number
-): Promise<{ buffer: number[]; fileName: string }> => {
-  try {
-    // 1. 템플릿 데이터 준비 (서버 API 호출)
-    const prepareResponse = await fetch("/api/contracts/prepare-template", {
-      method: "POST",
-      headers: { "Content-Type": "application/json" },
-      body: JSON.stringify({
-        templateName: template.templateName,
-        vendorId,
-      }),
-    });
-
-    if (!prepareResponse.ok) {
-      throw new Error("템플릿 준비 실패");
-    }
-
-    const { template: preparedTemplate, templateData } = await prepareResponse.json();
-
-    // 2. 템플릿 파일 다운로드
-    const templateResponse = await fetch("/api/contracts/get-template", {
-      method: "POST",
-      headers: { "Content-Type": "application/json" },
-      body: JSON.stringify({ templatePath: preparedTemplate.filePath }),
-    });
-
-    const templateBlob = await templateResponse.blob();
-    const templateFile = new window.File([templateBlob], "template.docx", {
-      type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
-    });
-
-    // 3. PDFTron WebViewer로 PDF 변환
-    const { default: WebViewer } = await import("@pdftron/webviewer");
-
-    const tempDiv = document.createElement('div');
-    tempDiv.style.display = 'none';
-    document.body.appendChild(tempDiv);
-
-    try {
-      const instance = await WebViewer(
-        {
-          path: "/pdftronWeb",
-          licenseKey: process.env.NEXT_PUBLIC_PDFTRON_WEBVIEW_KEY,
-          fullAPI: true,
-        },
-        tempDiv
-      );
-
-      const { Core } = instance;
-      const { createDocument } = Core;
-
-      const templateDoc = await createDocument(templateFile, {
-        filename: templateFile.name,
-        extension: 'docx',
-      });
-
-      // 변수 치환 적용
-      await templateDoc.applyTemplateValues(templateData);
-
-      // PDF 변환
-      const fileData = await templateDoc.getFileData();
-      const pdfBuffer = await Core.officeToPDFBuffer(fileData, { extension: 'docx' });
-
-      const fileName = `${template.templateName}_${Date.now()}.pdf`;
-
-      return {
-        buffer: Array.from(pdfBuffer), // Uint8Array를 일반 배열로 변환
-        fileName
-      };
-
-    } finally {
-      if (tempDiv.parentNode) {
-        document.body.removeChild(tempDiv);
-      }
-    }
-  } catch (error) {
-    console.error(`기본계약 PDF 생성 실패 (${template.templateName}):`, error);
-    throw error;
-  }
-};
-
-export function BiddingPreQuoteInvitationDialog({
-  open,
-  onOpenChange,
-  companies,
-  biddingId,
-  biddingTitle,
-  projectName,
-  onSuccess
-}: BiddingPreQuoteInvitationDialogProps) {
-  const { toast } = useToast()
-  const [isPending, startTransition] = useTransition()
-  const [selectedCompanyIds, setSelectedCompanyIds] = React.useState<number[]>([])
-  const [preQuoteDeadline, setPreQuoteDeadline] = React.useState('')
-  const [additionalMessage, setAdditionalMessage] = React.useState('')
-
-  // 기본계약 관련 상태
-  const [existingContracts, setExistingContracts] = React.useState<any[]>([])
-  const [isGeneratingPdfs, setIsGeneratingPdfs] = React.useState(false)
-  const [pdfGenerationProgress, setPdfGenerationProgress] = React.useState(0)
-  const [currentGeneratingContract, setCurrentGeneratingContract] = React.useState('')
-
-  // 기본계약서 템플릿 관련 상태
-  const [availableTemplates, setAvailableTemplates] = React.useState<BasicContractTemplate[]>([])
-  const [selectedContracts, setSelectedContracts] = React.useState<SelectedContract[]>([])
-  const [isLoadingTemplates, setIsLoadingTemplates] = React.useState(false)
-
-  // 초대 가능한 업체들 (pending 상태인 업체들)
-  const invitableCompanies = React.useMemo(() => companies.filter(company =>
-    company.invitationStatus === 'pending' && company.companyName
-  ), [companies])
-
-  // 다이얼로그가 열릴 때 기존 계약 조회 및 템플릿 로드
-  React.useEffect(() => {
-    if (open) {
-      const fetchInitialData = async () => {
-        setIsLoadingTemplates(true);
-        try {
-          const [contractsResult, templatesData] = await Promise.all([
-            getExistingBasicContractsForBidding(biddingId),
-            getActiveContractTemplates()
-          ]);
-
-          // 기존 계약 조회 - 서버 액션 사용
-          const existingContractsResult = await getExistingBasicContractsForBidding(biddingId);
-          setExistingContracts(existingContractsResult.success ? existingContractsResult.contracts || [] : []);
-
-          // 템플릿 로드 (4개 타입만 필터링)
-          // 4개 템플릿 타입만 필터링: 비밀, General, Project, 기술자료
-          const allowedTemplateNames = ['비밀', 'General GTC', '기술', '기술자료'];
-          const filteredTemplates = (templatesData.templates || []).filter((template: any) =>
-            allowedTemplateNames.some(allowedName =>
-              template.templateName.includes(allowedName) ||
-              allowedName.includes(template.templateName)
-            )
-          );
-          setAvailableTemplates(filteredTemplates as BasicContractTemplate[]);
-          const initialSelected = filteredTemplates.map((template: any) => ({
-            templateId: template.id,
-            templateName: template.templateName,
-            contractType: template.templateName,
-            checked: false
-          }));
-          setSelectedContracts(initialSelected);
-
-        } catch (error) {
-          console.error('초기 데이터 로드 실패:', error);
-          toast({
-            title: '오류',
-            description: '기본 정보를 불러오는 데 실패했습니다.',
-            variant: 'destructive',
-          });
-          setExistingContracts([]);
-          setAvailableTemplates([]);
-          setSelectedContracts([]);
-        } finally {
-          setIsLoadingTemplates(false);
-        }
-      }
-      fetchInitialData();
-    }
-  }, [open, biddingId, toast]);
-
-  const handleSelectAll = (checked: boolean | 'indeterminate') => {
-    if (checked) {
-      // 기존 계약이 없는 업체만 선택
-      const availableCompanies = invitableCompanies.filter(company =>
-        !existingContracts.some(ec => ec.vendorId === company.companyId)
-      )
-      setSelectedCompanyIds(availableCompanies.map(company => company.id))
-    } else {
-      setSelectedCompanyIds([])
-    }
-  }
-
-  const handleSelectCompany = (companyId: number, checked: boolean) => {
-    const company = invitableCompanies.find(c => c.id === companyId)
-    const hasExistingContract = company ? existingContracts.some(ec => ec.vendorId === company.companyId) : false
-
-    if (hasExistingContract) {
-      toast({
-        title: '선택 불가',
-        description: '이미 기본계약서를 받은 업체는 다시 선택할 수 없습니다.',
-        variant: 'default',
-      })
-      return
-    }
-
-    if (checked) {
-      setSelectedCompanyIds(prev => [...prev, companyId])
-    } else {
-      setSelectedCompanyIds(prev => prev.filter(id => id !== companyId))
-    }
-  }
-
-  // 기본계약서 선택 토글
-  const toggleContractSelection = (templateId: number) => {
-    setSelectedContracts(prev =>
-      prev.map(contract =>
-        contract.templateId === templateId
-          ? { ...contract, checked: !contract.checked }
-          : contract
-      )
-    )
-  }
-
-  // 모든 기본계약서 선택/해제
-  const toggleAllContractSelection = (checked: boolean | 'indeterminate') => {
-    setSelectedContracts(prev =>
-      prev.map(contract => ({ ...contract, checked: !!checked }))
-    )
-  }
-
-  const handleSendInvitations = () => {
-    if (selectedCompanyIds.length === 0) {
-      toast({
-        title: '알림',
-        description: '초대를 발송할 업체를 선택해주세요.',
-        variant: 'default',
-      })
-      return
-    }
-
-    const selectedContractTemplates = selectedContracts.filter(c => c.checked);
-    const companiesForContracts = invitableCompanies.filter(company => selectedCompanyIds.includes(company.id));
-
-    const vendorsToGenerateContracts = companiesForContracts.filter(company =>
-      !existingContracts.some(ec =>
-        ec.vendorId === company.companyId && ec.biddingCompanyId === company.id
-      )
-    );
-
-    startTransition(async () => {
-      try {
-        // 1. 사전견적 초대 발송
-        const invitationResponse = await sendPreQuoteInvitations(
-          selectedCompanyIds,
-          preQuoteDeadline || undefined
-        )
-
-        if (!invitationResponse.success) {
-          toast({
-            title: '초대 발송 실패',
-            description: invitationResponse.error,
-            variant: 'destructive',
-          })
-          return
-        }
-
-        // 2. 기본계약 발송 (선택된 템플릿과 업체가 있는 경우)
-        let contractResponse: Awaited<ReturnType<typeof sendBiddingBasicContracts>> | null = null
-        if (selectedContractTemplates.length > 0 && selectedCompanyIds.length > 0) {
-          setIsGeneratingPdfs(true)
-          setPdfGenerationProgress(0)
-
-          const generatedPdfsMap = new Map<string, { buffer: number[], fileName: string }>()
-
-          let generatedCount = 0;
-          for (const vendor of vendorsToGenerateContracts) {
-            for (const contract of selectedContractTemplates) {
-              setCurrentGeneratingContract(`${vendor.companyName} - ${contract.templateName}`);
-              const templateDetails = availableTemplates.find(t => t.id === contract.templateId);
-
-              if (templateDetails) {
-                const pdfData = await generateBasicContractPdf(templateDetails, vendor.companyId);
-                // sendBiddingBasicContracts와 동일한 키 형식 사용
-                let contractType = '';
-                if (contract.templateName.includes('비밀')) {
-                  contractType = 'NDA';
-                } else if (contract.templateName.includes('General GTC')) {
-                  contractType = 'General_GTC';
-                } else if (contract.templateName.includes('기술') && !contract.templateName.includes('기술자료')) {
-                  contractType = 'Project_GTC';
-                } else if (contract.templateName.includes('기술자료')) {
-                  contractType = '기술자료';
-                }
-                const key = `${vendor.companyId}_${contractType}_${contract.templateName}`;
-                generatedPdfsMap.set(key, pdfData);
-              }
-            }
-            generatedCount++;
-            setPdfGenerationProgress((generatedCount / vendorsToGenerateContracts.length) * 100);
-          }
-
-          setIsGeneratingPdfs(false);
-
-          const vendorData = companiesForContracts.map(company => {
-            // 선택된 템플릿에 따라 contractRequirements 동적으로 설정
-            const contractRequirements = {
-              ndaYn: selectedContractTemplates.some(c => c.templateName.includes('비밀')),
-              generalGtcYn: selectedContractTemplates.some(c => c.templateName.includes('General GTC')),
-              projectGtcYn: selectedContractTemplates.some(c => c.templateName.includes('기술') && !c.templateName.includes('기술자료')),
-              agreementYn: selectedContractTemplates.some(c => c.templateName.includes('기술자료'))
-            };
-
-            return {
-              vendorId: company.companyId,
-              vendorName: company.companyName || '',
-              vendorCode: company.companyCode,
-              vendorCountry: '대한민국',
-              selectedMainEmail: company.contactEmail || '',
-              contactPerson: company.contactPerson,
-              contactEmail: company.contactEmail,
-              biddingCompanyId: company.id,
-              biddingId: biddingId,
-              hasExistingContracts: existingContracts.some(ec =>
-                ec.vendorId === company.companyId && ec.biddingCompanyId === company.id
-              ),
-              contractRequirements,
-              additionalEmails: [],
-              customEmails: []
-            };
-          });
-
-          const pdfsArray = Array.from(generatedPdfsMap.entries()).map(([key, data]) => ({
-            key,
-            buffer: data.buffer,
-            fileName: data.fileName,
-          }));
-
-          console.log("Calling sendBiddingBasicContracts with biddingId:", biddingId);
-          console.log("vendorData:", vendorData.map(v => ({ vendorId: v.vendorId, biddingCompanyId: v.biddingCompanyId, biddingId: v.biddingId })));
-
-          contractResponse = await sendBiddingBasicContracts(
-            biddingId,
-            vendorData,
-            pdfsArray,
-            additionalMessage
-          );
-        }
-
-        let successMessage = '사전견적 초대가 성공적으로 발송되었습니다.';
-        if (contractResponse && contractResponse.success) {
-          successMessage += `\n${contractResponse.message}`;
-        }
-
-        toast({
-          title: '성공',
-          description: successMessage,
-        })
-
-        // 상태 초기화
-        setSelectedCompanyIds([]);
-        setPreQuoteDeadline('');
-        setAdditionalMessage('');
-        setExistingContracts([]);
-        setIsGeneratingPdfs(false);
-        setPdfGenerationProgress(0);
-        setCurrentGeneratingContract('');
-        setSelectedContracts(prev => prev.map(c => ({ ...c, checked: false })));
-
-        onOpenChange(false);
-        onSuccess();
-
-      } catch (error) {
-        console.error('발송 실패:', error);
-        toast({
-          title: '오류',
-          description: '발송 중 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.',
-          variant: 'destructive',
-        });
-        setIsGeneratingPdfs(false);
-      }
-    })
-  }
-
-  const handleOpenChange = (open: boolean) => {
-    onOpenChange(open)
-    if (!open) {
-      setSelectedCompanyIds([])
-      setPreQuoteDeadline('')
-      setAdditionalMessage('')
-      setExistingContracts([])
-      setIsGeneratingPdfs(false)
-      setPdfGenerationProgress(0)
-      setCurrentGeneratingContract('')
-      setSelectedContracts([])
-    }
-  }
-
-  const selectedContractCount = selectedContracts.filter(c => c.checked).length;
-  const selectedCompanyCount = selectedCompanyIds.length;
-  const companiesToReceiveContracts = invitableCompanies.filter(company => selectedCompanyIds.includes(company.id));
-
-  // 기존 계약이 없는 업체들만 계산
-  const availableCompanies = invitableCompanies.filter(company =>
-    !existingContracts.some(ec => ec.vendorId === company.companyId)
-  );
-  const selectedAvailableCompanyCount = selectedCompanyIds.filter(id =>
-    availableCompanies.some(company => company.id === id)
-  ).length;
-
-  // 선택된 업체들 중 기존 계약이 있는 업체들
-  const selectedCompaniesWithExistingContracts = invitableCompanies.filter(company =>
-    selectedCompanyIds.includes(company.id) &&
-    existingContracts.some(ec => ec.vendorId === company.companyId)
-  );
-
-  return (
-    <Dialog open={open} onOpenChange={handleOpenChange}>
-      <DialogContent className="sm:max-w-[800px] max-h-[90vh] flex flex-col">
-        <DialogHeader>
-          <DialogTitle className="flex items-center gap-2">
-            <Mail className="w-5 h-5" />
-            사전견적 초대 및 기본계약 발송
-          </DialogTitle>
-          <DialogDescription>
-            선택한 업체들에게 사전견적 요청과 기본계약서를 발송합니다.
-          </DialogDescription>
-        </DialogHeader>
-
-        <div className="flex-1 overflow-y-auto px-1" style={{ maxHeight: 'calc(70vh - 200px)' }}>
-          <div className="space-y-6 pr-4">
-            {/* 견적 마감일 설정 */}
-            <div className="mb-6 p-4 border rounded-lg bg-muted/30">
-              <Label htmlFor="preQuoteDeadline" className="text-sm font-medium mb-2 flex items-center gap-2">
-                <Calendar className="w-4 h-4" />
-                견적 마감일
-              </Label>
-              <Input
-                id="preQuoteDeadline"
-                type="datetime-local"
-                value={preQuoteDeadline}
-                onChange={(e) => setPreQuoteDeadline(e.target.value)}
-                className="w-full"
-              />
-            </div>
-
-            {/* 기존 계약 정보 알림 */}
-            {existingContracts.length > 0 && (
-              <Alert className="border-orange-500 bg-orange-50">
-                <Info className="h-4 w-4 text-orange-600" />
-                <AlertTitle className="text-orange-800">기존 계약 정보</AlertTitle>
-                <AlertDescription className="text-orange-700">
-                  이미 기본계약을 받은 업체가 있습니다.
-                  해당 업체들은 초대 대상에서 제외되며, 계약서 재생성도 건너뜁니다.
-                </AlertDescription>
-              </Alert>
-            )}
-
-            {/* 업체 선택 섹션 */}
-            <Card className="border-2 border-dashed">
-              <CardHeader className="pb-3">
-                <CardTitle className="flex items-center gap-2 text-base">
-                  <Building2 className="h-5 w-5 text-green-600" />
-                  초대 대상 업체
-                </CardTitle>
-              </CardHeader>
-              <CardContent className="space-y-4">
-                {invitableCompanies.length === 0 ? (
-                  <div className="text-center py-8 text-muted-foreground">
-                    초대 가능한 업체가 없습니다.
-                  </div>
-                ) : (
-                  <>
-                    <div className="flex items-center justify-between p-3 bg-muted/50 rounded-lg">
-                      <div className="flex items-center gap-2">
-                        <Checkbox
-                          id="select-all-companies"
-                          checked={selectedAvailableCompanyCount === availableCompanies.length && availableCompanies.length > 0}
-                          onCheckedChange={handleSelectAll}
-                        />
-                        <Label htmlFor="select-all-companies" className="font-medium">
-                          전체 선택 ({availableCompanies.length}개 업체)
-                        </Label>
-                      </div>
-                      <Badge variant="outline">
-                        {selectedCompanyCount}개 선택됨
-                      </Badge>
-                    </div>
-
-                    <div className="space-y-3 max-h-80 overflow-y-auto">
-                      {invitableCompanies.map((company) => {
-                        const hasExistingContract = existingContracts.some(ec => ec.vendorId === company.companyId);
-                        return (
-                          <div key={company.id} className={cn("flex items-center space-x-3 p-3 border rounded-lg transition-colors",
-                            selectedCompanyIds.includes(company.id) && !hasExistingContract && "border-green-500 bg-green-50",
-                            hasExistingContract && "border-orange-500 bg-orange-50 opacity-75"
-                          )}>
-                            <Checkbox
-                              id={`company-${company.id}`}
-                              checked={selectedCompanyIds.includes(company.id)}
-                              disabled={hasExistingContract}
-                              onCheckedChange={(checked) => handleSelectCompany(company.id, !!checked)}
-                            />
-                            <div className="flex-1">
-                              <div className="flex items-center gap-2">
-                                <span className={cn("font-medium", hasExistingContract && "text-muted-foreground")}>
-                                  {company.companyName}
-                                </span>
-                                <Badge variant="outline" className="text-xs">
-                                  {company.companyCode}
-                                </Badge>
-                                {hasExistingContract && (
-                                  <Badge variant="secondary" className="text-xs">
-                                    <CheckCircle className="h-3 w-3 mr-1" />
-                                    계약 체결됨
-                                  </Badge>
-                                )}
-                              </div>
-                              {hasExistingContract && (
-                                <p className="text-xs text-orange-600 mt-1">
-                                  이미 기본계약서를 받은 업체입니다. 선택에서 제외됩니다.
-                                </p>
-                              )}
-                            </div>
-                          </div>
-                        )
-                      })}
-                    </div>
-                  </>
-                )}
-              </CardContent>
-            </Card>
-
-            {/* 선택된 업체 중 기존 계약이 있는 경우 경고 */}
-            {selectedCompaniesWithExistingContracts.length > 0 && (
-              <Alert className="border-red-500 bg-red-50">
-                <Info className="h-4 w-4 text-red-600" />
-                <AlertTitle className="text-red-800">선택한 업체 중 제외될 업체</AlertTitle>
-                <AlertDescription className="text-red-700">
-                  선택한 {selectedCompaniesWithExistingContracts.length}개 업체가 이미 기본계약서를 받았습니다.
-                  이 업체들은 초대 발송 및 계약서 생성에서 제외됩니다.
-                  <br />
-                  <strong>실제 발송 대상: {selectedCompanyCount - selectedCompaniesWithExistingContracts.length}개 업체</strong>
-                </AlertDescription>
-              </Alert>
-            )}
-
-            {/* 기본계약서 선택 섹션 */}
-            <Separator />
-            <Card className="border-2 border-dashed">
-              <CardHeader className="pb-3">
-                <CardTitle className="flex items-center gap-2 text-base">
-                  <FileText className="h-5 w-5 text-blue-600" />
-                  기본계약서 선택 (선택된 업체에만 발송)
-                </CardTitle>
-              </CardHeader>
-              <CardContent className="space-y-4">
-                {isLoadingTemplates ? (
-                  <div className="text-center py-6">
-                    <RefreshCw className="h-6 w-6 animate-spin mx-auto mb-2 text-blue-600" />
-                    <p className="text-sm text-muted-foreground">기본계약서 템플릿을 불러오는 중...</p>
-                  </div>
-                ) : (
-                  <div className="space-y-4">
-                    {selectedCompanyCount === 0 && (
-                      <Alert className="border-red-500 bg-red-50">
-                        <Info className="h-4 w-4 text-red-600" />
-                        <AlertTitle className="text-red-800">알림</AlertTitle>
-                        <AlertDescription className="text-red-700">
-                          기본계약서를 발송할 업체를 먼저 선택해주세요.
-                        </AlertDescription>
-                      </Alert>
-                    )}
-                    {availableTemplates.length === 0 ? (
-                      <div className="text-center py-8 text-muted-foreground">
-                        <FileText className="h-12 w-12 mx-auto mb-4 opacity-50" />
-                        <p>사용 가능한 기본계약서 템플릿이 없습니다.</p>
-                      </div>
-                    ) : (
-                      <>
-                        <div className="flex items-center justify-between p-3 bg-muted/50 rounded-lg">
-                          <div className="flex items-center gap-2">
-                            <Checkbox
-                              id="select-all-contracts"
-                              checked={selectedContracts.length > 0 && selectedContracts.every(c => c.checked)}
-                              onCheckedChange={toggleAllContractSelection}
-                            />
-                            <Label htmlFor="select-all-contracts" className="font-medium">
-                              전체 선택 ({availableTemplates.length}개 템플릿)
-                            </Label>
-                          </div>
-                          <Badge variant="outline">
-                            {selectedContractCount}개 선택됨
-                          </Badge>
-                        </div>
-                        <div className="grid gap-3 max-h-60 overflow-y-auto">
-                          {selectedContracts.map((contract) => (
-                            <div
-                              key={contract.templateId}
-                              className={cn(
-                                "flex items-center justify-between p-3 border rounded-lg hover:bg-muted/50 transition-colors cursor-pointer",
-                                contract.checked && "border-blue-500 bg-blue-50"
-                              )}
-                              onClick={() => toggleContractSelection(contract.templateId)}
-                            >
-                              <div className="flex items-center gap-3">
-                                <Checkbox
-                                  id={`contract-${contract.templateId}`}
-                                  checked={contract.checked}
-                                  onCheckedChange={() => toggleContractSelection(contract.templateId)}
-                                />
-                                <div className="flex-1">
-                                  <Label
-                                    htmlFor={`contract-${contract.templateId}`}
-                                    className="font-medium cursor-pointer"
-                                  >
-                                    {contract.templateName}
-                                  </Label>
-                                  <p className="text-xs text-muted-foreground mt-1">
-                                    {contract.contractType}
-                                  </p>
-                                </div>
-                              </div>
-                            </div>
-                          ))}
-                        </div>
-                      </>
-                    )}
-                    {selectedContractCount > 0 && (
-                      <div className="mt-4 p-3 bg-green-50 border border-green-200 rounded-lg">
-                        <div className="flex items-center gap-2 mb-2">
-                          <CheckCircle className="h-4 w-4 text-green-600" />
-                          <span className="font-medium text-green-900 text-sm">
-                            선택된 기본계약서 ({selectedContractCount}개)
-                          </span>
-                        </div>
-                        <ul className="space-y-1 text-xs text-green-800 list-disc list-inside">
-                          {selectedContracts.filter(c => c.checked).map((contract) => (
-                            <li key={contract.templateId}>
-                              {contract.templateName}
-                            </li>
-                          ))}
-                        </ul>
-                      </div>
-                    )}
-                  </div>
-                )}
-              </CardContent>
-            </Card>
-
-            {/* 추가 메시지 */}
-            <div className="space-y-2">
-              <Label htmlFor="contractMessage" className="text-sm font-medium">
-                계약서 추가 메시지 (선택사항)
-              </Label>
-              <textarea
-                id="contractMessage"
-                className="w-full min-h-[60px] p-3 text-sm border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-primary"
-                placeholder="기본계약서와 함께 보낼 추가 메시지를 입력하세요..."
-                value={additionalMessage}
-                onChange={(e) => setAdditionalMessage(e.target.value)}
-              />
-            </div>
-
-            {/* PDF 생성 진행 상황 */}
-            {isGeneratingPdfs && (
-              <Alert className="border-blue-500 bg-blue-50">
-                <div className="space-y-3">
-                  <div className="flex items-center gap-2">
-                    <RefreshCw className="h-4 w-4 animate-spin text-blue-600" />
-                    <AlertTitle className="text-blue-800">기본계약서 생성 중</AlertTitle>
-                  </div>
-                  <AlertDescription>
-                    <div className="space-y-2">
-                      <p className="text-sm text-blue-700">{currentGeneratingContract}</p>
-                      <Progress value={pdfGenerationProgress} className="h-2" />
-                      <p className="text-xs text-blue-600">
-                        {Math.round(pdfGenerationProgress)}% 완료
-                      </p>
-                    </div>
-                  </AlertDescription>
-                </div>
-              </Alert>
-            )}
-          </div>
-        </div>
-
-        <DialogFooter className="flex-col sm:flex-row-reverse sm:justify-between items-center px-4 pt-4">
-          <div className="flex gap-2 w-full sm:w-auto">
-            <Button variant="outline" onClick={() => handleOpenChange(false)} className="w-full sm:w-auto">
-              취소
-            </Button>
-            <Button
-              onClick={handleSendInvitations}
-              disabled={isPending || selectedCompanyCount === 0 || isGeneratingPdfs}
-              className="w-full sm:w-auto"
-            >
-              {isPending ? (
-                <>
-                  <RefreshCw className="w-4 h-4 mr-2 animate-spin" />
-                  발송 중...
-                </>
-              ) : (
-                <>
-                  <Mail className="w-4 h-4 mr-2" />
-                  초대 발송 및 계약서 생성
-                </>
-              )}
-            </Button>
-          </div>
-          {/* {(selectedCompanyCount > 0 || selectedContractCount > 0) && (
-            <div className="mt-4 sm:mt-0 text-sm text-muted-foreground">
-              {selectedCompanyCount > 0 && (
-                <p>
-                  <strong>{selectedCompanyCount}개 업체</strong>에 초대를 발송합니다.
-                </p>
-              )}
-              {selectedContractCount > 0 && selectedCompanyCount > 0 && (
-                <p>
-                  이 중 <strong>{companiesToReceiveContracts.length}개 업체</strong>에 <strong>{selectedContractCount}개</strong>의 기본계약서를 발송합니다.
-                </p>
-              )}
-            </div>
-          )} */}
-        </DialogFooter>
-      </DialogContent>
-    </Dialog>
-  )
-}
\ No newline at end of file
-- 
cgit v1.2.3

