complete story 7.6 with qa tests

This commit is contained in:
Naser Mansour
2025-12-29 00:36:33 +02:00
parent 45e2be8468
commit 3bcbb13c61
8 changed files with 657 additions and 28 deletions
@@ -16,8 +16,11 @@ new class extends Component
public array $availableSlots = [];
public function mount(): void
public array $bookedDates = [];
public function mount(array $bookedDates = []): void
{
$this->bookedDates = $bookedDates;
$this->year = now()->year;
$this->month = now()->month;
$this->loadMonthAvailability();
@@ -57,6 +60,11 @@ new class extends Component
public function selectDate(string $date): void
{
// Prevent selecting user's already booked dates
if (in_array($date, $this->bookedDates)) {
return;
}
$status = $this->monthAvailability[$date] ?? 'unavailable';
if (in_array($status, ['available', 'partial'])) {
@@ -119,10 +127,16 @@ new class extends Component
$current = $firstDay->copy();
while ($current->lte($lastDay)) {
$dateStr = $current->format('Y-m-d');
// Check if user has already booked this date
$status = in_array($dateStr, $this->bookedDates)
? 'user_booked'
: ($this->monthAvailability[$dateStr] ?? 'unavailable');
$days[] = [
'date' => $dateStr,
'day' => $current->day,
'status' => $this->monthAvailability[$dateStr] ?? 'unavailable',
'status' => $status,
];
$current->addDay();
}
@@ -136,6 +150,7 @@ new class extends Component
'monthName' => Carbon::create($this->year, $this->month, 1)->translatedFormat('F Y'),
'calendarDays' => $this->buildCalendarDays(),
'dayHeaders' => $this->getDayHeaders(),
'bookedDates' => $this->bookedDates,
];
}
}; ?>
@@ -175,10 +190,11 @@ new class extends Component
'h-12 rounded-lg text-center transition-colors font-medium',
'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:hover:bg-emerald-900/50' => $dayData['status'] === 'available',
'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:hover:bg-amber-900/50' => $dayData['status'] === 'partial',
'bg-sky-100 text-sky-700 cursor-not-allowed dark:bg-sky-900/30 dark:text-sky-400' => $dayData['status'] === 'user_booked',
'bg-zinc-100 text-zinc-400 cursor-not-allowed dark:bg-zinc-800 dark:text-zinc-600' => in_array($dayData['status'], ['past', 'closed', 'blocked', 'full']),
'ring-2 ring-amber-500 dark:ring-amber-400' => $selectedDate === $dayData['date'],
])
@disabled(in_array($dayData['status'], ['past', 'closed', 'blocked', 'full']))
@disabled(in_array($dayData['status'], ['past', 'closed', 'blocked', 'full', 'user_booked']))
>
{{ $dayData['day'] }}
</button>
@@ -221,6 +237,12 @@ new class extends Component
<div class="w-4 h-4 rounded bg-amber-100 dark:bg-amber-900/30"></div>
<span class="text-zinc-600 dark:text-zinc-400">{{ __('booking.partial') }}</span>
</div>
@if(count($bookedDates) > 0)
<div class="flex items-center gap-2">
<div class="w-4 h-4 rounded bg-sky-100 dark:bg-sky-900/30"></div>
<span class="text-zinc-600 dark:text-zinc-400">{{ __('booking.user_booked') }}</span>
</div>
@endif
<div class="flex items-center gap-2">
<div class="w-4 h-4 rounded bg-zinc-100 dark:bg-zinc-800"></div>
<span class="text-zinc-600 dark:text-zinc-400">{{ __('booking.unavailable') }}</span>
@@ -23,6 +23,36 @@ new class extends Component
public bool $showConfirmation = false;
public function getBookingStatus(): array
{
$user = auth()->user();
$todayBooking = $user->consultations()
->whereDate('booking_date', today())
->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved])
->first();
$pendingRequests = $user->consultations()
->where('status', ConsultationStatus::Pending)
->where('booking_date', '>=', today())
->orderBy('booking_date')
->get();
$bookedDates = $user->consultations()
->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved])
->where('booking_date', '>=', today())
->pluck('booking_date')
->map(fn ($d) => $d->format('Y-m-d'))
->toArray();
return [
'canBookToday' => is_null($todayBooking),
'todayBooking' => $todayBooking,
'pendingRequests' => $pendingRequests,
'bookedDates' => $bookedDates,
];
}
public function selectSlot(string $date, string $time): void
{
$this->selectedDate = $date;
@@ -141,16 +171,64 @@ new class extends Component
$this->showConfirmation = false;
}
}
public function with(): array
{
return $this->getBookingStatus();
}
}; ?>
<div class="max-w-4xl mx-auto">
<flux:heading size="xl" class="mb-6">{{ __('booking.request_consultation') }}</flux:heading>
{{-- Booking Status Banner --}}
<div class="mb-6 rounded-lg border p-4 {{ $canBookToday ? 'border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-900/20' : 'border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-900/20' }}">
<div class="flex items-start gap-3">
@if($canBookToday)
<flux:icon name="check-circle" class="mt-0.5 h-5 w-5 flex-shrink-0 text-green-600 dark:text-green-400" />
<div>
<p class="font-medium text-green-700 dark:text-green-400">{{ __('booking.can_book_today') }}</p>
</div>
@else
<flux:icon name="information-circle" class="mt-0.5 h-5 w-5 flex-shrink-0 text-amber-600 dark:text-amber-400" />
<div>
<p class="font-medium text-amber-700 dark:text-amber-400">{{ __('booking.already_booked_today') }}</p>
</div>
@endif
</div>
@if($pendingRequests->isNotEmpty())
<div class="mt-3 border-t border-zinc-200 pt-3 dark:border-zinc-700">
@if($pendingRequests->count() === 1)
<p class="text-sm text-zinc-600 dark:text-zinc-400">
{{ __('booking.pending_for_date', ['date' => $pendingRequests->first()->booking_date->format('d/m/Y')]) }}
</p>
@else
<p class="text-sm text-zinc-600 dark:text-zinc-400">
{{ __('booking.pending_count', ['count' => $pendingRequests->count()]) }}
</p>
<ul class="mt-1 list-inside list-disc text-sm text-zinc-500 dark:text-zinc-400">
@foreach($pendingRequests->take(3) as $request)
<li>{{ $request->booking_date->format('d/m/Y') }}</li>
@endforeach
@if($pendingRequests->count() > 3)
<li>...</li>
@endif
</ul>
@endif
</div>
@endif
<p class="mt-3 text-xs text-zinc-500 dark:text-zinc-400">
{{ __('booking.limit_message') }}
</p>
</div>
@if(!$selectedDate || !$selectedTime)
<!-- Step 1: Calendar Selection -->
<div class="mt-6">
<p class="mb-4 text-zinc-600 dark:text-zinc-400">{{ __('booking.select_date_time') }}</p>
<livewire:availability-calendar />
<livewire:availability-calendar :booked-dates="$bookedDates" />
</div>
@else
<!-- Step 2: Problem Summary -->