summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-19 09:23:47 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-19 09:23:47 +0000
commit8077419e40368dc703f94d558fc746b73fbc6702 (patch)
tree333bdfb3b0d84336f1bf7d4f0f1bbced6bec2d4c /components
parentaa71f75ace013b2fe982e5a104e61440458e0fd2 (diff)
(최겸) 구매 PQ 비밀유지계약서 별첨 첨부파일 추가, 정규업체등록관리 개발
Diffstat (limited to 'components')
-rw-r--r--components/additional-info/join-form.tsx410
-rw-r--r--components/vendor-regular-registrations/additional-info-dialog.tsx122
2 files changed, 427 insertions, 105 deletions
diff --git a/components/additional-info/join-form.tsx b/components/additional-info/join-form.tsx
index da2ddac7..d9f5052e 100644
--- a/components/additional-info/join-form.tsx
+++ b/components/additional-info/join-form.tsx
@@ -36,12 +36,16 @@ import {
CommandGroup,
CommandItem,
} from "@/components/ui/command"
-import { Check, ChevronsUpDown, Download, Loader2, Plus, X } from "lucide-react"
+import { Check, ChevronsUpDown, Download, Loader2, Plus, X, FileText, Eye, Upload, CheckCircle } from "lucide-react"
import { cn } from "@/lib/utils"
import { useTranslation } from "@/i18n/client"
import { getVendorDetailById, downloadVendorAttachments, updateVendorInfo } from "@/lib/vendors/service"
import { updateVendorSchema, updateVendorSchemaWithConditions, type UpdateVendorInfoSchema } from "@/lib/vendors/validations"
+import { fetchVendorRegistrationStatus } from "@/lib/vendor-regular-registrations/service"
+import { DocumentStatusDialog } from "@/components/vendor-regular-registrations/document-status-dialog"
+import { AdditionalInfoDialog } from "@/components/vendor-regular-registrations/additional-info-dialog"
+import type { VendorRegularRegistration } from "@/config/vendorRegularRegistrationsColumnsConfig"
import {
Select,
SelectContent,
@@ -124,6 +128,17 @@ const cashFlowRatingScaleMap: Record<string, string[]> = {
const MAX_FILE_SIZE = 3e9
+// 첨부파일 타입 정의
+const ATTACHMENT_TYPES = [
+ { value: "BUSINESS_REGISTRATION", label: "사업자등록증" },
+ { value: "CREDIT_REPORT", label: "신용평가보고서" },
+ { value: "BANK_ACCOUNT_COPY", label: "통장사본" },
+ { value: "ISO_CERTIFICATION", label: "ISO인증서" },
+ { value: "GENERAL", label: "일반 문서" },
+] as const
+
+type AttachmentType = typeof ATTACHMENT_TYPES[number]['value']
+
// 파일 타입 정의
interface AttachmentFile {
id: number
@@ -151,12 +166,25 @@ export function InfoForm() {
const [existingFiles, setExistingFiles] = React.useState<AttachmentFile[]>([])
const [existingCreditFiles, setExistingCreditFiles] = React.useState<AttachmentFile[]>([])
const [existingCashFlowFiles, setExistingCashFlowFiles] = React.useState<AttachmentFile[]>([])
+ const [existingSignatureFiles, setExistingSignatureFiles] = React.useState<AttachmentFile[]>([])
const [selectedFiles, setSelectedFiles] = React.useState<File[]>([])
const [creditRatingFile, setCreditRatingFile] = React.useState<File[]>([])
const [cashFlowRatingFile, setCashFlowRatingFile] = React.useState<File[]>([])
const [isDownloading, setIsDownloading] = React.useState(false);
+ // 정규등록 관련 상태
+ const [registrationData, setRegistrationData] = React.useState<any>(null)
+ const [documentDialogOpen, setDocumentDialogOpen] = React.useState(false)
+ const [additionalInfoDialogOpen, setAdditionalInfoDialogOpen] = React.useState(false)
+
+ // 첨부파일 타입 선택 상태
+ const [selectedAttachmentType, setSelectedAttachmentType] = React.useState<AttachmentType>("GENERAL")
+
+ // 서명/직인 업로드 관련 상태
+ const [signatureFiles, setSignatureFiles] = React.useState<File[]>([])
+ const [hasSignature, setHasSignature] = React.useState(false)
+
// React Hook Form
const form = useForm<UpdateVendorInfoSchema>({
resolver: zodResolver(updateVendorSchemaWithConditions),
@@ -223,19 +251,34 @@ export function InfoForm() {
// 첨부파일 정보 분류 (view에서 이미 파싱된 attachments 배열 사용)
if (vendorData.attachments && Array.isArray(vendorData.attachments)) {
- const generalFiles = vendorData.attachments.filter(
- (file: AttachmentFile) => file.attachmentType === "GENERAL"
- )
+ // 신용평가 관련 파일들만 따로 분리
const creditFiles = vendorData.attachments.filter(
(file: AttachmentFile) => file.attachmentType === "CREDIT_RATING"
)
const cashFlowFiles = vendorData.attachments.filter(
(file: AttachmentFile) => file.attachmentType === "CASH_FLOW_RATING"
)
+
+ // 서명/직인 파일들 분리
+ const signatureFiles = vendorData.attachments.filter(
+ (file: AttachmentFile) =>
+ file.attachmentType === "SIGNATURE" ||
+ file.attachmentType === "SEAL"
+ )
+
+ // 나머지 모든 파일들 (사업자등록증, 신용평가보고서, 통장사본, ISO인증서, 일반문서 등)
+ const otherFiles = vendorData.attachments.filter(
+ (file: AttachmentFile) =>
+ file.attachmentType !== "CREDIT_RATING" &&
+ file.attachmentType !== "CASH_FLOW_RATING" &&
+ file.attachmentType !== "SIGNATURE" &&
+ file.attachmentType !== "SEAL"
+ )
- setExistingFiles(generalFiles)
+ setExistingFiles(otherFiles) // 모든 기타 파일들을 일반 첨부파일 섹션에 표시
setExistingCreditFiles(creditFiles)
setExistingCashFlowFiles(cashFlowFiles)
+ setExistingSignatureFiles(signatureFiles) // 서명/직인 파일들
}
// 폼 기본값 설정 (연락처 포함)
@@ -272,6 +315,25 @@ export function InfoForm() {
replaceContacts(formattedContacts)
}
+
+ // 정규등록 상태 데이터 로드 (없는 경우 에러가 아님)
+ try {
+ const registrationResult = await fetchVendorRegistrationStatus(Number(companyId))
+ if (registrationResult.success) {
+ setRegistrationData(registrationResult.data)
+ } else if (registrationResult.noRegistration) {
+ // 정규등록 데이터가 없는 경우는 정상적인 상황 (기존 정규업체 등)
+ console.log("정규등록 데이터 없음 - 기존 정규업체이거나 아직 등록 진행하지 않음")
+ setRegistrationData(null)
+ } else {
+ // 실제 에러인 경우
+ console.error("정규등록 상태 조회 오류:", registrationResult.error)
+ setRegistrationData(null)
+ }
+ } catch (error) {
+ console.error("정규등록 상태 조회 중 예외 발생:", error)
+ setRegistrationData(null)
+ }
} catch (error) {
console.error("Error fetching vendor data:", error)
toast({
@@ -465,6 +527,7 @@ export function InfoForm() {
setExistingFiles(existingFiles.filter(file => file.id !== fileId))
setExistingCreditFiles(existingCreditFiles.filter(file => file.id !== fileId))
setExistingCashFlowFiles(existingCashFlowFiles.filter(file => file.id !== fileId))
+ setExistingSignatureFiles(existingSignatureFiles.filter(file => file.id !== fileId))
toast({
title: "파일 삭제 표시됨",
@@ -472,6 +535,82 @@ export function InfoForm() {
})
}
+
+
+ // 서명/직인 업로드 핸들러들 (한 개만 허용)
+ const handleSignatureDropAccepted = (acceptedFiles: File[]) => {
+ // 첫 번째 파일만 사용 (한 개만 허용)
+ const newFile = acceptedFiles[0]
+ if (newFile) {
+ // 기존 서명/직인 파일이 있으면 삭제 목록에 추가
+ if (existingSignatureFiles.length > 0) {
+ const existingFileId = existingSignatureFiles[0].id
+ setFilesToDelete([...filesToDelete, existingFileId])
+ setExistingSignatureFiles([]) // UI에서 제거
+
+ toast({
+ title: "서명/직인 교체",
+ description: "기존 서명/직인이 새 파일로 교체됩니다.",
+ })
+ }
+
+ setSignatureFiles([newFile]) // 새 파일 설정
+ setHasSignature(true)
+
+ if (acceptedFiles.length > 1) {
+ toast({
+ title: "파일 제한",
+ description: "서명/직인은 한 개만 등록할 수 있습니다. 첫 번째 파일만 선택되었습니다.",
+ })
+ }
+ }
+ }
+
+ const handleSignatureDropRejected = (fileRejections: any[]) => {
+ fileRejections.forEach((rej) => {
+ toast({
+ variant: "destructive",
+ title: "파일 오류",
+ description: `${rej.file.name}: ${rej.errors[0]?.message || "업로드 실패"}`,
+ })
+ })
+ }
+
+ const removeSignatureFile = () => {
+ setSignatureFiles([])
+ setHasSignature(false)
+ }
+
+ // 파일 타입 라벨 가져오기
+ const getAttachmentTypeLabel = (type: string) => {
+ const attachmentType = ATTACHMENT_TYPES.find(t => t.value === type)
+ return attachmentType?.label || type
+ }
+
+ const handleAdditionalInfoSave = async () => {
+ // 데이터 새로고침
+ try {
+ const registrationResult = await fetchVendorRegistrationStatus(Number(companyId))
+ if (registrationResult.success) {
+ setRegistrationData(registrationResult.data)
+ toast({
+ title: "데이터 새로고침",
+ description: "등록 현황 데이터가 새로고침되었습니다.",
+ })
+ } else if (registrationResult.noRegistration) {
+ // 정규등록 데이터가 없는 경우는 정상적인 상황
+ setRegistrationData(null)
+ } else {
+ // 실제 에러인 경우
+ console.error("정규등록 상태 새로고침 오류:", registrationResult.error)
+ setRegistrationData(null)
+ }
+ } catch (error) {
+ console.error("정규등록 상태 새로고침 중 예외 발생:", error)
+ setRegistrationData(null)
+ }
+ }
+
// Submit
async function onSubmit(values: UpdateVendorInfoSchema) {
if (!companyId) {
@@ -519,8 +658,10 @@ export function InfoForm() {
files: mainFiles,
creditRatingFiles,
cashFlowRatingFiles,
+ signatureFiles, // 서명/직인 파일들
contacts: values.contacts,
filesToDelete, // 삭제할 파일 ID 목록
+ selectedAttachmentType, // 선택된 첨부파일 타입
})
if (!result.error) {
@@ -606,6 +747,172 @@ export function InfoForm() {
<Separator />
+ {/* 정규업체 등록 현황 섹션 */}
+ {registrationData ? (
+ <Card>
+ <CardHeader>
+ <CardTitle className="flex items-center gap-2">
+ <FileText className="w-5 h-5" />
+ 정규업체 등록 현황
+ </CardTitle>
+ <CardDescription>
+ 정규업체 등록을 위한 현황을 확인하고 관리하세요.
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ {/* 현재 상태 표시 */}
+ {registrationData.registration && (
+ <div className="flex items-center gap-2 p-3 bg-muted/30 rounded-lg">
+ <Badge variant="secondary">
+ {registrationData.registration.status === 'under_review' && '검토중'}
+ {registrationData.registration.status === 'approval_ready' && '조건충족'}
+ {registrationData.registration.status === 'in_review' && '정규등록검토'}
+ {registrationData.registration.status === 'completed' && '등록완료'}
+ {registrationData.registration.status === 'pending_approval' && '장기미등록'}
+ </Badge>
+ </div>
+ )}
+
+ {/* 서명/직인 등록 */}
+ <div className="space-y-2">
+ <h4 className="font-medium text-sm">회사 서명/직인 등록</h4>
+
+ {/* 현재 등록된 서명/직인 파일 표시 (한 개만) */}
+ {(existingSignatureFiles.length > 0 || signatureFiles.length > 0) && (
+ <div className="space-y-2">
+ <div className="flex items-center gap-2 p-2 border rounded-lg bg-green-50">
+ <CheckCircle className="w-4 h-4 text-green-600" />
+ <span className="text-sm text-green-800">서명/직인 등록됨</span>
+ </div>
+
+ {/* 기존 등록된 서명/직인 (첫 번째만 표시) */}
+ {existingSignatureFiles.length > 0 && signatureFiles.length === 0 && (
+ <div className="p-2 border rounded-lg">
+ {(() => {
+ const file = existingSignatureFiles[0];
+ const fileInfo = getFileInfo(file.fileName);
+ return (
+ <div className="flex items-center gap-2">
+ <FileListIcon />
+ <div className="flex-1">
+ <div className="text-xs font-medium">{fileInfo.icon} {file.fileName}</div>
+ <div className="text-xs text-muted-foreground">
+ {getAttachmentTypeLabel(file.attachmentType)} | {file.fileSize ? formatFileSize(file.fileSize) : '크기 정보 없음'}
+ </div>
+ </div>
+ <div className="flex items-center space-x-1">
+ <FileListAction
+ onClick={() => handleDownloadFile(file)}
+ disabled={isDownloading}
+ >
+ {isDownloading ? <Loader2 className="h-3 w-3 animate-spin" /> : <Download className="h-3 w-3" />}
+ </FileListAction>
+ <FileListAction onClick={() => handleDeleteExistingFile(file.id)}>
+ <X className="h-3 w-3" />
+ </FileListAction>
+ </div>
+ </div>
+ );
+ })()}
+ </div>
+ )}
+
+ {/* 새로 업로드된 서명/직인 */}
+ {signatureFiles.length > 0 && (
+ <div className="p-2 border rounded-lg bg-blue-50">
+ {(() => {
+ const file = signatureFiles[0];
+ return (
+ <div className="flex items-center gap-2">
+ <FileListIcon />
+ <div className="flex-1">
+ <div className="text-xs font-medium">{file.name}</div>
+ <div className="text-xs text-muted-foreground">
+ 서명/직인 (새 파일) | {prettyBytes(file.size)}
+ </div>
+ </div>
+ <FileListAction onClick={removeSignatureFile}>
+ <X className="h-3 w-3" />
+ </FileListAction>
+ </div>
+ );
+ })()}
+ </div>
+ )}
+ </div>
+ )}
+
+ {/* 서명/직인 업로드 드롭존 */}
+ <Dropzone
+ maxSize={MAX_FILE_SIZE}
+ onDropAccepted={handleSignatureDropAccepted}
+ onDropRejected={handleSignatureDropRejected}
+ disabled={isSubmitting}
+ >
+ {({ maxSize }) => (
+ <DropzoneZone className="flex justify-center min-h-[50px]">
+ <DropzoneInput />
+ <div className="flex items-center gap-2">
+ <Upload className="w-4 h-4" />
+ <div className="text-sm">
+ <DropzoneTitle>
+ {existingSignatureFiles.length > 0 || signatureFiles.length > 0
+ ? "서명/직인 교체"
+ : "서명/직인 업로드"
+ }
+ </DropzoneTitle>
+ <DropzoneDescription>
+ 한 개 파일만 업로드 가능 {maxSize ? ` | 최대: ${prettyBytes(maxSize)}` : ""}
+ </DropzoneDescription>
+ </div>
+ </div>
+ </DropzoneZone>
+ )}
+ </Dropzone>
+ </div>
+
+ {/* 액션 버튼들 */}
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
+ <Button
+ onClick={() => setDocumentDialogOpen(true)}
+ variant="outline"
+ size="sm"
+ >
+ <Eye className="w-4 h-4 mr-2" />
+ 문서 현황 확인
+ </Button>
+ <Button
+ onClick={() => setAdditionalInfoDialogOpen(true)}
+ variant={registrationData.additionalInfo ? "outline" : "default"}
+ size="sm"
+ >
+ <FileText className="w-4 h-4 mr-2" />
+ {registrationData.additionalInfo ? "추가정보 수정" : "추가정보 등록"}
+ </Button>
+ </div>
+ </CardContent>
+ </Card>
+ ) : (
+ // 정규업체 등록 데이터가 없는 경우 (기존 정규업체이거나 아직 등록 진행 안함)
+ <Card>
+ <CardHeader>
+ <CardTitle className="flex items-center gap-2">
+ <FileText className="w-5 h-5" />
+ 정규업체 등록 현황
+ </CardTitle>
+ <CardDescription>
+ 현재 정규업체 등록 진행 상황이 없습니다.
+ </CardDescription>
+ </CardHeader>
+ <CardContent>
+ <div className="text-center py-4 text-muted-foreground">
+ <p>이미 정규업체로 등록되어 있거나, 아직 정규업체 등록을 진행하지 않았습니다.</p>
+ <p className="text-sm mt-1">정규업체 등록이 필요한 경우 담당자에게 문의하세요.</p>
+ </div>
+ </CardContent>
+ </Card>
+ )}
+
{/* 첨부파일 요약 카드 - 기존 파일 있는 경우만 표시 */}
{(existingFiles.length > 0 || existingCreditFiles.length > 0 || existingCashFlowFiles.length > 0) && (
<Card>
@@ -619,8 +926,8 @@ export function InfoForm() {
<div className="grid gap-4">
{existingFiles.length > 0 && (
<div>
- <h4 className="font-medium mb-2">일반 첨부파일</h4>
- <ScrollArea className="h-32">
+ <h4 className="font-medium mb-2">첨부파일</h4>
+ <ScrollArea className="h-48">
<FileList className="gap-2">
{existingFiles.map((file) => {
const fileInfo = getFileInfo(file.fileName);
@@ -633,7 +940,7 @@ export function InfoForm() {
{fileInfo.icon} {file.fileName}
</FileListName>
<FileListDescription>
- {file.fileSize ? formatFileSize(file.fileSize) : '크기 정보 없음'}
+ {getAttachmentTypeLabel(file.attachmentType)} | {file.fileSize ? formatFileSize(file.fileSize) : '크기 정보 없음'}
</FileListDescription>
</FileListInfo>
<div className="flex items-center space-x-2">
@@ -659,7 +966,7 @@ export function InfoForm() {
{existingCreditFiles.length > 0 && (
<div>
<h4 className="font-medium mb-2">신용평가 첨부파일</h4>
- <ScrollArea className="h-32">
+ <ScrollArea className="h-24">
<FileList className="gap-2">
{existingCreditFiles.map((file) => {
const fileInfo = getFileInfo(file.fileName);
@@ -672,7 +979,7 @@ export function InfoForm() {
{fileInfo.icon} {file.fileName}
</FileListName>
<FileListDescription>
- {file.fileSize ? formatFileSize(file.fileSize) : '크기 정보 없음'}
+ {getAttachmentTypeLabel(file.attachmentType)} | {file.fileSize ? formatFileSize(file.fileSize) : '크기 정보 없음'}
</FileListDescription>
</FileListInfo>
<div className="flex items-center space-x-2">
@@ -698,7 +1005,7 @@ export function InfoForm() {
{existingCashFlowFiles.length > 0 && (
<div>
<h4 className="font-medium mb-2">현금흐름 첨부파일</h4>
- <ScrollArea className="h-32">
+ <ScrollArea className="h-24">
<FileList className="gap-2">
{existingCashFlowFiles.map((file) => {
const fileInfo = getFileInfo(file.fileName);
@@ -711,7 +1018,7 @@ export function InfoForm() {
{fileInfo.icon} {file.fileName}
</FileListName>
<FileListDescription>
- {file.fileSize ? formatFileSize(file.fileSize) : '크기 정보 없음'}
+ {getAttachmentTypeLabel(file.attachmentType)} | {file.fileSize ? formatFileSize(file.fileSize) : '크기 정보 없음'}
</FileListDescription>
</FileListInfo>
<div className="flex items-center space-x-2">
@@ -1383,13 +1690,33 @@ export function InfoForm() {
───────────────────────────────────────── */}
<div className="rounded-md border p-4 space-y-4">
<h4 className="text-md font-semibold">기타 첨부파일 추가</h4>
+
+ {/* 첨부파일 타입 선택 */}
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div>
+ <label className="text-sm font-medium mb-2 block">파일 타입</label>
+ <Select value={selectedAttachmentType} onValueChange={(value: AttachmentType) => setSelectedAttachmentType(value)}>
+ <SelectTrigger>
+ <SelectValue placeholder="파일 타입 선택" />
+ </SelectTrigger>
+ <SelectContent>
+ {ATTACHMENT_TYPES.map((type) => (
+ <SelectItem key={type.value} value={type.value}>
+ {type.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ </div>
+ </div>
+
<FormField
control={form.control}
name="attachedFiles"
render={() => (
<FormItem>
<FormLabel>
- 첨부 파일 (추가)
+ 첨부 파일 (추가) - {getAttachmentTypeLabel(selectedAttachmentType)}
</FormLabel>
<Dropzone
maxSize={MAX_FILE_SIZE}
@@ -1427,7 +1754,7 @@ export function InfoForm() {
<FileListInfo>
<FileListName>{file.name}</FileListName>
<FileListDescription>
- {prettyBytes(file.size)}
+ {getAttachmentTypeLabel(selectedAttachmentType)} | {prettyBytes(file.size)}
</FileListDescription>
</FileListInfo>
<FileListAction onClick={() => removeFile(i)}>
@@ -1459,6 +1786,61 @@ export function InfoForm() {
</div>
</form>
</Form>
+
+ {/* 정규등록 관련 다이얼로그들 - 정규등록 데이터가 있을 때만 표시 */}
+ {registrationData && (
+ <>
+ {/* 문서 현황 Dialog */}
+ <DocumentStatusDialog
+ open={documentDialogOpen}
+ onOpenChange={setDocumentDialogOpen}
+ registration={{
+ id: registrationData.registration?.id || 0,
+ vendorId: registrationData.vendor.id,
+ companyName: registrationData.vendor.vendorName,
+ businessNumber: registrationData.vendor.taxId,
+ representative: registrationData.vendor.representativeName || "",
+ country: registrationData.vendor.country || "KR",
+ potentialCode: registrationData.registration?.potentialCode || "",
+ status: registrationData.registration?.status || "under_review",
+ majorItems: "[]",
+ establishmentDate: registrationData.vendor.createdAt || new Date(),
+ registrationRequestDate: registrationData.registration?.registrationRequestDate,
+ assignedDepartment: registrationData.registration?.assignedDepartment,
+ assignedUser: registrationData.registration?.assignedUser,
+ remarks: registrationData.registration?.remarks,
+ safetyQualificationContent: registrationData.registration?.safetyQualificationContent || null,
+ gtcSkipped: registrationData.registration?.gtcSkipped || false,
+ additionalInfo: registrationData.additionalInfo,
+ documentSubmissions: registrationData.documentStatus,
+ contractAgreements: {
+ cp: "not_submitted",
+ gtc: "not_submitted",
+ standardSubcontract: "not_submitted",
+ safetyHealth: "not_submitted",
+ ethics: "not_submitted",
+ domesticCredit: "not_submitted",
+ },
+ basicContracts: registrationData.basicContracts || [],
+ documentFiles: {
+ businessRegistration: [],
+ creditEvaluation: [],
+ bankCopy: [],
+ auditResult: [],
+ },
+ } as VendorRegularRegistration}
+ onRefresh={handleAdditionalInfoSave}
+ />
+
+ {/* 추가정보 입력 Dialog */}
+ <AdditionalInfoDialog
+ open={additionalInfoDialogOpen}
+ onOpenChange={setAdditionalInfoDialogOpen}
+ vendorId={Number(companyId)}
+ onSave={handleAdditionalInfoSave}
+ />
+ </>
+ )}
</div>
</section>
</div>
diff --git a/components/vendor-regular-registrations/additional-info-dialog.tsx b/components/vendor-regular-registrations/additional-info-dialog.tsx
index fbd60515..84475877 100644
--- a/components/vendor-regular-registrations/additional-info-dialog.tsx
+++ b/components/vendor-regular-registrations/additional-info-dialog.tsx
@@ -16,13 +16,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
+
import {
Form,
FormControl,
@@ -54,12 +48,12 @@ const businessContactSchema = z.object({
// 추가정보 스키마
const additionalInfoSchema = z.object({
- businessType: z.string().optional(),
- industryType: z.string().optional(),
- companySize: z.string().optional(),
- revenue: z.string().optional(),
- factoryEstablishedDate: z.string().optional(),
- preferredContractTerms: z.string().optional(),
+ businessType: z.string().min(1, "사업유형은 필수입니다"),
+ industryType: z.string().min(1, "산업유형은 필수입니다"),
+ companySize: z.string().min(1, "기업규모는 필수입니다"),
+ revenue: z.string().min(1, "매출액은 필수입니다"),
+ factoryEstablishedDate: z.string().min(1, "공장설립일은 필수입니다"),
+ preferredContractTerms: z.string().min(1, "선호계약조건은 필수입니다"),
});
// 전체 폼 스키마
@@ -85,28 +79,7 @@ const contactTypes = [
{ value: "tax_invoice", label: "세금계산서", required: true },
];
-const businessTypes = [
- { value: "manufacturing", label: "제조업" },
- { value: "trading", label: "무역업" },
- { value: "service", label: "서비스업" },
- { value: "construction", label: "건설업" },
- { value: "other", label: "기타" },
-];
-const industryTypes = [
- { value: "shipbuilding", label: "조선업" },
- { value: "marine", label: "해양플랜트" },
- { value: "energy", label: "에너지" },
- { value: "automotive", label: "자동차" },
- { value: "other", label: "기타" },
-];
-
-const companySizes = [
- { value: "large", label: "대기업" },
- { value: "medium", label: "중견기업" },
- { value: "small", label: "중소기업" },
- { value: "startup", label: "스타트업" },
-];
export function AdditionalInfoDialog({
open,
@@ -169,16 +142,17 @@ export function AdditionalInfoDialog({
};
});
- // 추가정보 데이터 설정
+ // 추가정보 데이터 설정
+ const additionalInfoData = additionalInfo as any;
const additionalData = {
- businessType: additionalInfo?.businessType || "",
- industryType: additionalInfo?.industryType || "",
- companySize: additionalInfo?.companySize || "",
- revenue: additionalInfo?.revenue || "",
- factoryEstablishedDate: additionalInfo?.factoryEstablishedDate
- ? new Date(additionalInfo.factoryEstablishedDate).toISOString().split('T')[0]
+ businessType: additionalInfoData?.businessType || "",
+ industryType: additionalInfoData?.industryType || "",
+ companySize: additionalInfoData?.companySize || "",
+ revenue: additionalInfoData?.revenue || "",
+ factoryEstablishedDate: additionalInfoData?.factoryEstablishedDate
+ ? new Date(additionalInfoData.factoryEstablishedDate).toISOString().split('T')[0]
: "",
- preferredContractTerms: additionalInfo?.preferredContractTerms || "",
+ preferredContractTerms: additionalInfoData?.preferredContractTerms || "",
};
// 폼 데이터 업데이트
@@ -257,7 +231,6 @@ export function AdditionalInfoDialog({
<CardHeader className="pb-3">
<CardTitle className="text-lg flex items-center gap-2">
{contactType.label} 담당자
- <Badge variant="destructive" className="text-xs">필수</Badge>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
@@ -352,21 +325,10 @@ export function AdditionalInfoDialog({
name="additionalInfo.businessType"
render={({ field }) => (
<FormItem>
- <FormLabel>사업유형</FormLabel>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="사업유형 선택" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {businessTypes.map((type) => (
- <SelectItem key={type.value} value={type.value}>
- {type.label}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
+ <FormLabel>사업유형 *</FormLabel>
+ <FormControl>
+ <Input placeholder="사업유형 입력" {...field} />
+ </FormControl>
<FormMessage />
</FormItem>
)}
@@ -376,21 +338,10 @@ export function AdditionalInfoDialog({
name="additionalInfo.industryType"
render={({ field }) => (
<FormItem>
- <FormLabel>산업유형</FormLabel>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="산업유형 선택" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {industryTypes.map((type) => (
- <SelectItem key={type.value} value={type.value}>
- {type.label}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
+ <FormLabel>산업유형 *</FormLabel>
+ <FormControl>
+ <Input placeholder="산업유형 입력" {...field} />
+ </FormControl>
<FormMessage />
</FormItem>
)}
@@ -402,21 +353,10 @@ export function AdditionalInfoDialog({
name="additionalInfo.companySize"
render={({ field }) => (
<FormItem>
- <FormLabel>기업규모</FormLabel>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="기업규모 선택" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {companySizes.map((size) => (
- <SelectItem key={size.value} value={size.value}>
- {size.label}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
+ <FormLabel>기업규모 *</FormLabel>
+ <FormControl>
+ <Input placeholder="기업규모 입력" {...field} />
+ </FormControl>
<FormMessage />
</FormItem>
)}
@@ -426,7 +366,7 @@ export function AdditionalInfoDialog({
name="additionalInfo.revenue"
render={({ field }) => (
<FormItem>
- <FormLabel>매출액 (억원)</FormLabel>
+ <FormLabel>매출액 (억원) *</FormLabel>
<FormControl>
<Input
placeholder="매출액 입력"
@@ -445,7 +385,7 @@ export function AdditionalInfoDialog({
name="additionalInfo.factoryEstablishedDate"
render={({ field }) => (
<FormItem>
- <FormLabel>공장설립일</FormLabel>
+ <FormLabel>공장설립일 *</FormLabel>
<FormControl>
<Input
placeholder="YYYY-MM-DD"
@@ -463,7 +403,7 @@ export function AdditionalInfoDialog({
name="additionalInfo.preferredContractTerms"
render={({ field }) => (
<FormItem>
- <FormLabel>선호계약조건</FormLabel>
+ <FormLabel>선호계약조건 *</FormLabel>
<FormControl>
<Textarea
placeholder="선호하는 계약조건을 상세히 입력해주세요"