summaryrefslogtreecommitdiff
path: root/components/ship-vendor-document/revision-validation.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-10-02 18:02:11 +0900
committerjoonhoekim <26rote@gmail.com>2025-10-02 18:02:11 +0900
commit624cfcf4edb106e6cf0b041d9437ceaa94b6a46d (patch)
treec8b81d4969c994aa8f15aea308c24de1f972ce96 /components/ship-vendor-document/revision-validation.tsx
parented412083c785fc1fed7a2490f84f72a665c846be (diff)
(디버깅) 돌체 디버깅 - serialNo, 변경사항 카운트
Diffstat (limited to 'components/ship-vendor-document/revision-validation.tsx')
-rw-r--r--components/ship-vendor-document/revision-validation.tsx266
1 files changed, 266 insertions, 0 deletions
diff --git a/components/ship-vendor-document/revision-validation.tsx b/components/ship-vendor-document/revision-validation.tsx
new file mode 100644
index 00000000..4ff621a0
--- /dev/null
+++ b/components/ship-vendor-document/revision-validation.tsx
@@ -0,0 +1,266 @@
+import React from "react"
+import { z } from "zod"
+import { Input } from "@/components/ui/input"
+import { Alert, AlertDescription } from "@/components/ui/alert"
+import { Info } from "lucide-react"
+
+// 파일 검증 스키마
+export const MAX_FILE_SIZE = 1024 * 1024 * 1024 // 1GB
+
+// B3 리비전 검증 함수
+export 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 리비전 검증 함수
+export const validateB4Revision = (value: string) => {
+ // B4 리비전 패턴: R01-R99
+ const numericPattern = /^R(0[1-9]|[1-9][0-9])$/
+ return numericPattern.test(value)
+}
+
+// 리비전 검증 함수 (drawingKind에 따라)
+export const validateRevision = (value: string, drawingKind: string) => {
+ if (drawingKind === 'B3') {
+ return validateB3Revision(value)
+ } else {
+ return validateB4Revision(value)
+ }
+}
+
+// 리비전 수정용 스키마 생성
+export const createEditRevisionSchema = (drawingKind: string) => {
+ const baseSchema = {
+ usage: z.string().min(1, "Please select a usage"),
+ comment: z.string().optional(),
+ }
+
+ // drawingKind에 따른 리비전 검증 추가
+ 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(),
+ })
+ }
+}
+
+// 리비전 업로드용 스키마 생성
+export const createUploadRevisionSchema = (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"
+ ),
+ }
+
+ // drawingKind에 따른 리비전 검증 추가
+ 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에 따른 용도 옵션
+export 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 전용 용도 타입 옵션
+export 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 []
+ }
+}
+
+// 리비전 형식 가이드 생성
+export 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 리비전 자동 포맷팅 함수
+export 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
+}
+
+// B3 리비전 입력 컴포넌트
+export function B3RevisionInput({
+ value,
+ onChange,
+ error
+}: {
+ value: string
+ onChange: (value: string) => void
+ error?: string
+}) {
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ const rawValue = e.target.value.toUpperCase()
+
+ // 길이 제한 (알파벳은 1자, R숫자는 3자)
+ if (rawValue.length <= 3) {
+ onChange(rawValue)
+ }
+ }
+
+ const handleBlur = () => {
+ // blur 시점에 포맷팅 적용
+ const formattedValue = formatB3RevisionInput(value)
+ onChange(formattedValue)
+ }
+
+ const revisionGuide = getRevisionGuide('B3')
+
+ return (
+ <div className="space-y-2">
+ <Input
+ value={value}
+ onChange={handleInputChange}
+ onBlur={handleBlur}
+ 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>
+ )
+}