- 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.
381 lines
22 KiB
PHP
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>
|