Add 'new' status as default, in_transit becomes branch state

New tickets start as 'new' (just created, not yet examined). In Transit
is now a branch status off New for school pickups and shipped instruments.
Intake means the instrument has been physically received and documented.
Status progress bar, labels, filters, and default status all updated.
Removed debug logging from file upload endpoint.
This commit is contained in:
Ryan Moon
2026-03-29 11:45:41 -05:00
parent ba94adb8d7
commit b0379052d6
10 changed files with 28 additions and 9 deletions

View File

@@ -6,6 +6,7 @@ import { Label } from '@/components/ui/label'
import { Filter, X } from 'lucide-react' import { Filter, X } from 'lucide-react'
const ALL_STATUSES = [ const ALL_STATUSES = [
{ value: 'new', label: 'New' },
{ value: 'in_transit', label: 'In Transit' }, { value: 'in_transit', label: 'In Transit' },
{ value: 'intake', label: 'Intake' }, { value: 'intake', label: 'Intake' },
{ value: 'diagnosing', label: 'Diagnosing' }, { value: 'diagnosing', label: 'Diagnosing' },
@@ -19,7 +20,7 @@ const ALL_STATUSES = [
{ value: 'cancelled', label: 'Cancelled' }, { value: 'cancelled', label: 'Cancelled' },
] ]
const ACTIVE_STATUSES = ['in_transit', 'intake', 'diagnosing', 'pending_approval', 'approved', 'in_progress', 'pending_parts', 'ready'] const ACTIVE_STATUSES = ['new', 'in_transit', 'intake', 'diagnosing', 'pending_approval', 'approved', 'in_progress', 'pending_parts', 'ready']
const CONDITIONS = [ const CONDITIONS = [
{ value: 'excellent', label: 'Excellent' }, { value: 'excellent', label: 'Excellent' },

View File

@@ -1,7 +1,7 @@
import { Check, Truck, ClipboardList, Search, Clock, ThumbsUp, Wrench, Package, HandMetal, Ban } from 'lucide-react' import { Check, Truck, ClipboardList, Search, Clock, ThumbsUp, Wrench, Package, HandMetal, Ban, FilePlus } from 'lucide-react'
const STEPS = [ const STEPS = [
{ key: 'in_transit', label: 'In Transit', icon: Truck }, { key: 'new', label: 'New', icon: FilePlus },
{ key: 'intake', label: 'Intake', icon: ClipboardList }, { key: 'intake', label: 'Intake', icon: ClipboardList },
{ key: 'diagnosing', label: 'Diagnosing', icon: Search }, { key: 'diagnosing', label: 'Diagnosing', icon: Search },
{ key: 'pending_approval', label: 'Pending Approval', icon: Clock }, { key: 'pending_approval', label: 'Pending Approval', icon: Clock },
@@ -12,6 +12,7 @@ const STEPS = [
] as const ] as const
const BRANCH_STATUSES: Record<string, { label: string; parentStep: string }> = { const BRANCH_STATUSES: Record<string, { label: string; parentStep: string }> = {
in_transit: { label: 'In Transit', parentStep: 'new' },
pending_parts: { label: 'Pending Parts', parentStep: 'in_progress' }, pending_parts: { label: 'Pending Parts', parentStep: 'in_progress' },
delivered: { label: 'Delivered', parentStep: 'picked_up' }, delivered: { label: 'Delivered', parentStep: 'picked_up' },
} }

View File

@@ -112,10 +112,10 @@ export function TicketNotes({ ticketId }: TicketNotesProps) {
// Upload attached photos to the note // Upload attached photos to the note
for (const photo of photos) { for (const photo of photos) {
const formData = new FormData() const formData = new FormData()
formData.append('file', photo)
formData.append('entityType', 'repair_note') formData.append('entityType', 'repair_note')
formData.append('entityId', note.id) formData.append('entityId', note.id)
formData.append('category', 'attachment') formData.append('category', 'attachment')
formData.append('file', photo)
const uploadRes = await fetch('/v1/files', { const uploadRes = await fetch('/v1/files', {
method: 'POST', method: 'POST',
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },

View File

@@ -39,6 +39,7 @@ export const Route = createFileRoute('/_authenticated/repairs/$ticketId')({
}) })
const STATUS_LABELS: Record<string, string> = { const STATUS_LABELS: Record<string, string> = {
new: 'New',
in_transit: 'In Transit', in_transit: 'In Transit',
intake: 'Intake', intake: 'Intake',
diagnosing: 'Diagnosing', diagnosing: 'Diagnosing',
@@ -52,7 +53,7 @@ const STATUS_LABELS: Record<string, string> = {
cancelled: 'Cancelled', cancelled: 'Cancelled',
} }
const STATUS_FLOW = ['in_transit', 'intake', 'diagnosing', 'pending_approval', 'approved', 'in_progress', 'ready', 'picked_up'] const STATUS_FLOW = ['new', 'intake', 'diagnosing', 'pending_approval', 'approved', 'in_progress', 'ready', 'picked_up']
const TABS = [ const TABS = [
{ key: 'details', label: 'Details' }, { key: 'details', label: 'Details' },
@@ -217,6 +218,11 @@ function RepairTicketDetailPage() {
Move to {STATUS_LABELS[nextStatus]} Move to {STATUS_LABELS[nextStatus]}
</Button> </Button>
)} )}
{ticket.status === 'new' && (
<Button variant="secondary" onClick={() => statusMutation.mutate('in_transit')} disabled={statusMutation.isPending}>
In Transit
</Button>
)}
{ticket.status === 'in_progress' && ( {ticket.status === 'in_progress' && (
<Button variant="secondary" onClick={() => statusMutation.mutate('pending_parts')} disabled={statusMutation.isPending}> <Button variant="secondary" onClick={() => statusMutation.mutate('pending_parts')} disabled={statusMutation.isPending}>
Pending Parts Pending Parts
@@ -235,7 +241,7 @@ function RepairTicketDetailPage() {
</> </>
)} )}
{ticket.status === 'cancelled' && hasPermission('repairs.admin') && ( {ticket.status === 'cancelled' && hasPermission('repairs.admin') && (
<Button variant="outline" onClick={() => statusMutation.mutate('intake')} disabled={statusMutation.isPending}> <Button variant="outline" onClick={() => statusMutation.mutate('new')} disabled={statusMutation.isPending}>
<RotateCcw className="mr-2 h-4 w-4" />Reopen <RotateCcw className="mr-2 h-4 w-4" />Reopen
</Button> </Button>
)} )}

View File

@@ -26,6 +26,7 @@ export const Route = createFileRoute('/_authenticated/repairs/')({
function statusBadge(status: string) { function statusBadge(status: string) {
const variants: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = { const variants: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {
new: 'outline',
in_transit: 'secondary', in_transit: 'secondary',
intake: 'outline', intake: 'outline',
diagnosing: 'secondary', diagnosing: 'secondary',
@@ -39,6 +40,7 @@ function statusBadge(status: string) {
cancelled: 'destructive', cancelled: 'destructive',
} }
const labels: Record<string, string> = { const labels: Record<string, string> = {
new: 'New',
in_transit: 'In Transit', in_transit: 'In Transit',
intake: 'Intake', intake: 'Intake',
diagnosing: 'Diagnosing', diagnosing: 'Diagnosing',

View File

@@ -14,7 +14,7 @@ export interface RepairTicket {
conditionInNotes: string | null conditionInNotes: string | null
problemDescription: string problemDescription: string
technicianNotes: string | null technicianNotes: string | null
status: 'in_transit' | 'intake' | 'diagnosing' | 'pending_approval' | 'approved' | 'in_progress' | 'pending_parts' | 'ready' | 'picked_up' | 'delivered' | 'cancelled' status: 'new' | 'in_transit' | 'intake' | 'diagnosing' | 'pending_approval' | 'approved' | 'in_progress' | 'pending_parts' | 'ready' | 'picked_up' | 'delivered' | 'cancelled'
assignedTechnicianId: string | null assignedTechnicianId: string | null
estimatedCost: string | null estimatedCost: string | null
actualCost: string | null actualCost: string | null

View File

@@ -0,0 +1 @@
ALTER TYPE "repair_ticket_status" ADD VALUE 'new' BEFORE 'in_transit';

View File

@@ -134,6 +134,13 @@
"when": 1774780000000, "when": 1774780000000,
"tag": "0018_repair_notes", "tag": "0018_repair_notes",
"breakpoints": true "breakpoints": true
},
{
"idx": 19,
"version": "7",
"when": 1774790000000,
"tag": "0019_repair_new_status",
"breakpoints": true
} }
] ]
} }

View File

@@ -17,6 +17,7 @@ import { users } from './users.js'
// --- Enums --- // --- Enums ---
export const repairTicketStatusEnum = pgEnum('repair_ticket_status', [ export const repairTicketStatusEnum = pgEnum('repair_ticket_status', [
'new',
'in_transit', 'in_transit',
'intake', 'intake',
'diagnosing', 'diagnosing',
@@ -112,7 +113,7 @@ export const repairTickets = pgTable('repair_ticket', {
conditionInNotes: text('condition_in_notes'), conditionInNotes: text('condition_in_notes'),
problemDescription: text('problem_description').notNull(), problemDescription: text('problem_description').notNull(),
technicianNotes: text('technician_notes'), technicianNotes: text('technician_notes'),
status: repairTicketStatusEnum('status').notNull().default('intake'), status: repairTicketStatusEnum('status').notNull().default('new'),
assignedTechnicianId: uuid('assigned_technician_id').references(() => users.id), assignedTechnicianId: uuid('assigned_technician_id').references(() => users.id),
estimatedCost: numeric('estimated_cost', { precision: 10, scale: 2 }), estimatedCost: numeric('estimated_cost', { precision: 10, scale: 2 }),
actualCost: numeric('actual_cost', { precision: 10, scale: 2 }), actualCost: numeric('actual_cost', { precision: 10, scale: 2 }),

View File

@@ -8,7 +8,7 @@ function opt<T extends z.ZodTypeAny>(schema: T) {
// --- Status / Type enums --- // --- Status / Type enums ---
export const RepairTicketStatus = z.enum([ export const RepairTicketStatus = z.enum([
'in_transit', 'intake', 'diagnosing', 'pending_approval', 'approved', 'new', 'in_transit', 'intake', 'diagnosing', 'pending_approval', 'approved',
'in_progress', 'pending_parts', 'ready', 'picked_up', 'delivered', 'cancelled', 'in_progress', 'pending_parts', 'ready', 'picked_up', 'delivered', 'cancelled',
]) ])
export type RepairTicketStatus = z.infer<typeof RepairTicketStatus> export type RepairTicketStatus = z.infer<typeof RepairTicketStatus>