diff options
Diffstat (limited to 'lib/gtc-contract/gtc-clauses/validations.ts')
| -rw-r--r-- | lib/gtc-contract/gtc-clauses/validations.ts | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/lib/gtc-contract/gtc-clauses/validations.ts b/lib/gtc-contract/gtc-clauses/validations.ts new file mode 100644 index 00000000..edbcf612 --- /dev/null +++ b/lib/gtc-contract/gtc-clauses/validations.ts @@ -0,0 +1,124 @@ +import { type GtcClause } from "@/db/schema/gtc" +import { + createSearchParamsCache, + parseAsArrayOf, + parseAsInteger, + parseAsString, + parseAsStringEnum, +} from "nuqs/server" +import * as z from "zod" +import { getFiltersStateParser, getSortingStateParser } from "@/lib/parsers" + +export const searchParamsCache = createSearchParamsCache({ + flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault( + [] + ), + page: parseAsInteger.withDefault(1), + perPage: parseAsInteger.withDefault(20), + sort: getSortingStateParser<GtcClause>().withDefault([ + { id: "sortOrder", desc: false }, + ]), + // 검색 필터들 + category: parseAsString.withDefault(""), + depth: parseAsInteger.withDefault(0), + parentId: parseAsInteger.withDefault(0), + // advanced filter + filters: getFiltersStateParser().withDefault([]), + joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), + search: parseAsString.withDefault(""), +}) + +const clauseImageSchema = z.object({ + id: z.string(), + url: z.string().url(), + fileName: z.string(), + size: z.number().positive(), +}) + +export const createGtcClauseSchema = z.object({ + documentId: z.number(), + parentId: z.number().nullable().optional(), + itemNumber: z.string().min(1, "채번을 입력해주세요"), + category: z.string().optional(), + subtitle: z.string().min(1, "소제목을 입력해주세요"), + images: z.array(clauseImageSchema).optional(), // ✅ 이미지 배열 추가 + + content: z.string().optional(), // 그룹핑용 조항은 내용이 없을 수 있음 + sortOrder: z.number().default(0), + editReason: z.string().optional(), +}).superRefine(async (data, ctx) => { + // 채번 형식 검증 (숫자, 문자 모두 허용하되 특수문자 제한) + const itemNumberRegex = /^[a-zA-Z0-9._-]+$/ + if (!itemNumberRegex.test(data.itemNumber)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "채번은 영문, 숫자, 점(.), 하이픈(-), 언더스코어(_)만 사용 가능합니다", + path: ["itemNumber"], + }) + } +}) + +export const updateGtcClauseSchema = z.object({ + itemNumber: z.string().min(1, "채번을 입력해주세요").optional(), + category: z.string().optional(), + subtitle: z.string().min(1, "소제목을 입력해주세요").optional(), + content: z.string().optional(), // 내용도 nullable + sortOrder: z.number().optional(), + images: z.array(clauseImageSchema).optional(), // ✅ 이미지 배열 추가 + isActive: z.boolean().optional(), + editReason: z.string().optional(), +}).superRefine(async (data, ctx) => { + // 채번 형식 검증 + if (data.itemNumber) { + const itemNumberRegex = /^[a-zA-Z0-9._-]+$/ + if (!itemNumberRegex.test(data.itemNumber)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "채번은 영문, 숫자, 점(.), 하이픈(-), 언더스코어(_)만 사용 가능합니다", + path: ["itemNumber"], + }) + } + } +}) + +export const reorderGtcClausesSchema = z.object({ + clauses: z.array(z.object({ + id: z.number(), + sortOrder: z.number(), + parentId: z.number().nullable(), + depth: z.number(), + fullPath: z.string().optional(), + })), + editReason: z.string().optional(), +}) + +export const bulkUpdateGtcClausesSchema = z.object({ + clauseIds: z.array(z.number()).min(1, "수정할 조항을 선택해주세요"), + updates: z.object({ + category: z.string().optional(), + isActive: z.boolean().optional(), + }), + editReason: z.string().min(1, "편집 사유를 입력해주세요"), +}) + +export const generateVariableNamesSchema = z.object({ + documentId: z.number(), + prefix: z.string().default("CLAUSE"), + includeVendorCode: z.boolean().default(false), + vendorCode: z.string().optional(), +}).superRefine(async (data, ctx) => { + if (data.includeVendorCode && !data.vendorCode) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "벤더 코드 포함 시 벤더 코드를 입력해주세요", + path: ["vendorCode"], + }) + } +}) + +export type GetGtcClausesSchema = Awaited<ReturnType<typeof searchParamsCache.parse>> +export type CreateGtcClauseSchema = z.infer<typeof createGtcClauseSchema> +export type UpdateGtcClauseSchema = z.infer<typeof updateGtcClauseSchema> +export type ReorderGtcClausesSchema = z.infer<typeof reorderGtcClausesSchema> +export type BulkUpdateGtcClausesSchema = z.infer<typeof bulkUpdateGtcClausesSchema> +export type GenerateVariableNamesSchema = z.infer<typeof generateVariableNamesSchema>
\ No newline at end of file |
