where('access_token', $token) ->where('is_active', true) ->first(); } public function touch(CareDisplayScreen $screen): void { $screen->update(['last_seen_at' => now()]); } /** * @return array */ public function payload(CareDisplayScreen $screen): array { $screen->loadMissing(['branch', 'organization']); $queueIds = $this->resolveQueueIds($screen); $queues = CareServiceQueue::query() ->whereIn('id', $queueIds) ->where('is_active', true) ->orderBy('name') ->get(); // One active ticket per service point. Older called/serving tickets at the // same window must not crowd the public board. $nowServing = CareQueueTicket::query() ->whereIn('service_queue_id', $queueIds) ->whereIn('status', [ CareQueueTicket::STATUS_CALLED, CareQueueTicket::STATUS_SERVING, ]) ->with(['servicePoint', 'serviceQueue']) ->orderByDesc('called_at') ->limit(48) ->get() ->unique(fn (CareQueueTicket $t) => $t->service_point_id ? 'point:'.$t->service_point_id : 'queue:'.$t->service_queue_id) ->take(12) ->values() ->map(function (CareQueueTicket $t) { $point = $t->servicePoint; 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, 'point_key' => $point ? 'point:'.$point->id : 'queue:'.$t->service_queue_id, 'announcement_text' => $this->announcementText($t, $point), 'called_at' => optional($t->called_at)?->toIso8601String(), ]; }) ->sortBy(fn (array $item) => sprintf( '%s|%s|%s', mb_strtolower((string) ($item['queue_name'] ?? '')), mb_strtolower((string) ($item['destination'] ?? $item['counter'] ?? '')), (string) ($item['ticket_number'] ?? ''), )) ->values() ->all(); $waiting = CareQueueTicket::query() ->whereIn('service_queue_id', $queueIds) ->where('status', CareQueueTicket::STATUS_WAITING) ->count(); $avgWait = (int) CareQueueTicket::query() ->whereIn('service_queue_id', $queueIds) ->where('status', CareQueueTicket::STATUS_COMPLETED) ->whereDate('created_at', today()) ->whereNotNull('called_at') ->get(['created_at', 'called_at']) ->avg(fn (CareQueueTicket $t) => $t->called_at->diffInSeconds($t->created_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 (CareServiceQueue $q) => [ 'name' => $q->name, 'prefix' => $q->prefix, 'is_paused' => false, ])->values()->all(), 'announcements' => app(CareVoiceAnnouncementService::class)->pendingForBranch( (int) $screen->branch_id, $queueIds, ), 'updated_at' => now()->toIso8601String(), ]; } protected function announcementText(CareQueueTicket $ticket, ?CareServicePoint $point): ?string { if (! $point) { return $ticket->ticket_number; } $parts = array_values(array_filter([ $ticket->ticket_number, $point->staff_display_name ?: null, $ticket->serviceQueue?->name, $point->displayDestination(), ])); return implode(', ', $parts); } /** * @return list */ protected function resolveQueueIds(CareDisplayScreen $screen): array { $ids = array_values(array_unique(array_filter(array_map( fn ($id) => (int) $id, $screen->service_queue_ids ?? [], )))); if ($ids !== []) { return $ids; } return CareServiceQueue::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(); } }