Files
ladill-care/app/Http/Controllers/Care/QueueController.php
T
isaaccladandCursor 015a4cc7fe
Deploy Ladill Care / deploy (push) Successful in 1m45s
Wire Care lists into Ladill Queue workflows instead of reception panels.
When Queue integration is on, role pages issue tickets into their own
department queues and drive Call next → Serve → Complete on the native
lists. Integration off leaves existing UI unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 16:27:48 +00:00

167 lines
6.6 KiB
PHP

<?php
namespace App\Http\Controllers\Care;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Care\Concerns\ScopesToAccount;
use App\Models\Appointment;
use App\Models\Branch;
use App\Models\Practitioner;
use App\Services\Care\AppointmentService;
use App\Services\Care\CareQueueBridge;
use App\Services\Care\CareQueueContexts;
use App\Services\Care\ConsultationService;
use App\Services\Care\OrganizationResolver;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class QueueController extends Controller
{
use ScopesToAccount;
public function __construct(
protected AppointmentService $appointments,
protected ConsultationService $consultations,
protected CareQueueBridge $queueBridge,
) {}
public function index(Request $request): View
{
$this->authorizeAbility($request, 'appointments.view');
$organization = $this->organization($request);
$member = $this->member($request);
$branchScope = app(OrganizationResolver::class)->branchScope($member);
$branches = Branch::owned($this->ownerRef($request))
->where('organization_id', $organization->id)
->where('is_active', true)
->when($branchScope, fn ($q) => $q->where('id', $branchScope))
->orderBy('name')
->get();
$branchId = (int) ($request->input('branch_id') ?: $branchScope ?: $branches->first()?->id);
$practitionerId = $request->input('practitioner_id') ? (int) $request->input('practitioner_id') : null;
$queue = $branchId
? $this->appointments->queue($this->ownerRef($request), $branchId, $practitionerId)
: collect();
$inConsultation = Appointment::owned($this->ownerRef($request))
->where('organization_id', $organization->id)
->where('status', Appointment::STATUS_IN_CONSULTATION)
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
->when($practitionerId, fn ($q) => $q->where('practitioner_id', $practitionerId))
->with(['patient', 'practitioner', 'consultation'])
->orderBy('started_at')
->get();
$canManageQueue = app(\App\Services\Care\CarePermissions::class)
->can($this->member($request), 'queue.manage');
$canConsult = app(\App\Services\Care\CarePermissions::class)
->can($this->member($request), 'consultations.manage');
$heroStats = [
'waiting' => $queue->count(),
'in_consultation' => $inConsultation->count(),
'today' => Appointment::owned($this->ownerRef($request))
->where('organization_id', $organization->id)
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
->whereDate('scheduled_at', today())
->count(),
];
return view('care.queue.index', [
'organization' => $organization,
'branches' => $branches,
'branchId' => $branchId,
'practitioners' => Practitioner::owned($this->ownerRef($request))
->where('organization_id', $organization->id)
->where('is_active', true)
->orderBy('name')
->get(),
'practitionerId' => $practitionerId,
'queue' => $queue,
'inConsultation' => $inConsultation,
'canManageQueue' => $canManageQueue,
'canConsult' => $canConsult,
'heroStats' => $heroStats,
'queueIntegration' => $branchId
? $this->queueBridge->listMeta($organization, CareQueueContexts::CONSULTATION, $branchId)
: ['enabled' => false],
]);
}
public function callNext(Request $request): RedirectResponse
{
$this->authorizeAbility($request, 'appointments.view');
$organization = $this->organization($request);
abort_unless($this->queueBridge->isEnabled($organization), 404);
$member = $this->member($request);
$branchScope = app(OrganizationResolver::class)->branchScope($member);
$branchId = (int) ($request->input('branch_id') ?: $branchScope);
abort_unless($branchId > 0, 422);
$result = $this->queueBridge->callNext($organization, CareQueueContexts::CONSULTATION, $branchId);
$ticket = $result['ticket'] ?? null;
if (! $ticket) {
return back()->with('error', 'No patients waiting in the consultation queue.');
}
return back()->with('success', 'Called '.$ticket['ticket_number'].' — '.($ticket['customer_name'] ?? 'patient').'.');
}
public function completeTicket(Request $request, Appointment $appointment): RedirectResponse
{
$this->authorizeAbility($request, 'consultations.manage');
$this->authorizeAppointment($request, $appointment);
$organization = $this->organization($request);
abort_unless($this->queueBridge->isEnabled($organization), 404);
$ticket = $this->queueBridge->complete($organization, $appointment);
if (! $ticket) {
return back()->with('error', 'Could not complete queue ticket.');
}
return back()->with('success', 'Completed '.$ticket['ticket_number'].'.');
}
public function start(Request $request, Appointment $appointment): RedirectResponse
{
$this->authorizeAbility($request, 'consultations.manage');
$this->authorizeAppointment($request, $appointment);
$practitionerId = $request->input('practitioner_id')
? (int) $request->input('practitioner_id')
: $appointment->practitioner_id;
$this->appointments->startConsultation(
$appointment,
$this->ownerRef($request),
$practitionerId,
$this->ownerRef($request),
);
$consultation = $this->consultations->startFromAppointment(
$appointment->fresh(),
$this->ownerRef($request),
$this->ownerRef($request),
);
return redirect()->route('care.consultations.show', $consultation)
->with('success', 'Consultation started.');
}
protected function authorizeAppointment(Request $request, Appointment $appointment): void
{
$this->authorizeOwner($request, $appointment);
abort_unless($appointment->organization_id === $this->organization($request)->id, 404);
$branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
if ($branchId !== null && $appointment->branch_id !== $branchId) {
abort(404);
}
}
}