- Add pros/cons nullable TEXT columns to threadCandidates schema - Generate and apply Drizzle migration (0004_soft_synch.sql) - Mirror pros/cons columns in test helper CREATE TABLE - createCandidate: pass pros/cons to values() object - updateCandidate: add pros/cons to Partial type - getThreadWithCandidates: include pros/cons in select projection - createCandidateSchema: add optional pros/cons string fields
85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
import { z } from "zod";
|
|
|
|
export const createItemSchema = z.object({
|
|
name: z.string().min(1, "Name is required"),
|
|
weightGrams: z.number().nonnegative().optional(),
|
|
priceCents: z.number().int().nonnegative().optional(),
|
|
categoryId: z.number().int().positive(),
|
|
notes: z.string().optional(),
|
|
productUrl: z.string().url().optional().or(z.literal("")),
|
|
imageFilename: z.string().optional(),
|
|
});
|
|
|
|
export const updateItemSchema = createItemSchema.partial().extend({
|
|
id: z.number().int().positive(),
|
|
});
|
|
|
|
export const createCategorySchema = z.object({
|
|
name: z.string().min(1, "Category name is required"),
|
|
icon: z.string().min(1).max(50).default("package"),
|
|
});
|
|
|
|
export const updateCategorySchema = z.object({
|
|
id: z.number().int().positive(),
|
|
name: z.string().min(1).optional(),
|
|
icon: z.string().min(1).max(50).optional(),
|
|
});
|
|
|
|
// Thread schemas
|
|
export const createThreadSchema = z.object({
|
|
name: z.string().min(1, "Thread name is required"),
|
|
categoryId: z.number().int().positive(),
|
|
});
|
|
|
|
export const updateThreadSchema = z.object({
|
|
name: z.string().min(1).optional(),
|
|
categoryId: z.number().int().positive().optional(),
|
|
});
|
|
|
|
// Candidate status
|
|
export const candidateStatusSchema = z.enum([
|
|
"researching",
|
|
"ordered",
|
|
"arrived",
|
|
]);
|
|
|
|
// Candidate schemas (same fields as items)
|
|
export const createCandidateSchema = z.object({
|
|
name: z.string().min(1, "Name is required"),
|
|
weightGrams: z.number().nonnegative().optional(),
|
|
priceCents: z.number().int().nonnegative().optional(),
|
|
categoryId: z.number().int().positive(),
|
|
notes: z.string().optional(),
|
|
productUrl: z.string().url().optional().or(z.literal("")),
|
|
imageFilename: z.string().optional(),
|
|
status: candidateStatusSchema.optional(),
|
|
pros: z.string().optional(),
|
|
cons: z.string().optional(),
|
|
});
|
|
|
|
export const updateCandidateSchema = createCandidateSchema.partial();
|
|
|
|
export const resolveThreadSchema = z.object({
|
|
candidateId: z.number().int().positive(),
|
|
});
|
|
|
|
// Setup schemas
|
|
export const createSetupSchema = z.object({
|
|
name: z.string().min(1, "Setup name is required"),
|
|
});
|
|
|
|
export const updateSetupSchema = z.object({
|
|
name: z.string().min(1, "Setup name is required"),
|
|
});
|
|
|
|
export const syncSetupItemsSchema = z.object({
|
|
itemIds: z.array(z.number().int().positive()),
|
|
});
|
|
|
|
// Classification schemas
|
|
export const classificationSchema = z.enum(["base", "worn", "consumable"]);
|
|
|
|
export const updateClassificationSchema = z.object({
|
|
classification: classificationSchema,
|
|
});
|