complete story 7.2 with qa tests
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ConsultationStatus;
|
||||
use App\Models\Consultation;
|
||||
use App\Enums\ConsultationType;
|
||||
use App\Enums\PaymentStatus;
|
||||
use Livewire\Volt\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
@@ -11,78 +12,223 @@ new class extends Component
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
'consultations' => Consultation::query()
|
||||
->where('user_id', auth()->id())
|
||||
'upcoming' => $user->consultations()
|
||||
->approved()
|
||||
->where('booking_date', '>=', today())
|
||||
->orderBy('booking_date')
|
||||
->orderBy('booking_time')
|
||||
->get(),
|
||||
'pending' => $user->consultations()
|
||||
->pending()
|
||||
->latest()
|
||||
->get(),
|
||||
'past' => $user->consultations()
|
||||
->where(function ($query) use ($user) {
|
||||
$query->whereIn('status', [
|
||||
ConsultationStatus::Completed,
|
||||
ConsultationStatus::Cancelled,
|
||||
ConsultationStatus::NoShow,
|
||||
])
|
||||
->orWhere(function ($q) {
|
||||
$q->where('status', ConsultationStatus::Approved)
|
||||
->where('booking_date', '<', today());
|
||||
});
|
||||
})
|
||||
->orderBy('booking_date', 'desc')
|
||||
->paginate(10),
|
||||
];
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div class="space-y-8">
|
||||
{{-- Header --}}
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<flux:heading size="xl">{{ __('booking.my_consultations') }}</flux:heading>
|
||||
<flux:button href="{{ route('client.consultations.book') }}" variant="primary">
|
||||
<flux:button href="{{ route('client.consultations.book') }}" variant="primary" wire:navigate>
|
||||
{{ __('booking.request_consultation') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<flux:callout variant="success" class="mb-6">
|
||||
<flux:callout variant="success">
|
||||
{{ session('success') }}
|
||||
</flux:callout>
|
||||
@endif
|
||||
|
||||
<div class="space-y-4">
|
||||
@forelse($consultations as $consultation)
|
||||
<div wire:key="consultation-{{ $consultation->id }}" class="bg-white dark:bg-zinc-800 rounded-lg p-4 border border-zinc-200 dark:border-zinc-700">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<p class="font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ \Carbon\Carbon::parse($consultation->booking_date)->translatedFormat('l, d M Y') }}
|
||||
</p>
|
||||
<p class="text-zinc-600 dark:text-zinc-400">
|
||||
{{ \Carbon\Carbon::parse($consultation->booking_time)->format('g:i A') }}
|
||||
</p>
|
||||
</div>
|
||||
<flux:badge :variant="match($consultation->status) {
|
||||
ConsultationStatus::Pending => 'warning',
|
||||
ConsultationStatus::Approved => 'success',
|
||||
ConsultationStatus::Completed => 'default',
|
||||
ConsultationStatus::Cancelled => 'danger',
|
||||
ConsultationStatus::NoShow => 'danger',
|
||||
default => 'default',
|
||||
}">
|
||||
{{ $consultation->status->label() }}
|
||||
</flux:badge>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400 line-clamp-2">
|
||||
{{ $consultation->problem_summary }}
|
||||
</p>
|
||||
@if($consultation->status === ConsultationStatus::Approved)
|
||||
<div class="mt-3">
|
||||
<flux:button
|
||||
size="sm"
|
||||
href="{{ route('client.consultations.calendar', $consultation) }}"
|
||||
>
|
||||
<flux:icon name="calendar" class="w-4 h-4 me-1" />
|
||||
{{ __('booking.add_to_calendar') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@empty
|
||||
<div class="text-center py-12 text-zinc-500 dark:text-zinc-400">
|
||||
<p>{{ __('booking.no_consultations') }}</p>
|
||||
<flux:button href="{{ route('client.consultations.book') }}" class="mt-4">
|
||||
{{ __('booking.book_first_consultation') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
{{-- Upcoming Consultations Section --}}
|
||||
<section>
|
||||
<flux:heading size="lg" class="mb-4">{{ __('booking.upcoming_consultations') }}</flux:heading>
|
||||
|
||||
<div class="mt-6">
|
||||
{{ $consultations->links() }}
|
||||
</div>
|
||||
@if($upcoming->isNotEmpty())
|
||||
<div class="space-y-4">
|
||||
@foreach($upcoming as $consultation)
|
||||
<div wire:key="upcoming-{{ $consultation->id }}" class="bg-white dark:bg-zinc-800 rounded-lg p-4 border border-zinc-200 dark:border-zinc-700">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<flux:icon name="calendar" class="w-5 h-5 text-zinc-500" />
|
||||
<span class="font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ $consultation->booking_date->translatedFormat(app()->getLocale() === 'ar' ? 'l، j F Y' : 'l, F j, Y') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<flux:icon name="clock" class="w-5 h-5 text-zinc-500" />
|
||||
<span class="text-zinc-600 dark:text-zinc-400">
|
||||
{{ \Carbon\Carbon::parse($consultation->booking_time)->format('g:i A') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{{-- Consultation Type Badge --}}
|
||||
@if($consultation->consultation_type === ConsultationType::Free)
|
||||
<flux:badge color="sky">{{ __('booking.type_free') }}</flux:badge>
|
||||
@else
|
||||
<flux:badge color="amber">{{ __('booking.type_paid') }}</flux:badge>
|
||||
@endif
|
||||
|
||||
{{-- Status Badge --}}
|
||||
<flux:badge color="green">{{ $consultation->status->label() }}</flux:badge>
|
||||
|
||||
{{-- Payment Status (for paid consultations) --}}
|
||||
@if($consultation->consultation_type === ConsultationType::Paid)
|
||||
@if($consultation->payment_status === PaymentStatus::Received)
|
||||
<flux:badge color="green">{{ __('booking.payment_received') }}</flux:badge>
|
||||
@else
|
||||
<flux:badge color="yellow">{{ __('booking.payment_pending') }}</flux:badge>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<flux:button
|
||||
size="sm"
|
||||
href="{{ route('client.consultations.calendar', $consultation) }}"
|
||||
icon="calendar-days"
|
||||
>
|
||||
{{ __('booking.add_to_calendar') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-zinc-50 dark:bg-zinc-800/50 rounded-lg p-8 text-center">
|
||||
<flux:icon name="calendar-days" class="w-12 h-12 mx-auto text-zinc-300 dark:text-zinc-600 mb-3" />
|
||||
<flux:text class="text-zinc-500 dark:text-zinc-400">{{ __('booking.no_upcoming_consultations') }}</flux:text>
|
||||
<div class="mt-4">
|
||||
<flux:button href="{{ route('client.consultations.book') }}" variant="primary" size="sm" wire:navigate>
|
||||
{{ __('booking.book_consultation') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
{{-- Pending Requests Section --}}
|
||||
<section>
|
||||
<flux:heading size="lg" class="mb-4">{{ __('booking.pending_requests') }}</flux:heading>
|
||||
|
||||
@if($pending->isNotEmpty())
|
||||
<div class="space-y-4">
|
||||
@foreach($pending as $consultation)
|
||||
<div wire:key="pending-{{ $consultation->id }}" class="bg-white dark:bg-zinc-800 rounded-lg p-4 border border-zinc-200 dark:border-zinc-700">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<flux:icon name="calendar" class="w-5 h-5 text-zinc-500" />
|
||||
<span class="font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ $consultation->booking_date->translatedFormat(app()->getLocale() === 'ar' ? 'l، j F Y' : 'l, F j, Y') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<flux:icon name="clock" class="w-5 h-5 text-zinc-500" />
|
||||
<span class="text-zinc-600 dark:text-zinc-400">
|
||||
{{ \Carbon\Carbon::parse($consultation->booking_time)->format('g:i A') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-sm text-zinc-500 dark:text-zinc-400 mb-2">
|
||||
{{ __('booking.submitted_on') }}: {{ $consultation->created_at->translatedFormat(app()->getLocale() === 'ar' ? 'j F Y' : 'F j, Y') }}
|
||||
</div>
|
||||
@if($consultation->problem_summary)
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-400 line-clamp-2">
|
||||
{{ Str::limit($consultation->problem_summary, 150) }}
|
||||
</p>
|
||||
@endif
|
||||
<div class="mt-3">
|
||||
<flux:badge color="yellow">{{ __('booking.pending_review') }}</flux:badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-zinc-50 dark:bg-zinc-800/50 rounded-lg p-8 text-center">
|
||||
<flux:icon name="inbox" class="w-12 h-12 mx-auto text-zinc-300 dark:text-zinc-600 mb-3" />
|
||||
<flux:text class="text-zinc-500 dark:text-zinc-400">{{ __('booking.no_pending_requests') }}</flux:text>
|
||||
</div>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
{{-- Past Consultations Section --}}
|
||||
<section>
|
||||
<flux:heading size="lg" class="mb-4">{{ __('booking.past_consultations') }}</flux:heading>
|
||||
|
||||
@if($past->isNotEmpty())
|
||||
<div class="space-y-4">
|
||||
@foreach($past as $consultation)
|
||||
<div wire:key="past-{{ $consultation->id }}" class="bg-white dark:bg-zinc-800 rounded-lg p-4 border border-zinc-200 dark:border-zinc-700">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<flux:icon name="calendar" class="w-5 h-5 text-zinc-500" />
|
||||
<span class="font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ $consultation->booking_date->translatedFormat(app()->getLocale() === 'ar' ? 'l، j F Y' : 'l, F j, Y') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<flux:icon name="clock" class="w-5 h-5 text-zinc-500" />
|
||||
<span class="text-zinc-600 dark:text-zinc-400">
|
||||
{{ \Carbon\Carbon::parse($consultation->booking_time)->format('g:i A') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{{-- Consultation Type Badge --}}
|
||||
@if($consultation->consultation_type === ConsultationType::Free)
|
||||
<flux:badge color="sky">{{ __('booking.type_free') }}</flux:badge>
|
||||
@else
|
||||
<flux:badge color="amber">{{ __('booking.type_paid') }}</flux:badge>
|
||||
@endif
|
||||
|
||||
{{-- Status Badge --}}
|
||||
@php
|
||||
$statusColor = match($consultation->status) {
|
||||
ConsultationStatus::Completed => 'zinc',
|
||||
ConsultationStatus::Cancelled => 'red',
|
||||
ConsultationStatus::NoShow => 'red',
|
||||
ConsultationStatus::Approved => 'zinc',
|
||||
default => 'zinc',
|
||||
};
|
||||
@endphp
|
||||
<flux:badge :color="$statusColor">{{ $consultation->status->label() }}</flux:badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
{{ $past->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-zinc-50 dark:bg-zinc-800/50 rounded-lg p-8 text-center">
|
||||
<flux:icon name="archive-box" class="w-12 h-12 mx-auto text-zinc-300 dark:text-zinc-600 mb-3" />
|
||||
<flux:text class="text-zinc-500 dark:text-zinc-400">{{ __('booking.no_past_consultations') }}</flux:text>
|
||||
</div>
|
||||
@endif
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user