- 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.
231 lines
13 KiB
PHP
231 lines
13 KiB
PHP
<div class="space-y-6">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between">
|
|
<flux:heading size="xl">Vehicle Management</flux:heading>
|
|
<flux:button href="/vehicles/create" size="sm">
|
|
<flux:icon name="plus" class="size-4" />
|
|
Add New Vehicle
|
|
</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
|
|
|
|
<!-- 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-4 gap-4 p-4">
|
|
<div class="relative">
|
|
<flux:input
|
|
wire:model.live="search"
|
|
placeholder="Search vehicles..."
|
|
icon="magnifying-glass"
|
|
:loading="false"
|
|
/>
|
|
<!-- Custom loading indicator with truck icon -->
|
|
<div wire:loading wire:target="search" class="absolute right-3 top-1/2 transform -translate-y-1/2">
|
|
<flux:icon.truck class="w-6 h-6 text-zinc-400 animate-bounce" />
|
|
</div>
|
|
</div>
|
|
|
|
<select wire:model.live="customer_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 Customers</option>
|
|
@foreach($customers as $customer)
|
|
<option value="{{ $customer->id }}">{{ $customer->full_name }}</option>
|
|
@endforeach
|
|
</select>
|
|
|
|
<select wire:model.live="make" 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 Makes</option>
|
|
@foreach($makes as $make)
|
|
<option value="{{ $make }}">{{ $make }}</option>
|
|
@endforeach
|
|
</select>
|
|
|
|
<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="active">Active</option>
|
|
<option value="inactive">Inactive</option>
|
|
<option value="sold">Sold</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vehicles 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('year')" class="flex items-center space-x-1 hover:text-blue-600">
|
|
<span>Vehicle</span>
|
|
@if($sortBy === 'year')
|
|
<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('customer_id')" class="flex items-center space-x-1 hover:text-blue-600">
|
|
<span>Owner</span>
|
|
@if($sortBy === 'customer_id')
|
|
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
|
|
@endif
|
|
</button>
|
|
</th>
|
|
<th class="text-left py-3 px-4">VIN</th>
|
|
<th class="text-left py-3 px-4">License Plate</th>
|
|
<th class="text-left py-3 px-4">
|
|
<button wire:click="sortBy('mileage')" class="flex items-center space-x-1 hover:text-blue-600">
|
|
<span>Mileage</span>
|
|
@if($sortBy === 'mileage')
|
|
<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('last_service_date')" class="flex items-center space-x-1 hover:text-blue-600">
|
|
<span>Last Service</span>
|
|
@if($sortBy === 'last_service_date')
|
|
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
|
|
@endif
|
|
</button>
|
|
</th>
|
|
<th class="text-left py-3 px-4">Status</th>
|
|
<th class="text-left py-3 px-4">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($vehicles as $vehicle)
|
|
<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="flex items-center space-x-3">
|
|
<div>
|
|
<div class="font-medium">{{ $vehicle->display_name }}</div>
|
|
<div class="flex items-center space-x-2 text-sm text-zinc-500 dark:text-zinc-400">
|
|
<div
|
|
class="w-6 h-6 rounded border border-zinc-300 dark:border-zinc-600"
|
|
style="background-color: {{ $vehicle->color }}"
|
|
title="{{ $vehicle->color }}"
|
|
></div>
|
|
<span>{{ $vehicle->color }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div>
|
|
<div class="text-sm">{{ $vehicle->customer->full_name }}</div>
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">ID: {{ $vehicle->customer->id }}</div>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm font-mono">{{ $vehicle->vin_display }}</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm">{{ $vehicle->license_plate }}</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm">{{ number_format($vehicle->mileage) }} mi</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm">
|
|
@if($vehicle->last_service_date)
|
|
{{ $vehicle->last_service_date->format('M j, Y') }}
|
|
@else
|
|
<span class="text-gray-400">Never</span>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<flux:badge
|
|
:color="$vehicle->status === 'active' ? 'green' : ($vehicle->status === 'sold' ? 'blue' : 'gray')"
|
|
size="sm"
|
|
>
|
|
{{ ucfirst($vehicle->status) }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="flex space-x-2">
|
|
<flux:button href="/vehicles/{{ $vehicle->id }}" variant="outline" size="sm">
|
|
View
|
|
</flux:button>
|
|
<flux:button href="/vehicles/{{ $vehicle->id }}/edit" variant="outline" size="sm">
|
|
Edit
|
|
</flux:button>
|
|
<flux:button href="/service-orders/create?vehicle={{ $vehicle->id }}" size="sm">
|
|
Service
|
|
</flux:button>
|
|
<flux:button
|
|
wire:click="deleteVehicle({{ $vehicle->id }})"
|
|
wire:confirm="Are you sure you want to delete {{ $vehicle->display_name }}? This action cannot be undone."
|
|
variant="danger"
|
|
size="sm"
|
|
>
|
|
Delete
|
|
</flux:button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="8" class="text-center py-8 text-zinc-500 dark:text-zinc-400">
|
|
@if($search)
|
|
No vehicles found matching "{{ $search }}"
|
|
@else
|
|
No vehicles found. <a href="/vehicles/create" class="text-blue-600 hover:underline">Add your first vehicle</a>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
@if($vehicles->hasPages())
|
|
<div class="mt-4 px-4 pb-4">
|
|
{{ $vehicles->links() }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- 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">{{ $vehicles->total() }}</div>
|
|
<div class="text-sm text-zinc-600 dark:text-zinc-400">Total Vehicles</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">{{ $vehicles->where('status', 'active')->count() }}</div>
|
|
<div class="text-sm text-zinc-600 dark:text-zinc-400">Active Vehicles</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">{{ $makes->count() }}</div>
|
|
<div class="text-sm text-zinc-600 dark:text-zinc-400">Different Makes</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-purple-600">{{ $customers->count() }}</div>
|
|
<div class="text-sm text-zinc-600 dark:text-zinc-400">Vehicle Owners</div>
|
|
</div>
|
|
</div>
|
|
</div>
|