1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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>
|