sackey cbae4564b9
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
Add customer portal views for dashboard, estimates, invoices, vehicles, and work orders
- Implemented dashboard view with vehicle stats, active services, recent activity, and upcoming appointments.
- Created estimates view with filtering options and a list of service estimates.
- Developed invoices view to manage service invoices and payment history with filtering.
- Added vehicles view to display registered vehicles and their details.
- Built work orders view to track the progress of vehicle services with filtering and detailed information.
2025-08-08 09:56:26 +00:00

321 lines
18 KiB
PHP

<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<flux:heading size="xl">{{ $customer->full_name }}</flux:heading>
<flux:subheading>Customer #{{ $customer->id }} - {{ ucfirst($customer->status) }}
@if($customer->user && $customer->user->isCustomer())
<flux:badge variant="success" size="sm" class="ml-2">Portal Access</flux:badge>
@else
<flux:badge variant="outline" size="sm" class="ml-2">No Portal</flux:badge>
@endif
</flux:subheading>
</div>
<div class="flex space-x-3">
<flux:button href="/customers-list" variant="outline" size="sm">
<flux:icon name="arrow-left" class="size-4" />
Back to Customers
</flux:button>
<flux:button href="/customers/{{ $customer->id }}/edit" variant="outline" size="sm">
<flux:icon name="pencil" class="size-4" />
Edit Customer
</flux:button>
<flux:button href="/service-orders/create?customer={{ $customer->id }}" size="sm">
<flux:icon name="plus" class="size-4" />
New Service Order
</flux:button>
</div>
</div>
<!-- Customer Stats -->
<div class="grid grid-cols-2 md:grid-cols-6 gap-4">
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-blue-600">{{ $stats['total_vehicles'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Vehicles</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-green-600">{{ $stats['total_service_orders'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Service Orders</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-purple-600">{{ $stats['total_appointments'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Appointments</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-orange-600">{{ $stats['active_job_cards'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Active Jobs</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-teal-600">{{ $stats['completed_services'] }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Completed</div>
</div>
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4">
<div class="text-2xl font-bold text-indigo-600">${{ number_format($stats['total_spent'], 2) }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">Total Spent</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Customer Information -->
<div class="lg:col-span-2 space-y-6">
<!-- Contact Information -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
<flux:heading size="lg">Contact Information</flux:heading>
</div>
<div class="p-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<flux:label>Full Name</flux:label>
<div class="mt-1 text-sm">{{ $customer->full_name }}</div>
</div>
<div>
<flux:label>Email</flux:label>
<div class="mt-1 text-sm">
<a href="mailto:{{ $customer->email }}" class="text-blue-600 hover:underline">
{{ $customer->email }}
</a>
</div>
</div>
<div>
<flux:label>Phone</flux:label>
<div class="mt-1 text-sm">
<a href="tel:{{ $customer->phone }}" class="text-blue-600 hover:underline">
{{ $customer->phone }}
</a>
</div>
</div>
@if($customer->secondary_phone)
<div>
<flux:label>Secondary Phone</flux:label>
<div class="mt-1 text-sm">
<a href="tel:{{ $customer->secondary_phone }}" class="text-blue-600 hover:underline">
{{ $customer->secondary_phone }}
</a>
</div>
</div>
@endif
<div class="md:col-span-2">
<flux:label>Address</flux:label>
<div class="mt-1 text-sm">{{ $customer->formatted_address }}</div>
</div>
@if($customer->notes)
<div class="md:col-span-2">
<flux:label>Notes</flux:label>
<div class="mt-1 text-sm">{{ $customer->notes }}</div>
</div>
@endif
</div>
</div>
</div>
<!-- User Account Information -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
<flux:heading size="lg">Customer Portal Access</flux:heading>
</div>
<div class="p-4">
@if($customer->user && $customer->user->isCustomer())
<div class="flex items-center space-x-3 mb-4">
<flux:icon name="check-circle" class="h-6 w-6 text-green-600" />
<div>
<div class="font-medium text-green-800 dark:text-green-200">Customer has portal access</div>
<div class="text-sm text-green-600 dark:text-green-400">Can login and view service history</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<flux:label>User ID</flux:label>
<div class="mt-1 text-sm">#{{ $customer->user->id }}</div>
</div>
<div>
<flux:label>Account Status</flux:label>
<div class="mt-1">
@if($customer->user->status === 'active')
<flux:badge variant="success" size="sm">Active</flux:badge>
@else
<flux:badge variant="danger" size="sm">Inactive</flux:badge>
@endif
</div>
</div>
<div>
<flux:label>Last Login</flux:label>
<div class="mt-1 text-sm">
{{ $customer->user->last_login_at ? $customer->user->last_login_at->format('M j, Y g:i A') : 'Never' }}
</div>
</div>
</div>
<div class="mt-4 pt-4 border-t border-zinc-200 dark:border-zinc-700">
<div class="text-sm text-zinc-600 dark:text-zinc-400">
Portal URL: <a href="/customer-portal" class="text-blue-600 hover:underline">/customer-portal</a>
</div>
</div>
@else
<div class="flex items-center space-x-3">
<flux:icon name="exclamation-triangle" class="h-6 w-6 text-yellow-600" />
<div>
<div class="font-medium text-yellow-800 dark:text-yellow-200">No portal access</div>
<div class="text-sm text-yellow-600 dark:text-yellow-400">Customer cannot login to view their information</div>
</div>
</div>
<div class="mt-4">
<flux:button href="/customers/{{ $customer->id }}/edit" variant="outline" size="sm">
<flux:icon name="plus" class="size-4" />
Create Portal Account
</flux:button>
</div>
@endif
</div>
</div>
<!-- Vehicles -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4 flex items-center justify-between">
<flux:heading size="lg">Vehicles ({{ $customer->vehicles->count() }})</flux:heading>
<flux:button href="/vehicles/create?customer={{ $customer->id }}" size="sm">
<flux:icon name="plus" class="size-4" />
Add Vehicle
</flux:button>
</div>
<div class="p-4">
@forelse($customer->vehicles as $vehicle)
<div class="border border-zinc-200 dark:border-zinc-700 rounded-lg p-4 mb-4 last:mb-0">
<div class="flex items-center justify-between">
<div>
<div class="font-medium">{{ $vehicle->display_name }}</div>
<div class="text-sm text-zinc-600 dark:text-zinc-400">VIN: {{ $vehicle->vin_display }} {{ number_format($vehicle->mileage) }} miles</div>
<div class="text-sm text-zinc-500 dark:text-zinc-400">{{ $vehicle->color }} {{ $vehicle->license_plate }}</div>
</div>
<div class="flex space-x-2">
<flux:button href="/vehicles/{{ $vehicle->id }}" variant="outline" size="sm">View</flux:button>
<flux:button href="/service-orders/create?vehicle={{ $vehicle->id }}" size="sm">Service</flux:button>
</div>
</div>
</div>
@empty
<div class="text-center py-8 text-zinc-500 dark:text-zinc-400">
No vehicles registered yet.
<a href="/vehicles/create?customer={{ $customer->id }}" class="text-blue-600 hover:underline">Add the first vehicle</a>
</div>
@endforelse
</div>
</div>
<!-- Service History -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
<flux:heading size="lg">Service History</flux:heading>
</div>
<div class="overflow-x-auto">
<table class="min-w-full">
<thead>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-2 px-4">Order #</th>
<th class="text-left py-2 px-4">Vehicle</th>
<th class="text-left py-2 px-4">Date</th>
<th class="text-left py-2 px-4">Technician</th>
<th class="text-left py-2 px-4">Status</th>
<th class="text-left py-2 px-4">Total</th>
<th class="text-left py-2 px-4">Actions</th>
</tr>
</thead>
<tbody>
@forelse($customer->serviceOrders as $order)
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<td class="py-2 px-4 font-medium">{{ $order->order_number }}</td>
<td class="py-2 px-4">{{ $order->vehicle->display_name }}</td>
<td class="py-2 px-4">{{ $order->created_at->format('M j, Y') }}</td>
<td class="py-2 px-4">{{ $order->assignedTechnician?->full_name ?? 'Unassigned' }}</td>
<td class="py-2 px-4">
<flux:badge
size="sm"
:color="$order->status === 'completed' ? 'green' : ($order->status === 'in_progress' ? 'blue' : 'gray')"
>
{{ ucfirst(str_replace('_', ' ', $order->status)) }}
</flux:badge>
</td>
<td class="py-2 px-4">${{ number_format($order->total_amount, 2) }}</td>
<td class="py-2 px-4">
<flux:button href="/service-orders/{{ $order->id }}" variant="outline" size="sm">View</flux:button>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center py-8 text-zinc-500 dark:text-zinc-400">No service history yet.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="space-y-6">
<!-- Quick Stats -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4">
<flux:heading size="lg">Quick Stats</flux:heading>
</div>
<div class="p-4 space-y-3">
<div class="flex justify-between">
<span class="text-sm text-zinc-600 dark:text-zinc-400">Total Vehicles</span>
<span class="font-medium">{{ $customer->vehicles->count() }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-zinc-600 dark:text-zinc-400">Service Orders</span>
<span class="font-medium">{{ $customer->serviceOrders->count() }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-zinc-600 dark:text-zinc-400">Total Spent</span>
<span class="font-medium">${{ number_format($customer->serviceOrders->sum('total_amount'), 2) }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-zinc-600 dark:text-zinc-400">Last Service</span>
<span class="font-medium">
@if($customer->last_service_date)
{{ $customer->last_service_date->format('M j, Y') }}
@else
Never
@endif
</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-zinc-600 dark:text-zinc-400">Customer Since</span>
<span class="font-medium">{{ $customer->created_at->format('M j, Y') }}</span>
</div>
</div>
</div>
<!-- Recent Appointments -->
<div class="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700">
<div class="border-b border-zinc-200 dark:border-zinc-700 p-4 flex items-center justify-between">
<flux:heading size="lg">Upcoming Appointments</flux:heading>
<flux:button href="/appointments/create?customer={{ $customer->id }}" size="sm">
<flux:icon name="plus" class="size-4" />
Schedule
</flux:button>
</div>
<div class="p-4">
@forelse($customer->appointments->where('scheduled_datetime', '>=', now())->take(3) as $appointment)
<div class="mb-3 last:mb-0">
<div class="text-sm font-medium">{{ $appointment->scheduled_datetime->format('M j, Y g:i A') }}</div>
<div class="text-xs text-zinc-600 dark:text-zinc-400">{{ $appointment->service_requested }}</div>
<div class="text-xs">
<flux:badge size="sm" :color="$appointment->status === 'confirmed' ? 'green' : 'gray'">
{{ ucfirst($appointment->status) }}
</flux:badge>
</div>
</div>
@empty
<div class="text-sm text-zinc-500 dark:text-zinc-400">No upcoming appointments</div>
@endforelse
</div>
</div>
</div>
</div>
</div>