sackey e3b2b220d2
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
Enhance UI and functionality across various components
- Increased icon sizes in service items, service orders, users, and technician management for better visibility.
- Added custom loading indicators with appropriate icons in search fields for vehicles, work orders, and technicians.
- Introduced invoice management routes for better organization and access control.
- Created a new test for the estimate PDF functionality to ensure proper rendering and data integrity.
2025-08-16 14:36:58 +00:00

289 lines
16 KiB
PHP

<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<flux:heading size="xl">Service Order Management</flux:heading>
<flux:button href="/service-orders/create" size="sm">
<flux:icon name="plus" class="size-4" />
Create New Service Order
</flux:button>
</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
@if (session()->has('error'))
<div class="bg-red-50 border border-red-200 text-red-800 rounded-md p-4">
<div class="flex">
<flux:icon name="exclamation-circle" class="h-5 w-5 text-red-400" />
<div class="ml-3">
<p class="text-sm font-medium">{{ session('error') }}</p>
</div>
</div>
</div>
@endif
<!-- Quick Stats -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-blue-600">{{ $stats['total'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Total Orders</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-yellow-600">{{ $stats['pending'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Pending</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-orange-600">{{ $stats['in_progress'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">In Progress</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-green-600">{{ $stats['completed_today'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Completed Today</div>
</div>
</div>
<!-- Filters -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="grid grid-cols-1 md:grid-cols-6 gap-4 p-4">
<div class="relative">
<flux:input
wire:model.live="search"
placeholder="Search orders..."
icon="magnifying-glass"
:loading="false"
/>
<!-- Custom loading indicator with wrench icon -->
<div wire:loading wire:target="search" class="absolute right-3 top-1/2 transform -translate-y-1/2">
<flux:icon.wrench class="w-6 h-6 text-zinc-400 animate-spin" />
</div>
</div>
<select wire:model.live="status" class="rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-2 focus:ring-blue-500">
<option value="">All Statuses</option>
<option value="pending">Pending</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
<option value="cancelled">Cancelled</option>
<option value="on_hold">On Hold</option>
</select>
<select wire:model.live="priority" class="rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-2 focus:ring-blue-500">
<option value="">All Priorities</option>
<option value="low">Low</option>
<option value="normal">Normal</option>
<option value="high">High</option>
<option value="urgent">Urgent</option>
</select>
<select wire:model.live="technician_id" class="rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-2 focus:ring-blue-500">
<option value="">All Technicians</option>
@foreach($technicians as $technician)
<option value="{{ $technician->id }}">{{ $technician->full_name }}</option>
@endforeach
</select>
<input
type="date"
wire:model.live="date_from"
class="rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-2 focus:ring-blue-500"
placeholder="From Date"
/>
<input
type="date"
wire:model.live="date_to"
class="rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-2 focus:ring-blue-500"
placeholder="To Date"
/>
</div>
</div>
<!-- Service Orders Table -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="overflow-x-auto">
<table class="min-w-full">
<thead>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-3 px-4">
<button wire:click="sortBy('order_number')" class="flex items-center space-x-1 hover:text-blue-600">
<span>Order #</span>
@if($sortBy === 'order_number')
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
@endif
</button>
</th>
<th class="text-left py-3 px-4">Customer & Vehicle</th>
<th class="text-left py-3 px-4">Complaint</th>
<th class="text-left py-3 px-4">
<button wire:click="sortBy('assigned_technician_id')" class="flex items-center space-x-1 hover:text-blue-600">
<span>Technician</span>
@if($sortBy === 'assigned_technician_id')
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
@endif
</button>
</th>
<th class="text-left py-3 px-4">
<button wire:click="sortBy('priority')" class="flex items-center space-x-1 hover:text-blue-600">
<span>Priority</span>
@if($sortBy === 'priority')
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
@endif
</button>
</th>
<th class="text-left py-3 px-4">
<button wire:click="sortBy('status')" class="flex items-center space-x-1 hover:text-blue-600">
<span>Status</span>
@if($sortBy === 'status')
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
@endif
</button>
</th>
<th class="text-left py-3 px-4">
<button wire:click="sortBy('total_amount')" class="flex items-center space-x-1 hover:text-blue-600">
<span>Total</span>
@if($sortBy === 'total_amount')
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
@endif
</button>
</th>
<th class="text-left py-3 px-4">
<button wire:click="sortBy('created_at')" class="flex items-center space-x-1 hover:text-blue-600">
<span>Created</span>
@if($sortBy === 'created_at')
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
@endif
</button>
</th>
<th class="text-left py-3 px-4">Actions</th>
</tr>
</thead>
<tbody>
@forelse($serviceOrders as $order)
<tr class="border-b border-zinc-200 dark:border-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-700">
<td class="py-3 px-4">
<div class="font-medium">{{ $order->order_number }}</div>
@if($order->scheduled_date)
<div class="text-xs text-zinc-500 dark:text-zinc-400">Scheduled: {{ $order->scheduled_date->format('M j, Y') }}</div>
@endif
</td>
<td class="py-3 px-4">
<div>
<div class="font-medium text-sm">{{ $order->customer->full_name }}</div>
<div class="text-xs text-zinc-500 dark:text-zinc-400">{{ $order->vehicle->display_name }}</div>
<div class="text-xs text-zinc-500 dark:text-zinc-400">{{ $order->vehicle->license_plate }}</div>
</div>
</td>
<td class="py-3 px-4">
<div class="text-sm max-w-xs truncate" title="{{ $order->customer_complaint }}">
{{ $order->customer_complaint }}
</div>
</td>
<td class="py-3 px-4">
<div class="text-sm">
{{ $order->assignedTechnician?->full_name ?? 'Unassigned' }}
</div>
</td>
<td class="py-3 px-4">
<flux:badge
:color="$order->priority === 'urgent' ? 'red' : ($order->priority === 'high' ? 'orange' : ($order->priority === 'normal' ? 'blue' : 'gray'))"
size="sm"
>
{{ ucfirst($order->priority) }}
</flux:badge>
</td>
<td class="py-3 px-4">
<div class="flex items-center space-x-2">
<flux:badge
:color="$order->status === 'completed' ? 'green' : ($order->status === 'in_progress' ? 'blue' : ($order->status === 'cancelled' ? 'red' : 'gray'))"
size="sm"
>
{{ ucfirst(str_replace('_', ' ', $order->status)) }}
</flux:badge>
@if($order->status === 'pending')
<button
wire:click="updateStatus({{ $order->id }}, 'in_progress')"
class="text-xs text-blue-600 hover:underline"
title="Start Work"
>
Start
</button>
@elseif($order->status === 'in_progress')
<button
wire:click="updateStatus({{ $order->id }}, 'completed')"
class="text-xs text-green-600 hover:underline"
title="Complete Work"
>
Complete
</button>
@endif
</div>
</td>
<td class="py-3 px-4">
<div class="font-medium">${{ number_format($order->total_amount, 2) }}</div>
@if($order->serviceItems->count() > 0 || $order->parts->count() > 0)
<div class="text-xs text-zinc-500 dark:text-zinc-400">
{{ $order->serviceItems->count() }} services, {{ $order->parts->count() }} parts
</div>
@endif
</td>
<td class="py-3 px-4">
<div class="text-sm">{{ $order->created_at->format('M j, Y') }}</div>
<div class="text-xs text-zinc-500 dark:text-zinc-400">{{ $order->created_at->format('g:i A') }}</div>
</td>
<td class="py-3 px-4">
<div class="flex space-x-1">
<flux:button href="/service-orders/{{ $order->id }}" variant="outline" size="sm">
View
</flux:button>
<flux:button href="/service-orders/{{ $order->id }}/edit" variant="outline" size="sm">
Edit
</flux:button>
@if($order->status === 'completed')
<flux:button href="/service-orders/{{ $order->id }}/invoice" size="sm">
Invoice
</flux:button>
@endif
<flux:button
wire:click="deleteServiceOrder({{ $order->id }})"
wire:confirm="Are you sure you want to delete service order {{ $order->order_number }}? This action cannot be undone."
variant="danger"
size="sm"
>
Delete
</flux:button>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="9" class="text-center py-8 text-zinc-500 dark:text-zinc-400">
@if($search)
No service orders found matching "{{ $search }}"
@else
No service orders found. <a href="/service-orders/create" class="text-blue-600 hover:underline">Create your first service order</a>
@endif
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($serviceOrders->hasPages())
<div class="mt-4 px-4 pb-4">
{{ $serviceOrders->links() }}
</div>
@endif
</div>
</div>