Files
lunarfront-app/packages/admin/src/components/repairs/status-progress.tsx
Ryan Moon 7d55fbe7ef Add repair ticket detail improvements and intake estimate builder
Status progress bar component with visual step indicator, in_transit
status for instruments being transported to shop. Ticket detail page
reworked with inline edit form, reopen for cancelled tickets, photos
grouped by repair phase (intake/in_progress/completed). Intake form
now supports building estimates with template picker and manual line
items that carry over to the ticket. Service template API client and
types added for template search in line item dialogs.
2026-03-29 09:56:28 -05:00

116 lines
4.6 KiB
TypeScript

import { Check, Truck, ClipboardList, Search, Clock, ThumbsUp, Wrench, Package, HandMetal, Ban } from 'lucide-react'
const STEPS = [
{ key: 'in_transit', label: 'In Transit', icon: Truck },
{ key: 'intake', label: 'Intake', icon: ClipboardList },
{ key: 'diagnosing', label: 'Diagnosing', icon: Search },
{ key: 'pending_approval', label: 'Pending Approval', icon: Clock },
{ key: 'approved', label: 'Approved', icon: ThumbsUp },
{ key: 'in_progress', label: 'In Progress', icon: Wrench },
{ key: 'ready', label: 'Ready', icon: Package },
{ key: 'picked_up', label: 'Picked Up', icon: HandMetal },
] as const
const BRANCH_STATUSES: Record<string, { label: string; parentStep: string }> = {
pending_parts: { label: 'Pending Parts', parentStep: 'in_progress' },
delivered: { label: 'Delivered', parentStep: 'picked_up' },
}
interface StatusProgressProps {
currentStatus: string
onStatusClick?: (status: string) => void
}
export function StatusProgress({ currentStatus, onStatusClick }: StatusProgressProps) {
const isCancelled = currentStatus === 'cancelled'
const isBranch = currentStatus in BRANCH_STATUSES
// Find the effective step index for branch statuses
const effectiveStatus = isBranch ? BRANCH_STATUSES[currentStatus].parentStep : currentStatus
const currentIdx = STEPS.findIndex((s) => s.key === effectiveStatus)
return (
<div className="space-y-2">
<div className="flex items-center w-full">
{STEPS.map((step, idx) => {
const isCompleted = !isCancelled && currentIdx > idx
const isCurrent = !isCancelled && currentIdx === idx
const isFuture = isCancelled || currentIdx < idx
return (
<div key={step.key} className="flex items-center flex-1 last:flex-none">
{/* Step circle */}
<button
type="button"
disabled={!onStatusClick || isCancelled}
onClick={() => onStatusClick?.(step.key)}
className={`
relative flex flex-col items-center gap-1 group
${onStatusClick && !isCancelled ? 'cursor-pointer' : 'cursor-default'}
`}
>
<div
className={`
flex items-center justify-center h-9 w-9 rounded-full border-2 transition-colors
${isCompleted ? 'bg-primary border-primary text-primary-foreground' : ''}
${isCurrent ? 'border-primary bg-primary/10 text-primary ring-2 ring-primary/30' : ''}
${isFuture ? 'border-muted-foreground/30 text-muted-foreground/40' : ''}
${isCancelled ? 'border-destructive/30 text-destructive/40' : ''}
${onStatusClick && !isCancelled ? 'group-hover:border-primary group-hover:text-primary' : ''}
`}
>
{isCompleted ? (
<Check className="h-4 w-4" />
) : (
<step.icon className="h-4 w-4" />
)}
</div>
<span
className={`
text-[10px] font-medium text-center leading-tight max-w-[70px]
${isCompleted ? 'text-primary' : ''}
${isCurrent ? 'text-primary font-semibold' : ''}
${isFuture ? 'text-muted-foreground/50' : ''}
${isCancelled ? 'text-destructive/50' : ''}
`}
>
{step.label}
</span>
</button>
{/* Connector line */}
{idx < STEPS.length - 1 && (
<div
className={`
flex-1 h-0.5 mx-1 mt-[-18px]
${!isCancelled && currentIdx > idx ? 'bg-primary' : 'bg-muted-foreground/20'}
${isCancelled ? 'bg-destructive/20' : ''}
`}
/>
)}
</div>
)
})}
</div>
{/* Branch status indicator */}
{isBranch && (
<div className="flex items-center gap-2 pl-4">
<div className="h-3 w-3 rounded-full bg-amber-500" />
<span className="text-sm font-medium text-amber-600">
{BRANCH_STATUSES[currentStatus].label}
</span>
</div>
)}
{/* Cancelled overlay */}
{isCancelled && (
<div className="flex items-center gap-2 pl-4">
<Ban className="h-4 w-4 text-destructive" />
<span className="text-sm font-medium text-destructive">Cancelled</span>
</div>
)}
</div>
)
}