- 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.
218 lines
12 KiB
PHP
218 lines
12 KiB
PHP
<div class="space-y-6">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h1 class="text-3xl font-bold text-gray-900 dark:text-gray-100">Create Estimate</h1>
|
|
<p class="mt-1 text-gray-600 dark:text-gray-400">
|
|
Create a detailed estimate for {{ $diagnosis->jobCard->customer->name }} -
|
|
{{ $diagnosis->jobCard->vehicle->year }} {{ $diagnosis->jobCard->vehicle->make }} {{ $diagnosis->jobCard->vehicle->model }}
|
|
</p>
|
|
</div>
|
|
<div class="flex space-x-3">
|
|
<flux:button variant="ghost" href="{{ route('estimates.index') }}">
|
|
Cancel
|
|
</flux:button>
|
|
<flux:button wire:click="save" variant="primary">
|
|
Save Estimate
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Card Information -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Job Information</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Job Card Number</flux:label>
|
|
<flux:input value="{{ $diagnosis->jobCard->job_number }}" readonly />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Customer</flux:label>
|
|
<flux:input value="{{ $diagnosis->jobCard->customer->name }}" readonly />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Vehicle</flux:label>
|
|
<flux:input value="{{ $diagnosis->jobCard->vehicle->year }} {{ $diagnosis->jobCard->vehicle->make }} {{ $diagnosis->jobCard->vehicle->model }}" readonly />
|
|
</flux:field>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Estimate Settings -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Estimate Settings</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Validity Period (Days)</flux:label>
|
|
<flux:input wire:model.live="validity_period_days" type="number" min="1" max="365" />
|
|
<flux:error name="validity_period_days" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Tax Rate (%)</flux:label>
|
|
<flux:input wire:model.live="tax_rate" type="number" step="0.01" min="0" max="100" />
|
|
<flux:error name="tax_rate" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Discount Amount ($)</flux:label>
|
|
<flux:input wire:model.live="discount_amount" type="number" step="0.01" min="0" />
|
|
<flux:error name="discount_amount" />
|
|
</flux:field>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Line Items -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
|
|
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Line Items</h3>
|
|
<flux:button wire:click="addLineItem" variant="outline" size="sm">
|
|
<svg class="w-6 h-6 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
</svg>
|
|
Add Item
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
<thead class="bg-gray-50 dark:bg-gray-700">
|
|
<tr>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Type</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Description</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Qty</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Unit Price</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Total</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
|
@forelse($lineItems as $index => $item)
|
|
<tr wire:key="line-item-{{ $index }}">
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<flux:select wire:model.live="lineItems.{{ $index }}.type">
|
|
<option value="labour">Labour</option>
|
|
<option value="parts">Parts</option>
|
|
<option value="miscellaneous">Miscellaneous</option>
|
|
</flux:select>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<flux:input wire:model.live="lineItems.{{ $index }}.description" placeholder="Item description" class="w-full" />
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<flux:input wire:model.live="lineItems.{{ $index }}.quantity" type="number" step="0.01" min="0" class="w-24" />
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<flux:input wire:model.live="lineItems.{{ $index }}.unit_price" type="number" step="0.01" min="0" class="w-32" />
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
${{ number_format($item['total_amount'] ?? 0, 2) }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
@if(!($item['required'] ?? false))
|
|
<flux:button wire:click="removeLineItem({{ $index }})" variant="ghost" size="sm" 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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
|
</svg>
|
|
</flux:button>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="6" class="px-6 py-12 text-center text-gray-500 dark:text-gray-400">
|
|
No line items added yet. Click "Add Item" to get started.
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Totals Section -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Estimate Totals</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<!-- Terms and Conditions -->
|
|
<flux:field>
|
|
<flux:label>Terms and Conditions</flux:label>
|
|
<flux:textarea wire:model="terms_and_conditions" rows="4" placeholder="Enter terms and conditions..." />
|
|
<flux:error name="terms_and_conditions" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<!-- Financial Summary -->
|
|
<div class="space-y-3">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600 dark:text-gray-400">Subtotal:</span>
|
|
<span class="font-medium text-gray-900 dark:text-gray-100">${{ number_format($subtotal, 2) }}</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600 dark:text-gray-400">Discount:</span>
|
|
<span class="font-medium text-gray-900 dark:text-gray-100">-${{ number_format($discount_amount, 2) }}</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600 dark:text-gray-400">Tax ({{ number_format($tax_rate, 2) }}%):</span>
|
|
<span class="font-medium text-gray-900 dark:text-gray-100">${{ number_format($tax_amount, 2) }}</span>
|
|
</div>
|
|
<div class="border-t border-gray-200 dark:border-gray-700 pt-3">
|
|
<div class="flex justify-between">
|
|
<span class="text-lg font-semibold text-gray-900 dark:text-gray-100">Total:</span>
|
|
<span class="text-xl font-bold text-accent">${{ number_format($total_amount, 2) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notes Section -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Notes</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Customer Notes</flux:label>
|
|
<flux:textarea wire:model="notes" rows="4" placeholder="Notes visible to customer..." />
|
|
<flux:error name="notes" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Internal Notes</flux:label>
|
|
<flux:textarea wire:model="internal_notes" rows="4" placeholder="Internal notes (not visible to customer)..." />
|
|
<flux:error name="internal_notes" />
|
|
</flux:field>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex justify-end space-x-3">
|
|
<flux:button variant="ghost" href="{{ route('estimates.index') }}">
|
|
Cancel
|
|
</flux:button>
|
|
<flux:button wire:click="save" variant="primary">
|
|
<svg class="w-6 h-6 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
</svg>
|
|
Create Estimate
|
|
</flux:button>
|
|
</div>
|
|
</div>
|