generated sotries
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
# Story 3.5: Admin Booking Review & Approval
|
||||
|
||||
## Epic Reference
|
||||
**Epic 3:** Booking & Consultation System
|
||||
|
||||
## User Story
|
||||
As an **admin**,
|
||||
I want **to review, categorize, and approve or reject booking requests**,
|
||||
So that **I can manage my consultation schedule and set appropriate consultation types**.
|
||||
|
||||
## Story Context
|
||||
|
||||
### Existing System Integration
|
||||
- **Integrates with:** consultations table, notifications, .ics generation
|
||||
- **Technology:** Livewire Volt, Flux UI
|
||||
- **Follows pattern:** Admin action workflow
|
||||
- **Touch points:** Client notifications, calendar file
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
### Pending Bookings List
|
||||
- [ ] View all pending booking requests
|
||||
- [ ] Display: client name, requested date/time, submission date
|
||||
- [ ] Show problem summary preview
|
||||
- [ ] Click to view full details
|
||||
- [ ] Sort by date (oldest first default)
|
||||
- [ ] Filter by date range
|
||||
|
||||
### Booking Details View
|
||||
- [ ] Full client information
|
||||
- [ ] Complete problem summary
|
||||
- [ ] Client consultation history
|
||||
- [ ] Requested date and time
|
||||
|
||||
### Approval Workflow
|
||||
- [ ] Set consultation type:
|
||||
- Free consultation
|
||||
- Paid consultation
|
||||
- [ ] If paid: set payment amount
|
||||
- [ ] If paid: add payment instructions (optional)
|
||||
- [ ] Approve button with confirmation
|
||||
- [ ] On approval:
|
||||
- Status changes to 'approved'
|
||||
- Client notified via email
|
||||
- .ics calendar file attached to email
|
||||
- Payment instructions included if paid
|
||||
|
||||
### Rejection Workflow
|
||||
- [ ] Optional rejection reason field
|
||||
- [ ] Reject button with confirmation
|
||||
- [ ] On rejection:
|
||||
- Status changes to 'rejected'
|
||||
- Client notified via email with reason
|
||||
|
||||
### Quick Actions
|
||||
- [ ] Quick approve (free) button on list
|
||||
- [ ] Quick reject button on list
|
||||
- [ ] Bulk actions (optional)
|
||||
|
||||
### Quality Requirements
|
||||
- [ ] Audit log for all decisions
|
||||
- [ ] Bilingual notifications
|
||||
- [ ] Tests for approval/rejection flow
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### Consultation Status Flow
|
||||
```
|
||||
pending -> approved (admin approves)
|
||||
pending -> rejected (admin rejects)
|
||||
approved -> completed (after consultation)
|
||||
approved -> no_show (client didn't attend)
|
||||
approved -> cancelled (admin cancels)
|
||||
```
|
||||
|
||||
### Volt Component for Review
|
||||
```php
|
||||
<?php
|
||||
|
||||
use App\Models\Consultation;
|
||||
use App\Notifications\BookingApproved;
|
||||
use App\Notifications\BookingRejected;
|
||||
use App\Services\CalendarService;
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new class extends Component {
|
||||
public Consultation $consultation;
|
||||
|
||||
public string $consultationType = 'free';
|
||||
public ?float $paymentAmount = null;
|
||||
public string $paymentInstructions = '';
|
||||
public string $rejectionReason = '';
|
||||
|
||||
public bool $showApproveModal = false;
|
||||
public bool $showRejectModal = false;
|
||||
|
||||
public function mount(Consultation $consultation): void
|
||||
{
|
||||
$this->consultation = $consultation;
|
||||
}
|
||||
|
||||
public function approve(): void
|
||||
{
|
||||
$this->validate([
|
||||
'consultationType' => ['required', 'in:free,paid'],
|
||||
'paymentAmount' => ['required_if:consultationType,paid', 'nullable', 'numeric', 'min:0'],
|
||||
'paymentInstructions' => ['nullable', 'string', 'max:1000'],
|
||||
]);
|
||||
|
||||
$this->consultation->update([
|
||||
'status' => 'approved',
|
||||
'type' => $this->consultationType,
|
||||
'payment_amount' => $this->consultationType === 'paid' ? $this->paymentAmount : null,
|
||||
'payment_status' => $this->consultationType === 'paid' ? 'pending' : 'not_applicable',
|
||||
]);
|
||||
|
||||
// Generate calendar file
|
||||
$calendarService = app(CalendarService::class);
|
||||
$icsContent = $calendarService->generateIcs($this->consultation);
|
||||
|
||||
// Send notification with .ics attachment
|
||||
$this->consultation->user->notify(
|
||||
new BookingApproved(
|
||||
$this->consultation,
|
||||
$icsContent,
|
||||
$this->paymentInstructions
|
||||
)
|
||||
);
|
||||
|
||||
// Log action
|
||||
AdminLog::create([
|
||||
'admin_id' => auth()->id(),
|
||||
'action_type' => 'approve',
|
||||
'target_type' => 'consultation',
|
||||
'target_id' => $this->consultation->id,
|
||||
'old_values' => ['status' => 'pending'],
|
||||
'new_values' => [
|
||||
'status' => 'approved',
|
||||
'type' => $this->consultationType,
|
||||
'payment_amount' => $this->paymentAmount,
|
||||
],
|
||||
'ip_address' => request()->ip(),
|
||||
]);
|
||||
|
||||
session()->flash('success', __('messages.booking_approved'));
|
||||
$this->redirect(route('admin.bookings.pending'));
|
||||
}
|
||||
|
||||
public function reject(): void
|
||||
{
|
||||
$this->validate([
|
||||
'rejectionReason' => ['nullable', 'string', 'max:1000'],
|
||||
]);
|
||||
|
||||
$this->consultation->update([
|
||||
'status' => 'rejected',
|
||||
]);
|
||||
|
||||
// Send rejection notification
|
||||
$this->consultation->user->notify(
|
||||
new BookingRejected($this->consultation, $this->rejectionReason)
|
||||
);
|
||||
|
||||
// Log action
|
||||
AdminLog::create([
|
||||
'admin_id' => auth()->id(),
|
||||
'action_type' => 'reject',
|
||||
'target_type' => 'consultation',
|
||||
'target_id' => $this->consultation->id,
|
||||
'old_values' => ['status' => 'pending'],
|
||||
'new_values' => [
|
||||
'status' => 'rejected',
|
||||
'reason' => $this->rejectionReason,
|
||||
],
|
||||
'ip_address' => request()->ip(),
|
||||
]);
|
||||
|
||||
session()->flash('success', __('messages.booking_rejected'));
|
||||
$this->redirect(route('admin.bookings.pending'));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Blade Template for Approval Modal
|
||||
```blade
|
||||
<flux:modal wire:model="showApproveModal">
|
||||
<flux:heading>{{ __('admin.approve_booking') }}</flux:heading>
|
||||
|
||||
<div class="space-y-4 mt-4">
|
||||
<!-- Client Info -->
|
||||
<div class="bg-cream p-3 rounded-lg">
|
||||
<p><strong>{{ __('admin.client') }}:</strong> {{ $consultation->user->name }}</p>
|
||||
<p><strong>{{ __('admin.date') }}:</strong>
|
||||
{{ $consultation->scheduled_date->translatedFormat('l, d M Y') }}</p>
|
||||
<p><strong>{{ __('admin.time') }}:</strong>
|
||||
{{ Carbon::parse($consultation->scheduled_time)->format('g:i A') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<div class="flex gap-3 mt-6">
|
||||
<flux:button wire:click="$set('showApproveModal', false)">
|
||||
{{ __('common.cancel') }}
|
||||
</flux:button>
|
||||
<flux:button variant="primary" wire:click="approve">
|
||||
{{ __('admin.approve') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</flux:modal>
|
||||
```
|
||||
|
||||
### Pending Bookings List Component
|
||||
```php
|
||||
<?php
|
||||
|
||||
use App\Models\Consultation;
|
||||
use Livewire\Volt\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
new class extends Component {
|
||||
use WithPagination;
|
||||
|
||||
public string $dateFrom = '';
|
||||
public string $dateTo = '';
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
return [
|
||||
'bookings' => Consultation::where('status', 'pending')
|
||||
->when($this->dateFrom, fn($q) => $q->where('scheduled_date', '>=', $this->dateFrom))
|
||||
->when($this->dateTo, fn($q) => $q->where('scheduled_date', '<=', $this->dateTo))
|
||||
->with('user')
|
||||
->orderBy('scheduled_date')
|
||||
->orderBy('scheduled_time')
|
||||
->paginate(15),
|
||||
];
|
||||
}
|
||||
|
||||
public function quickApprove(int $id): void
|
||||
{
|
||||
$consultation = Consultation::findOrFail($id);
|
||||
$consultation->update([
|
||||
'status' => 'approved',
|
||||
'type' => 'free',
|
||||
'payment_status' => 'not_applicable',
|
||||
]);
|
||||
|
||||
// Generate and send notification with .ics
|
||||
// ...
|
||||
|
||||
session()->flash('success', __('messages.booking_approved'));
|
||||
}
|
||||
|
||||
public function quickReject(int $id): void
|
||||
{
|
||||
$consultation = Consultation::findOrFail($id);
|
||||
$consultation->update(['status' => 'rejected']);
|
||||
|
||||
// Send rejection notification
|
||||
// ...
|
||||
|
||||
session()->flash('success', __('messages.booking_rejected'));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] Pending bookings list displays correctly
|
||||
- [ ] Can view booking details
|
||||
- [ ] Can approve as free consultation
|
||||
- [ ] Can approve as paid with amount
|
||||
- [ ] Can reject with optional reason
|
||||
- [ ] Approval sends email with .ics file
|
||||
- [ ] Rejection sends email with reason
|
||||
- [ ] Quick actions work from list
|
||||
- [ ] Audit log entries created
|
||||
- [ ] Bilingual support complete
|
||||
- [ ] Tests for approval/rejection
|
||||
- [ ] Code formatted with Pint
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Story 3.4:** Booking submission (creates pending bookings)
|
||||
- **Story 3.6:** Calendar file generation (.ics)
|
||||
- **Epic 8:** Email notifications
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
- **Primary Risk:** Approving wrong booking
|
||||
- **Mitigation:** Confirmation dialog, clear booking details display
|
||||
- **Rollback:** Admin can cancel approved booking
|
||||
|
||||
## Estimation
|
||||
|
||||
**Complexity:** Medium
|
||||
**Estimated Effort:** 4-5 hours
|
||||
Reference in New Issue
Block a user