- 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.
751 lines
48 KiB
PHP
751 lines
48 KiB
PHP
<div>
|
|
<div class="max-w-6xl mx-auto space-y-6">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<flux:heading size="xl">{{ ucfirst($type) }} Vehicle Inspection</flux:heading>
|
|
<flux:subheading>Job Card: {{ $jobCard->job_card_number }} - {{ $jobCard->customer->name ?? 'Unknown Customer' }}</flux:subheading>
|
|
</div>
|
|
|
|
<flux:button href="{{ route('job-cards.show', $jobCard) }}" variant="ghost" size="sm" icon="arrow-left">
|
|
Back to Job Card
|
|
</flux:button>
|
|
</div>
|
|
|
|
<!-- Workflow Progress -->
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-6">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center space-x-4">
|
|
<!-- Step 1: Vehicle Reception -->
|
|
<div class="flex items-center">
|
|
<div class="flex items-center justify-center w-8 h-8 bg-green-500 text-white rounded-full text-sm font-semibold">
|
|
✓
|
|
</div>
|
|
<span class="ml-2 text-sm font-medium text-green-600 dark:text-green-400">Vehicle Reception</span>
|
|
</div>
|
|
|
|
<div class="w-8 h-0.5 bg-green-500"></div>
|
|
|
|
<!-- Step 2: Initial Inspection (Current) -->
|
|
<div class="flex items-center">
|
|
<div class="flex items-center justify-center w-8 h-8 bg-blue-500 text-white rounded-full text-sm font-semibold animate-pulse">
|
|
2
|
|
</div>
|
|
<span class="ml-2 text-sm font-medium text-blue-600 dark:text-blue-400">{{ ucfirst($type) }} Inspection</span>
|
|
</div>
|
|
|
|
<div class="w-8 h-0.5 bg-zinc-300 dark:bg-zinc-600"></div>
|
|
|
|
<!-- Step 3: Diagnosis -->
|
|
<div class="flex items-center">
|
|
<div class="flex items-center justify-center w-8 h-8 bg-zinc-300 dark:bg-zinc-600 text-zinc-600 dark:text-zinc-400 rounded-full text-sm font-semibold">
|
|
3
|
|
</div>
|
|
<span class="ml-2 text-sm text-zinc-500 dark:text-zinc-400">Diagnosis</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vehicle Information -->
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-6">
|
|
<flux:heading size="lg" class="mb-4 flex items-center">
|
|
<svg class="w-5 h-5 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12a1 1 0 01-1-1V7a1 1 0 011-1h6a1 1 0 011 1v4a1 1 0 01-1 1m-6 0h6m-6 0v4a1 1 0 001 1h4a1 1 0 001-1v-4"></path>
|
|
</svg>
|
|
Vehicle Information
|
|
</flux:heading>
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 text-sm">
|
|
<div>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">Customer:</span>
|
|
<div class="text-zinc-900 dark:text-zinc-100">{{ $jobCard->customer->name ?? 'Unknown Customer' }}</div>
|
|
@if($jobCard->customer->phone)
|
|
<div class="text-zinc-500 dark:text-zinc-400">{{ $jobCard->customer->phone }}</div>
|
|
@endif
|
|
</div>
|
|
<div>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">Vehicle:</span>
|
|
<div class="text-zinc-900 dark:text-zinc-100">{{ $jobCard->vehicle->year ?? '' }} {{ $jobCard->vehicle->make ?? '' }} {{ $jobCard->vehicle->model ?? '' }}</div>
|
|
</div>
|
|
<div>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">License Plate:</span>
|
|
<div class="text-zinc-900 dark:text-zinc-100">{{ $jobCard->vehicle->license_plate ?? '' }}</div>
|
|
</div>
|
|
<div>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">VIN:</span>
|
|
<div class="text-zinc-900 dark:text-zinc-100 text-xs break-all">{{ $jobCard->vehicle->vin ?? 'N/A' }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if($jobCard->customer_reported_issues)
|
|
<div class="mt-4 p-4 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
|
<h5 class="font-medium text-amber-800 dark:text-amber-200 mb-2">Customer Reported Issues:</h5>
|
|
<p class="text-amber-700 dark:text-amber-300 text-sm">{{ $jobCard->customer_reported_issues }}</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Inspection Form -->
|
|
<form wire:submit="save" class="space-y-6">
|
|
<!-- Basic Information -->
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-6">
|
|
<flux:heading size="lg" class="mb-6">Basic Information</flux:heading>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<div>
|
|
@if($jobCard->mileage_in)
|
|
<flux:input
|
|
wire:model="current_mileage"
|
|
label="Current Mileage (km)"
|
|
type="number"
|
|
readonly
|
|
required
|
|
/>
|
|
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
|
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
Pulled from Job Card
|
|
</p>
|
|
@else
|
|
<flux:input
|
|
wire:model="current_mileage"
|
|
label="Current Mileage (km)"
|
|
type="number"
|
|
placeholder="Enter current mileage"
|
|
required
|
|
/>
|
|
@endif
|
|
@error('current_mileage')
|
|
<flux:error>{{ $message }}</flux:error>
|
|
@enderror
|
|
</div>
|
|
|
|
<div>
|
|
@if($jobCard->fuel_level_in)
|
|
<div>
|
|
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Fuel Level</label>
|
|
<select disabled class="w-full px-3 py-2 border border-zinc-300 dark:border-zinc-600 rounded-md bg-zinc-50 dark:bg-zinc-700 text-zinc-900 dark:text-zinc-100">
|
|
<option value="{{ $fuel_level }}">{{ ucwords(str_replace('_', ' ', $fuel_level)) }}</option>
|
|
</select>
|
|
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
|
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
Pulled from Job Card
|
|
</p>
|
|
</div>
|
|
@else
|
|
<flux:select wire:model="fuel_level" label="Fuel Level" required>
|
|
<option value="">Select fuel level...</option>
|
|
<option value="empty">Empty (0-10%)</option>
|
|
<option value="low">Low (10-25%)</option>
|
|
<option value="quarter">Quarter (25-40%)</option>
|
|
<option value="half">Half (40-60%)</option>
|
|
<option value="three_quarter">Three Quarter (60-85%)</option>
|
|
<option value="full">Full (85-100%)</option>
|
|
</flux:select>
|
|
@endif
|
|
@error('fuel_level')
|
|
<flux:error>{{ $message }}</flux:error>
|
|
@enderror
|
|
</div>
|
|
|
|
<div>
|
|
<flux:select wire:model="overall_condition" label="Overall Condition" required>
|
|
<option value="">Select condition...</option>
|
|
<option value="excellent">Excellent</option>
|
|
<option value="good">Good</option>
|
|
<option value="fair">Fair</option>
|
|
<option value="poor">Poor</option>
|
|
</flux:select>
|
|
@error('overall_condition')
|
|
<flux:error>{{ $message }}</flux:error>
|
|
@enderror
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inspection Checklist -->
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-6">
|
|
<flux:heading size="lg" class="mb-6">Vehicle Inspection Checklist</flux:heading>
|
|
|
|
@error('checklist')
|
|
<div class="mb-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
|
<div class="flex">
|
|
<svg class="w-5 h-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
|
</svg>
|
|
<div class="ml-3">
|
|
<h3 class="text-sm font-medium text-red-800 dark:text-red-200">Checklist Incomplete</h3>
|
|
<p class="mt-1 text-sm text-red-700 dark:text-red-300">{{ $message }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@enderror
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<!-- Left Column -->
|
|
<div class="space-y-8">
|
|
<!-- Documentation -->
|
|
<div>
|
|
<h4 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4 flex items-center">
|
|
<svg class="w-5 h-5 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
</svg>
|
|
Documentation
|
|
</h4>
|
|
<div class="space-y-3">
|
|
@foreach([
|
|
'manufacturers_handbook' => "Manufacturer's handbook",
|
|
'service_record_book' => 'Service record book (up-to-date)',
|
|
'company_drivers_handbook' => "Company driver's handbook",
|
|
'accident_report_form' => 'Accident report form',
|
|
'safety_inspection_sticker' => 'Copy of annual safety inspection sticker or form'
|
|
] as $key => $label)
|
|
<div class="flex items-center justify-between py-2 border-b border-zinc-100 dark:border-zinc-700">
|
|
<span class="text-sm text-zinc-700 dark:text-zinc-300">{{ $label }}</span>
|
|
<div class="flex space-x-4">
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.documentation.{{ $key }}" value="yes" class="text-green-600 focus:ring-green-500">
|
|
<span class="ml-1 text-xs text-green-600">Yes</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.documentation.{{ $key }}" value="no" class="text-red-600 focus:ring-red-500">
|
|
<span class="ml-1 text-xs text-red-600">No</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vehicle Interior -->
|
|
<div>
|
|
<h4 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4 flex items-center">
|
|
<svg class="w-5 h-5 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path>
|
|
</svg>
|
|
Vehicle Interior
|
|
</h4>
|
|
<div class="space-y-3">
|
|
@foreach([
|
|
'heating' => 'Heating',
|
|
'air_conditioning' => 'Air Conditioning',
|
|
'windshield_defrosting_system' => 'Windshield defrosting system',
|
|
'window_operation' => 'Window operation',
|
|
'door_handles_locks' => 'Door handles / locks',
|
|
'alarm' => 'Alarm',
|
|
'signals' => 'Signals',
|
|
'seat_belts_work' => 'Seat belts work and free of damage / excessive wear',
|
|
'interior_lights' => 'Interior Lights',
|
|
'mirrors_good_position' => 'Mirrors are in good position and properly adjusted',
|
|
'warning_lights' => 'No warning lights are on',
|
|
'fuel_levels' => 'Fuel levels',
|
|
'oil_level_sufficient' => 'Oil level is sufficiently high',
|
|
'washer_fluids_sufficient' => 'Washer fluids levels are sufficiently high',
|
|
'radiator_fluid_sufficient' => 'Radiator fluid levels are sufficient',
|
|
'emergency_roadside_supplies' => 'Emergency roadside supplies are properly stocked and located in the trunk of vehicle'
|
|
] as $key => $label)
|
|
<div class="flex items-center justify-between py-2 border-b border-zinc-100 dark:border-zinc-700">
|
|
<span class="text-sm text-zinc-700 dark:text-zinc-300">{{ $label }}</span>
|
|
<div class="flex space-x-4">
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.interior.{{ $key }}" value="yes" class="text-green-600 focus:ring-green-500">
|
|
<span class="ml-1 text-xs text-green-600">Yes</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.interior.{{ $key }}" value="no" class="text-red-600 focus:ring-red-500">
|
|
<span class="ml-1 text-xs text-red-600">No</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Engine Compartment -->
|
|
<div>
|
|
<h4 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4 flex items-center">
|
|
<svg class="w-5 h-5 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
|
</svg>
|
|
Engine Compartment
|
|
</h4>
|
|
<div class="space-y-3">
|
|
@foreach([
|
|
'engine_oil_level' => 'Engine oil level',
|
|
'coolant_level_antifreeze' => 'Coolant level (anti-freeze)',
|
|
'battery_secured' => 'Battery secured',
|
|
'brake_fluid_level' => 'Brake fluid level',
|
|
'air_filter_clean' => 'Air filter is clean',
|
|
'belts_hoses_good' => 'Belts and hoses are in good condition - no cracks, full with adequate tension'
|
|
] as $key => $label)
|
|
<div class="flex items-center justify-between py-2 border-b border-zinc-100 dark:border-zinc-700">
|
|
<span class="text-sm text-zinc-700 dark:text-zinc-300">{{ $label }}</span>
|
|
<div class="flex space-x-4">
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.engine.{{ $key }}" value="yes" class="text-green-600 focus:ring-green-500">
|
|
<span class="ml-1 text-xs text-green-600">Yes</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.engine.{{ $key }}" value="no" class="text-red-600 focus:ring-red-500">
|
|
<span class="ml-1 text-xs text-red-600">No</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column -->
|
|
<div class="space-y-8">
|
|
<!-- Vehicle Exterior -->
|
|
<div>
|
|
<h4 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4 flex items-center">
|
|
<svg class="w-5 h-5 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 14v3m4-3v3m4-3v3M3 21h18M3 10h18M3 7l9-4 9 4M4 10h16v11H4V10z"></path>
|
|
</svg>
|
|
Vehicle Exterior
|
|
</h4>
|
|
<div class="space-y-3">
|
|
@foreach([
|
|
'windshield_not_cracked' => 'Windows/windshield not severely cracked',
|
|
'windshield_wipers_functional' => 'Functional Windshield wipers',
|
|
'headlights_high_low' => 'Headlights (high/low beam)',
|
|
'tail_lights_brake_lights' => 'Tail lights / brake lights',
|
|
'emergency_brake_working' => 'Emergency brake in good working order',
|
|
'power_brakes_working' => 'Power brakes are in good working order',
|
|
'horn_works' => 'Horn works',
|
|
'tires_good_shape' => 'Tires in good shape (no damages and adequately inflated)',
|
|
'no_air_leaks' => 'No air leaks (walk around the vehicle and listen for air leaks while driver applies the brakes)',
|
|
'no_oil_grease_leaks' => 'No oil / grease leaks (oil wheel seats or under the vehicle)',
|
|
'no_fuel_leaks' => 'No fuel leaks or odour of gasoline detected',
|
|
'mirrors_good_position' => 'Mirrors are in good position and properly adjusted',
|
|
'exhaust_system_working' => 'Exhaust system is in good working order',
|
|
'wheels_fasteners_tight' => 'Wheels and fasteners are fitted tightly',
|
|
'turn_signals' => 'Turn signals',
|
|
'vehicle_free_damage' => 'Vehicle is free of excessive damage',
|
|
'loads_fastened' => 'All loads are fastened / secured',
|
|
'spare_tire_good' => 'Spare tire is in good condition',
|
|
'vehicle_condition_satisfactory' => 'Vehicle condition is satisfactory',
|
|
'defects_recorded' => 'Defects recorded'
|
|
] as $key => $label)
|
|
<div class="flex items-center justify-between py-2 border-b border-zinc-100 dark:border-zinc-700">
|
|
<span class="text-sm text-zinc-700 dark:text-zinc-300">{{ $label }}</span>
|
|
<div class="flex space-x-4">
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.exterior.{{ $key }}" value="yes" class="text-green-600 focus:ring-green-500">
|
|
<span class="ml-1 text-xs text-green-600">Yes</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="radio" wire:model="checklist.exterior.{{ $key }}" value="no" class="text-red-600 focus:ring-red-500">
|
|
<span class="ml-1 text-xs text-red-600">No</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vehicle Damage Diagram -->
|
|
<div>
|
|
<h4 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4 flex items-center">
|
|
<svg class="w-5 h-5 mr-2 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
|
</svg>
|
|
Vehicle Damage Diagram
|
|
</h4>
|
|
<div class="bg-zinc-50 dark:bg-zinc-700 rounded-lg p-4">
|
|
<p class="text-sm text-zinc-600 dark:text-zinc-400 mb-4">Click on any area of the vehicle to mark damage, dents, or scratches</p>
|
|
|
|
<!-- Vehicle Diagram Container -->
|
|
<div class="border-2 border-dashed border-zinc-300 dark:border-zinc-600 rounded-lg bg-white dark:bg-zinc-800 p-4">
|
|
<div id="vehicle-diagram-container" class="relative">
|
|
<!-- Vehicle SVG Diagram -->
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450" class="w-full h-auto max-h-96">
|
|
<!-- Left Side View -->
|
|
<g id="left-side" transform="translate(50, 50)">
|
|
<rect x="0" y="50" width="180" height="70" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="left-body"/>
|
|
<circle cx="25" cy="130" r="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="left-front-wheel"/>
|
|
<circle cx="155" cy="130" r="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="left-rear-wheel"/>
|
|
<rect x="0" y="30" width="35" height="20" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="left-front-bumper"/>
|
|
<rect x="145" y="30" width="35" height="20" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="left-rear-bumper"/>
|
|
<text x="90" y="20" text-anchor="middle" class="text-xs fill-zinc-700 dark:fill-zinc-300">Left Side</text>
|
|
</g>
|
|
|
|
<!-- Top View -->
|
|
<g id="top-view" transform="translate(320, 50)">
|
|
<rect x="0" y="0" width="70" height="180" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="roof"/>
|
|
<rect x="-8" y="25" width="16" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="front-left-wheel"/>
|
|
<rect x="62" y="25" width="16" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="front-right-wheel"/>
|
|
<rect x="-8" y="135" width="16" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="rear-left-wheel"/>
|
|
<rect x="62" y="135" width="16" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="rear-right-wheel"/>
|
|
<rect x="10" y="-5" width="50" height="10" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="windshield"/>
|
|
<rect x="10" y="175" width="50" height="10" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="rear-window"/>
|
|
<text x="35" y="-15" text-anchor="middle" class="text-xs fill-zinc-700 dark:fill-zinc-300">Top View</text>
|
|
</g>
|
|
|
|
<!-- Right Side View -->
|
|
<g id="right-side" transform="translate(450, 50)">
|
|
<rect x="0" y="50" width="180" height="70" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="right-body"/>
|
|
<circle cx="25" cy="130" r="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="right-front-wheel"/>
|
|
<circle cx="155" cy="130" r="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="right-rear-wheel"/>
|
|
<rect x="0" y="30" width="35" height="20" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="right-front-bumper"/>
|
|
<rect x="145" y="30" width="35" height="20" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="right-rear-bumper"/>
|
|
<text x="90" y="20" text-anchor="middle" class="text-xs fill-zinc-700 dark:fill-zinc-300">Right Side</text>
|
|
</g>
|
|
|
|
<!-- Front View -->
|
|
<g id="front-view" transform="translate(150, 250)">
|
|
<rect x="0" y="0" width="90" height="70" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="front"/>
|
|
<rect x="-8" y="60" width="20" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="front-left-tire"/>
|
|
<rect x="78" y="60" width="20" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="front-right-tire"/>
|
|
<rect x="15" y="-8" width="60" height="8" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="hood"/>
|
|
<text x="45" y="-15" text-anchor="middle" class="text-xs fill-zinc-700 dark:fill-zinc-300">Front View</text>
|
|
</g>
|
|
|
|
<!-- Rear View -->
|
|
<g id="rear-view" transform="translate(350, 250)">
|
|
<rect x="0" y="0" width="90" height="70" fill="#f8fafc" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="rear"/>
|
|
<rect x="-8" y="60" width="20" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="rear-left-tire"/>
|
|
<rect x="78" y="60" width="20" height="20" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="rear-right-tire"/>
|
|
<rect x="15" y="70" width="60" height="8" fill="#e2e8f0" stroke="#64748b" stroke-width="2" class="clickable-area" data-area="trunk"/>
|
|
<text x="45" y="-10" text-anchor="middle" class="text-xs fill-zinc-700 dark:fill-zinc-300">Rear View</text>
|
|
</g>
|
|
|
|
<!-- Damage markers will be added here dynamically -->
|
|
<g id="damage-markers"></g>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Damage Legend -->
|
|
<div class="mt-4 grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
|
<div class="flex items-center">
|
|
<div class="w-6 h-6 bg-red-500 rounded-full mr-2"></div>
|
|
<span class="text-zinc-700 dark:text-zinc-300">Damage</span>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<div class="w-6 h-6 bg-orange-500 rounded-full mr-2"></div>
|
|
<span class="text-zinc-700 dark:text-zinc-300">Dent</span>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<div class="w-6 h-6 bg-yellow-500 rounded-full mr-2"></div>
|
|
<span class="text-zinc-700 dark:text-zinc-300">Scratch</span>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<div class="w-6 h-6 bg-blue-500 rounded-full mr-2"></div>
|
|
<span class="text-zinc-700 dark:text-zinc-300">Other</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Selected Damage List -->
|
|
<div id="damage-list" class="mt-4">
|
|
<h5 class="font-medium text-zinc-900 dark:text-zinc-100 mb-2">Marked Damage:</h5>
|
|
<div id="damage-items" class="space-y-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Additional Comments Section -->
|
|
<div class="mt-8 pt-6 border-t border-zinc-200 dark:border-zinc-700">
|
|
<h4 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4">Additional Comments</h4>
|
|
<flux:textarea
|
|
wire:model="additional_comments"
|
|
placeholder="Record any additional observations, defects, or comments..."
|
|
rows="4"
|
|
class="w-full"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Inspection Signature Section -->
|
|
<div class="mt-8 pt-6 border-t border-zinc-200 dark:border-zinc-700">
|
|
<h4 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4">Inspection Performed By</h4>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Employee Name</label>
|
|
<input type="text" value="{{ auth()->user()->name }}" readonly
|
|
class="w-full px-3 py-2 border border-zinc-300 dark:border-zinc-600 rounded-md bg-zinc-50 dark:bg-zinc-700 text-zinc-900 dark:text-zinc-100">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Date & Time</label>
|
|
<input type="text" value="{{ now()->format('M d, Y \a\t g:i A') }}" readonly
|
|
class="w-full px-3 py-2 border border-zinc-300 dark:border-zinc-600 rounded-md bg-zinc-50 dark:bg-zinc-700 text-zinc-900 dark:text-zinc-100">
|
|
</div>
|
|
</div>
|
|
<div class="mt-4">
|
|
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Digital Signature</label>
|
|
<div class="px-3 py-2 border border-zinc-300 dark:border-zinc-600 rounded-md bg-zinc-50 dark:bg-zinc-700 text-zinc-600 dark:text-zinc-400 italic">
|
|
By completing this inspection, {{ auth()->user()->name }} confirms the accuracy of all recorded information.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Additional Notes -->
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-6">
|
|
<flux:heading size="lg" class="mb-6">Additional Information</flux:heading>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<flux:textarea
|
|
wire:model="damage_notes"
|
|
label="Damage Notes"
|
|
placeholder="Note any visible damage, scratches, dents, etc..."
|
|
rows="4"
|
|
/>
|
|
@error('damage_notes')
|
|
<flux:error>{{ $message }}</flux:error>
|
|
@enderror
|
|
</div>
|
|
|
|
<div>
|
|
<flux:textarea
|
|
wire:model="recommendations"
|
|
label="Recommendations"
|
|
placeholder="Any recommendations for maintenance or repairs..."
|
|
rows="4"
|
|
/>
|
|
@error('recommendations')
|
|
<flux:error>{{ $message }}</flux:error>
|
|
@enderror
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-6">
|
|
<flux:textarea
|
|
wire:model="notes"
|
|
label="Additional Notes"
|
|
placeholder="Any other observations or notes..."
|
|
rows="3"
|
|
/>
|
|
@error('notes')
|
|
<flux:error>{{ $message }}</flux:error>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Cleanliness Rating</label>
|
|
<flux:select wire:model="cleanliness_rating">
|
|
<option value="1">1 - Very Poor</option>
|
|
<option value="2">2 - Poor</option>
|
|
<option value="3">3 - Fair</option>
|
|
<option value="4">4 - Good</option>
|
|
<option value="5">5 - Excellent</option>
|
|
</flux:select>
|
|
</div>
|
|
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
wire:model="follow_up_required"
|
|
id="follow_up_required"
|
|
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-zinc-300 rounded"
|
|
>
|
|
<label for="follow_up_required" class="ml-2 block text-sm text-zinc-700 dark:text-zinc-300">
|
|
Follow-up inspection required
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="flex justify-between items-center">
|
|
<flux:button href="{{ route('job-cards.show', $jobCard) }}" variant="ghost" size="sm">
|
|
Cancel
|
|
</flux:button>
|
|
<flux:button type="submit" variant="primary" size="sm" wire:loading.attr="disabled" wire:target="save">
|
|
<span wire:loading.remove wire:target="save">Complete {{ ucfirst($type) }} Inspection</span>
|
|
<span wire:loading wire:target="save">Processing...</span>
|
|
</flux:button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Vehicle Damage Diagram Script -->
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
let damageData = @this.get('damage_diagram_data');
|
|
let currentDamageType = 'damage';
|
|
|
|
// Add damage type selector
|
|
const diagramContainer = document.querySelector('#vehicle-diagram-container');
|
|
if (diagramContainer) {
|
|
const typeSelector = document.createElement('div');
|
|
typeSelector.className = 'mb-4 flex gap-2 flex-wrap';
|
|
typeSelector.innerHTML = `
|
|
<button type="button" onclick="setDamageType('damage')" class="damage-type-btn active px-3 py-1 text-xs rounded-full bg-red-500 text-white" data-type="damage">Damage</button>
|
|
<button type="button" onclick="setDamageType('dent')" class="damage-type-btn px-3 py-1 text-xs rounded-full bg-orange-500 text-white" data-type="dent">Dent</button>
|
|
<button type="button" onclick="setDamageType('scratch')" class="damage-type-btn px-3 py-1 text-xs rounded-full bg-yellow-500 text-white" data-type="scratch">Scratch</button>
|
|
<button type="button" onclick="setDamageType('other')" class="damage-type-btn px-3 py-1 text-xs rounded-full bg-blue-500 text-white" data-type="other">Other</button>
|
|
`;
|
|
diagramContainer.insertBefore(typeSelector, diagramContainer.firstChild);
|
|
}
|
|
|
|
// Set damage type function
|
|
window.setDamageType = function(type) {
|
|
currentDamageType = type;
|
|
document.querySelectorAll('.damage-type-btn').forEach(btn => {
|
|
btn.classList.remove('active', 'ring-2', 'ring-offset-2');
|
|
if (btn.dataset.type === type) {
|
|
btn.classList.add('active', 'ring-2', 'ring-offset-2');
|
|
}
|
|
});
|
|
};
|
|
|
|
// Add click handlers to clickable areas
|
|
document.querySelectorAll('.clickable-area').forEach(area => {
|
|
area.style.cursor = 'pointer';
|
|
area.addEventListener('click', function(e) {
|
|
const areaName = this.dataset.area;
|
|
const rect = this.getBoundingClientRect();
|
|
const svg = document.querySelector('#vehicle-diagram-container svg');
|
|
const svgRect = svg.getBoundingClientRect();
|
|
|
|
// Calculate relative position within the SVG
|
|
const x = ((e.clientX - svgRect.left) / svgRect.width) * 800;
|
|
const y = ((e.clientY - svgRect.top) / svgRect.height) * 450;
|
|
|
|
addDamageMarker(areaName, x, y, currentDamageType);
|
|
});
|
|
|
|
// Add hover effect
|
|
area.addEventListener('mouseenter', function() {
|
|
this.style.fill = 'rgba(59, 130, 246, 0.2)';
|
|
});
|
|
|
|
area.addEventListener('mouseleave', function() {
|
|
this.style.fill = '';
|
|
});
|
|
});
|
|
|
|
// Add damage marker function
|
|
function addDamageMarker(area, x, y, type) {
|
|
const colors = {
|
|
damage: '#ef4444',
|
|
dent: '#f97316',
|
|
scratch: '#eab308',
|
|
other: '#3b82f6'
|
|
};
|
|
|
|
const markerId = Date.now();
|
|
const damageMarkers = document.querySelector('#damage-markers');
|
|
|
|
// Create marker circle
|
|
const marker = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
marker.setAttribute('cx', x);
|
|
marker.setAttribute('cy', y);
|
|
marker.setAttribute('r', '8');
|
|
marker.setAttribute('fill', colors[type]);
|
|
marker.setAttribute('stroke', '#fff');
|
|
marker.setAttribute('stroke-width', '2');
|
|
marker.setAttribute('class', 'damage-marker');
|
|
marker.setAttribute('data-id', markerId);
|
|
marker.style.cursor = 'pointer';
|
|
|
|
// Add click to remove
|
|
marker.addEventListener('click', function(e) {
|
|
e.stopPropagation();
|
|
removeDamageMarker(markerId);
|
|
});
|
|
|
|
damageMarkers.appendChild(marker);
|
|
|
|
// Update damage data
|
|
const newDamage = {
|
|
id: markerId,
|
|
area: area,
|
|
x: x,
|
|
y: y,
|
|
type: type,
|
|
description: `${type} on ${area.replace(/-/g, ' ')}`
|
|
};
|
|
|
|
damageData.push(newDamage);
|
|
updateDamageList();
|
|
}
|
|
|
|
// Remove damage marker function
|
|
function removeDamageMarker(id) {
|
|
const marker = document.querySelector(`[data-id="${id}"]`);
|
|
if (marker) {
|
|
marker.remove();
|
|
}
|
|
|
|
const index = damageData.findIndex(item => item.id == id);
|
|
if (index > -1) {
|
|
damageData.splice(index, 1);
|
|
}
|
|
updateDamageList();
|
|
}
|
|
|
|
// Update damage list display
|
|
function updateDamageList() {
|
|
const damageItems = document.querySelector('#damage-items');
|
|
damageItems.innerHTML = '';
|
|
|
|
damageData.forEach(damage => {
|
|
const colors = {
|
|
damage: 'bg-red-100 text-red-800',
|
|
dent: 'bg-orange-100 text-orange-800',
|
|
scratch: 'bg-yellow-100 text-yellow-800',
|
|
other: 'bg-blue-100 text-blue-800'
|
|
};
|
|
|
|
const item = document.createElement('div');
|
|
item.className = `flex items-center justify-between p-2 rounded ${colors[damage.type]}`;
|
|
item.innerHTML = `
|
|
<span class="text-sm">${damage.description}</span>
|
|
<button type="button" onclick="removeDamageMarker(${damage.id})" class="text-red-600 hover:text-red-800">
|
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
</svg>
|
|
</button>
|
|
`;
|
|
damageItems.appendChild(item);
|
|
});
|
|
}
|
|
|
|
// Initialize with existing damage data
|
|
if (damageData && damageData.length > 0) {
|
|
damageData.forEach(damage => {
|
|
const colors = {
|
|
damage: '#ef4444',
|
|
dent: '#f97316',
|
|
scratch: '#eab308',
|
|
other: '#3b82f6'
|
|
};
|
|
|
|
const damageMarkers = document.querySelector('#damage-markers');
|
|
const marker = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
marker.setAttribute('cx', damage.x);
|
|
marker.setAttribute('cy', damage.y);
|
|
marker.setAttribute('r', '8');
|
|
marker.setAttribute('fill', colors[damage.type]);
|
|
marker.setAttribute('stroke', '#fff');
|
|
marker.setAttribute('stroke-width', '2');
|
|
marker.setAttribute('class', 'damage-marker');
|
|
marker.setAttribute('data-id', damage.id);
|
|
marker.style.cursor = 'pointer';
|
|
|
|
marker.addEventListener('click', function(e) {
|
|
e.stopPropagation();
|
|
removeDamageMarker(damage.id);
|
|
});
|
|
|
|
damageMarkers.appendChild(marker);
|
|
});
|
|
updateDamageList();
|
|
}
|
|
|
|
// Make remove function global
|
|
window.removeDamageMarker = removeDamageMarker;
|
|
});
|
|
</script>
|
|
</div>
|