Files
ladill-queue/app/Http/Controllers/Qms/AppointmentController.php
T
isaaccladandCursor bf879d6abe
Deploy Ladill Queue / deploy (push) Successful in 36s
Add hero sections to Queue index pages and soften analytics cards.
Introduce shared x-qms.page-hero with summary stats across queues,
tickets, counters, admin pages, and insights; remove bordered cards on
Analytics, Reports, Feedback, and Branches list views.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 07:32:10 +00:00

159 lines
6.9 KiB
PHP

<?php
namespace App\Http\Controllers\Qms;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Qms\Concerns\ScopesToAccount;
use App\Models\Branch;
use App\Models\QueueAppointment;
use App\Models\ServiceQueue;
use App\Services\Qms\AppointmentService;
use App\Services\Qms\AuditLogger;
use App\Services\Qms\OrganizationResolver;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;
class AppointmentController extends Controller
{
use ScopesToAccount;
public function __construct(
protected AppointmentService $appointments,
) {}
public function index(Request $request): View
{
$this->authorizeAbility($request, 'appointments.view');
$owner = $this->ownerRef($request);
$organization = $this->organization($request);
$branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));
$appointments = QueueAppointment::owned($owner)
->where('organization_id', $organization->id)
->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope))
->when($request->query('status'), fn ($q, $s) => $q->where('status', $s))
->with(['serviceQueue', 'branch', 'ticket'])
->orderBy('scheduled_at')
->paginate(30);
$appointmentQuery = QueueAppointment::owned($owner)
->where('organization_id', $organization->id)
->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope));
$heroStats = [
'today' => (clone $appointmentQuery)->whereDate('scheduled_at', today())->count(),
'scheduled' => (clone $appointmentQuery)->where('status', 'scheduled')->count(),
'checked_in' => (clone $appointmentQuery)->where('status', 'checked_in')->count(),
];
return view('qms.appointments.index', compact('appointments', 'organization', 'heroStats'));
}
public function create(Request $request): View
{
$this->authorizeAbility($request, 'appointments.manage');
$owner = $this->ownerRef($request);
$organization = $this->organization($request);
$branches = Branch::owned($owner)->where('organization_id', $organization->id)->where('is_active', true)->orderBy('name')->get();
$queues = ServiceQueue::owned($owner)->where('organization_id', $organization->id)->where('is_active', true)->orderBy('name')->get();
return view('qms.appointments.create', [
'organization' => $organization,
'branches' => $branches,
'queues' => $queues,
'modes' => config('qms.appointment_modes'),
]);
}
public function store(Request $request): RedirectResponse
{
$this->authorizeAbility($request, 'appointments.manage');
$owner = $this->ownerRef($request);
$organization = $this->organization($request);
$validated = $request->validate([
'branch_id' => ['required', 'exists:queue_branches,id'],
'service_queue_id' => ['nullable', 'exists:queue_service_queues,id'],
'customer_name' => ['required', 'string', 'max:255'],
'customer_phone' => ['nullable', 'string', 'max:50'],
'customer_email' => ['nullable', 'email', 'max:255'],
'scheduled_at' => ['required', 'date'],
'mode' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('qms.appointment_modes')))],
]);
$appointment = QueueAppointment::create([
'owner_ref' => $owner,
'organization_id' => $organization->id,
...$validated,
'mode' => $validated['mode'] ?? $organization->settings['appointment_mode'] ?? 'hybrid',
'status' => 'scheduled',
'reference' => 'APT-'.Str::upper(Str::random(6)),
]);
AuditLogger::record($owner, 'appointment.created', $organization->id, $owner, QueueAppointment::class, $appointment->id);
return redirect()->route('qms.appointments.index')->with('success', 'Appointment scheduled.');
}
public function edit(Request $request, QueueAppointment $appointment): View
{
$this->authorizeAbility($request, 'appointments.manage');
$this->authorizeOwner($request, $appointment);
$owner = $this->ownerRef($request);
$organization = $this->organization($request);
return view('qms.appointments.edit', [
'appointment' => $appointment,
'branches' => Branch::owned($owner)->where('organization_id', $organization->id)->orderBy('name')->get(),
'queues' => ServiceQueue::owned($owner)->where('organization_id', $organization->id)->orderBy('name')->get(),
'modes' => config('qms.appointment_modes'),
]);
}
public function update(Request $request, QueueAppointment $appointment): RedirectResponse
{
$this->authorizeAbility($request, 'appointments.manage');
$this->authorizeOwner($request, $appointment);
abort_unless($appointment->status === 'scheduled', 422, 'Only scheduled appointments can be edited.');
$validated = $request->validate([
'branch_id' => ['required', 'exists:queue_branches,id'],
'service_queue_id' => ['nullable', 'exists:queue_service_queues,id'],
'customer_name' => ['required', 'string', 'max:255'],
'customer_phone' => ['nullable', 'string', 'max:50'],
'customer_email' => ['nullable', 'email', 'max:255'],
'scheduled_at' => ['required', 'date'],
'mode' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('qms.appointment_modes')))],
]);
$appointment->update($validated);
AuditLogger::record($this->ownerRef($request), 'appointment.created', $appointment->organization_id, $this->ownerRef($request), QueueAppointment::class, $appointment->id, ['updated' => true]);
return redirect()->route('qms.appointments.index')->with('success', 'Appointment updated.');
}
public function checkIn(Request $request, QueueAppointment $appointment): RedirectResponse
{
$this->authorizeAbility($request, 'appointments.manage');
$this->authorizeOwner($request, $appointment);
$ticket = $this->appointments->checkIn($appointment, $this->ownerRef($request));
return redirect()->route('qms.tickets.show', $ticket)->with('success', 'Appointment checked in. Ticket issued.');
}
public function cancel(Request $request, QueueAppointment $appointment): RedirectResponse
{
$this->authorizeAbility($request, 'appointments.manage');
$this->authorizeOwner($request, $appointment);
$appointment->update(['status' => 'cancelled']);
AuditLogger::record($this->ownerRef($request), 'appointment.cancelled', $appointment->organization_id, $this->ownerRef($request), QueueAppointment::class, $appointment->id);
return back()->with('success', 'Appointment cancelled.');
}
}