- Added buttons for assigning diagnosis and starting diagnosis based on job card status in the job card view. - Implemented a modal for assigning technicians for diagnosis, including form validation and technician selection. - Updated routes to include a test route for job cards. - Created a new Blade view for testing inspection inputs. - Developed comprehensive feature tests for the estimate module, including creation, viewing, editing, and validation of estimates. - Added tests for estimate model relationships and statistics calculations. - Introduced a basic feature test for job cards index.
260 lines
13 KiB
PHP
260 lines
13 KiB
PHP
<div class="space-y-6">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between">
|
|
<flux:heading size="xl">Job Cards</flux:heading>
|
|
<flux:button href="{{ route('job-cards.create') }}" size="sm">
|
|
<flux:icon name="plus" class="size-4" />
|
|
New Job Card
|
|
</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
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-4">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<div class="w-8 h-8 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center">
|
|
<flux:icon name="document-text" class="size-4 text-blue-600 dark:text-blue-400" />
|
|
</div>
|
|
</div>
|
|
<div class="ml-3">
|
|
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Total</p>
|
|
<p class="text-2xl font-bold text-zinc-900 dark:text-zinc-100">{{ $statistics['total'] }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-4">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<div class="w-8 h-8 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center">
|
|
<flux:icon name="check" class="size-4 text-green-600 dark:text-green-400" />
|
|
</div>
|
|
</div>
|
|
<div class="ml-3">
|
|
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Received</p>
|
|
<p class="text-2xl font-bold text-zinc-900 dark:text-zinc-100">{{ $statistics['received'] }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-4">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<div class="w-8 h-8 bg-yellow-100 dark:bg-yellow-900 rounded-lg flex items-center justify-center">
|
|
<flux:icon name="clock" class="size-4 text-yellow-600 dark:text-yellow-400" />
|
|
</div>
|
|
</div>
|
|
<div class="ml-3">
|
|
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">In Progress</p>
|
|
<p class="text-2xl font-bold text-zinc-900 dark:text-zinc-100">{{ $statistics['in_progress'] }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-4">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<div class="w-8 h-8 bg-orange-100 dark:bg-orange-900 rounded-lg flex items-center justify-center">
|
|
<flux:icon name="exclamation-triangle" class="size-4 text-orange-600 dark:text-orange-400" />
|
|
</div>
|
|
</div>
|
|
<div class="ml-3">
|
|
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Pending Approval</p>
|
|
<p class="text-2xl font-bold text-zinc-900 dark:text-zinc-100">{{ $statistics['pending_approval'] }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-4">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<div class="w-8 h-8 bg-purple-100 dark:bg-purple-900 rounded-lg flex items-center justify-center">
|
|
<flux:icon name="check-badge" class="size-4 text-purple-600 dark:text-purple-400" />
|
|
</div>
|
|
</div>
|
|
<div class="ml-3">
|
|
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Completed Today</p>
|
|
<p class="text-2xl font-bold text-zinc-900 dark:text-zinc-100">{{ $statistics['completed_today'] }}</p>
|
|
</div>
|
|
</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-3 gap-4 p-4">
|
|
<flux:input
|
|
wire:model.live="search"
|
|
placeholder="Search job cards..."
|
|
icon="magnifying-glass"
|
|
/>
|
|
|
|
<flux:select wire:model.live="statusFilter" placeholder="All Statuses">
|
|
@foreach($statusOptions as $value => $label)
|
|
<option value="{{ $value }}">{{ $label }}</option>
|
|
@endforeach
|
|
</flux:select>
|
|
|
|
<flux:select wire:model.live="branchFilter" placeholder="All Branches">
|
|
@foreach($branchOptions as $value => $label)
|
|
<option value="{{ $value }}">{{ $label }}</option>
|
|
@endforeach
|
|
</flux:select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Cards Table -->
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg overflow-hidden">
|
|
<div class="px-6 py-4 border-b border-zinc-200 dark:border-zinc-700">
|
|
<flux:heading size="lg">Job Cards</flux:heading>
|
|
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
|
Showing {{ $jobCards->count() }} of {{ $jobCards->total() }} job cards
|
|
</p>
|
|
</div>
|
|
|
|
@if($jobCards->count() > 0)
|
|
<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('job_card_number')" class="flex items-center space-x-1 hover:text-blue-600">
|
|
<span>Job Card</span>
|
|
@if($sortBy === 'job_card_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</th>
|
|
<th class="text-left py-3 px-4">Vehicle</th>
|
|
<th class="text-left py-3 px-4">Status</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>Date 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>
|
|
@foreach($jobCards as $jobCard)
|
|
<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 text-zinc-900 dark:text-zinc-100">
|
|
#{{ $jobCard->job_card_number }}
|
|
</div>
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">
|
|
{{ $jobCard->branch_code }}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
|
|
<td class="py-3 px-4">
|
|
<div>
|
|
<div class="text-zinc-900 dark:text-zinc-100">
|
|
{{ $jobCard->customer->name ?? 'Unknown Customer' }}
|
|
</div>
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">
|
|
{{ $jobCard->customer->phone ?? '' }}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
|
|
<td class="py-3 px-4">
|
|
<div>
|
|
<div class="text-zinc-900 dark:text-zinc-100">
|
|
{{ $jobCard->vehicle->make ?? '' }} {{ $jobCard->vehicle->model ?? '' }}
|
|
</div>
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">
|
|
{{ $jobCard->vehicle->license_plate ?? '' }}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
|
|
<td class="py-3 px-4">
|
|
<flux:badge
|
|
size="sm"
|
|
:color="match($jobCard->status) {
|
|
'received' => 'blue',
|
|
'in_diagnosis', 'in_progress', 'parts_procurement' => 'yellow',
|
|
'completed' => 'green',
|
|
'delivered' => 'purple',
|
|
default => 'zinc'
|
|
}"
|
|
>
|
|
{{ $statusOptions[$jobCard->status] ?? $jobCard->status }}
|
|
</flux:badge>
|
|
</td>
|
|
|
|
<td class="py-3 px-4">
|
|
<div class="text-sm text-zinc-500 dark:text-zinc-400">
|
|
{{ $jobCard->created_at->format('M j, Y') }}
|
|
</div>
|
|
</td>
|
|
|
|
<td class="py-3 px-4">
|
|
<div class="flex items-center gap-2">
|
|
<flux:button size="xs" variant="ghost" href="{{ route('job-cards.show', $jobCard) }}">
|
|
<flux:icon name="eye" class="size-4" />
|
|
View
|
|
</flux:button>
|
|
<flux:button size="xs" variant="ghost" href="{{ route('job-cards.edit', $jobCard) }}">
|
|
<flux:icon name="pencil" class="size-4" />
|
|
Edit
|
|
</flux:button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div class="px-6 py-4 border-t border-zinc-200 dark:border-zinc-700">
|
|
{{ $jobCards->links() }}
|
|
</div>
|
|
@else
|
|
<div class="text-center py-12">
|
|
<flux:icon name="document-text" class="mx-auto size-12 text-zinc-400" />
|
|
<flux:heading size="lg" class="mt-4">No job cards found</flux:heading>
|
|
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">Get started by creating your first job card.</p>
|
|
<div class="mt-6">
|
|
<flux:button href="{{ route('job-cards.create') }}">
|
|
<flux:icon name="plus" class="size-4" />
|
|
New Job Card
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|