- 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.
420 lines
13 KiB
PHP
420 lines
13 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Estimate #{{ $estimate->estimate_number }}</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'DejaVu Sans', Arial, sans-serif;
|
|
font-size: 11px;
|
|
line-height: 1.4;
|
|
color: #333;
|
|
}
|
|
|
|
.header {
|
|
border-bottom: 3px solid #f97316;
|
|
padding-bottom: 20px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.header-content {
|
|
display: table;
|
|
width: 100%;
|
|
}
|
|
|
|
.company-info {
|
|
display: table-cell;
|
|
width: 50%;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.estimate-info {
|
|
display: table-cell;
|
|
width: 50%;
|
|
vertical-align: top;
|
|
text-align: right;
|
|
}
|
|
|
|
.company-name {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
color: #f97316;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.estimate-title {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: #1f2937;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.estimate-number {
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 25px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
color: #1f2937;
|
|
margin-bottom: 10px;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
padding-bottom: 5px;
|
|
}
|
|
|
|
.info-grid {
|
|
display: table;
|
|
width: 100%;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.info-column {
|
|
display: table-cell;
|
|
width: 50%;
|
|
vertical-align: top;
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.info-row {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.info-label {
|
|
font-weight: bold;
|
|
display: inline-block;
|
|
width: 100px;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
font-size: 10px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.status-draft {
|
|
background-color: #f3f4f6;
|
|
color: #374151;
|
|
}
|
|
|
|
.status-sent {
|
|
background-color: #dbeafe;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.status-approved {
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
}
|
|
|
|
.status-rejected {
|
|
background-color: #fee2e2;
|
|
color: #991b1b;
|
|
}
|
|
|
|
.items-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: 15px 0;
|
|
}
|
|
|
|
.items-table th,
|
|
.items-table td {
|
|
border: 1px solid #e5e7eb;
|
|
padding: 8px;
|
|
text-align: left;
|
|
}
|
|
|
|
.items-table th {
|
|
background-color: #f9fafb;
|
|
font-weight: bold;
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.items-table td {
|
|
font-size: 11px;
|
|
}
|
|
|
|
.text-right {
|
|
text-align: right;
|
|
}
|
|
|
|
.text-center {
|
|
text-align: center;
|
|
}
|
|
|
|
.type-badge {
|
|
padding: 2px 6px;
|
|
border-radius: 3px;
|
|
font-size: 9px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.type-labour {
|
|
background-color: #dbeafe;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.type-parts {
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
}
|
|
|
|
.type-miscellaneous {
|
|
background-color: #f3f4f6;
|
|
color: #374151;
|
|
}
|
|
|
|
.summary-table {
|
|
width: 300px;
|
|
margin-left: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.summary-table td {
|
|
padding: 5px 10px;
|
|
border: none;
|
|
}
|
|
|
|
.summary-row {
|
|
border-bottom: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.total-row {
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
border-top: 2px solid #f97316;
|
|
background-color: #fff7ed;
|
|
}
|
|
|
|
.notes-section {
|
|
margin-top: 30px;
|
|
padding: 15px;
|
|
background-color: #f9fafb;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.terms-section {
|
|
margin-top: 20px;
|
|
font-size: 10px;
|
|
color: #6b7280;
|
|
border-top: 1px solid #e5e7eb;
|
|
padding-top: 15px;
|
|
}
|
|
|
|
.footer {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
text-align: center;
|
|
font-size: 9px;
|
|
color: #9ca3af;
|
|
border-top: 1px solid #e5e7eb;
|
|
padding: 10px 0;
|
|
background-color: white;
|
|
}
|
|
|
|
.page-break {
|
|
page-break-before: always;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Header -->
|
|
<div class="header">
|
|
<div class="header-content">
|
|
<div class="company-info">
|
|
<div class="company-name">{{ app(\App\Settings\GeneralSettings::class)->shop_name ?? 'Car Repairs Shop' }}</div>
|
|
<div>{{ app(\App\Settings\GeneralSettings::class)->shop_address ?? 'Shop Address' }}</div>
|
|
<div>{{ app(\App\Settings\GeneralSettings::class)->shop_phone ?? 'Phone Number' }}</div>
|
|
<div>{{ app(\App\Settings\GeneralSettings::class)->shop_email ?? 'Email Address' }}</div>
|
|
</div>
|
|
<div class="estimate-info">
|
|
<div class="estimate-title">ESTIMATE</div>
|
|
<div class="estimate-number">#{{ $estimate->estimate_number }}</div>
|
|
<div>Date: {{ $estimate->created_at->format('M j, Y') }}</div>
|
|
@if($estimate->validity_period_days)
|
|
<div>Valid Until: {{ $estimate->valid_until->format('M j, Y') }}</div>
|
|
@endif
|
|
<div style="margin-top: 10px;">
|
|
<span class="status-badge status-{{ $estimate->status }}">{{ ucfirst($estimate->status) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Customer Information -->
|
|
<div class="section">
|
|
<div class="section-title">Customer Information</div>
|
|
<div class="info-grid">
|
|
<div class="info-column">
|
|
@php
|
|
$customer = $estimate->customer ?? $estimate->jobCard?->customer;
|
|
@endphp
|
|
@if($customer)
|
|
<div class="info-row">
|
|
<span class="info-label">Name:</span>
|
|
{{ $customer->name }}
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Email:</span>
|
|
{{ $customer->email }}
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Phone:</span>
|
|
{{ $customer->phone }}
|
|
</div>
|
|
@else
|
|
<div class="info-row">No customer information available</div>
|
|
@endif
|
|
</div>
|
|
<div class="info-column">
|
|
@php
|
|
$vehicle = $estimate->vehicle ?? $estimate->jobCard?->vehicle;
|
|
@endphp
|
|
@if($vehicle)
|
|
<div class="info-row">
|
|
<span class="info-label">Vehicle:</span>
|
|
{{ $vehicle->year }} {{ $vehicle->make }} {{ $vehicle->model }}
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">License:</span>
|
|
{{ $vehicle->license_plate }}
|
|
</div>
|
|
@if($vehicle->vin)
|
|
<div class="info-row">
|
|
<span class="info-label">VIN:</span>
|
|
{{ $vehicle->vin }}
|
|
</div>
|
|
@endif
|
|
@else
|
|
<div class="info-row">No vehicle specified</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Card Information (if applicable) -->
|
|
@if($estimate->jobCard)
|
|
<div class="section">
|
|
<div class="section-title">Job Information</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Job Card:</span>
|
|
#{{ $estimate->jobCard->job_number }}
|
|
</div>
|
|
@if($estimate->diagnosis)
|
|
<div class="info-row">
|
|
<span class="info-label">Diagnosis:</span>
|
|
{{ Str::limit($estimate->diagnosis->findings, 100) }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Line Items -->
|
|
<div class="section">
|
|
<div class="section-title">Services & Parts</div>
|
|
@if($estimate->lineItems->count() > 0)
|
|
<table class="items-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Type</th>
|
|
<th>Description</th>
|
|
<th class="text-center">Qty</th>
|
|
<th class="text-right">Unit Price</th>
|
|
<th class="text-right">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($estimate->lineItems as $item)
|
|
<tr>
|
|
<td>
|
|
<span class="type-badge type-{{ $item->type }}">
|
|
{{ ucfirst($item->type) }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{{ $item->description }}
|
|
@if($item->part)
|
|
<br><small style="color: #6b7280;">Part #: {{ $item->part->part_number }}</small>
|
|
@endif
|
|
</td>
|
|
<td class="text-center">{{ $item->quantity }}</td>
|
|
<td class="text-right">${{ number_format($item->unit_price, 2) }}</td>
|
|
<td class="text-right">${{ number_format($item->total_amount, 2) }}</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
@else
|
|
<div style="text-align: center; padding: 20px; color: #6b7280;">
|
|
No line items available
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Summary -->
|
|
<table class="summary-table">
|
|
<tr class="summary-row">
|
|
<td>Subtotal:</td>
|
|
<td class="text-right">${{ number_format($estimate->subtotal, 2) }}</td>
|
|
</tr>
|
|
@if($estimate->discount_amount > 0)
|
|
<tr class="summary-row">
|
|
<td>Discount:</td>
|
|
<td class="text-right" style="color: #dc2626;">-${{ number_format($estimate->discount_amount, 2) }}</td>
|
|
</tr>
|
|
@endif
|
|
<tr class="summary-row">
|
|
<td>Tax ({{ $estimate->tax_rate }}%):</td>
|
|
<td class="text-right">${{ number_format($estimate->tax_amount, 2) }}</td>
|
|
</tr>
|
|
<tr class="total-row">
|
|
<td><strong>Total:</strong></td>
|
|
<td class="text-right"><strong>${{ number_format($estimate->total_amount, 2) }}</strong></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<!-- Customer Notes -->
|
|
@if($estimate->notes)
|
|
<div class="notes-section">
|
|
<div class="section-title">Notes</div>
|
|
<div style="white-space: pre-wrap;">{{ $estimate->notes }}</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Terms & Conditions -->
|
|
@if($estimate->terms_and_conditions)
|
|
<div class="terms-section">
|
|
<div style="font-weight: bold; margin-bottom: 10px;">Terms & Conditions</div>
|
|
<div style="white-space: pre-wrap;">{{ $estimate->terms_and_conditions }}</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Footer -->
|
|
<div class="footer">
|
|
<div>Estimate generated on {{ now()->format('M j, Y g:i A') }}</div>
|
|
@if($estimate->preparedBy)
|
|
<div>Prepared by: {{ $estimate->preparedBy->name }}</div>
|
|
@endif
|
|
</div>
|
|
</body>
|
|
</html>
|