summaryrefslogtreecommitdiff
path: root/components/ship-vendor-document/new-revision-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/ship-vendor-document/new-revision-dialog.tsx')
-rw-r--r--components/ship-vendor-document/new-revision-dialog.tsx268
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>
)}