Initial Ladill Queue release — enterprise QMS standalone app.
Deploy Ladill Queue / deploy (push) Successful in 56s

Phases 1–6: tickets, counters, displays, appointments, workflows, rules, analytics, reports, feedback, admin, device API, and Gitea deploy workflow for queue.ladill.com.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-29 20:19:52 +00:00
co-authored by Cursor
commit cca98eefd2
297 changed files with 27263 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace App\Services\Qms;
use App\Models\DisplayScreen;
use App\Models\ServiceQueue;
use App\Models\Ticket;
class DisplayService
{
public function findByToken(string $token): ?DisplayScreen
{
return DisplayScreen::query()
->where('access_token', $token)
->where('is_active', true)
->first();
}
public function touch(DisplayScreen $screen): void
{
$screen->update(['last_seen_at' => now()]);
}
/**
* @return array<string, mixed>
*/
public function payload(DisplayScreen $screen): array
{
$queueIds = $screen->service_queue_ids ?? [];
$queues = ServiceQueue::query()
->whereIn('id', $queueIds)
->where('is_active', true)
->get();
$nowServing = Ticket::query()
->whereIn('service_queue_id', $queueIds)
->whereIn('status', ['called', 'serving'])
->with(['counter', 'serviceQueue'])
->orderByDesc('called_at')
->limit(5)
->get()
->map(fn (Ticket $t) => [
'ticket_number' => $t->ticket_number,
'queue_name' => $t->serviceQueue?->name,
'counter' => $t->counter?->name,
]);
$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')
->selectRaw('AVG(TIMESTAMPDIFF(SECOND, issued_at, called_at)) as avg_wait')
->value('avg_wait');
return [
'screen' => [
'name' => $screen->name,
'layout' => $screen->layout,
],
'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' => $q->is_paused,
]),
'announcements' => app(VoiceAnnouncementService::class)->pendingForBranch((int) $screen->branch_id),
'updated_at' => now()->toIso8601String(),
];
}
}