Fit public display to viewport and use Ladill Queue logo in footer.
Deploy Ladill Queue / deploy (push) Successful in 39s
Deploy Ladill Queue / deploy (push) Successful in 39s
Lock layout to 100dvh without scroll, scale ticket cards with clamp(), and replace powered-by text with the dark wordmark. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,6 +38,7 @@ class DisplayPublicController extends Controller
|
|||||||
'logoAlt' => $organization
|
'logoAlt' => $organization
|
||||||
? OrganizationBranding::logoAlt($organization)
|
? OrganizationBranding::logoAlt($organization)
|
||||||
: 'Ladill Queue',
|
: 'Ladill Queue',
|
||||||
|
'poweredByLogoUrl' => OrganizationBranding::assetUrl(OrganizationBranding::POWERED_BY_LOGO),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,15 @@ class OrganizationBranding
|
|||||||
{
|
{
|
||||||
public const DEFAULT_LOGO = 'images/logo/ladillqueue-logo.svg';
|
public const DEFAULT_LOGO = 'images/logo/ladillqueue-logo.svg';
|
||||||
|
|
||||||
|
public const POWERED_BY_LOGO = 'images/logo/ladillqueue-logo.svg';
|
||||||
|
|
||||||
|
public static function assetUrl(string $path = self::DEFAULT_LOGO): string
|
||||||
|
{
|
||||||
|
$file = public_path($path);
|
||||||
|
|
||||||
|
return asset($path).'?v='.(@filemtime($file) ?: '1');
|
||||||
|
}
|
||||||
|
|
||||||
public static function logoUrl(Organization $organization): string
|
public static function logoUrl(Organization $organization): string
|
||||||
{
|
{
|
||||||
if ($organization->logo_path && Storage::disk('public')->exists($organization->logo_path)) {
|
if ($organization->logo_path && Storage::disk('public')->exists($organization->logo_path)) {
|
||||||
@@ -18,9 +27,7 @@ class OrganizationBranding
|
|||||||
return Storage::disk('public')->url($organization->logo_path).'?v='.$version;
|
return Storage::disk('public')->url($organization->logo_path).'?v='.$version;
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = public_path(self::DEFAULT_LOGO);
|
return self::assetUrl(self::DEFAULT_LOGO);
|
||||||
|
|
||||||
return asset(self::DEFAULT_LOGO).'?v='.(@filemtime($path) ?: '1');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function logoAlt(Organization $organization): string
|
public static function logoAlt(Organization $organization): string
|
||||||
|
|||||||
+50
-1
@@ -269,14 +269,55 @@ html.qr-mobile-page body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Public queue display — waiting-area TV screens (light theme) */
|
/* Public queue display — waiting-area TV screens (light theme) */
|
||||||
|
html:has(.qms-display),
|
||||||
|
html:has(.qms-display) body {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.qms-display {
|
.qms-display {
|
||||||
min-height: 100vh;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100dvh;
|
||||||
|
max-height: 100dvh;
|
||||||
|
overflow: hidden;
|
||||||
background:
|
background:
|
||||||
radial-gradient(ellipse 90% 60% at 50% -20%, rgb(238 242 255 / 0.9), transparent),
|
radial-gradient(ellipse 90% 60% at 50% -20%, rgb(238 242 255 / 0.9), transparent),
|
||||||
linear-gradient(180deg, rgb(248 250 252) 0%, rgb(255 255 255) 100%);
|
linear-gradient(180deg, rgb(248 250 252) 0%, rgb(255 255 255) 100%);
|
||||||
color: rgb(15 23 42);
|
color: rgb(15 23 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.qms-display__shell {
|
||||||
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qms-display__header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qms-display__main {
|
||||||
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qms-display__footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qms-display__tickets {
|
||||||
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.qms-display__grid-bg {
|
.qms-display__grid-bg {
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(rgb(148 163 184 / 0.08) 1px, transparent 1px),
|
linear-gradient(rgb(148 163 184 / 0.08) 1px, transparent 1px),
|
||||||
@@ -285,6 +326,12 @@ html.qr-mobile-page body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.qms-display__ticket {
|
.qms-display__ticket {
|
||||||
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
background: linear-gradient(180deg, #fff 0%, rgb(248 250 252) 100%);
|
background: linear-gradient(180deg, #fff 0%, rgb(248 250 252) 100%);
|
||||||
border: 1px solid rgb(226 232 240);
|
border: 1px solid rgb(226 232 240);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
@@ -293,8 +340,10 @@ html.qr-mobile-page body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.qms-display__ticket-number {
|
.qms-display__ticket-number {
|
||||||
|
font-size: clamp(2.75rem, 11vh, 6rem);
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
letter-spacing: -0.04em;
|
letter-spacing: -0.04em;
|
||||||
|
line-height: 1;
|
||||||
color: rgb(67 56 202);
|
color: rgb(67 56 202);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" class="h-full bg-slate-50">
|
<html lang="en" class="h-full overflow-hidden bg-slate-50">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
class="qms-display qms-display__grid-bg min-h-full bg-slate-50 text-slate-900"
|
class="qms-display qms-display__grid-bg overflow-hidden bg-slate-50 text-slate-900"
|
||||||
x-data="displayBoard(@js([
|
x-data="displayBoard(@js([
|
||||||
'dataUrl' => $dataUrl,
|
'dataUrl' => $dataUrl,
|
||||||
'playedUrl' => $playedUrl,
|
'playedUrl' => $playedUrl,
|
||||||
@@ -48,67 +48,76 @@
|
|||||||
x-text="pollError"
|
x-text="pollError"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div class="flex min-h-screen flex-col">
|
<div class="qms-display__shell">
|
||||||
<header class="border-b border-slate-200/80 bg-white/90 backdrop-blur-md">
|
<header class="qms-display__header border-b border-slate-200/80 bg-white/90 backdrop-blur-md">
|
||||||
<div class="mx-auto flex max-w-6xl flex-wrap items-center justify-between gap-6 px-6 py-6 lg:px-10">
|
<div class="mx-auto flex max-w-6xl items-center justify-between gap-4 px-5 py-4 lg:px-8">
|
||||||
<div class="flex min-w-0 items-center">
|
<div class="flex min-w-0 items-center">
|
||||||
<img
|
<img
|
||||||
src="{{ $logoUrl }}"
|
src="{{ $logoUrl }}"
|
||||||
alt="{{ $logoAlt }}"
|
alt="{{ $logoAlt }}"
|
||||||
class="h-10 w-auto max-w-[min(100%,280px)] object-contain object-left lg:h-12"
|
class="h-8 w-auto max-w-[min(100%,240px)] object-contain object-left lg:h-10"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div class="flex shrink-0 items-center gap-4 text-right">
|
||||||
<div class="inline-flex items-center gap-2 rounded-full border border-emerald-200 bg-emerald-50 px-3 py-1.5 text-xs font-semibold text-emerald-700">
|
<div class="inline-flex items-center gap-2 rounded-full border border-emerald-200 bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700">
|
||||||
<span class="qms-display__live-dot h-2 w-2 rounded-full bg-emerald-500"></span>
|
<span class="qms-display__live-dot h-1.5 w-1.5 rounded-full bg-emerald-500"></span>
|
||||||
Live
|
Live
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 font-mono text-3xl font-semibold tabular-nums tracking-tight text-slate-900 lg:text-4xl" x-text="clock">--:--</p>
|
<div>
|
||||||
<p class="text-sm text-slate-500" x-text="dateLabel"></p>
|
<p class="font-mono text-2xl font-semibold tabular-nums tracking-tight text-slate-900 lg:text-3xl" x-text="clock">--:--</p>
|
||||||
|
<p class="hidden text-xs text-slate-500 sm:block" x-text="dateLabel"></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="mx-auto flex w-full max-w-6xl flex-1 flex-col px-6 py-10 lg:px-10 lg:py-14">
|
<main class="qms-display__main mx-auto w-full max-w-6xl px-5 py-4 lg:px-8 lg:py-6">
|
||||||
<section class="flex flex-1 flex-col">
|
<section class="flex min-h-0 flex-1 flex-col">
|
||||||
<div class="mb-6 text-center">
|
<div class="mb-3 shrink-0 text-center">
|
||||||
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-indigo-600">Now serving</p>
|
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-indigo-600">Now serving</p>
|
||||||
<p class="mt-2 text-base text-slate-500">Please proceed to your counter when your number appears</p>
|
<p class="mt-1 text-sm text-slate-500">Please proceed to your counter when your number appears</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-1 flex-col gap-6" x-show="(payload?.now_serving ?? []).length">
|
<div class="qms-display__tickets" x-show="(payload?.now_serving ?? []).length">
|
||||||
<template x-for="item in (payload?.now_serving ?? [])" :key="item.ticket_number + (item.counter ?? '')">
|
<template x-for="item in (payload?.now_serving ?? []).slice(0, 3)" :key="item.ticket_number + (item.counter ?? '')">
|
||||||
<article class="qms-display__ticket flex flex-1 flex-col items-center justify-center rounded-3xl px-8 py-12 text-center lg:py-16">
|
<article class="qms-display__ticket rounded-2xl px-6 py-4 text-center lg:rounded-3xl lg:px-8">
|
||||||
<p class="text-sm font-semibold uppercase tracking-[0.25em] text-slate-500">Ticket number</p>
|
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500">Ticket number</p>
|
||||||
<p class="qms-display__ticket-number mt-4 text-6xl font-bold sm:text-7xl lg:text-8xl" x-text="item.ticket_number"></p>
|
<p class="qms-display__ticket-number mt-2 font-bold" x-text="item.ticket_number"></p>
|
||||||
<p class="mt-4 text-xl font-medium text-slate-700" x-text="item.queue_name"></p>
|
<p class="mt-2 truncate text-base font-medium text-slate-700 lg:text-lg" x-text="item.queue_name"></p>
|
||||||
<div class="mt-10 inline-flex items-center gap-3 rounded-full border border-indigo-100 bg-indigo-50 px-6 py-3">
|
<div class="mt-4 inline-flex max-w-full items-center gap-2 rounded-full border border-indigo-100 bg-indigo-50 px-4 py-2">
|
||||||
<span class="text-sm font-semibold uppercase tracking-widest text-indigo-600">Proceed to</span>
|
<span class="text-xs font-semibold uppercase tracking-widest text-indigo-600">Proceed to</span>
|
||||||
<span class="text-2xl font-semibold text-indigo-900" x-text="item.counter ?? '—'"></span>
|
<span class="truncate text-lg font-semibold text-indigo-900 lg:text-xl" x-text="item.counter ?? '—'"></span>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="qms-display__ticket flex flex-1 flex-col items-center justify-center rounded-3xl px-8 py-16 text-center"
|
class="qms-display__ticket flex min-h-0 flex-1 flex-col items-center justify-center rounded-2xl px-6 py-8 text-center lg:rounded-3xl"
|
||||||
x-show="!(payload?.now_serving ?? []).length"
|
x-show="!(payload?.now_serving ?? []).length"
|
||||||
>
|
>
|
||||||
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-slate-100 ring-1 ring-slate-200">
|
<div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-slate-100 ring-1 ring-slate-200 lg:h-14 lg:w-14">
|
||||||
<svg class="h-8 w-8 text-slate-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
<svg class="h-6 w-6 text-slate-400 lg:h-7 lg:w-7" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-6 text-3xl font-semibold text-slate-900">Please wait</p>
|
<p class="mt-4 text-2xl font-semibold text-slate-900 lg:text-3xl">Please wait</p>
|
||||||
<p class="mt-3 max-w-md text-lg text-slate-500">Your ticket number will appear here when it is called.</p>
|
<p class="mt-2 max-w-md text-sm text-slate-500 lg:text-base">Your ticket number will appear here when it is called.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="border-t border-slate-200 bg-white/80 px-6 py-4 lg:px-10">
|
<footer class="qms-display__footer border-t border-slate-200 bg-white/80 px-5 py-3 lg:px-8">
|
||||||
<div class="mx-auto flex max-w-6xl flex-wrap items-center justify-between gap-3 text-xs text-slate-400">
|
<div class="mx-auto flex max-w-6xl items-center justify-between gap-3">
|
||||||
<p>Powered by Ladill Queue</p>
|
<div class="flex items-center gap-2">
|
||||||
<p x-show="payload?.updated_at" x-text="payload?.updated_at ? 'Updated ' + new Date(payload.updated_at).toLocaleTimeString() : ''"></p>
|
<span class="text-[10px] font-medium uppercase tracking-wider text-slate-400">Powered by</span>
|
||||||
|
<img
|
||||||
|
src="{{ $poweredByLogoUrl }}"
|
||||||
|
alt="Ladill Queue"
|
||||||
|
class="h-3.5 w-auto opacity-80"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<p class="text-[10px] text-slate-400" x-show="payload?.updated_at" x-text="payload?.updated_at ? 'Updated ' + new Date(payload.updated_at).toLocaleTimeString() : ''"></p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user