sackey e3b2b220d2
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
Enhance UI and functionality across various components
- 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.
2025-08-16 14:36:58 +00:00

381 lines
22 KiB
PHP

<div>
<!-- Header -->
<div class="mb-8">
<div class="flex items-center justify-between">
<div>
<div class="flex items-center space-x-4">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Invoice {{ $invoice->invoice_number }}</h1>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium
@switch($invoice->status)
@case('draft')
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200
@break
@case('sent')
bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-200
@break
@case('paid')
bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-200
@break
@case('overdue')
bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-200
@break
@case('cancelled')
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200
@break
@endswitch
">
{{ ucfirst($invoice->status) }}
</span>
</div>
<p class="text-gray-600 dark:text-gray-400">
Created on {{ $invoice->created_at->format('M j, Y g:i A') }} by {{ $invoice->createdBy->name }}
</p>
</div>
<div class="flex space-x-4">
<flux:button variant="outline" wire:click="downloadPDF">
<flux:icon.arrow-down-tray class="w-4 h-4 mr-2" />
Download PDF
</flux:button>
@if($invoice->status !== 'paid')
<flux:button variant="outline" href="{{ route('invoices.edit', $invoice) }}">
<flux:icon.pencil class="w-4 h-4 mr-2" />
Edit
</flux:button>
@if($invoice->status === 'draft')
<flux:button variant="outline" wire:click="sendInvoice">
<flux:icon.paper-airplane class="w-4 h-4 mr-2" />
Send Invoice
</flux:button>
@endif
<flux:button variant="primary" wire:click="markAsPaid">
<flux:icon.check class="w-4 h-4 mr-2" />
Mark as Paid
</flux:button>
@endif
<flux:button variant="outline" wire:click="duplicateInvoice">
<flux:icon.document-duplicate class="w-4 h-4 mr-2" />
Duplicate
</flux:button>
</div>
</div>
</div>
<!-- Invoice Details -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Main Content -->
<div class="lg:col-span-2 space-y-8">
<!-- Invoice Information -->
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Invoice Information</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Invoice Number</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->invoice_number }}</p>
</div>
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Branch</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->branch->name }}</p>
</div>
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Invoice Date</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->invoice_date->format('M j, Y') }}</p>
</div>
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Due Date</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->due_date->format('M j, Y') }}
@if($invoice->isOverdue())
<span class="text-red-600 dark:text-red-400 font-medium">(Overdue)</span>
@endif
</p>
</div>
</div>
@if($invoice->description)
<div class="mt-4">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Description</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->description }}</p>
</div>
@endif
</div>
<!-- Customer Information -->
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Customer Information</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Name</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->customer->name }}</p>
</div>
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Email</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->customer->email }}</p>
</div>
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Phone</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->customer->phone }}</p>
</div>
@if($invoice->customer->address)
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Address</p>
<p class="text-gray-900 dark:text-white">{{ $invoice->customer->address }}</p>
</div>
@endif
</div>
</div>
<!-- Vehicle Information (if applicable) -->
@php
$vehicle = $invoice->serviceOrder?->vehicle ?? $invoice->jobCard?->vehicle;
@endphp
@if($vehicle)
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Vehicle Information</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Vehicle</p>
<p class="text-gray-900 dark:text-white">{{ $vehicle->year }} {{ $vehicle->make }} {{ $vehicle->model }}</p>
</div>
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">License Plate</p>
<p class="text-gray-900 dark:text-white">{{ $vehicle->license_plate }}</p>
</div>
@if($vehicle->vin)
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">VIN</p>
<p class="text-gray-900 dark:text-white">{{ $vehicle->vin }}</p>
</div>
@endif
@if($vehicle->mileage)
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Mileage</p>
<p class="text-gray-900 dark:text-white">{{ number_format($vehicle->mileage) }} miles</p>
</div>
@endif
</div>
</div>
@endif
<!-- Line Items -->
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200 dark:border-zinc-700">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Services & Parts</h2>
</div>
@if($invoice->lineItems->count() > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-zinc-700">
<thead class="bg-gray-50 dark:bg-zinc-900">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Description</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Qty</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Unit Price</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Total</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-zinc-800 divide-y divide-gray-200 dark:divide-zinc-700">
@foreach($invoice->lineItems as $item)
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
@switch($item->type)
@case('labour')
bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-200
@break
@case('parts')
bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-200
@break
@case('miscellaneous')
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200
@break
@endswitch
">
{{ ucfirst($item->type) }}
</span>
</td>
<td class="px-6 py-4">
<div class="text-sm font-medium text-gray-900 dark:text-white">{{ $item->description }}</div>
@if($item->part)
<div class="text-sm text-gray-500 dark:text-gray-400">Part #: {{ $item->part->part_number }}</div>
@endif
@if($item->part_number)
<div class="text-sm text-gray-500 dark:text-gray-400">Part #: {{ $item->part_number }}</div>
@endif
@if($item->technical_notes)
<div class="text-sm text-gray-500 dark:text-gray-400">{{ $item->technical_notes }}</div>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-900 dark:text-white">
{{ $item->quantity }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white">
${{ number_format($item->unit_price, 2) }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium text-gray-900 dark:text-white">
${{ number_format($item->total_amount, 2) }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="px-6 py-12 text-center text-gray-500 dark:text-gray-400">
<flux:icon.document-text class="w-12 h-12 mx-auto mb-4 opacity-50" />
<p>No line items added to this invoice.</p>
</div>
@endif
</div>
<!-- Notes -->
@if($invoice->notes)
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Notes</h2>
<p class="text-gray-700 dark:text-gray-300 whitespace-pre-wrap">{{ $invoice->notes }}</p>
</div>
@endif
</div>
<!-- Sidebar -->
<div class="space-y-6">
<!-- Financial Summary -->
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Financial Summary</h2>
<div class="space-y-3">
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Subtotal:</span>
<span class="text-gray-900 dark:text-white">${{ number_format($invoice->subtotal, 2) }}</span>
</div>
@if($invoice->discount_amount > 0)
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Discount:</span>
<span class="text-red-600 dark:text-red-400">-${{ number_format($invoice->discount_amount, 2) }}</span>
</div>
@endif
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Tax ({{ $invoice->tax_rate }}%):</span>
<span class="text-gray-900 dark:text-white">${{ number_format($invoice->tax_amount, 2) }}</span>
</div>
<div class="border-t border-gray-200 dark:border-zinc-700 pt-3">
<div class="flex justify-between">
<span class="text-lg font-semibold text-gray-900 dark:text-white">Total:</span>
<span class="text-lg font-semibold text-gray-900 dark:text-white">${{ number_format($invoice->total_amount, 2) }}</span>
</div>
</div>
</div>
</div>
<!-- Payment Information -->
@if($invoice->isPaid())
<div class="bg-green-50 dark:bg-green-900/20 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-green-800 dark:text-green-200">Payment Information</h2>
<div class="space-y-3">
<div>
<p class="text-sm font-medium text-green-600 dark:text-green-400">Payment Date</p>
<p class="text-green-800 dark:text-green-200">{{ $invoice->paid_at->format('M j, Y g:i A') }}</p>
</div>
@if($invoice->payment_method)
<div>
<p class="text-sm font-medium text-green-600 dark:text-green-400">Payment Method</p>
<p class="text-green-800 dark:text-green-200">{{ ucfirst(str_replace('_', ' ', $invoice->payment_method)) }}</p>
</div>
@endif
@if($invoice->payment_reference)
<div>
<p class="text-sm font-medium text-green-600 dark:text-green-400">Reference</p>
<p class="text-green-800 dark:text-green-200">{{ $invoice->payment_reference }}</p>
</div>
@endif
@if($invoice->payment_notes)
<div>
<p class="text-sm font-medium text-green-600 dark:text-green-400">Notes</p>
<p class="text-green-800 dark:text-green-200">{{ $invoice->payment_notes }}</p>
</div>
@endif
</div>
</div>
@endif
<!-- Related Records -->
@if($invoice->serviceOrder || $invoice->jobCard || $invoice->estimate)
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Related Records</h2>
<div class="space-y-3">
@if($invoice->serviceOrder)
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Service Order</p>
<flux:button variant="ghost" href="{{ route('service-orders.show', $invoice->serviceOrder) }}" class="p-0 text-blue-600 hover:text-blue-800">
#{{ $invoice->serviceOrder->order_number }}
</flux:button>
</div>
@endif
@if($invoice->jobCard)
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Job Card</p>
<flux:button variant="ghost" href="{{ route('job-cards.show', $invoice->jobCard) }}" class="p-0 text-blue-600 hover:text-blue-800">
#{{ $invoice->jobCard->job_number }}
</flux:button>
</div>
@endif
@if($invoice->estimate)
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Estimate</p>
<flux:button variant="ghost" href="{{ route('estimates.show', $invoice->estimate) }}" class="p-0 text-blue-600 hover:text-blue-800">
#{{ $invoice->estimate->estimate_number }}
</flux:button>
</div>
@endif
</div>
</div>
@endif
<!-- Actions -->
<div class="bg-white dark:bg-zinc-800 rounded-lg shadow p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Quick Actions</h2>
<div class="space-y-3">
<flux:button variant="outline" wire:click="downloadPDF" class="w-full justify-center">
<flux:icon.arrow-down-tray class="w-4 h-4 mr-2" />
Download PDF
</flux:button>
@if($invoice->status !== 'paid')
@if($invoice->status === 'draft')
<flux:button variant="outline" wire:click="sendInvoice" class="w-full justify-center">
<flux:icon.paper-airplane class="w-4 h-4 mr-2" />
Send Invoice
</flux:button>
@endif
<flux:button variant="primary" wire:click="markAsPaid" class="w-full justify-center">
<flux:icon.check class="w-4 h-4 mr-2" />
Mark as Paid
</flux:button>
@endif
<flux:button variant="outline" wire:click="duplicateInvoice" class="w-full justify-center">
<flux:icon.document-duplicate class="w-4 h-4 mr-2" />
Duplicate Invoice
</flux:button>
<flux:button variant="outline" href="{{ route('invoices.index') }}" class="w-full justify-center">
<flux:icon.arrow-left class="w-4 h-4 mr-2" />
Back to Invoices
</flux:button>
</div>
</div>
</div>
</div>
</div>