added set free/paid consultation to the quick approve

This commit is contained in:
Naser Mansour 2026-01-09 19:56:49 +02:00
parent c1cc24c78d
commit 0dfb2a8759
3 changed files with 94 additions and 11 deletions

View File

@ -22,6 +22,13 @@ new class extends Component
public string $dateFrom = ''; public string $dateFrom = '';
public string $dateTo = ''; 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 public function updatedDateFrom(): void
{ {
$this->resetPage(); $this->resetPage();
@ -39,22 +46,40 @@ new class extends Component
$this->resetPage(); $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) { if ($consultation->status !== ConsultationStatus::Pending) {
session()->flash('error', __('admin.booking_already_processed')); session()->flash('error', __('admin.booking_already_processed'));
$this->showApproveModal = false;
return; 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; $oldStatus = $consultation->status->value;
$type = $this->consultationType === 'paid' ? ConsultationType::Paid : ConsultationType::Free;
$consultation->update([ $consultation->update([
'status' => ConsultationStatus::Approved, 'status' => ConsultationStatus::Approved,
'consultation_type' => ConsultationType::Free, 'consultation_type' => $type,
'payment_status' => PaymentStatus::NotApplicable, 'payment_amount' => $type === ConsultationType::Paid ? $this->paymentAmount : null,
'payment_status' => $type === ConsultationType::Paid ? PaymentStatus::Pending : PaymentStatus::NotApplicable,
]); ]);
// Generate calendar file and send notification // Generate calendar file and send notification
@ -74,12 +99,13 @@ new class extends Component
Mail::to($consultation->guest_email)->queue( Mail::to($consultation->guest_email)->queue(
new GuestBookingApprovedMail( new GuestBookingApprovedMail(
$consultation, $consultation,
app()->getLocale() app()->getLocale(),
$this->paymentInstructions ?: null
) )
); );
} elseif ($consultation->user) { } elseif ($consultation->user) {
$consultation->user->notify( $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], 'old_values' => ['status' => $oldStatus],
'new_values' => [ 'new_values' => [
'status' => ConsultationStatus::Approved->value, 'status' => ConsultationStatus::Approved->value,
'consultation_type' => ConsultationType::Free->value, 'consultation_type' => $type->value,
'payment_amount' => $this->paymentAmount,
], ],
'ip_address' => request()->ip(), 'ip_address' => request()->ip(),
'created_at' => now(), 'created_at' => now(),
]); ]);
$this->showApproveModal = false;
session()->flash('success', __('admin.booking_approved')); session()->flash('success', __('admin.booking_approved'));
} }
@ -259,8 +287,7 @@ new class extends Component
</flux:button> </flux:button>
<flux:button <flux:button
wire:click="quickApprove({{ $booking->id }})" wire:click="openApproveModal({{ $booking->id }})"
wire:confirm="{{ __('admin.confirm_quick_approve') }}"
variant="outline" variant="outline"
size="sm" size="sm"
class="!bg-emerald-600 !text-white hover:!bg-emerald-700" class="!bg-emerald-600 !text-white hover:!bg-emerald-700"
@ -290,4 +317,52 @@ new class extends Component
<div class="mt-6"> <div class="mt-6">
{{ $bookings->links() }} {{ $bookings->links() }}
</div> </div>
<!-- Approve Modal -->
<flux:modal wire:model="showApproveModal">
<div class="space-y-6">
<flux:heading size="lg">{{ __('admin.approve_booking') }}</flux:heading>
<!-- Consultation Type -->
<flux:field>
<flux:label>{{ __('admin.consultation_type') }}</flux:label>
<flux:radio.group wire:model.live="consultationType">
<flux:radio value="free" label="{{ __('admin.free_consultation') }}" />
<flux:radio value="paid" label="{{ __('admin.paid_consultation') }}" />
</flux:radio.group>
</flux:field>
<!-- Payment Amount (if paid) -->
@if($consultationType === 'paid')
<flux:field>
<flux:label>{{ __('admin.payment_amount') }} *</flux:label>
<flux:input
type="number"
wire:model="paymentAmount"
step="0.01"
min="0"
/>
<flux:error name="paymentAmount" />
</flux:field>
<flux:field>
<flux:label>{{ __('admin.payment_instructions') }}</flux:label>
<flux:textarea
wire:model="paymentInstructions"
rows="3"
placeholder="{{ __('admin.payment_instructions_placeholder') }}"
/>
</flux:field>
@endif
<div class="flex gap-3 justify-end">
<flux:button wire:click="$set('showApproveModal', false)">
{{ __('common.cancel') }}
</flux:button>
<flux:button variant="primary" wire:click="quickApprove">
{{ __('admin.approve') }}
</flux:button>
</div>
</div>
</flux:modal>
</div> </div>

View File

@ -205,7 +205,11 @@ test('quick approve from list works', function () {
$this->actingAs($admin); $this->actingAs($admin);
Volt::test('admin.bookings.pending') 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(); ->assertHasNoErrors();
expect($consultation->fresh()) expect($consultation->fresh())

View File

@ -83,7 +83,11 @@ test('admin can quick approve guest booking from pending list', function () {
$this->actingAs($admin); $this->actingAs($admin);
Volt::test('admin.bookings.pending') 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(); ->assertHasNoErrors();
expect($consultation->fresh()->status)->toBe(ConsultationStatus::Approved); expect($consultation->fresh()->status)->toBe(ConsultationStatus::Approved);