- 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.
199 lines
10 KiB
PHP
199 lines
10 KiB
PHP
<div class="space-y-6">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between">
|
|
<flux:heading size="xl">Customer Management</flux:heading>
|
|
<flux:button href="/customers/create" size="sm">
|
|
<flux:icon name="plus" class="size-4" />
|
|
Add New Customer
|
|
</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-3 gap-4 p-4">
|
|
<div class="relative">
|
|
<flux:input
|
|
wire:model.live="search"
|
|
placeholder="Search customers..."
|
|
icon="magnifying-glass"
|
|
:loading="false"
|
|
/>
|
|
</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="active">Active</option>
|
|
<option value="inactive">Inactive</option>
|
|
</select>
|
|
|
|
<div class="flex gap-2">
|
|
<flux:button wire:click="$refresh" variant="outline" size="sm">
|
|
<flux:icon name="arrow-path" class="size-4" />
|
|
Refresh
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Customers 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('first_name')" class="flex items-center space-x-1 hover:text-blue-600">
|
|
<span>Name</span>
|
|
@if($sortBy === 'first_name')
|
|
<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('email')" class="flex items-center space-x-1 hover:text-blue-600">
|
|
<span>Contact</span>
|
|
@if($sortBy === 'email')
|
|
<flux:icon name="{{ $sortDirection === 'asc' ? 'chevron-up' : 'chevron-down' }}" class="size-3" />
|
|
@endif
|
|
</button>
|
|
</th>
|
|
<th class="text-left py-3 px-4">Address</th>
|
|
<th class="text-left py-3 px-4">Vehicles</th>
|
|
<th class="text-left py-3 px-4">Portal Access</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($customers as $customer)
|
|
<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>
|
|
<div class="font-medium">{{ $customer->full_name }}</div>
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">ID: {{ $customer->id }}</div>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div>
|
|
<div class="text-sm">{{ $customer->email }}</div>
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">{{ $customer->phone }}</div>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm">
|
|
{{ $customer->city }}, {{ $customer->state }}
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm">
|
|
<flux:badge variant="outline" size="sm">
|
|
{{ $customer->vehicles->count() }} vehicle(s)
|
|
</flux:badge>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
@if($customer->user && $customer->user->isCustomer())
|
|
<flux:badge variant="success" size="sm">
|
|
<flux:icon name="check-circle" class="size-3 mr-1" />
|
|
Active
|
|
</flux:badge>
|
|
@else
|
|
<flux:badge variant="outline" size="sm">
|
|
<flux:icon name="x-circle" class="size-3 mr-1" />
|
|
No Access
|
|
</flux:badge>
|
|
@endif
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm">
|
|
@if($customer->last_service_date)
|
|
{{ $customer->last_service_date->format('M j, Y') }}
|
|
@else
|
|
<span class="text-zinc-400 dark:text-zinc-500">Never</span>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<flux:badge
|
|
:color="$customer->status === 'active' ? 'green' : 'gray'"
|
|
size="sm"
|
|
>
|
|
{{ ucfirst($customer->status) }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="flex space-x-2">
|
|
<flux:button href="/customers/{{ $customer->id }}" variant="outline" size="sm">
|
|
View
|
|
</flux:button>
|
|
<flux:button href="/customers/{{ $customer->id }}/edit" variant="outline" size="sm">
|
|
Edit
|
|
</flux:button>
|
|
<flux:button href="/service-orders/create?customer={{ $customer->id }}" size="sm">
|
|
New Order
|
|
</flux:button>
|
|
<flux:button
|
|
wire:click="deleteCustomer({{ $customer->id }})"
|
|
wire:confirm="Are you sure you want to delete {{ $customer->full_name }}? This action cannot be undone."
|
|
variant="danger"
|
|
size="sm"
|
|
>
|
|
Delete
|
|
</flux:button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="7" class="text-center py-8 text-zinc-500 dark:text-zinc-400">
|
|
@if($search)
|
|
No customers found matching "{{ $search }}"
|
|
@else
|
|
No customers found. <a href="/customers/create" class="text-blue-600 hover:underline">Add your first customer</a>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
@if($customers->hasPages())
|
|
<div class="mt-4 px-4 pb-4">
|
|
{{ $customers->links() }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|