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.'); } }