feat: rework EOL, repairs, and hosts for real workflow
Four domain-model changes driven by exercising the deployed 2.0 build: - EOL moves from manufacturer to MPN via new PartModel catalog table, so alerts fire on the thing that actually ages. - Repairs re-home to Host (required hostId + problem text) with an optional RepairJobPart join for affected parts; drop Part.replacementPartId. - New /repairs/:id detail page with editable problem, part list, and a RepairComment thread (REPAIR_COMMENTED events fan out to each problem part's timeline). - Host.assetId (required, unique) surfaces prominently on the repair page so techs can confirm they're touching the right box. Single destructive migration reshapes existing dev data. All 7 packages typecheck clean; 30 API tests pass (9 new covering host membership, upsertByMpn idempotency + race, assetId 409, comment userId stamping). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,9 @@ import { RepairStatus } from './enums.js';
|
||||
import { PaginationQuery } from './pagination.js';
|
||||
|
||||
export const CreateRepairJobRequest = z.object({
|
||||
partId: z.string().uuid(),
|
||||
hostId: z.string().uuid().optional().nullable(),
|
||||
hostId: z.string().uuid(),
|
||||
problem: z.string().trim().min(1, 'Problem is required').max(2000),
|
||||
problemPartIds: z.array(z.string().uuid()).max(100).optional(),
|
||||
assigneeId: z.string().uuid().optional().nullable(),
|
||||
notes: z.string().max(4096).optional().nullable(),
|
||||
});
|
||||
@@ -13,7 +14,8 @@ export type CreateRepairJobRequest = z.infer<typeof CreateRepairJobRequest>;
|
||||
export const UpdateRepairJobRequest = z
|
||||
.object({
|
||||
status: RepairStatus.optional(),
|
||||
hostId: z.string().uuid().nullable().optional(),
|
||||
problem: z.string().trim().min(1).max(2000).optional(),
|
||||
problemPartIds: z.array(z.string().uuid()).max(100).optional(),
|
||||
assigneeId: z.string().uuid().nullable().optional(),
|
||||
notes: z.string().max(4096).nullable().optional(),
|
||||
})
|
||||
@@ -22,8 +24,8 @@ export type UpdateRepairJobRequest = z.infer<typeof UpdateRepairJobRequest>;
|
||||
|
||||
export const RepairJobListQuery = PaginationQuery.extend({
|
||||
status: RepairStatus.optional(),
|
||||
partId: z.string().uuid().optional(),
|
||||
hostId: z.string().uuid().optional(),
|
||||
problemPartId: z.string().uuid().optional(),
|
||||
assigneeId: z.string().uuid().optional(),
|
||||
openOnly: z
|
||||
.union([z.literal('true'), z.literal('false'), z.boolean()])
|
||||
@@ -31,3 +33,11 @@ export const RepairJobListQuery = PaginationQuery.extend({
|
||||
.optional(),
|
||||
});
|
||||
export type RepairJobListQuery = z.infer<typeof RepairJobListQuery>;
|
||||
|
||||
export const CreateRepairCommentRequest = z.object({
|
||||
content: z.string().trim().min(1, 'Comment cannot be empty').max(4000),
|
||||
});
|
||||
export type CreateRepairCommentRequest = z.infer<typeof CreateRepairCommentRequest>;
|
||||
|
||||
export const RepairCommentListQuery = PaginationQuery;
|
||||
export type RepairCommentListQuery = z.infer<typeof RepairCommentListQuery>;
|
||||
|
||||
Reference in New Issue
Block a user