diff options
Diffstat (limited to 'components/ship-vendor-document/new-revision-dialog.tsx')
| -rw-r--r-- | components/ship-vendor-document/new-revision-dialog.tsx | 268 |
1 files changed, 51 insertions, 217 deletions
diff --git a/components/ship-vendor-document/new-revision-dialog.tsx b/components/ship-vendor-document/new-revision-dialog.tsx index 3ec58d1d..91694827 100644 --- a/components/ship-vendor-document/new-revision-dialog.tsx +++ b/components/ship-vendor-document/new-revision-dialog.tsx @@ -31,17 +31,22 @@ import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { Button } from "@/components/ui/button" import { Progress } from "@/components/ui/progress" -import { - Upload, - FileText, - X, - Loader2, - CheckCircle, - Info +import { + Upload, + FileText, + X, + Loader2, + CheckCircle } from "lucide-react" import { toast } from "sonner" import { useSession } from "next-auth/react" -import { Alert, AlertDescription } from "@/components/ui/alert" +import { + createUploadRevisionSchema, + getUsageOptions, + getUsageTypeOptions, + getRevisionGuide, + B3RevisionInput +} from "./revision-validation" // 기존 메인 컴포넌트에서 추가할 import // import { NewRevisionDialog } from "./new-revision-dialog" @@ -50,166 +55,12 @@ import { Alert, AlertDescription } from "@/components/ui/alert" * Schema & Types * -----------------------------------------------------------------------------------------------*/ -// 파일 검증 스키마 -const MAX_FILE_SIZE = 1024 * 1024 * 1024 // 1GB -// B3 리비전 검증 함수 -const validateB3Revision = (value: string) => { - // B3 리비전 패턴: 단일 알파벳(A-Z) 또는 R01-R99 - const alphabetPattern = /^[A-Z]$/ - const numericPattern = /^R(0[1-9]|[1-9][0-9])$/ - - return alphabetPattern.test(value) || numericPattern.test(value) -} -// B4 리비전 검증 함수 -const validateB4Revision = (value: string) => { - // B4 리비전 패턴: R01-R99 - const numericPattern = /^R(0[1-9]|[1-9][0-9])$/ - return numericPattern.test(value) -} -// drawingKind에 따른 동적 스키마 생성 -const createRevisionUploadSchema = (drawingKind: string) => { - const baseSchema = { - usage: z.string().min(1, "Please select a usage"), - comment: z.string().optional(), - attachments: z - .array(z.instanceof(File)) - .min(1, "Please upload at least 1 file") - .refine( - (files) => files.every((file) => file.size <= MAX_FILE_SIZE), - "File size must be 50MB or less" - ), - } - // B3와 B4에 따른 리비전 검증 추가 - const revisionField = drawingKind === 'B3' - ? z.string() - .min(1, "Please enter a revision") - .max(3, "Revision must be 3 characters or less") - .refine( - validateB3Revision, - "Invalid format. Use A-Z or R01-R99" - ) - : z.string() - .min(1, "Please enter a revision") - .max(3, "Revision must be 3 characters or less") - .refine( - validateB4Revision, - "Invalid format. Use R01-R99" - ) - - // B3인 경우에만 usageType 필드 추가 - if (drawingKind === 'B3') { - return z.object({ - ...baseSchema, - revision: revisionField, - usageType: z.string().min(1, "Please select a usage type"), - }) - } else { - return z.object({ - ...baseSchema, - revision: revisionField, - usageType: z.string().optional(), - }) - } -} -// drawingKind에 따른 용도 옵션 -const getUsageOptions = (drawingKind: string) => { - switch (drawingKind) { - case 'B3': - return [ - { value: "Approval", label: "Approval" }, - { value: "Working", label: "Working" }, - { value: "Comments", label: "Comments" }, - ] - case 'B4': - return [ - { value: "Pre", label: "Pre" }, - { value: "Working", label: "Working" }, - ] - case 'B5': - return [ - { value: "Pre", label: "Pre" }, - { value: "Working", label: "Working" }, - ] - default: - return [ - { value: "Pre", label: "Pre" }, - { value: "Working", label: "Working" }, - ] - } -} -// B3 전용 용도 타입 옵션 -const getUsageTypeOptions = (usage: string) => { - switch (usage) { - case 'Approval': - return [ - { value: "Full", label: "Full" }, - { value: "Partial", label: "Partial" }, - ] - case 'Working': - return [ - { value: "Full", label: "Full" }, - { value: "Partial", label: "Partial" }, - ] - case 'Comments': - return [ - { value: "Comments", label: "Comments" }, - ] - default: - return [] - } -} - -// 리비전 형식 가이드 생성 -const getRevisionGuide = (drawingKind: string) => { - if (drawingKind === 'B3') { - return { - placeholder: "e.g., A, B, C or R01, R02", - helpText: "Use single letter (A-Z) or R01-R99 format", - examples: [ - "A, B, C, ... Z (alphabetic revisions)", - "R01, R02, ... R99 (numeric revisions)" - ] - } - } - return { - placeholder: "e.g., R01, R02, R03", - helpText: "Enter in R01, R02, R03... format", - examples: ["R01, R02, R03, ... R99"] - } -} - -// B3 리비전 자동 포맷팅 함수 -const formatB3RevisionInput = (value: string): string => { - // 입력값을 대문자로 변환 - const upperValue = value.toUpperCase() - - // 단일 알파벳인 경우 - if (/^[A-Z]$/.test(upperValue)) { - return upperValue - } - - // R로 시작하는 경우 - if (upperValue.startsWith('R')) { - // R 뒤의 숫자 추출 - const numPart = upperValue.slice(1).replace(/\D/g, '') - if (numPart) { - const num = parseInt(numPart, 10) - // 1-99 범위 체크 - if (num >= 1 && num <= 99) { - // 01-09는 0을 붙이고, 10-99는 그대로 - return `R${num.toString().padStart(2, '0')}` - } - } - } - - return upperValue -} interface NewRevisionDialogProps { open: boolean @@ -217,7 +68,7 @@ interface NewRevisionDialogProps { documentId: number documentTitle?: string drawingKind: string - onSuccess?: (result?: any) => void + onSuccess?: (result?: unknown) => void } /* ------------------------------------------------------------------------------------------------- @@ -329,55 +180,6 @@ function FileUploadArea({ ) } -/* ------------------------------------------------------------------------------------------------- - * Revision Input Component for B3 - * -----------------------------------------------------------------------------------------------*/ -function B3RevisionInput({ - value, - onChange, - error -}: { - value: string - onChange: (value: string) => void - error?: string -}) { - const [inputValue, setInputValue] = React.useState(value) - - const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { - const rawValue = e.target.value - const formattedValue = formatB3RevisionInput(rawValue) - - // 길이 제한 (알파벳은 1자, R숫자는 3자) - if (rawValue.length <= 3) { - setInputValue(formattedValue) - onChange(formattedValue) - } - } - - const revisionGuide = getRevisionGuide('B3') - - return ( - <div className="space-y-2"> - <Input - value={inputValue} - onChange={handleInputChange} - placeholder={revisionGuide.placeholder} - className={error ? "border-red-500" : ""} - /> - <Alert className="bg-blue-50 border-blue-200"> - <Info className="h-4 w-4 text-blue-600" /> - <AlertDescription className="text-xs space-y-1"> - <div className="font-medium text-blue-900">{revisionGuide.helpText}</div> - <div className="text-blue-700"> - {revisionGuide.examples.map((example, idx) => ( - <div key={idx}>• {example}</div> - ))} - </div> - </AlertDescription> - </Alert> - </div> - ) -} /* ------------------------------------------------------------------------------------------------- * Main Dialog Component @@ -398,15 +200,30 @@ export function NewRevisionDialog({ // Serial No 조회 const fetchNextSerialNo = React.useCallback(async () => { + console.log('🔍 fetchNextSerialNo called with documentId:', documentId) setIsLoadingSerialNo(true) try { - const response = await fetch(`/api/revisions/max-serial-no?documentId=${documentId}`) + const apiUrl = `/api/revisions/max-serial-no?documentId=${documentId}` + console.log('🔍 Calling API:', apiUrl) + + const response = await fetch(apiUrl) + console.log('🔍 API Response status:', response.status) + if (response.ok) { const data = await response.json() - setNextSerialNo(String(data.nextSerialNo)) + console.log('🔍 API Response data:', data) + console.log('🔍 data.nextSerialNo:', data.nextSerialNo) + + const serialNoString = String(data.nextSerialNo) + console.log('🔍 Setting nextSerialNo to:', serialNoString) + + setNextSerialNo(serialNoString) + console.log('🔍 nextSerialNo state updated') + } else { + console.error('🔍 API call failed with status:', response.status) } } catch (error) { - console.error('Failed to fetch serial no:', error) + console.error('❌ Failed to fetch serial no:', error) // 에러 시 기본값 1 사용 setNextSerialNo("1") } finally { @@ -416,8 +233,12 @@ export function NewRevisionDialog({ // Dialog 열릴 때 Serial No 조회 React.useEffect(() => { + console.log('🎯 useEffect triggered - open:', open, 'documentId:', documentId) if (open && documentId) { + console.log('🎯 Calling fetchNextSerialNo') fetchNextSerialNo() + } else { + console.log('🎯 Conditions not met for fetchNextSerialNo') } }, [open, documentId, fetchNextSerialNo]) @@ -427,7 +248,7 @@ export function NewRevisionDialog({ }, [session]); // drawingKind에 따른 동적 스키마 및 옵션 생성 - const revisionUploadSchema = React.useMemo(() => createRevisionUploadSchema(drawingKind), [drawingKind]) + const revisionUploadSchema = React.useMemo(() => createUploadRevisionSchema(drawingKind), [drawingKind]) const usageOptions = React.useMemo(() => getUsageOptions(drawingKind), [drawingKind]) const showUsageType = drawingKind === 'B3' @@ -480,6 +301,10 @@ export function NewRevisionDialog({ } const onSubmit = async (data: RevisionUploadSchema) => { + console.log('🚀 onSubmit called with data:', data) + console.log('🚀 Current nextSerialNo state:', nextSerialNo) + console.log('🚀 documentId:', documentId) + setIsUploading(true) setUploadProgress(0) @@ -487,6 +312,8 @@ export function NewRevisionDialog({ const formData = new FormData() formData.append("documentId", String(documentId)) formData.append("serialNo", nextSerialNo) // 추가 + console.log('🚀 Appending serialNo to formData:', nextSerialNo) + formData.append("usage", data.usage) formData.append("revision", data.revision) formData.append("uploaderName", userName || "evcp") @@ -603,8 +430,15 @@ export function NewRevisionDialog({ <div className="text-xs text-muted-foreground"> Drawing Type: {drawingKind} | Serial No: {nextSerialNo} {isLoadingSerialNo && ( - <Loader2 className="inline-block ml-2 h-3 w-3 animate-spin" /> + <> + <Loader2 className="inline-block ml-2 h-3 w-3 animate-spin" /> + <span className="ml-1">Loading...</span> + </> )} + {/* 디버그용 임시 표시 */} + <div className="mt-1 text-xs text-orange-600"> + Debug: nextSerialNo={nextSerialNo}, isLoading={isLoadingSerialNo} + </div> </div> </DialogDescription> )} |
