444 lines
24 KiB
PHP
444 lines
24 KiB
PHP
<div class="space-y-6">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<flux:heading size="xl">Service Order {{ $serviceOrder->order_number }}</flux:heading>
|
|
<flux:subheading>{{ $serviceOrder->customer->full_name }} • {{ $serviceOrder->vehicle->display_name }}</flux:subheading>
|
|
</div>
|
|
<div class="flex space-x-3">
|
|
<flux:button href="/service-orders" variant="outline" size="sm">
|
|
<flux:icon name="arrow-left" class="size-4" />
|
|
Back to Service Orders
|
|
</flux:button>
|
|
<flux:button href="/service-orders/{{ $serviceOrder->id }}/edit" variant="outline" size="sm">
|
|
<flux:icon name="pencil" class="size-4" />
|
|
Edit Order
|
|
</flux:button>
|
|
@if($serviceOrder->status === 'completed')
|
|
<flux:button href="/service-orders/{{ $serviceOrder->id }}/invoice" size="sm">
|
|
<flux:icon name="document-text" class="size-4" />
|
|
Generate Invoice
|
|
</flux:button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Flash Messages -->
|
|
@if (session()->has('success'))
|
|
<div class="bg-green-50 border border-green-200 text-green-800 rounded-md p-4">
|
|
<div class="flex">
|
|
<flux:icon name="check-circle" class="h-5 w-5 text-green-400" />
|
|
<div class="ml-3">
|
|
<p class="text-sm font-medium">{{ session('success') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<!-- Main Content -->
|
|
<div class="lg:col-span-2 space-y-6">
|
|
<!-- Service Order Information -->
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Service Order Information</flux:heading>
|
|
</div>
|
|
<div class="p-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<flux:label>Order Number</flux:label>
|
|
<div class="mt-1 text-sm font-medium">{{ $serviceOrder->order_number }}</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>Priority</flux:label>
|
|
<div class="mt-1">
|
|
<flux:badge
|
|
:color="$serviceOrder->priority === 'urgent' ? 'red' : ($serviceOrder->priority === 'high' ? 'orange' : ($serviceOrder->priority === 'normal' ? 'blue' : 'gray'))"
|
|
size="sm"
|
|
>
|
|
{{ ucfirst($serviceOrder->priority) }}
|
|
</flux:badge>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>Status</flux:label>
|
|
<div class="mt-1 flex items-center space-x-2">
|
|
<flux:badge
|
|
:color="$serviceOrder->status === 'completed' ? 'green' : ($serviceOrder->status === 'in_progress' ? 'blue' : ($serviceOrder->status === 'cancelled' ? 'red' : 'gray'))"
|
|
size="sm"
|
|
>
|
|
{{ ucfirst(str_replace('_', ' ', $serviceOrder->status)) }}
|
|
</flux:badge>
|
|
|
|
@if($serviceOrder->status === 'pending')
|
|
<button
|
|
wire:click="updateStatus('in_progress')"
|
|
class="text-xs text-blue-600 hover:underline"
|
|
>
|
|
Start Work
|
|
</button>
|
|
@elseif($serviceOrder->status === 'in_progress')
|
|
<button
|
|
wire:click="updateStatus('completed')"
|
|
class="text-xs text-green-600 hover:underline"
|
|
>
|
|
Complete Work
|
|
</button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>Assigned Technician</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->assignedTechnician?->full_name ?? 'Unassigned' }}</div>
|
|
</div>
|
|
@if($serviceOrder->scheduled_date)
|
|
<div>
|
|
<flux:label>Scheduled Date</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->scheduled_date->format('M j, Y') }}</div>
|
|
</div>
|
|
@endif
|
|
<div>
|
|
<flux:label>Created</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->created_at->format('M j, Y g:i A') }}</div>
|
|
</div>
|
|
@if($serviceOrder->started_at)
|
|
<div>
|
|
<flux:label>Started</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->started_at->format('M j, Y g:i A') }}</div>
|
|
</div>
|
|
@endif
|
|
@if($serviceOrder->completed_at)
|
|
<div>
|
|
<flux:label>Completed</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->completed_at->format('M j, Y g:i A') }}</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class="mt-4">
|
|
<flux:label>Customer Complaint</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->customer_complaint }}</div>
|
|
</div>
|
|
@if($serviceOrder->recommended_services)
|
|
<div class="mt-4">
|
|
<flux:label>Recommended Services</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->recommended_services }}</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Customer & Vehicle Information -->
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Customer & Vehicle Information</flux:heading>
|
|
</div>
|
|
<div class="p-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<h4 class="font-medium mb-2">Customer Information</h4>
|
|
<div class="space-y-2">
|
|
<div>
|
|
<flux:label>Name</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->customer->full_name }}</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>Email</flux:label>
|
|
<div class="mt-1 text-sm">
|
|
<a href="mailto:{{ $serviceOrder->customer->email }}" class="text-blue-600 hover:underline">
|
|
{{ $serviceOrder->customer->email }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>Phone</flux:label>
|
|
<div class="mt-1 text-sm">
|
|
<a href="tel:{{ $serviceOrder->customer->phone }}" class="text-blue-600 hover:underline">
|
|
{{ $serviceOrder->customer->phone }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-medium mb-2">Vehicle Information</h4>
|
|
<div class="space-y-2">
|
|
<div>
|
|
<flux:label>Vehicle</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->vehicle->display_name }}</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>License Plate</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->vehicle->license_plate }}</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>VIN</flux:label>
|
|
<div class="mt-1 text-sm font-mono">{{ $serviceOrder->vehicle->vin_display }}</div>
|
|
</div>
|
|
<div>
|
|
<flux:label>Mileage</flux:label>
|
|
<div class="mt-1 text-sm">{{ number_format($serviceOrder->vehicle->mileage) }} miles</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Service Items -->
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Service Items</flux:heading>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
@if($serviceOrder->serviceItems->count() > 0)
|
|
<table class="min-w-full">
|
|
<thead>
|
|
<tr class="border-b border-zinc-200 dark:border-zinc-700">
|
|
<th class="text-left py-2 px-4">Service</th>
|
|
<th class="text-left py-2 px-4">Category</th>
|
|
<th class="text-left py-2 px-4">Rate</th>
|
|
<th class="text-left py-2 px-4">Hours</th>
|
|
<th class="text-left py-2 px-4">Status</th>
|
|
<th class="text-left py-2 px-4">Cost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($serviceOrder->serviceItems as $item)
|
|
<tr class="border-b border-zinc-100 dark:border-zinc-700">
|
|
<td class="py-2 px-4">
|
|
<div class="font-medium">{{ $item->service_name }}</div>
|
|
@if($item->description)
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">{{ $item->description }}</div>
|
|
@endif
|
|
</td>
|
|
<td class="py-2 px-4">{{ $item->category }}</td>
|
|
<td class="py-2 px-4">${{ number_format($item->labor_rate, 2) }}</td>
|
|
<td class="py-2 px-4">{{ $item->estimated_hours }}h</td>
|
|
<td class="py-2 px-4">
|
|
<flux:badge
|
|
:color="$item->status === 'completed' ? 'green' : ($item->status === 'in_progress' ? 'blue' : 'gray')"
|
|
size="sm"
|
|
>
|
|
{{ ucfirst($item->status) }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="py-2 px-4 font-medium">${{ number_format($item->labor_cost, 2) }}</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
@else
|
|
<div class="p-4 text-center text-zinc-500 dark:text-zinc-400">
|
|
No service items added yet.
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Parts -->
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Parts</flux:heading>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
@if($serviceOrder->parts->count() > 0)
|
|
<table class="min-w-full">
|
|
<thead>
|
|
<tr class="border-b border-zinc-200 dark:border-zinc-700">
|
|
<th class="text-left py-2 px-4">Part</th>
|
|
<th class="text-left py-2 px-4">Part Number</th>
|
|
<th class="text-left py-2 px-4">Quantity</th>
|
|
<th class="text-left py-2 px-4">Unit Price</th>
|
|
<th class="text-left py-2 px-4">Status</th>
|
|
<th class="text-left py-2 px-4">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($serviceOrder->parts as $part)
|
|
<tr class="border-b border-zinc-100 dark:border-zinc-700">
|
|
<td class="py-2 px-4">
|
|
<div class="font-medium">{{ $part->name }}</div>
|
|
@if($part->pivot->notes)
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">{{ $part->pivot->notes }}</div>
|
|
@endif
|
|
</td>
|
|
<td class="py-2 px-4">{{ $part->part_number }}</td>
|
|
<td class="py-2 px-4">{{ $part->pivot->quantity_used }}</td>
|
|
<td class="py-2 px-4">${{ number_format($part->pivot->unit_price, 2) }}</td>
|
|
<td class="py-2 px-4">
|
|
<flux:badge
|
|
:color="$part->pivot->status === 'installed' ? 'green' : ($part->pivot->status === 'ordered' ? 'blue' : 'gray')"
|
|
size="sm"
|
|
>
|
|
{{ ucfirst($part->pivot->status) }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="py-2 px-4 font-medium">${{ number_format($part->pivot->total_price, 2) }}</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
@else
|
|
<div class="p-4 text-center text-zinc-500 dark:text-zinc-400">
|
|
No parts added yet.
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notes -->
|
|
@if($serviceOrder->internal_notes || $serviceOrder->customer_notes)
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Notes</flux:heading>
|
|
</div>
|
|
<div class="p-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
@if($serviceOrder->internal_notes)
|
|
<div>
|
|
<flux:label>Internal Notes</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->internal_notes }}</div>
|
|
</div>
|
|
@endif
|
|
@if($serviceOrder->customer_notes)
|
|
<div>
|
|
<flux:label>Customer Notes</flux:label>
|
|
<div class="mt-1 text-sm">{{ $serviceOrder->customer_notes }}</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="space-y-6">
|
|
<!-- Order Totals -->
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Order Totals</flux:heading>
|
|
</div>
|
|
<div class="p-4 space-y-3">
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Labor Cost</span>
|
|
<span class="font-medium">${{ number_format($serviceOrder->labor_cost, 2) }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Parts Cost</span>
|
|
<span class="font-medium">${{ number_format($serviceOrder->parts_cost, 2) }}</span>
|
|
</div>
|
|
@if($serviceOrder->discount_amount > 0)
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Discount</span>
|
|
<span class="font-medium text-green-600">-${{ number_format($serviceOrder->discount_amount, 2) }}</span>
|
|
</div>
|
|
@endif
|
|
<div class="flex justify-between border-t pt-2">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Subtotal</span>
|
|
<span class="font-medium">${{ number_format($serviceOrder->labor_cost + $serviceOrder->parts_cost - $serviceOrder->discount_amount, 2) }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Tax</span>
|
|
<span class="font-medium">${{ number_format($serviceOrder->tax_amount, 2) }}</span>
|
|
</div>
|
|
<div class="flex justify-between border-t pt-2 font-bold text-lg">
|
|
<span>Total</span>
|
|
<span>${{ number_format($serviceOrder->total_amount, 2) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Stats -->
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Quick Stats</flux:heading>
|
|
</div>
|
|
<div class="p-4 space-y-3">
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Service Items</span>
|
|
<span class="font-medium">{{ $serviceOrder->serviceItems->count() }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Parts Used</span>
|
|
<span class="font-medium">{{ $serviceOrder->parts->count() }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Estimated Hours</span>
|
|
<span class="font-medium">{{ $serviceOrder->estimated_hours ?? 0 }}h</span>
|
|
</div>
|
|
@if($serviceOrder->actual_hours)
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-zinc-600 dark:text-zinc-400">Actual Hours</span>
|
|
<span class="font-medium">{{ $serviceOrder->actual_hours }}h</span>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
|
|
<flux:heading size="lg">Quick Actions</flux:heading>
|
|
</div>
|
|
<div class="p-4 space-y-2">
|
|
@if($serviceOrder->status === 'pending')
|
|
<flux:button
|
|
wire:click="updateStatus('in_progress')"
|
|
class="w-full"
|
|
size="sm"
|
|
>
|
|
Start Work
|
|
</flux:button>
|
|
@elseif($serviceOrder->status === 'in_progress')
|
|
<flux:button
|
|
wire:click="updateStatus('completed')"
|
|
class="w-full"
|
|
variant="primary"
|
|
size="sm"
|
|
>
|
|
Complete Work
|
|
</flux:button>
|
|
<flux:button
|
|
wire:click="updateStatus('on_hold')"
|
|
class="w-full"
|
|
variant="outline"
|
|
size="sm"
|
|
>
|
|
Put on Hold
|
|
</flux:button>
|
|
@elseif($serviceOrder->status === 'on_hold')
|
|
<flux:button
|
|
wire:click="updateStatus('in_progress')"
|
|
class="w-full"
|
|
size="sm"
|
|
>
|
|
Resume Work
|
|
</flux:button>
|
|
@endif
|
|
|
|
@if($serviceOrder->status === 'completed')
|
|
<flux:button
|
|
href="/service-orders/{{ $serviceOrder->id }}/invoice"
|
|
class="w-full"
|
|
variant="primary"
|
|
size="sm"
|
|
>
|
|
Generate Invoice
|
|
</flux:button>
|
|
@endif
|
|
|
|
<flux:button
|
|
href="/appointments/create?service_order={{ $serviceOrder->id }}"
|
|
class="w-full"
|
|
variant="outline"
|
|
size="sm"
|
|
>
|
|
Schedule Follow-up
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|