diff --git a/app/Http/Controllers/Qms/DisplayPublicController.php b/app/Http/Controllers/Qms/DisplayPublicController.php index 2ecb10c..cb6b71d 100644 --- a/app/Http/Controllers/Qms/DisplayPublicController.php +++ b/app/Http/Controllers/Qms/DisplayPublicController.php @@ -22,10 +22,13 @@ class DisplayPublicController extends Controller $screen->loadMissing(['organization', 'branch']); $this->displays->touch($screen); + $payload = $this->displays->payload($screen); + return view('qms.display.public', [ 'screen' => $screen, - 'dataUrl' => route('qms.display.data', $token), - 'playedUrl' => route('qms.display.announcement.played', ['token' => $token, 'announcement' => '__ID__']), + 'initialPayload' => $payload, + 'dataUrl' => '/display/'.$token.'/data', + 'playedUrl' => '/display/'.$token.'/announcements/__ID__/played', 'pollMs' => config('qms.display_poll_ms', 1200), ]); } diff --git a/app/Services/Qms/DisplayService.php b/app/Services/Qms/DisplayService.php index 5b09e41..948b358 100644 --- a/app/Services/Qms/DisplayService.php +++ b/app/Services/Qms/DisplayService.php @@ -27,10 +27,11 @@ class DisplayService public function payload(DisplayScreen $screen): array { $screen->loadMissing(['branch', 'organization']); - $queueIds = $screen->service_queue_ids ?? []; + $queueIds = $this->resolveQueueIds($screen); $queues = ServiceQueue::query() ->whereIn('id', $queueIds) ->where('is_active', true) + ->orderBy('name') ->get(); $nowServing = Ticket::query() @@ -44,7 +45,9 @@ class DisplayService 'ticket_number' => $t->ticket_number, 'queue_name' => $t->serviceQueue?->name, 'counter' => $t->counter?->name, - ]); + ]) + ->values() + ->all(); $waiting = Ticket::query() ->whereIn('service_queue_id', $queueIds) @@ -72,13 +75,37 @@ class DisplayService 'queues' => $queues->map(fn (ServiceQueue $q) => [ 'name' => $q->name, 'prefix' => $q->prefix, - 'is_paused' => $q->is_paused, - ]), + 'is_paused' => (bool) $q->is_paused, + ])->values()->all(), 'announcements' => app(VoiceAnnouncementService::class)->pendingForBranch( (int) $screen->branch_id, - array_map('intval', $screen->service_queue_ids ?? []), + $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(); + } } diff --git a/resources/js/kiosk-flow.js b/resources/js/kiosk-flow.js index dabfc82..69529a7 100644 --- a/resources/js/kiosk-flow.js +++ b/resources/js/kiosk-flow.js @@ -94,24 +94,38 @@ export function registerKioskFlow(Alpine) { })); Alpine.data('displayBoard', (config = {}) => ({ - payload: null, + payload: config.initial ?? null, announcedIds: {}, isAnnouncing: false, speechReady: false, voices: [], clock: '', dateLabel: '', + pollError: null, dataUrl: typeof config === 'string' ? config : config.dataUrl, playedUrlTemplate: config.playedUrl ?? null, pollMs: config.pollMs ?? 1200, csrf: config.csrf ?? document.querySelector('meta[name="csrf-token"]')?.content ?? '', + formatCount(value) { + return value === null || value === undefined ? '—' : String(value); + }, + + formatWait(minutes) { + return minutes === null || minutes === undefined ? '—' : String(minutes); + }, + async poll() { try { const res = await fetch(this.dataUrl, { headers: { Accept: 'application/json' } }); + if (! res.ok) { + throw new Error(`Display poll failed (${res.status})`); + } this.payload = await res.json(); + this.pollError = null; this.maybeAnnounce(this.payload?.announcements ?? []); } catch (e) { + this.pollError = e.message || 'Could not refresh display data'; console.error('Display poll failed', e); } }, diff --git a/resources/views/qms/display/public.blade.php b/resources/views/qms/display/public.blade.php index 4f0d3a2..c6f2a0b 100644 --- a/resources/views/qms/display/public.blade.php +++ b/resources/views/qms/display/public.blade.php @@ -1,15 +1,25 @@ +@php + $p = $initialPayload; +@endphp - + + {{ $screen->name }} · Queue Display @vite(['resources/css/app.css', 'resources/js/app.js']) {{-- Voice unlock — required by browsers before TTS --}} @@ -19,7 +29,7 @@ @click="unlockSpeech()" @keydown.enter.prevent="unlockSpeech()" tabindex="0" - class="qms-display__unlock fixed inset-0 z-50 flex cursor-pointer items-center justify-center p-8 text-center outline-none" + class="qms-display__unlock fixed inset-0 z-50 flex cursor-pointer items-center justify-center bg-slate-950/95 p-8 text-center outline-none" role="button" aria-label="Enable voice announcements" > @@ -34,31 +44,34 @@ +
+
{{-- Header --}} -
+
-

-

-

+

+ {{ $p['screen']['organization_name'] ?? $screen->organization?->name ?? 'Queue display' }} +

+

+ {{ $p['screen']['name'] ?? $screen->name }} +

+ @if (! empty($p['screen']['branch_name'])) +

{{ $p['screen']['branch_name'] }}

+ @endif
Live
-

+

--:--

@@ -93,39 +106,39 @@ class="qms-display__stat flex flex-1 flex-col items-center justify-center rounded-3xl px-8 py-16 text-center" x-show="!(payload?.now_serving ?? []).length" > -
- +
+ +
+

Please wait

+

Your ticket number will appear here when it is called.

-

Please wait

-

Your ticket number will appear here when it is called.

-
{{-- Stats sidebar --}}