where('access_token', $token) ->where('is_active', true) ->first(); } public function touch(DisplayScreen $screen): void { $screen->update(['last_seen_at' => now()]); } /** * @return array */ public function payload(DisplayScreen $screen): array { $screen->loadMissing(['branch', 'organization']); $queueIds = $this->resolveQueueIds($screen); $queues = ServiceQueue::query() ->whereIn('id', $queueIds) ->where('is_active', true) ->orderBy('name') ->get(); // One active ticket per destination (service point / counter). Older called/serving // tickets at the same window must not crowd the public board. $nowServing = Ticket::query() ->whereIn('service_queue_id', $queueIds) ->whereIn('status', ['called', 'serving']) ->with(['counter', 'assignedCounter', 'serviceQueue']) ->orderByDesc('called_at') ->limit(48) ->get() ->unique(fn (Ticket $t) => $t->counter_id ? 'counter:'.$t->counter_id : ($t->assigned_counter_id ? 'assigned:'.$t->assigned_counter_id : 'queue:'.$t->service_queue_id)) ->take(12) ->values() ->map(function (Ticket $t) { $point = $t->counter ?? $t->assignedCounter; return [ 'ticket_number' => $t->ticket_number, 'queue_name' => $t->serviceQueue?->name, 'department' => $t->serviceQueue?->name, 'counter' => $point?->displayDestination() ?? $point?->name, 'destination' => $point?->displayDestination(), 'staff_display_name' => $point?->staff_display_name, 'announcement_text' => TicketPresenter::announcementText($t, $point), ]; }) ->all(); $waiting = Ticket::query() ->whereIn('service_queue_id', $queueIds) ->where('status', 'waiting') ->count(); $avgWait = (int) Ticket::query() ->whereIn('service_queue_id', $queueIds) ->where('status', 'completed') ->whereDate('issued_at', today()) ->whereNotNull('called_at') ->get(['issued_at', 'called_at']) ->avg(fn (Ticket $t) => $t->called_at->diffInSeconds($t->issued_at)); return [ 'screen' => [ 'name' => $screen->name, 'layout' => $screen->layout, 'branch_name' => $screen->branch?->name, 'organization_name' => $screen->organization?->name, ], 'now_serving' => $nowServing, 'waiting_count' => $waiting, 'estimated_wait_minutes' => $avgWait > 0 ? (int) ceil($avgWait / 60) : null, 'queues' => $queues->map(fn (ServiceQueue $q) => [ 'name' => $q->name, 'prefix' => $q->prefix, 'is_paused' => (bool) $q->is_paused, ])->values()->all(), 'announcements' => app(VoiceAnnouncementService::class)->pendingForBranch( (int) $screen->branch_id, $queueIds, ), 'updated_at' => now()->toIso8601String(), ]; } /** * @return list */ protected function resolveQueueIds(DisplayScreen $screen): array { $ids = array_values(array_unique(array_filter(array_map( fn ($id) => (int) $id, $screen->service_queue_ids ?? [], )))); if ($ids !== []) { return $ids; } return ServiceQueue::query() ->where('organization_id', $screen->organization_id) ->when($screen->branch_id, fn ($query) => $query->where('branch_id', $screen->branch_id)) ->where('is_active', true) ->orderBy('name') ->pluck('id') ->map(fn ($id) => (int) $id) ->all(); } }