@dragonmastery/dragoncore-shared 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +10090 -0
- package/dist/index.mjs +1965 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +2 -2
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1965 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/validation/attachment/attachment_filters_zod.ts
|
|
4
|
+
const AttachmentFiltersSchema = z.object({
|
|
5
|
+
record_id: z.string(),
|
|
6
|
+
record_type: z.string(),
|
|
7
|
+
filters: z.object({
|
|
8
|
+
start_date: z.string().optional().nullable(),
|
|
9
|
+
end_date: z.string().optional().nullable(),
|
|
10
|
+
limit: z.number().optional().nullable(),
|
|
11
|
+
cursor: z.string().optional().nullable(),
|
|
12
|
+
folder_id: z.string().optional().nullable(),
|
|
13
|
+
include_folders: z.boolean().optional().default(true)
|
|
14
|
+
}).optional().nullable()
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/validation/attachment/attachment_folder_input_zod.ts
|
|
19
|
+
const AttachmentFolderBaseSchema = z.object({
|
|
20
|
+
record_id: z.string(),
|
|
21
|
+
record_type: z.string(),
|
|
22
|
+
sanitized_name: z.string(),
|
|
23
|
+
original_name: z.string(),
|
|
24
|
+
description: z.string().optional().nullable(),
|
|
25
|
+
metadata: z.string().optional().nullable(),
|
|
26
|
+
parent_folder_id: z.string().optional().nullable(),
|
|
27
|
+
file_count: z.number().optional()
|
|
28
|
+
});
|
|
29
|
+
const AttachmentFolderCreateSchema = AttachmentFolderBaseSchema;
|
|
30
|
+
const AttachmentFolderUpdateSchema = AttachmentFolderBaseSchema.extend({
|
|
31
|
+
id: z.string(),
|
|
32
|
+
record_id: z.string().optional(),
|
|
33
|
+
record_type: z.string().optional(),
|
|
34
|
+
sanitized_name: z.string().optional(),
|
|
35
|
+
original_name: z.string().optional(),
|
|
36
|
+
parent_folder_id: z.string().optional().nullable()
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/validation/attachment/attachment_folder_read_zod.ts
|
|
41
|
+
const AttachmentFolderReadSchema = AttachmentFolderCreateSchema.extend({
|
|
42
|
+
id: z.string(),
|
|
43
|
+
file_count: z.number().optional(),
|
|
44
|
+
created_at: z.string(),
|
|
45
|
+
created_by: z.string(),
|
|
46
|
+
updated_at: z.string().optional().nullable(),
|
|
47
|
+
updated_by: z.string().optional().nullable(),
|
|
48
|
+
archived_at: z.string().optional().nullable(),
|
|
49
|
+
archived_by: z.string().optional().nullable(),
|
|
50
|
+
deleted_at: z.string().optional().nullable(),
|
|
51
|
+
deleted_by: z.string().optional().nullable()
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/validation/attachment/attachment_input_zod.ts
|
|
56
|
+
const AttachmentBaseSchema = z.object({
|
|
57
|
+
record_id: z.string(),
|
|
58
|
+
record_type: z.string(),
|
|
59
|
+
sanitized_name: z.string(),
|
|
60
|
+
original_name: z.string(),
|
|
61
|
+
content_type: z.string(),
|
|
62
|
+
file_size: z.string(),
|
|
63
|
+
description: z.string().optional().nullable(),
|
|
64
|
+
metadata: z.string().optional().nullable(),
|
|
65
|
+
folder_id: z.string().optional().nullable()
|
|
66
|
+
});
|
|
67
|
+
const AttachmentCreateSchema = AttachmentBaseSchema;
|
|
68
|
+
const AttachmentUpdateSchema = AttachmentBaseSchema.extend({
|
|
69
|
+
id: z.string(),
|
|
70
|
+
record_id: z.string().optional(),
|
|
71
|
+
record_type: z.string().optional(),
|
|
72
|
+
sanitized_name: z.string().optional(),
|
|
73
|
+
original_name: z.string().optional(),
|
|
74
|
+
content_type: z.string().optional(),
|
|
75
|
+
file_size: z.string().optional(),
|
|
76
|
+
folder_id: z.string().optional().nullable()
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/validation/attachment/attachment_read_zod.ts
|
|
81
|
+
const AttachmentReadSchema = AttachmentCreateSchema.extend({
|
|
82
|
+
id: z.string(),
|
|
83
|
+
created_at: z.string(),
|
|
84
|
+
created_by: z.string(),
|
|
85
|
+
updated_at: z.string().optional().nullable(),
|
|
86
|
+
updated_by: z.string().optional().nullable(),
|
|
87
|
+
archived_at: z.string().optional().nullable(),
|
|
88
|
+
archived_by: z.string().optional().nullable(),
|
|
89
|
+
deleted_at: z.string().optional().nullable(),
|
|
90
|
+
deleted_by: z.string().optional().nullable()
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/validation/attachment/attachment_page_zod.ts
|
|
95
|
+
const AttachmentPageSchema = z.object({
|
|
96
|
+
files: z.array(AttachmentReadSchema),
|
|
97
|
+
folders: z.array(AttachmentFolderReadSchema),
|
|
98
|
+
pageInfo: z.object({
|
|
99
|
+
hasNextPage: z.boolean(),
|
|
100
|
+
endCursor: z.string().optional().nullable()
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/validation/login_zod.ts
|
|
106
|
+
const isCommonPassword = (password) => {
|
|
107
|
+
return [
|
|
108
|
+
"password",
|
|
109
|
+
"12345678",
|
|
110
|
+
"qwerty123",
|
|
111
|
+
"admin1234"
|
|
112
|
+
].includes(password.toLowerCase());
|
|
113
|
+
};
|
|
114
|
+
const passwordSchema = z.string().min(8, { message: "Password must be at least 8 characters long" }).max(64, { message: "Password must not exceed 64 characters" }).refine((password) => !/^\s|\s$/.test(password), { message: "Password must not have leading or trailing whitespace" }).refine((password) => !isCommonPassword(password), { message: "Password is too common and easily guessed" });
|
|
115
|
+
const loginSchema = z.object({
|
|
116
|
+
email: z.string().trim().toLowerCase().min(3, "Please enter your email.").email("The email address is badly formatted."),
|
|
117
|
+
password: passwordSchema
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/validation/change_password_zod.ts
|
|
122
|
+
const changePasswordSchema = z.object({ passwords: z.object({
|
|
123
|
+
current_password: z.string().min(8).max(64),
|
|
124
|
+
new_password: passwordSchema,
|
|
125
|
+
new_password_confirm: z.string()
|
|
126
|
+
}).refine((data) => data.new_password === data.new_password_confirm, {
|
|
127
|
+
message: "Passwords do not match",
|
|
128
|
+
path: ["new_password_confirm"]
|
|
129
|
+
}).refine((data) => data.new_password !== data.current_password, {
|
|
130
|
+
message: "New password must be different from current password",
|
|
131
|
+
path: ["new_password"]
|
|
132
|
+
}) });
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/validation/common/filter_operators_zod.ts
|
|
136
|
+
/**
|
|
137
|
+
* Individual operator constants - use these instead of string literals
|
|
138
|
+
* All operators use shorthand names for URL compatibility
|
|
139
|
+
*/
|
|
140
|
+
const OPERATORS = {
|
|
141
|
+
EQUALS: "eq",
|
|
142
|
+
NOT_EQUALS: "ne",
|
|
143
|
+
GREATER_THAN: "gt",
|
|
144
|
+
GREATER_THAN_OR_EQUAL: "gte",
|
|
145
|
+
LESS_THAN: "lt",
|
|
146
|
+
LESS_THAN_OR_EQUAL: "lte",
|
|
147
|
+
BETWEEN: "between",
|
|
148
|
+
CONTAINS: "contains",
|
|
149
|
+
STARTS_WITH: "sw",
|
|
150
|
+
ENDS_WITH: "ew",
|
|
151
|
+
IS_ONE_OF: "in",
|
|
152
|
+
IS_NOT_ONE_OF: "notIn",
|
|
153
|
+
IS_EMPTY: "isEmpty",
|
|
154
|
+
IS_NOT_EMPTY: "isNotEmpty"
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* String operators - shorthand names for URL compatibility
|
|
158
|
+
* Only operators that make sense for string filtering
|
|
159
|
+
*/
|
|
160
|
+
const StringOperatorSchema = z.enum([
|
|
161
|
+
OPERATORS.EQUALS,
|
|
162
|
+
OPERATORS.NOT_EQUALS,
|
|
163
|
+
OPERATORS.CONTAINS,
|
|
164
|
+
OPERATORS.STARTS_WITH,
|
|
165
|
+
OPERATORS.ENDS_WITH,
|
|
166
|
+
OPERATORS.IS_ONE_OF,
|
|
167
|
+
OPERATORS.IS_NOT_ONE_OF
|
|
168
|
+
]);
|
|
169
|
+
/**
|
|
170
|
+
* Date operators - shorthand names for URL compatibility
|
|
171
|
+
* Only operators that make sense for date filtering
|
|
172
|
+
*/
|
|
173
|
+
const DateOperatorSchema = z.enum([
|
|
174
|
+
OPERATORS.EQUALS,
|
|
175
|
+
OPERATORS.NOT_EQUALS,
|
|
176
|
+
OPERATORS.GREATER_THAN,
|
|
177
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
178
|
+
OPERATORS.LESS_THAN,
|
|
179
|
+
OPERATORS.LESS_THAN_OR_EQUAL,
|
|
180
|
+
OPERATORS.BETWEEN,
|
|
181
|
+
OPERATORS.IS_EMPTY,
|
|
182
|
+
OPERATORS.IS_NOT_EMPTY
|
|
183
|
+
]);
|
|
184
|
+
/**
|
|
185
|
+
* Number operators - shorthand names for URL compatibility
|
|
186
|
+
* Only operators that make sense for number filtering
|
|
187
|
+
*/
|
|
188
|
+
const NumberOperatorSchema = z.enum([
|
|
189
|
+
OPERATORS.EQUALS,
|
|
190
|
+
OPERATORS.NOT_EQUALS,
|
|
191
|
+
OPERATORS.GREATER_THAN,
|
|
192
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
193
|
+
OPERATORS.LESS_THAN,
|
|
194
|
+
OPERATORS.LESS_THAN_OR_EQUAL,
|
|
195
|
+
OPERATORS.BETWEEN,
|
|
196
|
+
OPERATORS.IS_ONE_OF,
|
|
197
|
+
OPERATORS.IS_NOT_ONE_OF
|
|
198
|
+
]);
|
|
199
|
+
/**
|
|
200
|
+
* Shared operator enum for equality comparisons (used by boolean and enum filters)
|
|
201
|
+
*/
|
|
202
|
+
const EqualityOperatorSchema = z.enum([OPERATORS.EQUALS, OPERATORS.NOT_EQUALS]);
|
|
203
|
+
/**
|
|
204
|
+
* Shared operator enum for equality and array operations (used by enum filters)
|
|
205
|
+
*/
|
|
206
|
+
const EqualityArrayOperatorSchema = z.enum([
|
|
207
|
+
OPERATORS.EQUALS,
|
|
208
|
+
OPERATORS.NOT_EQUALS,
|
|
209
|
+
OPERATORS.IS_ONE_OF,
|
|
210
|
+
OPERATORS.IS_NOT_ONE_OF
|
|
211
|
+
]);
|
|
212
|
+
/**
|
|
213
|
+
* Data type enum for filters (spec-compliant)
|
|
214
|
+
*/
|
|
215
|
+
const DataTypeSchema = z.enum([
|
|
216
|
+
"string",
|
|
217
|
+
"number",
|
|
218
|
+
"date",
|
|
219
|
+
"boolean",
|
|
220
|
+
"enum",
|
|
221
|
+
"money",
|
|
222
|
+
"percentage"
|
|
223
|
+
]);
|
|
224
|
+
/**
|
|
225
|
+
* Base filter schema - spec-compliant structure
|
|
226
|
+
* Field name is the object key, not part of the filter object
|
|
227
|
+
* All filters must include: operator
|
|
228
|
+
* Optional: value, values, caseSensitive
|
|
229
|
+
* Type is inferred from registry using the field name (object key)
|
|
230
|
+
*/
|
|
231
|
+
const BaseFilterSchema = z.object({
|
|
232
|
+
operator: z.string(),
|
|
233
|
+
value: z.any().optional(),
|
|
234
|
+
values: z.array(z.any()).optional(),
|
|
235
|
+
caseSensitive: z.boolean().optional()
|
|
236
|
+
});
|
|
237
|
+
/**
|
|
238
|
+
* String filter input - Spec-compliant structure
|
|
239
|
+
* Restricted operators: only operators that make sense for string filtering
|
|
240
|
+
* Field name is the object key, type is inferred from registry
|
|
241
|
+
*
|
|
242
|
+
* Examples:
|
|
243
|
+
* - Equality: { operator: "eq", value: "search" }
|
|
244
|
+
* - Contains: { operator: "contains", value: "search", caseSensitive: false }
|
|
245
|
+
* - Starts with: { operator: "sw", value: "prefix" }
|
|
246
|
+
* - In array: { operator: "in", values: ["option1", "option2"] }
|
|
247
|
+
*/
|
|
248
|
+
const StringFilterSchema = BaseFilterSchema.extend({
|
|
249
|
+
operator: StringOperatorSchema,
|
|
250
|
+
value: z.string().optional(),
|
|
251
|
+
values: z.array(z.string()).optional(),
|
|
252
|
+
caseSensitive: z.boolean().optional()
|
|
253
|
+
}).refine((data) => {
|
|
254
|
+
if (data.operator === OPERATORS.IS_ONE_OF || data.operator === OPERATORS.IS_NOT_ONE_OF) return data.values !== void 0 && data.values.length > 0;
|
|
255
|
+
return data.value !== void 0;
|
|
256
|
+
}, { message: "value is required for non-array operators, values array is required for in/notIn" });
|
|
257
|
+
/**
|
|
258
|
+
* Boolean filter input - Spec-compliant structure
|
|
259
|
+
* Field name is the object key, type is inferred from registry
|
|
260
|
+
*
|
|
261
|
+
* Examples:
|
|
262
|
+
* - Equality: { operator: "eq", value: true }
|
|
263
|
+
* - Not equals: { operator: "ne", value: false }
|
|
264
|
+
*/
|
|
265
|
+
const BooleanFilterSchema = BaseFilterSchema.extend({
|
|
266
|
+
operator: EqualityOperatorSchema,
|
|
267
|
+
value: z.boolean()
|
|
268
|
+
});
|
|
269
|
+
/**
|
|
270
|
+
* Number filter input - Spec-compliant structure
|
|
271
|
+
* Restricted operators: only operators that make sense for number filtering
|
|
272
|
+
* Useful for ranges (credits > 100, priority <= 3, etc.)
|
|
273
|
+
*
|
|
274
|
+
* Examples:
|
|
275
|
+
* - Equality: { field: "age", operator: "eq", value: 100, type: "number" }
|
|
276
|
+
* - Greater than: { field: "price", operator: "gt", value: 100, type: "number" }
|
|
277
|
+
* - Range: { field: "score", operator: "gte", value: 50, type: "number" }
|
|
278
|
+
* - In array: { field: "priority", operator: "in", values: [1, 2, 3], type: "number" }
|
|
279
|
+
*/
|
|
280
|
+
const NumberFilterSchema = BaseFilterSchema.extend({
|
|
281
|
+
operator: NumberOperatorSchema,
|
|
282
|
+
value: z.number().optional(),
|
|
283
|
+
values: z.array(z.number()).optional()
|
|
284
|
+
}).refine((data) => {
|
|
285
|
+
if (data.operator === OPERATORS.BETWEEN) return data.values !== void 0 && data.values.length === 2;
|
|
286
|
+
if (data.operator === OPERATORS.IS_ONE_OF || data.operator === OPERATORS.IS_NOT_ONE_OF) return data.values !== void 0 && data.values.length > 0;
|
|
287
|
+
return data.value !== void 0;
|
|
288
|
+
}, { message: "value is required for non-array operators, values array is required for in/notIn, exactly 2 values required for between" }).refine((data) => {
|
|
289
|
+
if (data.operator === OPERATORS.BETWEEN && data.values && data.values.length === 2) {
|
|
290
|
+
const [min, max] = data.values;
|
|
291
|
+
return min !== void 0 && max !== void 0 && min <= max;
|
|
292
|
+
}
|
|
293
|
+
return true;
|
|
294
|
+
}, {
|
|
295
|
+
message: "For between operator, values[0] must be less than or equal to values[1]",
|
|
296
|
+
path: ["values"]
|
|
297
|
+
});
|
|
298
|
+
/**
|
|
299
|
+
* Date/DateTime string filter - Spec-compliant structure
|
|
300
|
+
* Restricted operators: only operators that make sense for date filtering
|
|
301
|
+
* Dates should be in ISO 8601 format: YYYY-MM-DD or full datetime
|
|
302
|
+
*
|
|
303
|
+
* Examples:
|
|
304
|
+
* - Exact match: { field: "createdAt", operator: "eq", value: "2024-01-01", type: "date" }
|
|
305
|
+
* - After date: { field: "expiresAt", operator: "gte", value: "2024-01-01", type: "date" }
|
|
306
|
+
* - Before date: { field: "deadline", operator: "lt", value: "2024-12-31", type: "date" }
|
|
307
|
+
* - Is empty: { field: "deletedAt", operator: "isEmpty", type: "date" } // No value property
|
|
308
|
+
* - Is not empty: { field: "publishedAt", operator: "isNotEmpty", type: "date" } // No value property
|
|
309
|
+
*/
|
|
310
|
+
const DateFilterSchema = z.union([
|
|
311
|
+
BaseFilterSchema.extend({ operator: z.enum([OPERATORS.IS_EMPTY, OPERATORS.IS_NOT_EMPTY]) }).refine((data) => data.value === void 0 && data.values === void 0, { message: "isEmpty and isNotEmpty operators must not have value or values properties" }),
|
|
312
|
+
BaseFilterSchema.extend({
|
|
313
|
+
operator: z.literal(OPERATORS.BETWEEN),
|
|
314
|
+
values: z.array(z.string()).length(2)
|
|
315
|
+
}).refine((data) => data.values !== void 0 && data.values.length === 2, { message: "between operator requires exactly 2 values" }).refine((data) => {
|
|
316
|
+
if (data.values && data.values.length === 2) {
|
|
317
|
+
const [startDate, endDate] = data.values;
|
|
318
|
+
if (startDate !== void 0 && endDate !== void 0) return new Date(startDate) <= new Date(endDate);
|
|
319
|
+
}
|
|
320
|
+
return true;
|
|
321
|
+
}, {
|
|
322
|
+
message: "For between operator, values[0] must represent an earlier or equal date/time compared to values[1]",
|
|
323
|
+
path: ["values"]
|
|
324
|
+
}),
|
|
325
|
+
BaseFilterSchema.extend({
|
|
326
|
+
operator: z.enum([
|
|
327
|
+
OPERATORS.EQUALS,
|
|
328
|
+
OPERATORS.NOT_EQUALS,
|
|
329
|
+
OPERATORS.GREATER_THAN,
|
|
330
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
331
|
+
OPERATORS.LESS_THAN,
|
|
332
|
+
OPERATORS.LESS_THAN_OR_EQUAL
|
|
333
|
+
]),
|
|
334
|
+
value: z.string()
|
|
335
|
+
}).refine((data) => data.value !== void 0, { message: "value is required for non-null operators" })
|
|
336
|
+
]);
|
|
337
|
+
/**
|
|
338
|
+
* Enum filter input - Spec-compliant structure
|
|
339
|
+
* Supports both single value (eq/ne) and multiple values (in/notIn)
|
|
340
|
+
* Field name is the object key, type is inferred from registry
|
|
341
|
+
*
|
|
342
|
+
* Usage: createEnumFilter(MyEnumSchema)
|
|
343
|
+
*
|
|
344
|
+
* Examples:
|
|
345
|
+
* - Equality: { operator: "eq", value: "OPTION_A" }
|
|
346
|
+
* - Not equals: { operator: "ne", value: "OPTION_B" }
|
|
347
|
+
* - In array: { operator: "in", values: ["OPTION_A", "OPTION_B"] }
|
|
348
|
+
* - Not in array: { operator: "notIn", values: ["OPTION_C", "OPTION_D"] }
|
|
349
|
+
*/
|
|
350
|
+
function createEnumFilter(enumSchema) {
|
|
351
|
+
return BaseFilterSchema.extend({
|
|
352
|
+
operator: EqualityArrayOperatorSchema,
|
|
353
|
+
value: enumSchema.optional(),
|
|
354
|
+
values: z.array(enumSchema).optional()
|
|
355
|
+
}).refine((data) => {
|
|
356
|
+
if (data.operator === OPERATORS.IS_ONE_OF || data.operator === OPERATORS.IS_NOT_ONE_OF) return data.values !== void 0 && data.values.length > 0;
|
|
357
|
+
return data.value !== void 0;
|
|
358
|
+
}, { message: "value is required for non-array operators, values array is required for in/notIn" });
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Filter configuration - Spec-compliant structure
|
|
362
|
+
* Uses a flat object mapping field names to filter value objects
|
|
363
|
+
* Field name is the object key, type is inferred from registry
|
|
364
|
+
* All filters are combined with implicit AND logic
|
|
365
|
+
* OR logic within a single field is supported through isOneOf/isNotOneOf operators
|
|
366
|
+
*
|
|
367
|
+
* Examples:
|
|
368
|
+
* {
|
|
369
|
+
* "status": { operator: "eq", value: "active" },
|
|
370
|
+
* "age": { operator: "gt", value: 18 }
|
|
371
|
+
* }
|
|
372
|
+
*/
|
|
373
|
+
const FilterConfigSchema = z.record(z.string(), BaseFilterSchema);
|
|
374
|
+
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/validation/common/pagination_zod.ts
|
|
377
|
+
/**
|
|
378
|
+
* Sort direction enum
|
|
379
|
+
*/
|
|
380
|
+
const SortDirectionSchema = z.enum(["asc", "desc"]);
|
|
381
|
+
const PaginationFiltersSchema = z.object({
|
|
382
|
+
first: z.number().min(1).max(100).optional(),
|
|
383
|
+
after: z.string().optional(),
|
|
384
|
+
sortBy: z.string().optional(),
|
|
385
|
+
sortDirection: SortDirectionSchema.optional(),
|
|
386
|
+
paginationToken: z.string().optional()
|
|
387
|
+
});
|
|
388
|
+
const PageInfoSchema = z.object({
|
|
389
|
+
hasNextPage: z.boolean(),
|
|
390
|
+
hasPreviousPage: z.boolean(),
|
|
391
|
+
prevPageCursor: z.string().nullish(),
|
|
392
|
+
nextPageCursor: z.string().optional(),
|
|
393
|
+
currentPageIndex: z.number().optional(),
|
|
394
|
+
paginationToken: z.string().optional()
|
|
395
|
+
});
|
|
396
|
+
function createPaginatedSchema(itemSchema) {
|
|
397
|
+
return z.object({
|
|
398
|
+
items: z.array(itemSchema),
|
|
399
|
+
pageInfo: PageInfoSchema
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/validation/common/regex_patterns.ts
|
|
405
|
+
/**
|
|
406
|
+
* Common regex patterns for validation
|
|
407
|
+
*/
|
|
408
|
+
/**
|
|
409
|
+
* Decimal amount regex - allows up to 2 decimal places
|
|
410
|
+
* Matches: "100", "100.00", "100.5", "0.99", etc.
|
|
411
|
+
* Also allows empty string for optional fields
|
|
412
|
+
*/
|
|
413
|
+
const DECIMAL_AMOUNT_REGEX = /^((\d*\.?\d{1,2}|\d+)|)$/;
|
|
414
|
+
/**
|
|
415
|
+
* Decimal amount regex error message
|
|
416
|
+
*/
|
|
417
|
+
const DECIMAL_AMOUNT_ERROR_MESSAGE = "Must be a valid dollar amount with up to 2 decimal places or empty";
|
|
418
|
+
/**
|
|
419
|
+
* Decimal amount regex for 2 decimals or empty
|
|
420
|
+
* Matches: "100.00", "0.99", "1234.56", etc. or empty string
|
|
421
|
+
* Requires exactly 2 decimal places when not empty
|
|
422
|
+
*/
|
|
423
|
+
const DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_REGEX = /^((\d*\.?\d{1,2}|\d+)|)$/;
|
|
424
|
+
/**
|
|
425
|
+
* Decimal amount regex for 2 decimals or empty error message
|
|
426
|
+
*/
|
|
427
|
+
const DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_ERROR_MESSAGE = "Must be a valid dollar amount with up to 2 decimal places or empty";
|
|
428
|
+
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/validation/user/user_enums_zod.ts
|
|
431
|
+
const USER_TYPES = [
|
|
432
|
+
"consumer",
|
|
433
|
+
"lead",
|
|
434
|
+
"staff",
|
|
435
|
+
"super_admin"
|
|
436
|
+
];
|
|
437
|
+
const DEFAULT_USER_TYPE = USER_TYPES[0];
|
|
438
|
+
const UserTypeEnum = z.enum([...USER_TYPES]);
|
|
439
|
+
|
|
440
|
+
//#endregion
|
|
441
|
+
//#region src/validation/user/user_create_zod.ts
|
|
442
|
+
const createUserSchema = z.object({
|
|
443
|
+
email: z.string().trim().toLowerCase().min(3, "Please enter your email.").email("The email address is badly formatted."),
|
|
444
|
+
user_type: UserTypeEnum,
|
|
445
|
+
password: z.string().optional().nullable()
|
|
446
|
+
});
|
|
447
|
+
const createUserSchemaOutput = z.object({
|
|
448
|
+
id: z.string(),
|
|
449
|
+
email: z.string()
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
//#endregion
|
|
453
|
+
//#region src/validation/credit_transaction/credit_balance_schema.ts
|
|
454
|
+
const CreditBalanceSchema = z.object({
|
|
455
|
+
monthly: z.string(),
|
|
456
|
+
rollover: z.string()
|
|
457
|
+
});
|
|
458
|
+
const AddCreditsSchema = z.object({
|
|
459
|
+
amount: z.string().regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE),
|
|
460
|
+
reason: z.string().optional()
|
|
461
|
+
});
|
|
462
|
+
const SetMonthlyAllocationSchema = z.object({ amount: z.string().regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE) });
|
|
463
|
+
const ResetMonthlyBalanceSchema = z.object({}).optional();
|
|
464
|
+
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/validation/credit_transaction/credit_transaction_read_zod.ts
|
|
467
|
+
/**
|
|
468
|
+
* Transaction types for credit operations
|
|
469
|
+
*/
|
|
470
|
+
const CreditTransactionTypeEnum = z.enum([
|
|
471
|
+
"DEDUCTION",
|
|
472
|
+
"REFUND",
|
|
473
|
+
"PURCHASE_ONETIME",
|
|
474
|
+
"PURCHASE_RECURRING",
|
|
475
|
+
"ADJUSTMENT"
|
|
476
|
+
]);
|
|
477
|
+
/**
|
|
478
|
+
* Schema for a credit transaction record
|
|
479
|
+
*/
|
|
480
|
+
const CreditTransactionReadSchema = z.object({
|
|
481
|
+
id: z.string(),
|
|
482
|
+
support_ticket_id: z.string().nullable().optional(),
|
|
483
|
+
amount: z.string(),
|
|
484
|
+
type: CreditTransactionTypeEnum,
|
|
485
|
+
description: z.string().nullable().optional(),
|
|
486
|
+
balance_after: z.string(),
|
|
487
|
+
created_at: z.string(),
|
|
488
|
+
created_by: z.string()
|
|
489
|
+
});
|
|
490
|
+
/**
|
|
491
|
+
* Paginated schema for credit transactions
|
|
492
|
+
*/
|
|
493
|
+
const CreditTransactionPageSchema = createPaginatedSchema(CreditTransactionReadSchema);
|
|
494
|
+
|
|
495
|
+
//#endregion
|
|
496
|
+
//#region src/validation/credit_transaction/credit_transaction_filters_zod.ts
|
|
497
|
+
/**
|
|
498
|
+
* Filters for querying credit transactions
|
|
499
|
+
* Supports operator-based filtering for advanced queries
|
|
500
|
+
*
|
|
501
|
+
* All filters support operators for advanced filtering:
|
|
502
|
+
* - Enums: eq, ne, in, notIn
|
|
503
|
+
* - Strings: eq, ne, contains, startsWith, endsWith
|
|
504
|
+
* - Numbers: eq, ne, gt, gte, lt, lte, between
|
|
505
|
+
* - Dates: eq, ne, gt, gte, lt, lte, between
|
|
506
|
+
*
|
|
507
|
+
* Examples:
|
|
508
|
+
* - { type: { operator: "eq", value: "DEDUCTION" } }
|
|
509
|
+
* - { amount: { operator: "gte", value: 100 } }
|
|
510
|
+
* - { created_at: { operator: "gte", value: "2024-01-01" } }
|
|
511
|
+
*/
|
|
512
|
+
const CreditTransactionFiltersSchema = PaginationFiltersSchema.extend({
|
|
513
|
+
type: createEnumFilter(CreditTransactionTypeEnum).optional(),
|
|
514
|
+
support_ticket_id: StringFilterSchema.optional(),
|
|
515
|
+
amount: NumberFilterSchema.optional(),
|
|
516
|
+
balance_after: NumberFilterSchema.optional(),
|
|
517
|
+
created_at: DateFilterSchema.optional(),
|
|
518
|
+
created_by: StringFilterSchema.optional(),
|
|
519
|
+
search: z.object({
|
|
520
|
+
query: z.string(),
|
|
521
|
+
searchableFields: z.array(z.string())
|
|
522
|
+
}).optional()
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
//#endregion
|
|
526
|
+
//#region src/validation/forgot_password_zod.ts
|
|
527
|
+
const forgot_password_zod = z.object({ email: z.string().email().trim().toLowerCase() });
|
|
528
|
+
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/types/record_types.ts
|
|
531
|
+
/**
|
|
532
|
+
* Record type constants and types for the application
|
|
533
|
+
* These define all the different types of records that can exist in the system
|
|
534
|
+
*/
|
|
535
|
+
const RecordTypeValues = [
|
|
536
|
+
"attachment_folder",
|
|
537
|
+
"attachment",
|
|
538
|
+
"user",
|
|
539
|
+
"user_session",
|
|
540
|
+
"user_profile",
|
|
541
|
+
"refresh_token",
|
|
542
|
+
"refresh_token_family",
|
|
543
|
+
"user_subscription",
|
|
544
|
+
"password_reset",
|
|
545
|
+
"record_version",
|
|
546
|
+
"support_ticket",
|
|
547
|
+
"support_ticket_activity",
|
|
548
|
+
"credit_transaction",
|
|
549
|
+
"team_member",
|
|
550
|
+
"client_contact",
|
|
551
|
+
"client_location",
|
|
552
|
+
"client_profile",
|
|
553
|
+
"business_profile",
|
|
554
|
+
"tracker",
|
|
555
|
+
"tracker_activity",
|
|
556
|
+
"team",
|
|
557
|
+
"quote",
|
|
558
|
+
"note",
|
|
559
|
+
"followup",
|
|
560
|
+
"saved_filter",
|
|
561
|
+
"user_pinned_preset",
|
|
562
|
+
"record_subscriber",
|
|
563
|
+
"support_staff"
|
|
564
|
+
];
|
|
565
|
+
const RecordConst = {
|
|
566
|
+
ATTACHMENT: "attachment",
|
|
567
|
+
ATTACHMENT_FOLDER: "attachment_folder",
|
|
568
|
+
USER: "user",
|
|
569
|
+
USER_SESSION: "user_session",
|
|
570
|
+
USER_PROFILE: "user_profile",
|
|
571
|
+
REFRESH_TOKEN: "refresh_token",
|
|
572
|
+
REFRESH_TOKEN_FAMILY: "refresh_token_family",
|
|
573
|
+
USER_SUBSCRIPTION: "user_subscription",
|
|
574
|
+
PASSWORD_RESET: "password_reset",
|
|
575
|
+
RECORD_VERSION: "record_version",
|
|
576
|
+
SUPPORT_TICKET: "support_ticket",
|
|
577
|
+
SUPPORT_TICKET_ACTIVITY: "support_ticket_activity",
|
|
578
|
+
CREDIT_TRANSACTION: "credit_transaction",
|
|
579
|
+
TEAM_MEMBER: "team_member",
|
|
580
|
+
CLIENT_CONTACT: "client_contact",
|
|
581
|
+
CLIENT_LOCATION: "client_location",
|
|
582
|
+
CLIENT_PROFILE: "client_profile",
|
|
583
|
+
BUSINESS_PROFILE: "business_profile",
|
|
584
|
+
TRACKER: "tracker",
|
|
585
|
+
TRACKER_ACTIVITY: "tracker_activity",
|
|
586
|
+
TEAM: "team",
|
|
587
|
+
NOTE: "note",
|
|
588
|
+
FOLLOWUP: "followup",
|
|
589
|
+
SAVED_FILTER: "saved_filter",
|
|
590
|
+
USER_PINNED_PRESET: "user_pinned_preset",
|
|
591
|
+
RECORD_SUBSCRIBER: "record_subscriber",
|
|
592
|
+
SUPPORT_STAFF: "support_staff"
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/validation/note/note_read_zod.ts
|
|
597
|
+
const RecordTypeEnum$5 = z.enum(RecordTypeValues);
|
|
598
|
+
const NoteReadSchema = z.object({
|
|
599
|
+
id: z.string(),
|
|
600
|
+
record_id: z.string(),
|
|
601
|
+
record_type: RecordTypeEnum$5,
|
|
602
|
+
tag: z.string().optional().nullable(),
|
|
603
|
+
title: z.string().optional().nullable(),
|
|
604
|
+
body: z.string().optional().nullable(),
|
|
605
|
+
original_id: z.number().optional().nullable(),
|
|
606
|
+
is_internal: z.boolean(),
|
|
607
|
+
created_by: z.string(),
|
|
608
|
+
created_by_display_name: z.string().optional().nullable(),
|
|
609
|
+
created_at: z.string(),
|
|
610
|
+
updated_by: z.string().optional().nullable(),
|
|
611
|
+
updated_by_display_name: z.string().optional().nullable(),
|
|
612
|
+
updated_at: z.string().optional().nullable(),
|
|
613
|
+
archived_by: z.string().optional().nullable(),
|
|
614
|
+
archived_at: z.string().optional().nullable(),
|
|
615
|
+
deleted_by: z.string().optional().nullable(),
|
|
616
|
+
deleted_at: z.string().optional().nullable()
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/validation/note/note_create_zod.ts
|
|
621
|
+
const RecordTypeEnum$4 = z.enum(RecordTypeValues);
|
|
622
|
+
const NoteCreateSchema = z.object({
|
|
623
|
+
record_id: z.string(),
|
|
624
|
+
record_type: RecordTypeEnum$4,
|
|
625
|
+
tag: z.string().optional().nullable(),
|
|
626
|
+
title: z.string().optional().nullable(),
|
|
627
|
+
body: z.string().optional().nullable(),
|
|
628
|
+
original_id: z.number().optional().nullable(),
|
|
629
|
+
is_internal: z.boolean().optional().default(false)
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
//#endregion
|
|
633
|
+
//#region src/validation/note/note_update_zod.ts
|
|
634
|
+
const RecordTypeEnum$3 = z.enum(RecordTypeValues);
|
|
635
|
+
const NoteUpdateSchema = z.object({
|
|
636
|
+
id: z.string(),
|
|
637
|
+
record_id: z.string().optional(),
|
|
638
|
+
record_type: RecordTypeEnum$3.optional(),
|
|
639
|
+
tag: z.string().optional().nullable(),
|
|
640
|
+
title: z.string().optional().nullable(),
|
|
641
|
+
body: z.string().optional().nullable(),
|
|
642
|
+
original_id: z.number().optional().nullable(),
|
|
643
|
+
is_internal: z.boolean().optional()
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region src/validation/note/note_filters_zod.ts
|
|
648
|
+
const RecordTypeFilterEnum = z.enum(RecordTypeValues);
|
|
649
|
+
/**
|
|
650
|
+
* Filters for note
|
|
651
|
+
* Supports operator-based filtering for advanced queries
|
|
652
|
+
*
|
|
653
|
+
* All filters support operators for advanced filtering:
|
|
654
|
+
* - Booleans: eq, ne
|
|
655
|
+
* - Strings: eq, ne, contains, startsWith, endsWith
|
|
656
|
+
* - Dates: eq, ne, gt, gte, lt, lte
|
|
657
|
+
*
|
|
658
|
+
* Examples:
|
|
659
|
+
* - { record_type: { operator: "eq", value: "SUPPORT_TICKET" } }
|
|
660
|
+
* - { title: { operator: "contains", value: "search" } }
|
|
661
|
+
*/
|
|
662
|
+
const NoteFiltersSchema = PaginationFiltersSchema.extend({
|
|
663
|
+
record_id: StringFilterSchema.optional(),
|
|
664
|
+
record_type: createEnumFilter(RecordTypeFilterEnum).optional(),
|
|
665
|
+
title: StringFilterSchema.optional(),
|
|
666
|
+
body: StringFilterSchema.optional(),
|
|
667
|
+
tag: StringFilterSchema.optional(),
|
|
668
|
+
created_at: DateFilterSchema.optional(),
|
|
669
|
+
updated_at: DateFilterSchema.optional(),
|
|
670
|
+
is_internal: BooleanFilterSchema.optional(),
|
|
671
|
+
search: z.object({
|
|
672
|
+
query: z.string(),
|
|
673
|
+
searchableFields: z.array(z.string())
|
|
674
|
+
}).optional()
|
|
675
|
+
});
|
|
676
|
+
const NoteFiltersSortDirectionEnum = z.enum(["asc", "desc"]);
|
|
677
|
+
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/validation/record_version_zod.ts
|
|
680
|
+
const RecordTypeEnum$2 = z.enum(RecordTypeValues);
|
|
681
|
+
const recordVersionSchema = z.object({
|
|
682
|
+
id: z.string(),
|
|
683
|
+
record_id: z.string(),
|
|
684
|
+
operation: z.string(),
|
|
685
|
+
recorded_at: z.string(),
|
|
686
|
+
record_type: z.string(),
|
|
687
|
+
record: z.string().optional().nullable(),
|
|
688
|
+
old_record: z.string().optional().nullable(),
|
|
689
|
+
auth_uid: z.string().optional().nullable(),
|
|
690
|
+
auth_role: z.string().optional().nullable(),
|
|
691
|
+
auth_username: z.string().optional().nullable()
|
|
692
|
+
});
|
|
693
|
+
const recordVersionPageSchema = z.object({
|
|
694
|
+
items: z.array(recordVersionSchema),
|
|
695
|
+
pageInfo: z.object({
|
|
696
|
+
hasNextPage: z.boolean(),
|
|
697
|
+
endCursor: z.string().optional().nullable()
|
|
698
|
+
})
|
|
699
|
+
});
|
|
700
|
+
const pageInfoSchema = z.object({
|
|
701
|
+
hasNextPage: z.boolean(),
|
|
702
|
+
endCursor: z.string().optional().nullable()
|
|
703
|
+
});
|
|
704
|
+
const recordVersionFiltersSchema = z.object({
|
|
705
|
+
start_date: z.string().optional().nullable(),
|
|
706
|
+
end_date: z.string().optional().nullable(),
|
|
707
|
+
limit: z.number().optional().nullable(),
|
|
708
|
+
cursor: z.string().optional().nullable()
|
|
709
|
+
});
|
|
710
|
+
const recordVersionFiltersInputSchema = z.object({
|
|
711
|
+
record_id: z.string(),
|
|
712
|
+
record_type: RecordTypeEnum$2,
|
|
713
|
+
filters: recordVersionFiltersSchema.optional().nullable()
|
|
714
|
+
});
|
|
715
|
+
/**
|
|
716
|
+
* NEW: Breadcrumb-paginated record version response
|
|
717
|
+
* Use this for new implementations
|
|
718
|
+
* user_display_map: optional map of user_id -> display_name for timeline display (assigned_to, created_by, etc.)
|
|
719
|
+
*/
|
|
720
|
+
const recordVersionPageBreadcrumbSchema = createPaginatedSchema(recordVersionSchema).extend({ user_display_map: z.record(z.string(), z.string()).optional() });
|
|
721
|
+
/**
|
|
722
|
+
* NEW: Filters with breadcrumb pagination support
|
|
723
|
+
* Extends PaginationFiltersSchema for consistent pagination
|
|
724
|
+
*/
|
|
725
|
+
const recordVersionFiltersBreadcrumbSchema = z.object({
|
|
726
|
+
first: z.number().min(1).max(100).optional(),
|
|
727
|
+
after: z.string().optional(),
|
|
728
|
+
last: z.number().min(1).max(100).optional(),
|
|
729
|
+
before: z.string().optional(),
|
|
730
|
+
sortBy: z.string().optional(),
|
|
731
|
+
sortDirection: SortDirectionSchema.optional(),
|
|
732
|
+
paginationToken: z.string().optional(),
|
|
733
|
+
start_date: z.string().optional().nullable(),
|
|
734
|
+
end_date: z.string().optional().nullable(),
|
|
735
|
+
record_types: z.array(RecordTypeEnum$2).min(1).optional(),
|
|
736
|
+
record_ids: z.array(z.string()).min(1).optional()
|
|
737
|
+
});
|
|
738
|
+
const recordVersionFiltersInputBreadcrumbSchema = z.object({
|
|
739
|
+
record_id: z.string(),
|
|
740
|
+
record_type: RecordTypeEnum$2,
|
|
741
|
+
filters: recordVersionFiltersBreadcrumbSchema.optional().nullable()
|
|
742
|
+
});
|
|
743
|
+
/** Input for listTrackerActivityVersions - tracker ID + optional filters */
|
|
744
|
+
const recordVersionTrackerActivityInputSchema = z.object({
|
|
745
|
+
tracker_id: z.string(),
|
|
746
|
+
filters: recordVersionFiltersBreadcrumbSchema.optional().nullable()
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
//#endregion
|
|
750
|
+
//#region src/validation/saved_filter/saved_filter_create_zod.ts
|
|
751
|
+
/**
|
|
752
|
+
* Schema for creating a saved filter preset
|
|
753
|
+
*/
|
|
754
|
+
const SavedFilterCreateSchema = z.object({
|
|
755
|
+
name: z.string().min(1, "Name is required").max(100),
|
|
756
|
+
context: z.string().min(1, "Context is required"),
|
|
757
|
+
route_path: z.string().min(1, "Route path is required"),
|
|
758
|
+
filters: z.record(z.union([z.string(), z.array(z.string())])),
|
|
759
|
+
sort_by: z.string().optional(),
|
|
760
|
+
sort_direction: z.enum(["asc", "desc"]).optional()
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/validation/saved_filter/saved_filter_read_zod.ts
|
|
765
|
+
const SavedFilterReadSchema = SavedFilterCreateSchema.extend({
|
|
766
|
+
id: z.string(),
|
|
767
|
+
user_id: z.string(),
|
|
768
|
+
created_at: z.string(),
|
|
769
|
+
updated_at: z.string().optional()
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
//#endregion
|
|
773
|
+
//#region src/validation/saved_filter/saved_filter_update_zod.ts
|
|
774
|
+
const SavedFilterUpdateSchema = SavedFilterCreateSchema.partial().extend({ id: z.string() });
|
|
775
|
+
|
|
776
|
+
//#endregion
|
|
777
|
+
//#region src/validation/reset_password_zod.ts
|
|
778
|
+
const resetPasswordInputSchema = z.object({ passwords: z.object({
|
|
779
|
+
password: z.string().min(8, { message: "Password must be at least 8 characters long" }).max(64, { message: "Password must not exceed 64 characters" }).refine((password) => !/^\s|\s$/.test(password), { message: "Password must not have leading or trailing whitespace" }).refine((password) => !isCommonPassword(password), { message: "Password is too common and easily guessed" }),
|
|
780
|
+
password_confirm: z.string().min(8)
|
|
781
|
+
}).refine((data) => data.password === data.password_confirm, {
|
|
782
|
+
message: "Passwords do not match",
|
|
783
|
+
path: ["password_confirm"]
|
|
784
|
+
}) });
|
|
785
|
+
const resetPasswordSchema = resetPasswordInputSchema.extend({ token: z.string().min(20) });
|
|
786
|
+
|
|
787
|
+
//#endregion
|
|
788
|
+
//#region src/validation/signup_zod.ts
|
|
789
|
+
const signupSchema = z.object({
|
|
790
|
+
email: z.string().trim().toLowerCase().min(3, "Please enter your email.").email("The email address is badly formatted."),
|
|
791
|
+
passwords: z.object({
|
|
792
|
+
password: passwordSchema,
|
|
793
|
+
password_confirm: z.string().min(1)
|
|
794
|
+
}).refine((data) => data.password === data.password_confirm, {
|
|
795
|
+
message: "Passwords do not match",
|
|
796
|
+
path: ["password_confirm"]
|
|
797
|
+
})
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
//#endregion
|
|
801
|
+
//#region src/validation/record_subscriber/record_subscriber_create_zod.ts
|
|
802
|
+
const RecordTypeEnum$1 = z.enum(RecordTypeValues);
|
|
803
|
+
const RecordSubscriberCreateSchema = z.object({
|
|
804
|
+
record_type: RecordTypeEnum$1,
|
|
805
|
+
record_id: z.string().min(1, "Record ID is required"),
|
|
806
|
+
user_id: z.string().min(1, "User ID is required"),
|
|
807
|
+
subscribed_events: z.array(z.string()).optional().nullable()
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
//#endregion
|
|
811
|
+
//#region src/validation/record_subscriber/record_subscriber_read_zod.ts
|
|
812
|
+
const RecordTypeEnum = z.enum(RecordTypeValues);
|
|
813
|
+
const RecordSubscriberReadSchema = z.object({
|
|
814
|
+
id: z.string().min(1, "ID is required"),
|
|
815
|
+
record_type: RecordTypeEnum,
|
|
816
|
+
record_id: z.string().min(1, "Record ID is required"),
|
|
817
|
+
user_id: z.string().min(1, "User ID is required"),
|
|
818
|
+
user_id_display_name: z.string().optional().nullable(),
|
|
819
|
+
subscribed_events: z.array(z.string()).optional().nullable(),
|
|
820
|
+
created_at: z.string(),
|
|
821
|
+
created_by: z.string(),
|
|
822
|
+
created_by_display_name: z.string().optional().nullable()
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
//#endregion
|
|
826
|
+
//#region src/validation/record_subscriber/record_subscriber_update_zod.ts
|
|
827
|
+
const RecordSubscriberUpdateSchema = z.object({
|
|
828
|
+
id: z.string().min(1, "ID is required"),
|
|
829
|
+
subscribed_events: z.array(z.string()).optional().nullable()
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
//#endregion
|
|
833
|
+
//#region src/validation/support_ticket/support_ticket_shared/support_ticket_enums_zod.ts
|
|
834
|
+
const SupportTicketTypeEnum = [
|
|
835
|
+
"IMPROVEMENT",
|
|
836
|
+
"BUG",
|
|
837
|
+
"FEATURE_REQUEST",
|
|
838
|
+
"OPERATIONAL"
|
|
839
|
+
];
|
|
840
|
+
const SupportTicketTypeSchema = z.enum(SupportTicketTypeEnum);
|
|
841
|
+
const SupportTicketPriorityEnum = [
|
|
842
|
+
"LOW",
|
|
843
|
+
"MEDIUM",
|
|
844
|
+
"HIGH",
|
|
845
|
+
"CRITICAL"
|
|
846
|
+
];
|
|
847
|
+
const SupportTicketPriorityNumberEnum = [
|
|
848
|
+
1,
|
|
849
|
+
2,
|
|
850
|
+
3,
|
|
851
|
+
4
|
|
852
|
+
];
|
|
853
|
+
/**
|
|
854
|
+
* Enum for feature request priority levels
|
|
855
|
+
*/
|
|
856
|
+
const SupportTicketPrioritySchema = z.enum(SupportTicketPriorityEnum);
|
|
857
|
+
/**
|
|
858
|
+
* Numeric priority for create/update DTOs (1–4).
|
|
859
|
+
* Used in forms and API input; read DTOs still return string for display.
|
|
860
|
+
*/
|
|
861
|
+
const SupportTicketPriorityNumberSchema = z.number().int().min(1, "Priority must be between 1 and 4").max(4, "Priority must be between 1 and 4");
|
|
862
|
+
/**
|
|
863
|
+
* Numeric mapping for DB storage and proper sorting.
|
|
864
|
+
* Higher number = higher priority.
|
|
865
|
+
*/
|
|
866
|
+
const SUPPORT_TICKET_PRIORITY_TO_NUMBER = {
|
|
867
|
+
LOW: 1,
|
|
868
|
+
MEDIUM: 2,
|
|
869
|
+
HIGH: 3,
|
|
870
|
+
CRITICAL: 4
|
|
871
|
+
};
|
|
872
|
+
const SUPPORT_TICKET_NUMBER_TO_PRIORITY = {
|
|
873
|
+
1: "LOW",
|
|
874
|
+
2: "MEDIUM",
|
|
875
|
+
3: "HIGH",
|
|
876
|
+
4: "CRITICAL"
|
|
877
|
+
};
|
|
878
|
+
function supportTicketPriorityToNumber(priority) {
|
|
879
|
+
return SUPPORT_TICKET_PRIORITY_TO_NUMBER[priority];
|
|
880
|
+
}
|
|
881
|
+
function supportTicketNumberToPriority(num) {
|
|
882
|
+
const p = SUPPORT_TICKET_NUMBER_TO_PRIORITY[num];
|
|
883
|
+
if (!p) return "MEDIUM";
|
|
884
|
+
return p;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Label/value options for priority filter dropdowns (Zinia columns)
|
|
888
|
+
*/
|
|
889
|
+
const SUPPORT_TICKET_PRIORITY_FILTER_OPTIONS = [
|
|
890
|
+
{
|
|
891
|
+
label: "Low",
|
|
892
|
+
value: 1
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
label: "Medium",
|
|
896
|
+
value: 2
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
label: "High",
|
|
900
|
+
value: 3
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
label: "Critical",
|
|
904
|
+
value: 4
|
|
905
|
+
}
|
|
906
|
+
];
|
|
907
|
+
/**
|
|
908
|
+
* Maps numeric priority (1–4) to display label for Zinia SelectField.
|
|
909
|
+
* Use with valueToLabel + valueType: 'number' for number-backed select fields.
|
|
910
|
+
*/
|
|
911
|
+
const SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL = {
|
|
912
|
+
1: "Low",
|
|
913
|
+
2: "Medium",
|
|
914
|
+
3: "High",
|
|
915
|
+
4: "Critical"
|
|
916
|
+
};
|
|
917
|
+
/**
|
|
918
|
+
* Enum for customer-facing support_ticket status (computed from approval_status + dev_lifecycle)
|
|
919
|
+
* - PENDING: Awaiting admin review
|
|
920
|
+
* - FOLLOWUP: Approved but not started
|
|
921
|
+
* - IN_PROGRESS: Actively being worked on
|
|
922
|
+
* - VERIFICATION: Work deployed; consumer should verify and leave a comment
|
|
923
|
+
* - COMPLETED: Deployed to production and verified
|
|
924
|
+
* - CANCELLED: Rejected by admin
|
|
925
|
+
*/
|
|
926
|
+
const SupportTicketStatusEnum = [
|
|
927
|
+
"PENDING",
|
|
928
|
+
"FOLLOWUP",
|
|
929
|
+
"IN_PROGRESS",
|
|
930
|
+
"VERIFICATION",
|
|
931
|
+
"COMPLETED",
|
|
932
|
+
"CANCELLED"
|
|
933
|
+
];
|
|
934
|
+
const SupportTicketStatusSchema = z.enum(SupportTicketStatusEnum);
|
|
935
|
+
/**
|
|
936
|
+
* Enum for feature request approval status
|
|
937
|
+
* - PENDING: Awaiting staff decision (customer-submitted)
|
|
938
|
+
* - APPROVED: Approved by staff (with customer credits)
|
|
939
|
+
* - REJECTED: Rejected by staff
|
|
940
|
+
* - INTERNAL: Internal staff support_ticket (no approval/credits needed)
|
|
941
|
+
*/
|
|
942
|
+
const SupportTicketApprovalEnum = [
|
|
943
|
+
"PENDING",
|
|
944
|
+
"APPROVED",
|
|
945
|
+
"REJECTED",
|
|
946
|
+
"INTERNAL"
|
|
947
|
+
];
|
|
948
|
+
const SupportTicketApprovalSchema = z.enum(SupportTicketApprovalEnum);
|
|
949
|
+
/**
|
|
950
|
+
* Enum for internal development lifecycle stages
|
|
951
|
+
* - BACKLOG: Approved but not started
|
|
952
|
+
* - PLANNING: Scoping/design phase
|
|
953
|
+
* - DEVELOPMENT: Actively coding
|
|
954
|
+
* - CODE_REVIEW: In review
|
|
955
|
+
* - TESTING: QA testing
|
|
956
|
+
* - STAGING: On staging environment
|
|
957
|
+
* - PO_APPROVAL: Waiting for PO/customer sign-off
|
|
958
|
+
* - VERIFICATION: Deployed; waiting for consumer to verify and leave a comment
|
|
959
|
+
* - DEPLOYED: Live in production and verified
|
|
960
|
+
* - CANCELLED: Internal task cancelled (only for INTERNAL approval status)
|
|
961
|
+
*/
|
|
962
|
+
const SupportTicketDevLifecycleEnum = [
|
|
963
|
+
"PENDING",
|
|
964
|
+
"BACKLOG",
|
|
965
|
+
"PLANNING",
|
|
966
|
+
"DEVELOPMENT",
|
|
967
|
+
"CODE_REVIEW",
|
|
968
|
+
"TESTING",
|
|
969
|
+
"STAGING",
|
|
970
|
+
"PO_APPROVAL",
|
|
971
|
+
"VERIFICATION",
|
|
972
|
+
"DEPLOYED",
|
|
973
|
+
"CANCELLED"
|
|
974
|
+
];
|
|
975
|
+
const SupportTicketDevLifecycleSchema = z.enum(SupportTicketDevLifecycleEnum);
|
|
976
|
+
/**
|
|
977
|
+
* Dev lifecycle stages that can be manually set via update form
|
|
978
|
+
* Excludes workflow-only stages:
|
|
979
|
+
* - PENDING: Set by revert workflow
|
|
980
|
+
* - DEPLOYED: Set by completeSupportTicket workflow
|
|
981
|
+
* Staff can manually progress through: BACKLOG → PLANNING → DEVELOPMENT → CODE_REVIEW → TESTING → STAGING → PO_APPROVAL → VERIFICATION
|
|
982
|
+
*/
|
|
983
|
+
const SupportTicketDevLifecycleUpdateEnum = [
|
|
984
|
+
"BACKLOG",
|
|
985
|
+
"PLANNING",
|
|
986
|
+
"DEVELOPMENT",
|
|
987
|
+
"CODE_REVIEW",
|
|
988
|
+
"TESTING",
|
|
989
|
+
"STAGING",
|
|
990
|
+
"PO_APPROVAL",
|
|
991
|
+
"VERIFICATION"
|
|
992
|
+
];
|
|
993
|
+
const SupportTicketDevLifecycleUpdateSchema = z.enum(SupportTicketDevLifecycleUpdateEnum);
|
|
994
|
+
/**
|
|
995
|
+
* Label/value options for type, approval, dev lifecycle, and status filter dropdowns
|
|
996
|
+
*/
|
|
997
|
+
const SUPPORT_TICKET_TYPE_FILTER_OPTIONS = [
|
|
998
|
+
{
|
|
999
|
+
label: "Bug",
|
|
1000
|
+
value: "BUG"
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
label: "Feature",
|
|
1004
|
+
value: "FEATURE_REQUEST"
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
label: "Improvement",
|
|
1008
|
+
value: "IMPROVEMENT"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
label: "Operational",
|
|
1012
|
+
value: "OPERATIONAL"
|
|
1013
|
+
}
|
|
1014
|
+
];
|
|
1015
|
+
const SUPPORT_TICKET_APPROVAL_FILTER_OPTIONS = [
|
|
1016
|
+
{
|
|
1017
|
+
label: "Pending",
|
|
1018
|
+
value: "PENDING"
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
label: "Approved",
|
|
1022
|
+
value: "APPROVED"
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
label: "Rejected",
|
|
1026
|
+
value: "REJECTED"
|
|
1027
|
+
},
|
|
1028
|
+
{
|
|
1029
|
+
label: "Internal",
|
|
1030
|
+
value: "INTERNAL"
|
|
1031
|
+
}
|
|
1032
|
+
];
|
|
1033
|
+
const SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS = [
|
|
1034
|
+
{
|
|
1035
|
+
label: "Pending",
|
|
1036
|
+
value: "PENDING"
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
label: "Backlog",
|
|
1040
|
+
value: "BACKLOG"
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
label: "Planning",
|
|
1044
|
+
value: "PLANNING"
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
label: "Dev",
|
|
1048
|
+
value: "DEVELOPMENT"
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
label: "Review",
|
|
1052
|
+
value: "CODE_REVIEW"
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
label: "Testing",
|
|
1056
|
+
value: "TESTING"
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
label: "Staging",
|
|
1060
|
+
value: "STAGING"
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
label: "PO Approval",
|
|
1064
|
+
value: "PO_APPROVAL"
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
label: "Verification",
|
|
1068
|
+
value: "VERIFICATION"
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
label: "Live",
|
|
1072
|
+
value: "DEPLOYED"
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
label: "Cancelled",
|
|
1076
|
+
value: "CANCELLED"
|
|
1077
|
+
}
|
|
1078
|
+
];
|
|
1079
|
+
const SUPPORT_TICKET_STATUS_FILTER_OPTIONS = [
|
|
1080
|
+
{
|
|
1081
|
+
label: "Pending",
|
|
1082
|
+
value: "PENDING"
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
label: "Todo",
|
|
1086
|
+
value: "FOLLOWUP"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
label: "Active",
|
|
1090
|
+
value: "IN_PROGRESS"
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
label: "Verification",
|
|
1094
|
+
value: "VERIFICATION"
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
label: "Done",
|
|
1098
|
+
value: "COMPLETED"
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
label: "Cancelled",
|
|
1102
|
+
value: "CANCELLED"
|
|
1103
|
+
}
|
|
1104
|
+
];
|
|
1105
|
+
/**
|
|
1106
|
+
* Filter-specific enum instances
|
|
1107
|
+
* These are separate Zod objects with the same values as above,
|
|
1108
|
+
* allowing GraphQL to generate distinct enum types for filters vs domain objects
|
|
1109
|
+
*/
|
|
1110
|
+
const SupportTicketTypeFilterSchema = z.enum(SupportTicketTypeEnum);
|
|
1111
|
+
const SupportTicketPriorityFilterSchema = z.enum(SupportTicketPriorityEnum);
|
|
1112
|
+
const SupportTicketStatusFilterSchema = z.enum(SupportTicketStatusEnum);
|
|
1113
|
+
const SupportTicketApprovalFilterSchema = z.enum(SupportTicketApprovalEnum);
|
|
1114
|
+
const SupportTicketDevLifecycleFilterSchema = z.enum(SupportTicketDevLifecycleEnum);
|
|
1115
|
+
|
|
1116
|
+
//#endregion
|
|
1117
|
+
//#region src/validation/support_ticket/support_ticket_customer/customer_input_zod.ts
|
|
1118
|
+
/**
|
|
1119
|
+
* Schema for creating new support_ticket (customer)
|
|
1120
|
+
*/
|
|
1121
|
+
const CustomerSupportTicketCreateSchema = z.object({
|
|
1122
|
+
title: z.string().min(5, "Please add a more descriptive title (at least 5 characters)").max(256, "Title is too long - keep it brief (under 256 characters)"),
|
|
1123
|
+
description: z.string().optional().nullable(),
|
|
1124
|
+
type: SupportTicketTypeSchema.default("FEATURE_REQUEST"),
|
|
1125
|
+
priority: SupportTicketPriorityNumberSchema.default(SUPPORT_TICKET_PRIORITY_TO_NUMBER.MEDIUM)
|
|
1126
|
+
});
|
|
1127
|
+
/**
|
|
1128
|
+
* Schema for updating support_ticket (customer - PENDING items only)
|
|
1129
|
+
*/
|
|
1130
|
+
const CustomerSupportTicketUpdateSchema = CustomerSupportTicketCreateSchema.extend({ id: z.string() });
|
|
1131
|
+
|
|
1132
|
+
//#endregion
|
|
1133
|
+
//#region src/validation/support_ticket/support_ticket_customer/customer_read_zod.ts
|
|
1134
|
+
/**
|
|
1135
|
+
* Customer-specific read schema - ONLY includes fields customers can see
|
|
1136
|
+
* Excludes all staff-only fields like internalNotes, devLifecycle, timeline tracking, etc.
|
|
1137
|
+
*/
|
|
1138
|
+
const CustomerSupportTicketReadSchema = z.object({
|
|
1139
|
+
id: z.string(),
|
|
1140
|
+
display_id: z.string().optional().nullable(),
|
|
1141
|
+
display_id_prefix: z.string().optional().nullable(),
|
|
1142
|
+
title: z.string(),
|
|
1143
|
+
description: z.string(),
|
|
1144
|
+
type: SupportTicketTypeSchema,
|
|
1145
|
+
priority: SupportTicketPrioritySchema,
|
|
1146
|
+
status: SupportTicketStatusSchema,
|
|
1147
|
+
is_locked: z.boolean(),
|
|
1148
|
+
created_by_display_name: z.string().optional().nullable(),
|
|
1149
|
+
credit_value: z.string().optional().nullable(),
|
|
1150
|
+
start_at: z.string().optional().nullable(),
|
|
1151
|
+
target_at: z.string().optional().nullable(),
|
|
1152
|
+
completed_at: z.string().optional().nullable(),
|
|
1153
|
+
locked_approval_at: z.string().optional().nullable(),
|
|
1154
|
+
created_by: z.string().optional().nullable(),
|
|
1155
|
+
created_at: z.string().optional().nullable(),
|
|
1156
|
+
updated_by: z.string().optional().nullable(),
|
|
1157
|
+
updated_at: z.string().optional().nullable(),
|
|
1158
|
+
archived_at: z.string().optional().nullable(),
|
|
1159
|
+
my_subscription: RecordSubscriberReadSchema.nullable().optional()
|
|
1160
|
+
});
|
|
1161
|
+
/**
|
|
1162
|
+
* Paginated customer support_ticket response
|
|
1163
|
+
*/
|
|
1164
|
+
const CustomerSupportTicketPageSchema = createPaginatedSchema(CustomerSupportTicketReadSchema);
|
|
1165
|
+
|
|
1166
|
+
//#endregion
|
|
1167
|
+
//#region src/validation/support_ticket/support_ticket_customer/customer_support_ticket_filters_zod.ts
|
|
1168
|
+
/**
|
|
1169
|
+
* Customer-facing filters for support_ticket
|
|
1170
|
+
* Includes all fields customers can see and filter on
|
|
1171
|
+
*
|
|
1172
|
+
* - Cannot filter by internal fields (isInternalOnly, approvalStatus, devLifecycle)
|
|
1173
|
+
* - Can filter on all visible fields including credits, requester info, etc.
|
|
1174
|
+
*/
|
|
1175
|
+
const CustomerSupportTicketFiltersSchema = PaginationFiltersSchema.extend({
|
|
1176
|
+
type: createEnumFilter(SupportTicketTypeFilterSchema).optional(),
|
|
1177
|
+
status: createEnumFilter(SupportTicketStatusFilterSchema).optional(),
|
|
1178
|
+
priority: NumberFilterSchema.optional(),
|
|
1179
|
+
title: StringFilterSchema.optional(),
|
|
1180
|
+
description: StringFilterSchema.optional(),
|
|
1181
|
+
created_by: StringFilterSchema.optional(),
|
|
1182
|
+
is_locked: BooleanFilterSchema.optional(),
|
|
1183
|
+
credit_value: NumberFilterSchema.optional(),
|
|
1184
|
+
created_at: DateFilterSchema.optional(),
|
|
1185
|
+
updated_at: DateFilterSchema.optional(),
|
|
1186
|
+
start_at: DateFilterSchema.optional(),
|
|
1187
|
+
target_at: DateFilterSchema.optional(),
|
|
1188
|
+
completed_at: DateFilterSchema.optional(),
|
|
1189
|
+
search: z.object({
|
|
1190
|
+
query: z.string(),
|
|
1191
|
+
searchableFields: z.array(z.string())
|
|
1192
|
+
}).optional()
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
//#endregion
|
|
1196
|
+
//#region src/validation/support_ticket/support_ticket_staff/staff_read_zod.ts
|
|
1197
|
+
/**
|
|
1198
|
+
* Staff-specific read schema - includes ALL fields including staff-only data
|
|
1199
|
+
* This is what staff see when they query support_ticket (includes internal notes, dev lifecycle, etc.)
|
|
1200
|
+
*/
|
|
1201
|
+
const StaffSupportTicketReadSchema = z.object({
|
|
1202
|
+
id: z.string(),
|
|
1203
|
+
display_id: z.string().optional().nullable(),
|
|
1204
|
+
display_id_prefix: z.string().optional().nullable(),
|
|
1205
|
+
title: z.string(),
|
|
1206
|
+
description: z.string(),
|
|
1207
|
+
type: SupportTicketTypeSchema,
|
|
1208
|
+
priority: SupportTicketPrioritySchema,
|
|
1209
|
+
status: SupportTicketStatusSchema,
|
|
1210
|
+
approval_status: SupportTicketApprovalSchema,
|
|
1211
|
+
is_locked: z.boolean(),
|
|
1212
|
+
can_delete: z.boolean(),
|
|
1213
|
+
created_by_display_name: z.string().optional().nullable(),
|
|
1214
|
+
assigned_to: z.string().optional().nullable(),
|
|
1215
|
+
assigned_to_display_name: z.string().optional().nullable(),
|
|
1216
|
+
dev_lifecycle: SupportTicketDevLifecycleSchema.optional().nullable(),
|
|
1217
|
+
credit_value: z.string().optional().nullable(),
|
|
1218
|
+
delivered_value: z.string().optional().nullable(),
|
|
1219
|
+
start_at: z.string().optional().nullable(),
|
|
1220
|
+
target_at: z.string().optional().nullable(),
|
|
1221
|
+
completed_at: z.string().optional().nullable(),
|
|
1222
|
+
locked_approval_at: z.string().optional().nullable(),
|
|
1223
|
+
created_by: z.string().optional().nullable(),
|
|
1224
|
+
created_at: z.string().optional().nullable(),
|
|
1225
|
+
updated_by: z.string().optional().nullable(),
|
|
1226
|
+
updated_by_display_name: z.string().optional().nullable(),
|
|
1227
|
+
updated_at: z.string().optional().nullable(),
|
|
1228
|
+
archived_at: z.string().optional().nullable(),
|
|
1229
|
+
archived_by: z.string().optional().nullable()
|
|
1230
|
+
});
|
|
1231
|
+
/**
|
|
1232
|
+
* Paginated staff support_ticket response
|
|
1233
|
+
*/
|
|
1234
|
+
const StaffSupportTicketPageSchema = createPaginatedSchema(StaffSupportTicketReadSchema);
|
|
1235
|
+
|
|
1236
|
+
//#endregion
|
|
1237
|
+
//#region src/validation/support_ticket/support_ticket_staff/staff_support_ticket_filters_zod.ts
|
|
1238
|
+
/**
|
|
1239
|
+
* Staff/Admin filters for support_ticket
|
|
1240
|
+
* Full access to all filtering capabilities including internal fields
|
|
1241
|
+
*
|
|
1242
|
+
* All filters support operators for advanced filtering:
|
|
1243
|
+
* - Enums: eq, ne
|
|
1244
|
+
* - Booleans: eq, ne
|
|
1245
|
+
* - Strings: eq, ne, contains, startsWith, endsWith
|
|
1246
|
+
* - Numbers: eq, ne, gt, gte, lt, lte
|
|
1247
|
+
* - Dates: eq, ne, gt, gte, lt, lte
|
|
1248
|
+
*
|
|
1249
|
+
* Examples:
|
|
1250
|
+
* - { type: { operator: "eq", value: "BUG" } }
|
|
1251
|
+
* - { approvalStatus: { operator: "eq", value: "INTERNAL" } } // Filter for internal tasks
|
|
1252
|
+
* - { title: { operator: "contains", value: "search" } }
|
|
1253
|
+
* - { creditValue: { operator: "gte", value: 100 } }
|
|
1254
|
+
* - { createdAt: { operator: "gte", value: "2024-01-01" } }
|
|
1255
|
+
*/
|
|
1256
|
+
const StaffSupportTicketFiltersSchema = PaginationFiltersSchema.extend({
|
|
1257
|
+
type: createEnumFilter(SupportTicketTypeFilterSchema).optional(),
|
|
1258
|
+
status: createEnumFilter(SupportTicketStatusFilterSchema).optional(),
|
|
1259
|
+
approval_status: createEnumFilter(SupportTicketApprovalFilterSchema).optional(),
|
|
1260
|
+
priority: NumberFilterSchema.optional(),
|
|
1261
|
+
dev_lifecycle: createEnumFilter(SupportTicketDevLifecycleFilterSchema).optional(),
|
|
1262
|
+
created_by: StringFilterSchema.optional(),
|
|
1263
|
+
title: StringFilterSchema.optional(),
|
|
1264
|
+
description: StringFilterSchema.optional(),
|
|
1265
|
+
is_locked: BooleanFilterSchema.optional(),
|
|
1266
|
+
credit_value: NumberFilterSchema.optional(),
|
|
1267
|
+
delivered_value: NumberFilterSchema.optional(),
|
|
1268
|
+
created_at: DateFilterSchema.optional(),
|
|
1269
|
+
updated_at: DateFilterSchema.optional(),
|
|
1270
|
+
start_at: DateFilterSchema.optional(),
|
|
1271
|
+
target_at: DateFilterSchema.optional(),
|
|
1272
|
+
completed_at: DateFilterSchema.optional(),
|
|
1273
|
+
search: z.object({
|
|
1274
|
+
query: z.string(),
|
|
1275
|
+
searchableFields: z.array(z.string())
|
|
1276
|
+
}).optional()
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
//#endregion
|
|
1280
|
+
//#region src/validation/support_ticket/support_ticket_staff/staff_update_zod.ts
|
|
1281
|
+
/**
|
|
1282
|
+
* Base schema for staff support_ticket updates (general editing)
|
|
1283
|
+
*
|
|
1284
|
+
* Workflow-specific fields NOT included here:
|
|
1285
|
+
* - approvalStatus: Use approve/reject/revert workflows, or set to INTERNAL at creation
|
|
1286
|
+
*
|
|
1287
|
+
* Note: completeSupportTicket workflow sets deliveredValue + completedAt initially,
|
|
1288
|
+
* but they can be adjusted here afterwards for corrections.
|
|
1289
|
+
*
|
|
1290
|
+
* Business rules validated in backend:
|
|
1291
|
+
* - Can't edit creditValue unless PENDING (INTERNAL support_ticket can't have credits)
|
|
1292
|
+
* - Can't edit devLifecycle unless APPROVED or INTERNAL
|
|
1293
|
+
* - Can't edit deliveredValue unless DEPLOYED
|
|
1294
|
+
* - Can't edit completedAt unless DEPLOYED
|
|
1295
|
+
* - etc.
|
|
1296
|
+
*/
|
|
1297
|
+
const StaffSupportTicketInputBaseSchema = z.object({
|
|
1298
|
+
title: z.string().min(5, "Please add a more descriptive title (at least 5 characters)").max(256, "Title is too long - keep it brief (under 256 characters)"),
|
|
1299
|
+
description: z.string().optional().nullable(),
|
|
1300
|
+
type: SupportTicketTypeSchema,
|
|
1301
|
+
priority: SupportTicketPriorityNumberSchema,
|
|
1302
|
+
dev_lifecycle: SupportTicketDevLifecycleUpdateSchema.optional(),
|
|
1303
|
+
credit_value: z.string().regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE).optional().nullable(),
|
|
1304
|
+
delivered_value: z.string().regex(DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_REGEX, DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_ERROR_MESSAGE).optional().nullable(),
|
|
1305
|
+
start_at: z.string().regex(/\d{4}-\d{2}-\d{2}/, "Date must be in the format YYYY-MM-DD").optional().nullable(),
|
|
1306
|
+
target_at: z.string().regex(/\d{4}-\d{2}-\d{2}/, "Date must be in the format YYYY-MM-DD").optional().nullable(),
|
|
1307
|
+
completed_at: z.string().regex(/\d{4}-\d{2}-\d{2}/, "Date must be in the format YYYY-MM-DD").optional().nullable(),
|
|
1308
|
+
assigned_to: z.string().optional().nullable()
|
|
1309
|
+
});
|
|
1310
|
+
const StaffSupportTicketCreateSchema = StaffSupportTicketInputBaseSchema.extend({ is_internal: z.boolean().optional() });
|
|
1311
|
+
const StaffSupportTicketUpdateSchema = StaffSupportTicketInputBaseSchema.extend({ id: z.string() });
|
|
1312
|
+
const StaffSupportTicketInputSchema = StaffSupportTicketUpdateSchema;
|
|
1313
|
+
|
|
1314
|
+
//#endregion
|
|
1315
|
+
//#region src/validation/support_ticket/support_ticket_staff/staff_workflow_zod.ts
|
|
1316
|
+
/**
|
|
1317
|
+
* Staff workflow schemas
|
|
1318
|
+
* These are for critical state transitions in the support_ticket lifecycle
|
|
1319
|
+
*/
|
|
1320
|
+
/**
|
|
1321
|
+
* Approve support_ticket (PENDING → APPROVED)
|
|
1322
|
+
* Deducts credits and sets dev_lifecycle to BACKLOG
|
|
1323
|
+
* credit_value is required - can be set/updated in the approve flow
|
|
1324
|
+
*/
|
|
1325
|
+
const ApproveSupportTicketSchema = z.object({
|
|
1326
|
+
id: z.string(),
|
|
1327
|
+
credit_value: z.string().min(0, "Credit value is required to approve").regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE).refine((val) => parseFloat(val) >= 0, "Must be 0 or greater")
|
|
1328
|
+
});
|
|
1329
|
+
/**
|
|
1330
|
+
* Reject support_ticket (PENDING → REJECTED)
|
|
1331
|
+
* Locks the support_ticket without deducting credits
|
|
1332
|
+
*/
|
|
1333
|
+
const RejectSupportTicketSchema = z.object({ id: z.string() });
|
|
1334
|
+
/**
|
|
1335
|
+
* Revert support_ticket (APPROVED/REJECTED → PENDING)
|
|
1336
|
+
* Refunds credits if applicable and unlocks the support_ticket
|
|
1337
|
+
*/
|
|
1338
|
+
const RevertSupportTicketSchema = z.object({ id: z.string() });
|
|
1339
|
+
/**
|
|
1340
|
+
* Complete support_ticket (devLifecycle → DEPLOYED)
|
|
1341
|
+
* Marks support_ticket as deployed and captures actual delivered value
|
|
1342
|
+
* Auto-sets completedAt timestamp
|
|
1343
|
+
*/
|
|
1344
|
+
const CompleteSupportTicketSchema = z.object({
|
|
1345
|
+
id: z.string(),
|
|
1346
|
+
delivered_value: z.string().regex(/^(\d*\.?\d{1,2}|\d+)$/, "Must be a valid dollar amount with up to 2 decimal places")
|
|
1347
|
+
});
|
|
1348
|
+
/**
|
|
1349
|
+
* Delete support_ticket (soft delete)
|
|
1350
|
+
* Cannot delete APPROVED tickets (charged to customer)
|
|
1351
|
+
* Can delete PENDING, REJECTED, INTERNAL tickets
|
|
1352
|
+
*/
|
|
1353
|
+
const DeleteSupportTicketSchema = z.object({ id: z.string() });
|
|
1354
|
+
/**
|
|
1355
|
+
* Cancel internal task (devLifecycle → CANCELLED)
|
|
1356
|
+
* Only for INTERNAL approval_status tickets
|
|
1357
|
+
* Customer tickets use reject workflow instead
|
|
1358
|
+
*/
|
|
1359
|
+
const CancelInternalTaskSchema = z.object({ id: z.string() });
|
|
1360
|
+
/**
|
|
1361
|
+
* Reactivate terminal internal task (CANCELLED or DEPLOYED → BACKLOG)
|
|
1362
|
+
* Only for INTERNAL approval_status tickets with dev_lifecycle = CANCELLED or DEPLOYED
|
|
1363
|
+
* Clears completed_at timestamp and reopens task for more work
|
|
1364
|
+
*/
|
|
1365
|
+
const ReactivateInternalTaskSchema = z.object({ id: z.string() });
|
|
1366
|
+
/**
|
|
1367
|
+
* Archive support ticket (COMPLETED only)
|
|
1368
|
+
* Only completed tickets (dev_lifecycle = DEPLOYED) can be archived.
|
|
1369
|
+
* Archiving locks comments for both customer and staff.
|
|
1370
|
+
*/
|
|
1371
|
+
const ArchiveSupportTicketSchema = z.object({ id: z.string() });
|
|
1372
|
+
|
|
1373
|
+
//#endregion
|
|
1374
|
+
//#region src/validation/support_ticket/support_ticket_staff/support_ticket_event_types_zod.ts
|
|
1375
|
+
const SupportTicketEventTypeValues = [
|
|
1376
|
+
"TICKET_CREATED",
|
|
1377
|
+
"TICKET_ASSIGNED",
|
|
1378
|
+
"TICKET_UPDATED",
|
|
1379
|
+
"APPROVAL_STATUS_CHANGED",
|
|
1380
|
+
"DEV_LIFECYCLE_CHANGED",
|
|
1381
|
+
"PRIORITY_CHANGED",
|
|
1382
|
+
"NEW_CUSTOMER_NOTE",
|
|
1383
|
+
"NEW_INTERNAL_NOTE",
|
|
1384
|
+
"NEW_FOLLOWUP",
|
|
1385
|
+
"FOLLOWUP_COMPLETED"
|
|
1386
|
+
];
|
|
1387
|
+
const SupportTicketEventTypeEnum = z.enum(SupportTicketEventTypeValues);
|
|
1388
|
+
|
|
1389
|
+
//#endregion
|
|
1390
|
+
//#region src/validation/support_ticket/support_ticket_staff/support_ticket_subscriber_zod.ts
|
|
1391
|
+
/** Support-ticket-specific create input: uses support_ticket_id instead of generic record_type/record_id */
|
|
1392
|
+
const SupportTicketSubscriberCreateSchema = z.object({
|
|
1393
|
+
support_ticket_id: z.string().min(1, "Support ticket ID is required"),
|
|
1394
|
+
user_id: z.string().min(1, "User ID is required"),
|
|
1395
|
+
subscribed_events: z.array(SupportTicketEventTypeEnum).optional().nullable()
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
//#endregion
|
|
1399
|
+
//#region src/validation/support_ticket/support_ticket_enrichment.ts
|
|
1400
|
+
/**
|
|
1401
|
+
* Schema for support ticket record data in enriched contexts
|
|
1402
|
+
* This defines the minimal fields needed for display/enrichment
|
|
1403
|
+
* Includes id for mapping purposes, even though it's also in record_id
|
|
1404
|
+
*/
|
|
1405
|
+
const SupportTicketRecordDataSchema = z.object({
|
|
1406
|
+
id: z.string(),
|
|
1407
|
+
display_id: z.string().optional().nullable(),
|
|
1408
|
+
title: z.string()
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
//#endregion
|
|
1412
|
+
//#region src/validation/team/team_enums_zod.ts
|
|
1413
|
+
const TeamStatusSchema = z.enum(["active", "inactive"]).describe("TeamStatus");
|
|
1414
|
+
|
|
1415
|
+
//#endregion
|
|
1416
|
+
//#region src/validation/team/team_filters_zod.ts
|
|
1417
|
+
/**
|
|
1418
|
+
* Filters for teams
|
|
1419
|
+
*
|
|
1420
|
+
* All filters support operators for advanced filtering:
|
|
1421
|
+
* - Booleans: eq, ne
|
|
1422
|
+
* - Strings: eq, ne, contains, startsWith, endsWith
|
|
1423
|
+
* - Dates: eq, ne, gt, gte, lt, lte
|
|
1424
|
+
*
|
|
1425
|
+
* Examples:
|
|
1426
|
+
* - { display_name: { operator: "contains", value: "search" } }
|
|
1427
|
+
* - { contact_email: { operator: "eq", value: "test@example.com" } }
|
|
1428
|
+
* - { created_at: { operator: "gte", value: "2024-01-01" } }
|
|
1429
|
+
*/
|
|
1430
|
+
const TeamFiltersSchema = PaginationFiltersSchema.extend({
|
|
1431
|
+
unique_name: StringFilterSchema.optional(),
|
|
1432
|
+
display_name: StringFilterSchema.optional(),
|
|
1433
|
+
legal_name: StringFilterSchema.optional(),
|
|
1434
|
+
contact_email: StringFilterSchema.optional(),
|
|
1435
|
+
address_city: StringFilterSchema.optional(),
|
|
1436
|
+
address_zip: StringFilterSchema.optional(),
|
|
1437
|
+
includeArchived: BooleanFilterSchema.optional(),
|
|
1438
|
+
created_at: DateFilterSchema.optional(),
|
|
1439
|
+
updated_at: DateFilterSchema.optional(),
|
|
1440
|
+
archived_at: DateFilterSchema.optional(),
|
|
1441
|
+
search: z.object({
|
|
1442
|
+
query: z.string(),
|
|
1443
|
+
searchableFields: z.array(z.string())
|
|
1444
|
+
}).optional()
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
//#endregion
|
|
1448
|
+
//#region src/validation/team/team_input_zod.ts
|
|
1449
|
+
const TeamInputBaseSchema = z.object({
|
|
1450
|
+
unique_name: z.string().min(3, "Unique name must be between 3 and 64 characters").max(64).optional().nullable(),
|
|
1451
|
+
display_name: z.string().min(3, "Display name must be between 3 and 64 characters").max(64),
|
|
1452
|
+
legal_name: z.string().optional().nullable(),
|
|
1453
|
+
description: z.string().optional().nullable(),
|
|
1454
|
+
contact_name: z.string().optional().nullable(),
|
|
1455
|
+
contact_email: z.string().email().optional().nullable(),
|
|
1456
|
+
contact_business_phone: z.string().optional().nullable(),
|
|
1457
|
+
contact_mobile_phone: z.string().optional().nullable(),
|
|
1458
|
+
contact_time_zone: z.string().optional().nullable(),
|
|
1459
|
+
address_full: z.string().optional().nullable(),
|
|
1460
|
+
address_city: z.string().optional().nullable(),
|
|
1461
|
+
address_zip: z.string().optional().nullable(),
|
|
1462
|
+
twitter_username: z.string().optional().nullable(),
|
|
1463
|
+
url: z.string().url().optional().nullable(),
|
|
1464
|
+
logo: z.string().optional().nullable(),
|
|
1465
|
+
email_sent_from: z.string().email().optional().nullable(),
|
|
1466
|
+
email_reply_to: z.string().email().optional().nullable()
|
|
1467
|
+
});
|
|
1468
|
+
const TeamCreateSchema = TeamInputBaseSchema;
|
|
1469
|
+
const TeamUpdateSchema = TeamInputBaseSchema.extend({ id: z.string() });
|
|
1470
|
+
|
|
1471
|
+
//#endregion
|
|
1472
|
+
//#region src/validation/team/team_read_zod.ts
|
|
1473
|
+
const TeamReadSchema = TeamCreateSchema.extend({
|
|
1474
|
+
id: z.string(),
|
|
1475
|
+
original_id: z.string().optional().nullable(),
|
|
1476
|
+
path: z.string().optional().nullable(),
|
|
1477
|
+
created_at: z.string(),
|
|
1478
|
+
updated_at: z.string().optional().nullable(),
|
|
1479
|
+
created_by: z.string(),
|
|
1480
|
+
created_by_display_name: z.string().optional().nullable(),
|
|
1481
|
+
updated_by: z.string().optional().nullable(),
|
|
1482
|
+
updated_by_display_name: z.string().optional().nullable(),
|
|
1483
|
+
archived_at: z.string().optional().nullable(),
|
|
1484
|
+
archived_by: z.string().optional().nullable()
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
//#endregion
|
|
1488
|
+
//#region src/validation/team/team_page_zod.ts
|
|
1489
|
+
const TeamPageSchema = createPaginatedSchema(TeamReadSchema);
|
|
1490
|
+
|
|
1491
|
+
//#endregion
|
|
1492
|
+
//#region src/validation/team/team_user_teams_zod.ts
|
|
1493
|
+
/**
|
|
1494
|
+
* Schema for a team option in user teams list
|
|
1495
|
+
* Includes all team fields to match the GraphQL query
|
|
1496
|
+
*/
|
|
1497
|
+
const TeamOptionSchema = TeamReadSchema;
|
|
1498
|
+
/**
|
|
1499
|
+
* Schema for the list of teams that a user is part of
|
|
1500
|
+
*/
|
|
1501
|
+
const UserTeamsSchema = z.object({ items: z.array(TeamOptionSchema) });
|
|
1502
|
+
|
|
1503
|
+
//#endregion
|
|
1504
|
+
//#region src/validation/team_member/team_member_enums_zod.ts
|
|
1505
|
+
/**
|
|
1506
|
+
* Team Member Role Enum
|
|
1507
|
+
* - owner: Full permissions (create, read, update, delete)
|
|
1508
|
+
* - manager: Can create and read (cannot update or delete)
|
|
1509
|
+
* - member: Can create and read (cannot update or delete)
|
|
1510
|
+
* - client: Client role (permissions TBD)
|
|
1511
|
+
*/
|
|
1512
|
+
const TeamMemberRoleEnum = [
|
|
1513
|
+
"owner",
|
|
1514
|
+
"manager",
|
|
1515
|
+
"member",
|
|
1516
|
+
"client"
|
|
1517
|
+
];
|
|
1518
|
+
const TeamMemberRoleSchema = z.enum(TeamMemberRoleEnum);
|
|
1519
|
+
/**
|
|
1520
|
+
* Filter-specific enum instance
|
|
1521
|
+
* This is a separate Zod object with the same values as above,
|
|
1522
|
+
* allowing GraphQL to generate distinct enum types for filters vs domain objects
|
|
1523
|
+
*/
|
|
1524
|
+
const TeamMemberRoleFilterSchema = z.enum(TeamMemberRoleEnum);
|
|
1525
|
+
|
|
1526
|
+
//#endregion
|
|
1527
|
+
//#region src/validation/team_member/team_member_filters_zod.ts
|
|
1528
|
+
/**
|
|
1529
|
+
* Filters for team_member
|
|
1530
|
+
* Supports operator-based filtering for advanced queries
|
|
1531
|
+
*
|
|
1532
|
+
* All filters support operators for advanced filtering:
|
|
1533
|
+
* - Numbers: eq, ne, gt, gte, lt, lte
|
|
1534
|
+
* - Strings: eq, ne, contains, startsWith, endsWith
|
|
1535
|
+
* - Dates: eq, ne, gt, gte, lt, lte
|
|
1536
|
+
*
|
|
1537
|
+
* Examples:
|
|
1538
|
+
* - { original_id: { operator: "eq", value: 123 } }
|
|
1539
|
+
* - { team_id: { operator: "eq", value: "team_123" } }
|
|
1540
|
+
* - { display_name: { operator: "contains", value: "John" } }
|
|
1541
|
+
* - { created_at: { operator: "gte", value: "2024-01-01" } }
|
|
1542
|
+
*/
|
|
1543
|
+
const TeamMemberFiltersSchema = PaginationFiltersSchema.extend({
|
|
1544
|
+
original_id: NumberFilterSchema.optional(),
|
|
1545
|
+
team_id: StringFilterSchema.optional(),
|
|
1546
|
+
original_team_id: StringFilterSchema.optional(),
|
|
1547
|
+
user_id: StringFilterSchema.optional(),
|
|
1548
|
+
original_user_id: StringFilterSchema.optional(),
|
|
1549
|
+
role: createEnumFilter(TeamMemberRoleFilterSchema).optional(),
|
|
1550
|
+
display_name: StringFilterSchema.optional(),
|
|
1551
|
+
business_phone: StringFilterSchema.optional(),
|
|
1552
|
+
mobile_phone: StringFilterSchema.optional(),
|
|
1553
|
+
email_address: StringFilterSchema.optional(),
|
|
1554
|
+
website_address: StringFilterSchema.optional(),
|
|
1555
|
+
time_zone: StringFilterSchema.optional(),
|
|
1556
|
+
created_at: DateFilterSchema.optional(),
|
|
1557
|
+
updated_at: DateFilterSchema.optional(),
|
|
1558
|
+
deleted_at: DateFilterSchema.optional(),
|
|
1559
|
+
search: z.object({
|
|
1560
|
+
query: z.string(),
|
|
1561
|
+
searchableFields: z.array(z.string())
|
|
1562
|
+
}).optional()
|
|
1563
|
+
});
|
|
1564
|
+
|
|
1565
|
+
//#endregion
|
|
1566
|
+
//#region src/validation/team_member/team_member_input_zod.ts
|
|
1567
|
+
const TeamMemberBaseSchema = z.object({
|
|
1568
|
+
team_id: z.string(),
|
|
1569
|
+
user_id: z.string(),
|
|
1570
|
+
role: TeamMemberRoleSchema,
|
|
1571
|
+
display_name: z.string().min(3, "Display name must be at least 3 characters").max(64, "Display name must be at most 64 characters"),
|
|
1572
|
+
business_phone: z.string().optional().nullable(),
|
|
1573
|
+
mobile_phone: z.string().optional().nullable(),
|
|
1574
|
+
email_address: z.string().email(),
|
|
1575
|
+
website_address: z.string().url().optional().nullable(),
|
|
1576
|
+
time_zone: z.string().optional().nullable()
|
|
1577
|
+
});
|
|
1578
|
+
const TeamMemberCreateSchema = TeamMemberBaseSchema;
|
|
1579
|
+
const TeamMemberUpdateSchema = TeamMemberBaseSchema.extend({ id: z.string() });
|
|
1580
|
+
|
|
1581
|
+
//#endregion
|
|
1582
|
+
//#region src/validation/team_member/team_member_read_zod.ts
|
|
1583
|
+
const TeamMemberReadSchema = z.object({
|
|
1584
|
+
id: z.string(),
|
|
1585
|
+
original_id: z.number().optional().nullable(),
|
|
1586
|
+
team_id: z.string(),
|
|
1587
|
+
original_team_id: z.string().optional().nullable(),
|
|
1588
|
+
user_id: z.string(),
|
|
1589
|
+
original_user_id: z.string().optional().nullable(),
|
|
1590
|
+
role: TeamMemberRoleSchema,
|
|
1591
|
+
display_name: z.string().optional().nullable(),
|
|
1592
|
+
business_phone: z.string().optional().nullable(),
|
|
1593
|
+
mobile_phone: z.string().optional().nullable(),
|
|
1594
|
+
email_address: z.string().email(),
|
|
1595
|
+
website_address: z.string().optional().nullable(),
|
|
1596
|
+
time_zone: z.string().optional().nullable(),
|
|
1597
|
+
created_at: z.string(),
|
|
1598
|
+
created_by: z.string(),
|
|
1599
|
+
created_by_display_name: z.string().optional().nullable(),
|
|
1600
|
+
updated_at: z.string().optional().nullable(),
|
|
1601
|
+
updated_by: z.string().optional().nullable(),
|
|
1602
|
+
updated_by_display_name: z.string().optional().nullable(),
|
|
1603
|
+
deleted_at: z.string().optional().nullable(),
|
|
1604
|
+
deleted_by: z.string().optional().nullable()
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
//#endregion
|
|
1608
|
+
//#region src/validation/team_member/team_member_user_team_members_zod.ts
|
|
1609
|
+
/**
|
|
1610
|
+
* Schema for a team member option in user team members list
|
|
1611
|
+
* Includes all team member fields to match the query
|
|
1612
|
+
*/
|
|
1613
|
+
const TeamMemberOptionSchema = TeamMemberReadSchema;
|
|
1614
|
+
/**
|
|
1615
|
+
* Schema for the list of team members that a user can see (from their teams)
|
|
1616
|
+
*/
|
|
1617
|
+
const UserTeamMembersSchema = z.object({ items: z.array(TeamMemberOptionSchema) });
|
|
1618
|
+
|
|
1619
|
+
//#endregion
|
|
1620
|
+
//#region src/validation/user/user_read_zod.ts
|
|
1621
|
+
const UserReadSchema = z.object({
|
|
1622
|
+
id: z.string(),
|
|
1623
|
+
username: z.string(),
|
|
1624
|
+
email: z.string(),
|
|
1625
|
+
email_verified: z.boolean(),
|
|
1626
|
+
user_type: UserTypeEnum,
|
|
1627
|
+
created_at: z.string(),
|
|
1628
|
+
updated_at: z.string().nullable()
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
//#endregion
|
|
1632
|
+
//#region src/validation/user/user_update_zod.ts
|
|
1633
|
+
const UserUpdateSchema = z.object({
|
|
1634
|
+
id: z.string(),
|
|
1635
|
+
user_type: UserTypeEnum
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
//#endregion
|
|
1639
|
+
//#region src/validation/user_profile_zod.ts
|
|
1640
|
+
/**
|
|
1641
|
+
* Base schema for user profile
|
|
1642
|
+
*/
|
|
1643
|
+
const UserProfileBaseSchema = z.object({
|
|
1644
|
+
first_name: z.string().max(255, "First name is too long").nullable().optional(),
|
|
1645
|
+
last_name: z.string().max(255, "Last name is too long").nullable().optional(),
|
|
1646
|
+
bio: z.string().max(1e3, "Bio is too long").nullable().optional(),
|
|
1647
|
+
notification_email: z.string().email("Invalid notification email").nullable().optional()
|
|
1648
|
+
});
|
|
1649
|
+
/**
|
|
1650
|
+
* Schema for updating a user profile
|
|
1651
|
+
*/
|
|
1652
|
+
const UserProfileUpdateSchema = UserProfileBaseSchema.extend({ user_id: z.string().min(1, "User ID is required") });
|
|
1653
|
+
/**
|
|
1654
|
+
* Schema for reading a user profile
|
|
1655
|
+
*/
|
|
1656
|
+
const UserProfileReadSchema = UserProfileBaseSchema.extend({
|
|
1657
|
+
id: z.string().min(1, "ID is required"),
|
|
1658
|
+
user_id: z.string().min(1, "User ID is required"),
|
|
1659
|
+
created_at: z.string().datetime(),
|
|
1660
|
+
created_by: z.string().min(1, "Created by is required"),
|
|
1661
|
+
updated_at: z.string().datetime().nullable(),
|
|
1662
|
+
updated_by: z.string().nullable()
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
//#endregion
|
|
1666
|
+
//#region src/validation/user_session_zod.ts
|
|
1667
|
+
const userSessionSchema = z.object({
|
|
1668
|
+
created_at: z.string(),
|
|
1669
|
+
expires_at: z.string(),
|
|
1670
|
+
status: z.string(),
|
|
1671
|
+
user_agent: z.string().optional().nullable(),
|
|
1672
|
+
ip_address: z.string().optional().nullable(),
|
|
1673
|
+
user: z.object({
|
|
1674
|
+
userId: z.string(),
|
|
1675
|
+
email: z.string(),
|
|
1676
|
+
username: z.string(),
|
|
1677
|
+
email_verified: z.boolean(),
|
|
1678
|
+
user_type: z.string(),
|
|
1679
|
+
first_name: z.string().optional().nullable(),
|
|
1680
|
+
last_name: z.string().optional().nullable(),
|
|
1681
|
+
avatar_url: z.string().optional().nullable(),
|
|
1682
|
+
subscriptions: z.array(z.object({
|
|
1683
|
+
subscription_id: z.string(),
|
|
1684
|
+
subscription_status: z.string().optional().nullable(),
|
|
1685
|
+
subscription_created: z.string(),
|
|
1686
|
+
subscription_current_period_start: z.string(),
|
|
1687
|
+
subscription_current_period_end: z.string(),
|
|
1688
|
+
subscription_cancel_at: z.string().optional().nullable(),
|
|
1689
|
+
subscription_canceled_at: z.string().optional().nullable(),
|
|
1690
|
+
product_name: z.string().optional().nullable(),
|
|
1691
|
+
price_amount: z.number().nullable(),
|
|
1692
|
+
price_currency: z.string().optional().nullable()
|
|
1693
|
+
})).optional().nullable()
|
|
1694
|
+
})
|
|
1695
|
+
});
|
|
1696
|
+
const loginResponseSchema = z.object({
|
|
1697
|
+
frontend_session: userSessionSchema,
|
|
1698
|
+
access_token: z.string(),
|
|
1699
|
+
refresh_token: z.string(),
|
|
1700
|
+
user_details_token: z.string()
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
//#endregion
|
|
1704
|
+
//#region src/api/saved-filter-api.ts
|
|
1705
|
+
/** Maximum presets per context (e.g. per page like trackers, followups) */
|
|
1706
|
+
const MAX_PRESETS_PER_CONTEXT = 10;
|
|
1707
|
+
|
|
1708
|
+
//#endregion
|
|
1709
|
+
//#region src/utils/enum_labels.ts
|
|
1710
|
+
/**
|
|
1711
|
+
* Utility functions for converting enum values to human-readable display labels
|
|
1712
|
+
*/
|
|
1713
|
+
/**
|
|
1714
|
+
* Normalizes and validates input
|
|
1715
|
+
*/
|
|
1716
|
+
function normalizeInput(value) {
|
|
1717
|
+
if (typeof value !== "string") return String(value);
|
|
1718
|
+
return value.trim().toLowerCase();
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Converts a snake_case string to Title Case
|
|
1722
|
+
* Handles edge cases like empty strings, single characters, numbers, etc.
|
|
1723
|
+
*
|
|
1724
|
+
* Examples:
|
|
1725
|
+
* - "order_signed" -> "Order Signed"
|
|
1726
|
+
* - "active_mtm" -> "Active Mtm"
|
|
1727
|
+
* - "create_a_quote" -> "Create A Quote"
|
|
1728
|
+
*/
|
|
1729
|
+
function snakeCaseToTitleCase(value) {
|
|
1730
|
+
const normalized = normalizeInput(value);
|
|
1731
|
+
if (!normalized) return value;
|
|
1732
|
+
const parts = normalized.split("_").filter((part) => part.length > 0);
|
|
1733
|
+
if (parts.length === 0) return value;
|
|
1734
|
+
return parts.map((part) => {
|
|
1735
|
+
if (part.length === 0) return part;
|
|
1736
|
+
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
1737
|
+
}).join(" ");
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Determines if a value should use dash formatting based on structure
|
|
1741
|
+
* Uses heuristics: length of first part, total parts, etc.
|
|
1742
|
+
*/
|
|
1743
|
+
function shouldUseDashes(parts) {
|
|
1744
|
+
if (parts.length < 2) return false;
|
|
1745
|
+
const firstPart = parts[0];
|
|
1746
|
+
if (!firstPart) return false;
|
|
1747
|
+
if (firstPart.length <= 2) return true;
|
|
1748
|
+
if (firstPart.length >= 3 && firstPart.length <= 8 && parts.length >= 2) return true;
|
|
1749
|
+
if (parts.length >= 3) return true;
|
|
1750
|
+
return parts.length >= 2;
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Formats a value with dashes - intelligently splits based on structure
|
|
1754
|
+
*
|
|
1755
|
+
* Examples:
|
|
1756
|
+
* - "order_signed" -> "Order - Signed"
|
|
1757
|
+
* - "closed_lost" -> "Closed - Lost"
|
|
1758
|
+
* - "active_in_term" -> "Active - In Term"
|
|
1759
|
+
* - "on_hold_broker" -> "On Hold - Broker"
|
|
1760
|
+
*/
|
|
1761
|
+
function formatWithDashes(value) {
|
|
1762
|
+
const parts = normalizeInput(value).split("_").filter((part) => part.length > 0);
|
|
1763
|
+
if (parts.length < 2) return snakeCaseToTitleCase(value);
|
|
1764
|
+
const firstPart = parts[0];
|
|
1765
|
+
const secondPart = parts[1];
|
|
1766
|
+
if (!firstPart) return snakeCaseToTitleCase(value);
|
|
1767
|
+
if (parts.length === 2) {
|
|
1768
|
+
if (!secondPart) return snakeCaseToTitleCase(value);
|
|
1769
|
+
return `${snakeCaseToTitleCase(firstPart)} - ${snakeCaseToTitleCase(secondPart)}`;
|
|
1770
|
+
}
|
|
1771
|
+
if (firstPart.length <= 3) return `${snakeCaseToTitleCase(firstPart)} - ${snakeCaseToTitleCase(parts.slice(1).join("_"))}`;
|
|
1772
|
+
if (secondPart && firstPart.length > secondPart.length) return `${snakeCaseToTitleCase(firstPart)} - ${snakeCaseToTitleCase(parts.slice(1).join("_"))}`;
|
|
1773
|
+
else return `${snakeCaseToTitleCase(parts.slice(0, 2).join("_"))} - ${snakeCaseToTitleCase(parts.slice(2).join("_"))}`;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Converts an enum value to a display label
|
|
1777
|
+
* Uses intelligent formatting based on structure
|
|
1778
|
+
*
|
|
1779
|
+
* @param value - The enum value in snake_case (or any case)
|
|
1780
|
+
* @param customOverrides - Optional map of custom overrides for specific values
|
|
1781
|
+
* @returns The formatted display label
|
|
1782
|
+
*/
|
|
1783
|
+
function enumToDisplayLabel(value, customOverrides) {
|
|
1784
|
+
if (!value || typeof value !== "string") return String(value || "");
|
|
1785
|
+
const normalized = normalizeInput(value);
|
|
1786
|
+
if (customOverrides) {
|
|
1787
|
+
if (customOverrides[value]) return customOverrides[value];
|
|
1788
|
+
if (customOverrides[normalized]) return customOverrides[normalized];
|
|
1789
|
+
}
|
|
1790
|
+
const parts = normalized.split("_").filter((part) => part.length > 0);
|
|
1791
|
+
if (parts.length === 0) return value;
|
|
1792
|
+
if (shouldUseDashes(parts)) return formatWithDashes(value);
|
|
1793
|
+
return snakeCaseToTitleCase(value);
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Creates a label map for an array of enum values
|
|
1797
|
+
*
|
|
1798
|
+
* @param enumValues - Array of enum values
|
|
1799
|
+
* @param customOverrides - Optional map of custom overrides for specific values
|
|
1800
|
+
* @returns Record mapping enum values to display labels
|
|
1801
|
+
*/
|
|
1802
|
+
function createEnumLabelMap(enumValues, customOverrides) {
|
|
1803
|
+
const labelMap = {};
|
|
1804
|
+
for (const value of enumValues) labelMap[value] = enumToDisplayLabel(value, customOverrides);
|
|
1805
|
+
return labelMap;
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Converts an enum label map to select options for dropdowns
|
|
1809
|
+
*
|
|
1810
|
+
* @param labelMap - Record mapping enum values to display labels
|
|
1811
|
+
* @returns Array of select options
|
|
1812
|
+
*/
|
|
1813
|
+
function createSelectOptionsFromLabelMap(labelMap) {
|
|
1814
|
+
return Object.entries(labelMap).map(([value, label]) => ({
|
|
1815
|
+
value,
|
|
1816
|
+
label
|
|
1817
|
+
}));
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
//#endregion
|
|
1821
|
+
//#region src/utils/file.ts
|
|
1822
|
+
/**
|
|
1823
|
+
* File utility functions for sanitizing and processing file names.
|
|
1824
|
+
*/
|
|
1825
|
+
/**
|
|
1826
|
+
* Sanitizes a file name by replacing non-alphanumeric characters with underscores.
|
|
1827
|
+
* Provides several improvements over basic sanitization:
|
|
1828
|
+
* - Converts dashes to underscores first (to avoid mixed dash/underscore patterns)
|
|
1829
|
+
* - Collapses multiple consecutive underscores
|
|
1830
|
+
* - Trims leading/trailing underscores
|
|
1831
|
+
* - Handles files without extensions properly
|
|
1832
|
+
* - Sanitizes the extension too
|
|
1833
|
+
* - Enforces 800-byte limit for R2 compatibility
|
|
1834
|
+
* - Provides fallback for completely invalid names
|
|
1835
|
+
*
|
|
1836
|
+
* This function enforces an 800-byte limit to comply with Cloudflare R2's 1,024 byte
|
|
1837
|
+
* object key limit (leaving room for path overhead).
|
|
1838
|
+
*
|
|
1839
|
+
* @param fileName - The original file name to sanitize
|
|
1840
|
+
* @returns The sanitized file name with underscores replacing non-alphanumeric characters
|
|
1841
|
+
*
|
|
1842
|
+
* @example
|
|
1843
|
+
* ```typescript
|
|
1844
|
+
* sanitizeFileName('Sun Life Sheet 2021-10-13.xls')
|
|
1845
|
+
* // Returns: 'Sun_Life_Sheet_2021_10_13.xls'
|
|
1846
|
+
*
|
|
1847
|
+
* sanitizeFileName('my-file@2023.pdf')
|
|
1848
|
+
* // Returns: 'my_file_2023.pdf'
|
|
1849
|
+
*
|
|
1850
|
+
* sanitizeFileName('document.txt')
|
|
1851
|
+
* // Returns: 'document.txt'
|
|
1852
|
+
*
|
|
1853
|
+
* sanitizeFileName('file---name.txt')
|
|
1854
|
+
* // Returns: 'file_name.txt' (collapsed underscores)
|
|
1855
|
+
*
|
|
1856
|
+
* sanitizeFileName('file-name_test.txt')
|
|
1857
|
+
* // Returns: 'file_name_test.txt' (dashes converted first, no mixed patterns)
|
|
1858
|
+
*
|
|
1859
|
+
* sanitizeFileName('!!!file.txt')
|
|
1860
|
+
* // Returns: 'file.txt' (trimmed underscores)
|
|
1861
|
+
*
|
|
1862
|
+
* sanitizeFileName('')
|
|
1863
|
+
* // Returns: 'unnamed_file' (fallback)
|
|
1864
|
+
* ```
|
|
1865
|
+
*/
|
|
1866
|
+
function sanitizeFileName(fileName) {
|
|
1867
|
+
if (!fileName || fileName.trim() === "") return "unnamed_file";
|
|
1868
|
+
const lastDotIndex = fileName.lastIndexOf(".");
|
|
1869
|
+
const hasExtension = lastDotIndex > 0;
|
|
1870
|
+
let name = hasExtension ? fileName.substring(0, lastDotIndex) : fileName;
|
|
1871
|
+
let extension = hasExtension ? fileName.substring(lastDotIndex + 1) : "";
|
|
1872
|
+
const sanitize = (str) => str.replace(/-/g, "_").replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
1873
|
+
name = sanitize(name);
|
|
1874
|
+
extension = sanitize(extension);
|
|
1875
|
+
if (!name) name = "file";
|
|
1876
|
+
const sanitized = extension ? `${name}.${extension}` : name;
|
|
1877
|
+
const encoder = new TextEncoder();
|
|
1878
|
+
if (encoder.encode(sanitized).length > 800) {
|
|
1879
|
+
const maxNameBytes = 800 - (extension ? encoder.encode(`.${extension}`).length : 0);
|
|
1880
|
+
let truncatedName = "";
|
|
1881
|
+
let currentBytes = 0;
|
|
1882
|
+
for (const char of name) {
|
|
1883
|
+
const charBytes = encoder.encode(char).length;
|
|
1884
|
+
if (currentBytes + charBytes > maxNameBytes) break;
|
|
1885
|
+
truncatedName += char;
|
|
1886
|
+
currentBytes += charBytes;
|
|
1887
|
+
}
|
|
1888
|
+
return extension ? `${truncatedName}.${extension}` : truncatedName;
|
|
1889
|
+
}
|
|
1890
|
+
return sanitized;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
//#endregion
|
|
1894
|
+
//#region src/utils/money.ts
|
|
1895
|
+
/**
|
|
1896
|
+
* Money calculation utilities using strings for storage and numbers for calculations.
|
|
1897
|
+
* All monetary values are stored as strings.
|
|
1898
|
+
*/
|
|
1899
|
+
/**
|
|
1900
|
+
* Adds two or more monetary values
|
|
1901
|
+
* @param values Monetary values as strings
|
|
1902
|
+
* @returns Sum as string
|
|
1903
|
+
*/
|
|
1904
|
+
const addMoney = (...values) => values.reduce((sum, value) => sum + Number(value), 0).toString();
|
|
1905
|
+
/**
|
|
1906
|
+
* Subtracts monetary values from the first value
|
|
1907
|
+
* @param minuend Value to subtract from
|
|
1908
|
+
* @param subtrahends Values to subtract
|
|
1909
|
+
* @returns Difference as string
|
|
1910
|
+
*/
|
|
1911
|
+
const subtractMoney = (minuend, ...subtrahends) => subtrahends.reduce((diff, value) => diff - Number(value), Number(minuend)).toString();
|
|
1912
|
+
/**
|
|
1913
|
+
* Multiplies a monetary value by a number
|
|
1914
|
+
* @param amount Amount as string
|
|
1915
|
+
* @param multiplier Number to multiply by
|
|
1916
|
+
* @returns Product as string
|
|
1917
|
+
*/
|
|
1918
|
+
const multiplyMoney = (amount, multiplier) => (Number(amount) * multiplier).toString();
|
|
1919
|
+
/**
|
|
1920
|
+
* Divides a monetary value by a number
|
|
1921
|
+
* @param amount Amount as string
|
|
1922
|
+
* @param divisor Number to divide by
|
|
1923
|
+
* @returns Quotient as string
|
|
1924
|
+
*/
|
|
1925
|
+
const divideMoney = (amount, divisor) => (Number(amount) / divisor).toString();
|
|
1926
|
+
/**
|
|
1927
|
+
* Applies a percentage to a monetary value
|
|
1928
|
+
* @param amount Amount as string
|
|
1929
|
+
* @param percentage Percentage as decimal (e.g., 0.15 for 15%)
|
|
1930
|
+
* @returns Result as string
|
|
1931
|
+
*/
|
|
1932
|
+
const applyPercentage = (amount, percentage) => multiplyMoney(amount, 1 + percentage);
|
|
1933
|
+
/**
|
|
1934
|
+
* Rounds a monetary value
|
|
1935
|
+
* @param amount Amount to round
|
|
1936
|
+
* @returns Rounded amount as string
|
|
1937
|
+
*/
|
|
1938
|
+
const roundMoney = (amount) => (Math.round(Number(amount) * 100) / 100).toString();
|
|
1939
|
+
/**
|
|
1940
|
+
* Formats a number as currency
|
|
1941
|
+
* @param value Number to format
|
|
1942
|
+
* @returns Formatted currency string
|
|
1943
|
+
*/
|
|
1944
|
+
const formatCurrency = (value) => {
|
|
1945
|
+
return new Intl.NumberFormat("en-US", {
|
|
1946
|
+
style: "currency",
|
|
1947
|
+
currency: "USD"
|
|
1948
|
+
}).format(value);
|
|
1949
|
+
};
|
|
1950
|
+
/**
|
|
1951
|
+
* Formats a dollar amount as currency
|
|
1952
|
+
* @param amount Amount as string
|
|
1953
|
+
* @returns Formatted currency string
|
|
1954
|
+
*/
|
|
1955
|
+
const formatDollar = (amount) => {
|
|
1956
|
+
if (amount === null || amount === void 0) return "$0.00";
|
|
1957
|
+
return new Intl.NumberFormat("en-US", {
|
|
1958
|
+
style: "currency",
|
|
1959
|
+
currency: "USD"
|
|
1960
|
+
}).format(Number(amount));
|
|
1961
|
+
};
|
|
1962
|
+
|
|
1963
|
+
//#endregion
|
|
1964
|
+
export { AddCreditsSchema, ApproveSupportTicketSchema, ArchiveSupportTicketSchema, AttachmentCreateSchema, AttachmentFiltersSchema, AttachmentFiltersSchema as attachmentFilters_zod, AttachmentFolderCreateSchema, AttachmentFolderReadSchema, AttachmentFolderUpdateSchema, AttachmentPageSchema, AttachmentPageSchema as attachmentPage_zod, AttachmentReadSchema, AttachmentReadSchema as attachment_zod, AttachmentUpdateSchema, BaseFilterSchema, BooleanFilterSchema, CancelInternalTaskSchema, CompleteSupportTicketSchema, CreditBalanceSchema, CreditTransactionFiltersSchema, CreditTransactionPageSchema, CreditTransactionReadSchema, CreditTransactionTypeEnum, CustomerSupportTicketCreateSchema, CustomerSupportTicketFiltersSchema, CustomerSupportTicketPageSchema, CustomerSupportTicketReadSchema, CustomerSupportTicketUpdateSchema, DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_ERROR_MESSAGE, DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE, DECIMAL_AMOUNT_REGEX, DEFAULT_USER_TYPE, DataTypeSchema, DateFilterSchema, DateOperatorSchema, DeleteSupportTicketSchema, EqualityArrayOperatorSchema, EqualityOperatorSchema, FilterConfigSchema, MAX_PRESETS_PER_CONTEXT, NoteCreateSchema, NoteFiltersSchema, NoteFiltersSortDirectionEnum, NoteReadSchema, NoteUpdateSchema, NumberFilterSchema, NumberOperatorSchema, OPERATORS, PageInfoSchema, PaginationFiltersSchema, ReactivateInternalTaskSchema, RecordConst, RecordSubscriberCreateSchema, RecordSubscriberReadSchema, RecordSubscriberUpdateSchema, RecordTypeValues, RejectSupportTicketSchema, ResetMonthlyBalanceSchema, RevertSupportTicketSchema, SUPPORT_TICKET_APPROVAL_FILTER_OPTIONS, SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS, SUPPORT_TICKET_NUMBER_TO_PRIORITY, SUPPORT_TICKET_PRIORITY_FILTER_OPTIONS, SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL, SUPPORT_TICKET_PRIORITY_TO_NUMBER, SUPPORT_TICKET_STATUS_FILTER_OPTIONS, SUPPORT_TICKET_TYPE_FILTER_OPTIONS, SavedFilterCreateSchema, SavedFilterReadSchema, SavedFilterUpdateSchema, SetMonthlyAllocationSchema, SortDirectionSchema, StaffSupportTicketCreateSchema, StaffSupportTicketFiltersSchema, StaffSupportTicketInputSchema, StaffSupportTicketPageSchema, StaffSupportTicketReadSchema, StaffSupportTicketUpdateSchema, StringFilterSchema, StringOperatorSchema, SupportTicketApprovalEnum, SupportTicketApprovalFilterSchema, SupportTicketApprovalSchema, SupportTicketDevLifecycleEnum, SupportTicketDevLifecycleFilterSchema, SupportTicketDevLifecycleSchema, SupportTicketDevLifecycleUpdateEnum, SupportTicketDevLifecycleUpdateSchema, SupportTicketEventTypeEnum, SupportTicketEventTypeValues, SupportTicketPriorityEnum, SupportTicketPriorityFilterSchema, SupportTicketPriorityNumberEnum, SupportTicketPriorityNumberSchema, SupportTicketPrioritySchema, SupportTicketRecordDataSchema, SupportTicketStatusEnum, SupportTicketStatusFilterSchema, SupportTicketStatusSchema, SupportTicketSubscriberCreateSchema, SupportTicketTypeEnum, SupportTicketTypeFilterSchema, SupportTicketTypeSchema, TeamCreateSchema, TeamFiltersSchema, TeamInputBaseSchema, TeamMemberCreateSchema, TeamMemberFiltersSchema, TeamMemberOptionSchema, TeamMemberReadSchema, TeamMemberRoleEnum, TeamMemberRoleFilterSchema, TeamMemberRoleSchema, TeamMemberUpdateSchema, TeamOptionSchema, TeamPageSchema, TeamReadSchema, TeamStatusSchema, TeamUpdateSchema, USER_TYPES, UserProfileBaseSchema, UserProfileReadSchema, UserProfileUpdateSchema, UserReadSchema, UserTeamMembersSchema, UserTeamsSchema, UserTypeEnum, UserUpdateSchema, addMoney, applyPercentage, changePasswordSchema, createEnumFilter, createEnumLabelMap, createPaginatedSchema, createSelectOptionsFromLabelMap, createUserSchema, createUserSchemaOutput, divideMoney, enumToDisplayLabel, forgot_password_zod, formatCurrency, formatDollar, isCommonPassword, loginResponseSchema, loginSchema, multiplyMoney, pageInfoSchema, passwordSchema, recordVersionFiltersBreadcrumbSchema, recordVersionFiltersInputBreadcrumbSchema, recordVersionFiltersInputSchema, recordVersionFiltersSchema, recordVersionPageBreadcrumbSchema, recordVersionPageSchema, recordVersionSchema, recordVersionTrackerActivityInputSchema, resetPasswordInputSchema, resetPasswordSchema, roundMoney, sanitizeFileName, signupSchema, subtractMoney, supportTicketNumberToPriority, supportTicketPriorityToNumber, userSessionSchema };
|
|
1965
|
+
//# sourceMappingURL=index.mjs.map
|