167 lines
6.2 KiB
TypeScript
167 lines
6.2 KiB
TypeScript
import {
|
|
CalendarCheck2,
|
|
Clock,
|
|
User,
|
|
Car,
|
|
Users,
|
|
Wrench,
|
|
Building2,
|
|
FileText,
|
|
ClipboardList,
|
|
Info,
|
|
ExternalLink,
|
|
} from "lucide-react"
|
|
import Link from "next/link"
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
|
import { Badge } from "@/shared/components/ui/badge"
|
|
import { Button } from "@/shared/components/ui/button"
|
|
|
|
type AppointmentData = {
|
|
id?: number
|
|
job_card_id?: number
|
|
title?: string
|
|
date?: string
|
|
from_time?: string
|
|
to_time?: string
|
|
customer_id?: number
|
|
vehicle_id?: number
|
|
service_writer_id?: number
|
|
technician_id?: number
|
|
department_id?: number
|
|
notes?: string
|
|
status?: string
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
type AppointmentGeneralInfoProps = {
|
|
appointment: AppointmentData
|
|
}
|
|
|
|
function InfoItem({
|
|
icon: Icon,
|
|
label,
|
|
value,
|
|
}: {
|
|
icon: React.ComponentType<{ className?: string }>
|
|
label: string
|
|
value?: string | number | null
|
|
}) {
|
|
return (
|
|
<div className="flex items-start gap-3">
|
|
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
|
|
<Icon className="size-4" />
|
|
</div>
|
|
<div className="flex flex-col gap-0.5">
|
|
<span className="text-xs text-muted-foreground">{label}</span>
|
|
<span className="text-sm font-medium">
|
|
{value != null && value !== "" ? String(value) : <span className="text-muted-foreground">—</span>}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const STATUS_COLORS: Record<string, string> = {
|
|
requested: "bg-yellow-100 text-yellow-800",
|
|
confirmed: "bg-blue-100 text-blue-800",
|
|
in_progress: "bg-purple-100 text-purple-800",
|
|
completed: "bg-green-100 text-green-800",
|
|
cancelled: "bg-red-100 text-red-800",
|
|
}
|
|
|
|
export function AppointmentGeneralInfo({ appointment }: AppointmentGeneralInfoProps) {
|
|
const statusClass = appointment.status
|
|
? (STATUS_COLORS[appointment.status] ?? "bg-gray-100 text-gray-800")
|
|
: "bg-gray-100 text-gray-800"
|
|
|
|
return (
|
|
<div className="grid gap-6 md:grid-cols-2 p-6">
|
|
{/* Appointment Details */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<CalendarCheck2 className="size-4" />
|
|
Appointment Details
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-4">
|
|
<div className="flex items-center gap-2">
|
|
{appointment.status && (
|
|
<Badge className={statusClass}>
|
|
{appointment.status.replace("_", " ")}
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
<InfoItem icon={Info} label="Title" value={appointment.title} />
|
|
<InfoItem icon={CalendarCheck2} label="Date" value={appointment.date} />
|
|
<InfoItem icon={Clock} label="From Time" value={appointment.from_time} />
|
|
<InfoItem icon={Clock} label="To Time" value={appointment.to_time} />
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Customer & Vehicle */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<User className="size-4" />
|
|
Customer & Vehicle
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-4">
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
<InfoItem icon={User} label="Customer ID" value={appointment.customer_id} />
|
|
<InfoItem icon={Car} label="Vehicle ID" value={appointment.vehicle_id} />
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Assignment */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Users className="size-4" />
|
|
Assignment
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid gap-4">
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
<InfoItem icon={Users} label="Service Writer ID" value={appointment.service_writer_id} />
|
|
<InfoItem icon={Wrench} label="Technician ID" value={appointment.technician_id} />
|
|
<InfoItem icon={Building2} label="Department ID" value={appointment.department_id} />
|
|
</div>
|
|
{appointment.job_card_id && (
|
|
<div className="pt-2">
|
|
<span className="text-xs text-muted-foreground mb-1 block">Job Card</span>
|
|
<Button asChild variant="outline" size="sm" className="gap-1.5">
|
|
<Link href={`/sales/job-cards/${appointment.job_card_id}`}>
|
|
<ClipboardList className="size-3.5" />
|
|
Job Card #{appointment.job_card_id}
|
|
<ExternalLink className="size-3.5" />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Notes */}
|
|
{appointment.notes && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<FileText className="size-4" />
|
|
Notes
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground whitespace-pre-line">{appointment.notes}</p>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|