238 lines
6.3 KiB
TypeScript
238 lines
6.3 KiB
TypeScript
import { z } from "zod";
|
|
|
|
// ---- shared --------------------------------------------------------------
|
|
|
|
const NonEmpty = z.string().trim().min(1, "Required").max(200);
|
|
const Code = z.string().trim().min(1).max(64).regex(/^[A-Za-z0-9._\-/]+$/, "Use letters, digits, . _ - /");
|
|
const OptionalText = z
|
|
.string()
|
|
.trim()
|
|
.max(5000)
|
|
.transform((v) => (v.length === 0 ? null : v))
|
|
.nullable()
|
|
.optional();
|
|
const JsonString = z
|
|
.string()
|
|
.max(10_000)
|
|
.refine(
|
|
(s) => {
|
|
if (s.length === 0) return true;
|
|
try {
|
|
JSON.parse(s);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
{ message: "Must be valid JSON" },
|
|
)
|
|
.transform((s) => (s.length === 0 ? null : s))
|
|
.nullable()
|
|
.optional();
|
|
|
|
const Pin = z.string().regex(/^\d{4}$/, "PIN must be exactly 4 digits");
|
|
|
|
// ---- users ---------------------------------------------------------------
|
|
|
|
export const CreateAdminSchema = z.object({
|
|
role: z.literal("admin"),
|
|
name: NonEmpty,
|
|
email: z.string().email().max(200),
|
|
password: z.string().min(8).max(200),
|
|
});
|
|
|
|
export const CreateOperatorSchema = z.object({
|
|
role: z.literal("operator"),
|
|
name: NonEmpty,
|
|
pin: Pin,
|
|
});
|
|
|
|
export const CreateUserSchema = z.discriminatedUnion("role", [
|
|
CreateAdminSchema,
|
|
CreateOperatorSchema,
|
|
]);
|
|
|
|
export const UpdateUserSchema = z
|
|
.object({
|
|
name: NonEmpty.optional(),
|
|
active: z.boolean().optional(),
|
|
email: z.string().email().max(200).optional(),
|
|
password: z.string().min(8).max(200).optional(),
|
|
pin: Pin.optional(),
|
|
})
|
|
.strict();
|
|
|
|
// ---- machines ------------------------------------------------------------
|
|
|
|
export const MachineKinds = [
|
|
"NCT_PUNCH",
|
|
"PRESS_BRAKE",
|
|
"RIVET",
|
|
"WELD",
|
|
"LASER",
|
|
"SHEAR",
|
|
"ASSEMBLY",
|
|
"OTHER",
|
|
] as const;
|
|
|
|
export const CreateMachineSchema = z.object({
|
|
name: NonEmpty,
|
|
kind: z.enum(MachineKinds),
|
|
location: OptionalText,
|
|
notes: OptionalText,
|
|
});
|
|
|
|
export const UpdateMachineSchema = z
|
|
.object({
|
|
name: NonEmpty.optional(),
|
|
kind: z.enum(MachineKinds).optional(),
|
|
location: OptionalText,
|
|
notes: OptionalText,
|
|
active: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
// ---- operation templates -------------------------------------------------
|
|
|
|
export const CreateTemplateSchema = z.object({
|
|
name: NonEmpty,
|
|
machineId: z.string().min(1).nullable().optional(),
|
|
defaultSettings: JsonString,
|
|
defaultInstructions: OptionalText,
|
|
qcRequired: z.boolean().default(false),
|
|
});
|
|
|
|
export const UpdateTemplateSchema = z
|
|
.object({
|
|
name: NonEmpty.optional(),
|
|
machineId: z.string().min(1).nullable().optional(),
|
|
defaultSettings: JsonString,
|
|
defaultInstructions: OptionalText,
|
|
qcRequired: z.boolean().optional(),
|
|
active: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
// ---- projects ------------------------------------------------------------
|
|
|
|
export const ProjectStatuses = ["planning", "in_progress", "completed", "cancelled"] as const;
|
|
|
|
export const CreateProjectSchema = z.object({
|
|
code: Code,
|
|
name: NonEmpty,
|
|
customerCode: z
|
|
.string()
|
|
.trim()
|
|
.max(64)
|
|
.transform((v) => (v.length === 0 ? null : v))
|
|
.nullable()
|
|
.optional(),
|
|
dueDate: z.coerce.date().nullable().optional(),
|
|
notes: OptionalText,
|
|
});
|
|
|
|
export const UpdateProjectSchema = z
|
|
.object({
|
|
code: Code.optional(),
|
|
name: NonEmpty.optional(),
|
|
customerCode: z
|
|
.string()
|
|
.trim()
|
|
.max(64)
|
|
.transform((v) => (v.length === 0 ? null : v))
|
|
.nullable()
|
|
.optional(),
|
|
dueDate: z.coerce.date().nullable().optional(),
|
|
status: z.enum(ProjectStatuses).optional(),
|
|
notes: OptionalText,
|
|
})
|
|
.strict();
|
|
|
|
// ---- assemblies / parts --------------------------------------------------
|
|
|
|
export const CreateAssemblySchema = z.object({
|
|
code: Code,
|
|
name: NonEmpty,
|
|
qty: z.coerce.number().int().positive().max(100000).default(1),
|
|
notes: OptionalText,
|
|
});
|
|
|
|
export const UpdateAssemblySchema = z
|
|
.object({
|
|
code: Code.optional(),
|
|
name: NonEmpty.optional(),
|
|
qty: z.coerce.number().int().positive().max(100000).optional(),
|
|
notes: OptionalText,
|
|
})
|
|
.strict();
|
|
|
|
export const CreatePartSchema = z.object({
|
|
code: Code,
|
|
name: NonEmpty,
|
|
material: z
|
|
.string()
|
|
.trim()
|
|
.max(120)
|
|
.transform((v) => (v.length === 0 ? null : v))
|
|
.nullable()
|
|
.optional(),
|
|
qty: z.coerce.number().int().positive().max(100000).default(1),
|
|
notes: OptionalText,
|
|
});
|
|
|
|
export const UpdatePartSchema = z
|
|
.object({
|
|
code: Code.optional(),
|
|
name: NonEmpty.optional(),
|
|
material: z
|
|
.string()
|
|
.trim()
|
|
.max(120)
|
|
.transform((v) => (v.length === 0 ? null : v))
|
|
.nullable()
|
|
.optional(),
|
|
qty: z.coerce.number().int().positive().max(100000).optional(),
|
|
notes: OptionalText,
|
|
stepFileId: z.string().min(1).nullable().optional(),
|
|
drawingFileId: z.string().min(1).nullable().optional(),
|
|
cutFileId: z.string().min(1).nullable().optional(),
|
|
})
|
|
.strict();
|
|
|
|
// ---- operations ---------------------------------------------------------
|
|
|
|
export const OperationStatuses = ["pending", "in_progress", "completed"] as const;
|
|
|
|
export const CreateOperationSchema = z.object({
|
|
templateId: z.string().min(1).nullable().optional(),
|
|
name: NonEmpty,
|
|
machineId: z.string().min(1).nullable().optional(),
|
|
settings: JsonString,
|
|
materialNotes: OptionalText,
|
|
instructions: OptionalText,
|
|
qcRequired: z.boolean().default(false),
|
|
plannedMinutes: z.coerce.number().int().positive().max(100000).nullable().optional(),
|
|
plannedUnits: z.coerce.number().int().positive().max(100000).nullable().optional(),
|
|
sequence: z.coerce.number().int().positive().max(10000).optional(),
|
|
});
|
|
|
|
export const UpdateOperationSchema = z
|
|
.object({
|
|
templateId: z.string().min(1).nullable().optional(),
|
|
name: NonEmpty.optional(),
|
|
machineId: z.string().min(1).nullable().optional(),
|
|
settings: JsonString,
|
|
materialNotes: OptionalText,
|
|
instructions: OptionalText,
|
|
qcRequired: z.boolean().optional(),
|
|
plannedMinutes: z.coerce.number().int().positive().max(100000).nullable().optional(),
|
|
plannedUnits: z.coerce.number().int().positive().max(100000).nullable().optional(),
|
|
sequence: z.coerce.number().int().positive().max(10000).optional(),
|
|
status: z.enum(OperationStatuses).optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const ReorderOperationsSchema = z.object({
|
|
order: z.array(z.string().min(1)).min(1),
|
|
});
|