sackey 5403c3591d
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
feat: Enhance job card workflow with diagnosis actions and technician assignment modal
- 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.
2025-08-15 08:37:45 +00:00

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>