Rebrand from Forte (music-store-specific) to LunarFront (any small business): - Package namespace @forte/* → @lunarfront/* - Database forte/forte_test → lunarfront/lunarfront_test - Docker containers, volumes, connection strings - UI branding, localStorage keys, test emails - All documentation and planning docs Generalize music-specific terminology: - instrumentDescription → itemDescription - instrumentCount → itemCount - instrumentType → itemCategory (on service templates) - New migration 0027_generalize_terminology for column renames - Seed data updated with generic examples - RBAC descriptions updated
127 lines
4.9 KiB
TypeScript
127 lines
4.9 KiB
TypeScript
import { z } from 'zod'
|
|
|
|
/** Coerce empty strings to undefined — solves HTML form inputs sending '' for blank optional fields */
|
|
function opt<T extends z.ZodTypeAny>(schema: T) {
|
|
return z.preprocess((v) => (v === '' ? undefined : v), schema.optional())
|
|
}
|
|
|
|
// --- Status / Type enums ---
|
|
|
|
export const RepairTicketStatus = z.enum([
|
|
'new', 'in_transit', 'intake', 'diagnosing', 'pending_approval', 'approved',
|
|
'in_progress', 'pending_parts', 'ready', 'picked_up', 'delivered', 'cancelled',
|
|
])
|
|
export type RepairTicketStatus = z.infer<typeof RepairTicketStatus>
|
|
|
|
export const RepairLineItemType = z.enum(['labor', 'part', 'flat_rate', 'misc'])
|
|
export type RepairLineItemType = z.infer<typeof RepairLineItemType>
|
|
|
|
export const RepairConditionIn = z.enum(['excellent', 'good', 'fair', 'poor'])
|
|
export type RepairConditionIn = z.infer<typeof RepairConditionIn>
|
|
|
|
export const RepairBatchStatus = z.enum([
|
|
'intake', 'in_progress', 'pending_approval', 'approved', 'completed', 'delivered', 'cancelled',
|
|
])
|
|
export type RepairBatchStatus = z.infer<typeof RepairBatchStatus>
|
|
|
|
export const RepairBatchApproval = z.enum(['pending', 'approved', 'rejected'])
|
|
export type RepairBatchApproval = z.infer<typeof RepairBatchApproval>
|
|
|
|
// --- Repair Ticket schemas ---
|
|
|
|
export const RepairTicketCreateSchema = z.object({
|
|
customerName: z.string().min(1).max(255),
|
|
customerPhone: opt(z.string().max(50)),
|
|
accountId: opt(z.string().uuid()),
|
|
locationId: opt(z.string().uuid()),
|
|
repairBatchId: opt(z.string().uuid()),
|
|
inventoryUnitId: opt(z.string().uuid()),
|
|
itemDescription: opt(z.string()),
|
|
serialNumber: opt(z.string().max(255)),
|
|
conditionIn: opt(RepairConditionIn),
|
|
conditionInNotes: opt(z.string()),
|
|
problemDescription: z.string().min(1),
|
|
technicianNotes: opt(z.string()),
|
|
assignedTechnicianId: opt(z.string().uuid()),
|
|
estimatedCost: z.coerce.number().min(0).optional(),
|
|
promisedDate: opt(z.string()),
|
|
})
|
|
export type RepairTicketCreateInput = z.infer<typeof RepairTicketCreateSchema>
|
|
|
|
export const RepairTicketUpdateSchema = RepairTicketCreateSchema.partial()
|
|
export type RepairTicketUpdateInput = z.infer<typeof RepairTicketUpdateSchema>
|
|
|
|
export const RepairTicketStatusUpdateSchema = z.object({
|
|
status: RepairTicketStatus,
|
|
})
|
|
export type RepairTicketStatusUpdateInput = z.infer<typeof RepairTicketStatusUpdateSchema>
|
|
|
|
// --- Repair Line Item schemas ---
|
|
|
|
export const RepairLineItemCreateSchema = z.object({
|
|
repairTicketId: z.string().uuid(),
|
|
itemType: RepairLineItemType,
|
|
description: z.string().min(1).max(255),
|
|
productId: opt(z.string().uuid()),
|
|
qty: z.coerce.number().min(0).default(1),
|
|
unitPrice: z.coerce.number().min(0).default(0),
|
|
totalPrice: z.coerce.number().min(0).default(0),
|
|
cost: z.coerce.number().min(0).optional(),
|
|
technicianId: opt(z.string().uuid()),
|
|
})
|
|
export type RepairLineItemCreateInput = z.infer<typeof RepairLineItemCreateSchema>
|
|
|
|
export const RepairLineItemUpdateSchema = RepairLineItemCreateSchema.omit({ repairTicketId: true }).partial()
|
|
export type RepairLineItemUpdateInput = z.infer<typeof RepairLineItemUpdateSchema>
|
|
|
|
// --- Repair Batch schemas ---
|
|
|
|
export const RepairBatchCreateSchema = z.object({
|
|
accountId: z.string().uuid(),
|
|
locationId: opt(z.string().uuid()),
|
|
contactName: opt(z.string().max(255)),
|
|
contactPhone: opt(z.string().max(50)),
|
|
contactEmail: opt(z.string().email()),
|
|
pickupDate: opt(z.string()),
|
|
dueDate: opt(z.string()),
|
|
itemCount: z.coerce.number().int().min(0).default(0),
|
|
notes: opt(z.string()),
|
|
})
|
|
export type RepairBatchCreateInput = z.infer<typeof RepairBatchCreateSchema>
|
|
|
|
export const RepairBatchUpdateSchema = RepairBatchCreateSchema.partial()
|
|
export type RepairBatchUpdateInput = z.infer<typeof RepairBatchUpdateSchema>
|
|
|
|
export const RepairBatchStatusUpdateSchema = z.object({
|
|
status: RepairBatchStatus,
|
|
})
|
|
export type RepairBatchStatusUpdateInput = z.infer<typeof RepairBatchStatusUpdateSchema>
|
|
|
|
// --- Repair Note schemas ---
|
|
|
|
export const RepairNoteVisibility = z.enum(['internal', 'customer'])
|
|
export type RepairNoteVisibility = z.infer<typeof RepairNoteVisibility>
|
|
|
|
export const RepairNoteCreateSchema = z.object({
|
|
content: z.string().min(1),
|
|
visibility: RepairNoteVisibility.default('internal'),
|
|
})
|
|
export type RepairNoteCreateInput = z.infer<typeof RepairNoteCreateSchema>
|
|
|
|
// --- Repair Service Template schemas ---
|
|
|
|
export const RepairServiceTemplateCreateSchema = z.object({
|
|
name: z.string().min(1).max(255),
|
|
itemCategory: opt(z.string().max(100)),
|
|
size: opt(z.string().max(50)),
|
|
description: opt(z.string()),
|
|
itemType: RepairLineItemType.default('flat_rate'),
|
|
defaultPrice: z.coerce.number().min(0).default(0),
|
|
defaultCost: z.coerce.number().min(0).optional(),
|
|
sortOrder: z.coerce.number().int().default(0),
|
|
})
|
|
export type RepairServiceTemplateCreateInput = z.infer<typeof RepairServiceTemplateCreateSchema>
|
|
|
|
export const RepairServiceTemplateUpdateSchema = RepairServiceTemplateCreateSchema.partial()
|
|
export type RepairServiceTemplateUpdateInput = z.infer<typeof RepairServiceTemplateUpdateSchema>
|