Add touch-ready Filament login and admin update tooling
This commit is contained in:
31
resources/css/filament/admin/theme.css
Normal file
31
resources/css/filament/admin/theme.css
Normal file
@@ -0,0 +1,31 @@
|
||||
@import '../../../../vendor/filament/filament/resources/css/theme.css';
|
||||
|
||||
@source '../../../../app/Filament/**/*';
|
||||
@source '../../../../resources/views/filament/**/*';
|
||||
|
||||
@layer components {
|
||||
.fi-btn {
|
||||
@apply px-5 py-3 text-base;
|
||||
}
|
||||
|
||||
.fi-icon-btn {
|
||||
@apply p-3;
|
||||
}
|
||||
|
||||
.fi-input-wrp {
|
||||
@apply rounded-xl;
|
||||
}
|
||||
|
||||
input.fi-input {
|
||||
@apply px-4 py-3 text-base;
|
||||
}
|
||||
|
||||
.fi-input-wrp-prefix,
|
||||
.fi-input-wrp-suffix {
|
||||
@apply gap-x-4 ps-4 pe-4;
|
||||
}
|
||||
|
||||
.fi-input-wrp-label {
|
||||
@apply text-base;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,19 @@
|
||||
<template>
|
||||
<Head :title="props.galleryHeading" />
|
||||
<div class="min-h-screen bg-gradient-to-br from-slate-100 via-white to-slate-200 text-slate-900 transition-colors duration-500 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950 dark:text-slate-100">
|
||||
<div class="relative min-h-screen bg-gradient-to-br from-slate-100 via-white to-slate-200 text-slate-900 transition-colors duration-500 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950 dark:text-slate-100">
|
||||
<button
|
||||
type="button"
|
||||
class="absolute left-2 top-2 z-20 h-10 w-10 opacity-0"
|
||||
@click="handleAdminHotzoneTap"
|
||||
aria-label="Admin access"
|
||||
></button>
|
||||
<a
|
||||
v-if="showAdminLink"
|
||||
href="/admin"
|
||||
class="fixed left-6 top-6 z-[120] rounded-full border border-slate-200 bg-white/90 px-4 py-2 text-xs font-semibold uppercase tracking-[0.3em] text-slate-700 shadow-lg backdrop-blur transition hover:border-emerald-300 hover:text-emerald-600 dark:border-white/20 dark:bg-white/10 dark:text-white"
|
||||
>
|
||||
Admin
|
||||
</a>
|
||||
<div class="mx-auto flex w-full max-w-7xl flex-col gap-6 px-2 py-8 sm:px-4 lg:px-6">
|
||||
<header class="relative rounded-3xl border border-slate-200 bg-white/90 p-6 text-slate-900 shadow-2xl backdrop-blur transition-colors duration-300 dark:border-white/10 dark:bg-white/5 dark:text-white">
|
||||
<div class="absolute right-4 top-4 z-10 flex items-center gap-3 sm:right-6 sm:top-6">
|
||||
@@ -156,9 +169,16 @@ const lastRefreshedAt = ref(new Date());
|
||||
const refreshIntervalMs = ref(5000);
|
||||
const toastMessage = ref(null);
|
||||
const toastVariant = ref('info');
|
||||
const showAdminLink = ref(false);
|
||||
let toastTimer = null;
|
||||
let refreshTimer = null;
|
||||
let aiStatusTimer = null;
|
||||
let adminTapTimer = null;
|
||||
let adminHideTimer = null;
|
||||
let adminTapCount = 0;
|
||||
const adminTapTarget = 5;
|
||||
const adminTapWindowMs = 3000;
|
||||
const adminRevealDurationMs = 15000;
|
||||
|
||||
const getImageIdentifier = (image) => image?.id ?? image?.image_id ?? null;
|
||||
|
||||
@@ -317,6 +337,32 @@ const handleManualRefresh = () => {
|
||||
fetchImages({ silent: false });
|
||||
};
|
||||
|
||||
const handleAdminHotzoneTap = () => {
|
||||
adminTapCount += 1;
|
||||
|
||||
if (!adminTapTimer) {
|
||||
adminTapTimer = setTimeout(() => {
|
||||
adminTapCount = 0;
|
||||
adminTapTimer = null;
|
||||
}, adminTapWindowMs);
|
||||
}
|
||||
|
||||
if (adminTapCount >= adminTapTarget) {
|
||||
adminTapCount = 0;
|
||||
if (adminTapTimer) {
|
||||
clearTimeout(adminTapTimer);
|
||||
adminTapTimer = null;
|
||||
}
|
||||
showAdminLink.value = true;
|
||||
if (adminHideTimer) {
|
||||
clearTimeout(adminHideTimer);
|
||||
}
|
||||
adminHideTimer = setTimeout(() => {
|
||||
showAdminLink.value = false;
|
||||
}, adminRevealDurationMs);
|
||||
}
|
||||
};
|
||||
|
||||
const showContextMenu = (image) => {
|
||||
selectedImage.value = image;
|
||||
currentOverlayComponent.value = 'contextMenu';
|
||||
@@ -639,5 +685,11 @@ onUnmounted(() => {
|
||||
if (aiStatusTimer) {
|
||||
clearInterval(aiStatusTimer);
|
||||
}
|
||||
if (adminTapTimer) {
|
||||
clearTimeout(adminTapTimer);
|
||||
}
|
||||
if (adminHideTimer) {
|
||||
clearTimeout(adminHideTimer);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
137
resources/views/filament/pages/auth/kiosk-login.blade.php
Normal file
137
resources/views/filament/pages/auth/kiosk-login.blade.php
Normal file
@@ -0,0 +1,137 @@
|
||||
@php
|
||||
$selectedEmail = $get('email');
|
||||
$pin = (string) ($get('pin') ?? '');
|
||||
$pinLength = strlen($pin);
|
||||
@endphp
|
||||
|
||||
<div class="fi-grid" style="--cols-default: 1; gap: 1.5rem;">
|
||||
@if (! $hasPinUsers)
|
||||
<x-filament::empty-state
|
||||
heading="No admin PINs yet"
|
||||
description="Use the password login fields below to sign in, then set an Admin PIN in Users."
|
||||
icon="heroicon-o-lock-closed"
|
||||
icon-color="gray"
|
||||
>
|
||||
<x-slot:footer>
|
||||
<x-filament::badge color="gray" size="lg">
|
||||
Password login enabled
|
||||
</x-filament::badge>
|
||||
</x-slot:footer>
|
||||
</x-filament::empty-state>
|
||||
@else
|
||||
<x-filament::section
|
||||
heading="Admin selection"
|
||||
description="Tap your name to select an account."
|
||||
>
|
||||
<div class="fi-grid fi-grid-ctn sm:fi-grid-cols lg:fi-grid-cols" style="--cols-default: 1; --cols-sm: 2; --cols-lg: 3; gap: 1rem;">
|
||||
@foreach ($users as $user)
|
||||
<div class="fi-grid-col">
|
||||
<x-filament::button
|
||||
type="button"
|
||||
wire:click="selectUser({{ $user->id }})"
|
||||
:color="$selectedEmail === $user->email ? 'primary' : 'gray'"
|
||||
:outlined="$selectedEmail !== $user->email"
|
||||
size="lg"
|
||||
style="width: 100%; text-align: left;"
|
||||
>
|
||||
<div>
|
||||
<div>{{ $user->name }}</div>
|
||||
<div>{{ $user->email }}</div>
|
||||
</div>
|
||||
</x-filament::button>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-filament::section>
|
||||
|
||||
<x-filament::section
|
||||
heading="PIN"
|
||||
description="Enter the selected admin PIN to continue."
|
||||
>
|
||||
<x-slot:afterHeader>
|
||||
<x-filament::button
|
||||
type="button"
|
||||
color="gray"
|
||||
outlined
|
||||
size="sm"
|
||||
wire:click="clearPin"
|
||||
>
|
||||
Clear
|
||||
</x-filament::button>
|
||||
</x-slot:afterHeader>
|
||||
|
||||
<x-filament::badge
|
||||
color="gray"
|
||||
size="lg"
|
||||
style="font-size: 2.25rem; padding: 1.25rem 2rem; letter-spacing: 0.6rem;"
|
||||
>
|
||||
{{ $pinLength ? str_repeat('*', $pinLength) : '----' }}
|
||||
</x-filament::badge>
|
||||
|
||||
<table class="fi-ta-table" style="margin-top: 1rem;">
|
||||
<tbody>
|
||||
@foreach ([[1, 2, 3], [4, 5, 6], [7, 8, 9]] as $row)
|
||||
<tr>
|
||||
@foreach ($row as $digit)
|
||||
<td class="fi-ta-cell fi-align-center" style="width: 33%;">
|
||||
<x-filament::button
|
||||
type="button"
|
||||
wire:click="appendPinDigit({{ $digit }})"
|
||||
size="xl"
|
||||
color="gray"
|
||||
style="width: 100%;"
|
||||
>
|
||||
{{ $digit }}
|
||||
</x-filament::button>
|
||||
</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
<tr>
|
||||
<td class="fi-ta-cell fi-align-center" style="width: 33%;">
|
||||
<x-filament::button
|
||||
type="button"
|
||||
wire:click="clearPin"
|
||||
size="xl"
|
||||
color="gray"
|
||||
outlined
|
||||
style="width: 100%;"
|
||||
>
|
||||
Clear
|
||||
</x-filament::button>
|
||||
</td>
|
||||
<td class="fi-ta-cell fi-align-center" style="width: 33%;">
|
||||
<x-filament::button
|
||||
type="button"
|
||||
wire:click="appendPinDigit(0)"
|
||||
size="xl"
|
||||
color="gray"
|
||||
style="width: 100%;"
|
||||
>
|
||||
0
|
||||
</x-filament::button>
|
||||
</td>
|
||||
<td class="fi-ta-cell fi-align-center" style="width: 33%;">
|
||||
<x-filament::button
|
||||
type="button"
|
||||
wire:click="deletePinDigit"
|
||||
size="xl"
|
||||
color="gray"
|
||||
outlined
|
||||
style="width: 100%;"
|
||||
>
|
||||
Del
|
||||
</x-filament::button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</x-filament::section>
|
||||
|
||||
@error('data.email')
|
||||
<x-filament::badge color="danger" size="lg">
|
||||
{{ $message }}
|
||||
</x-filament::badge>
|
||||
@enderror
|
||||
@endif
|
||||
</div>
|
||||
Reference in New Issue
Block a user