diff --git a/resources/views/livewire/admin/bookings/pending.blade.php b/resources/views/livewire/admin/bookings/pending.blade.php index 39951f7..0c63703 100644 --- a/resources/views/livewire/admin/bookings/pending.blade.php +++ b/resources/views/livewire/admin/bookings/pending.blade.php @@ -22,6 +22,13 @@ new class extends Component public string $dateFrom = ''; public string $dateTo = ''; + // Quick approve modal state + public bool $showApproveModal = false; + public ?int $approvingBookingId = null; + public string $consultationType = 'free'; + public ?string $paymentAmount = null; + public string $paymentInstructions = ''; + public function updatedDateFrom(): void { $this->resetPage(); @@ -39,22 +46,40 @@ new class extends Component $this->resetPage(); } - public function quickApprove(int $id): void + public function openApproveModal(int $id): void { - $consultation = Consultation::with('user')->findOrFail($id); + $this->approvingBookingId = $id; + $this->consultationType = 'free'; + $this->paymentAmount = null; + $this->paymentInstructions = ''; + $this->showApproveModal = true; + } + + public function quickApprove(): void + { + $consultation = Consultation::with('user')->findOrFail($this->approvingBookingId); if ($consultation->status !== ConsultationStatus::Pending) { session()->flash('error', __('admin.booking_already_processed')); + $this->showApproveModal = false; return; } + $this->validate([ + 'consultationType' => ['required', 'in:free,paid'], + 'paymentAmount' => ['required_if:consultationType,paid', 'nullable', 'numeric', 'min:0'], + 'paymentInstructions' => ['nullable', 'string', 'max:1000'], + ]); + $oldStatus = $consultation->status->value; + $type = $this->consultationType === 'paid' ? ConsultationType::Paid : ConsultationType::Free; $consultation->update([ 'status' => ConsultationStatus::Approved, - 'consultation_type' => ConsultationType::Free, - 'payment_status' => PaymentStatus::NotApplicable, + 'consultation_type' => $type, + 'payment_amount' => $type === ConsultationType::Paid ? $this->paymentAmount : null, + 'payment_status' => $type === ConsultationType::Paid ? PaymentStatus::Pending : PaymentStatus::NotApplicable, ]); // Generate calendar file and send notification @@ -74,12 +99,13 @@ new class extends Component Mail::to($consultation->guest_email)->queue( new GuestBookingApprovedMail( $consultation, - app()->getLocale() + app()->getLocale(), + $this->paymentInstructions ?: null ) ); } elseif ($consultation->user) { $consultation->user->notify( - new BookingApproved($consultation, $icsContent ?? '', null) + new BookingApproved($consultation, $icsContent ?? '', $this->paymentInstructions ?: null) ); } @@ -92,12 +118,14 @@ new class extends Component 'old_values' => ['status' => $oldStatus], 'new_values' => [ 'status' => ConsultationStatus::Approved->value, - 'consultation_type' => ConsultationType::Free->value, + 'consultation_type' => $type->value, + 'payment_amount' => $this->paymentAmount, ], 'ip_address' => request()->ip(), 'created_at' => now(), ]); + $this->showApproveModal = false; session()->flash('success', __('admin.booking_approved')); } @@ -259,8 +287,7 @@ new class extends Component {{ $bookings->links() }} + + + +
+ {{ __('admin.approve_booking') }} + + + + {{ __('admin.consultation_type') }} + + + + + + + + @if($consultationType === 'paid') + + {{ __('admin.payment_amount') }} * + + + + + + {{ __('admin.payment_instructions') }} + + + @endif + +
+ + {{ __('common.cancel') }} + + + {{ __('admin.approve') }} + +
+
+
diff --git a/tests/Feature/Admin/BookingReviewApprovalTest.php b/tests/Feature/Admin/BookingReviewApprovalTest.php index f9571d7..9168be6 100644 --- a/tests/Feature/Admin/BookingReviewApprovalTest.php +++ b/tests/Feature/Admin/BookingReviewApprovalTest.php @@ -205,7 +205,11 @@ test('quick approve from list works', function () { $this->actingAs($admin); Volt::test('admin.bookings.pending') - ->call('quickApprove', $consultation->id) + ->call('openApproveModal', $consultation->id) + ->assertSet('showApproveModal', true) + ->assertSet('approvingBookingId', $consultation->id) + ->set('consultationType', 'free') + ->call('quickApprove') ->assertHasNoErrors(); expect($consultation->fresh()) diff --git a/tests/Feature/Admin/GuestBookingManagementTest.php b/tests/Feature/Admin/GuestBookingManagementTest.php index 3fc4448..a0ada38 100644 --- a/tests/Feature/Admin/GuestBookingManagementTest.php +++ b/tests/Feature/Admin/GuestBookingManagementTest.php @@ -83,7 +83,11 @@ test('admin can quick approve guest booking from pending list', function () { $this->actingAs($admin); Volt::test('admin.bookings.pending') - ->call('quickApprove', $consultation->id) + ->call('openApproveModal', $consultation->id) + ->assertSet('showApproveModal', true) + ->assertSet('approvingBookingId', $consultation->id) + ->set('consultationType', 'free') + ->call('quickApprove') ->assertHasNoErrors(); expect($consultation->fresh()->status)->toBe(ConsultationStatus::Approved);