From 0351dcd497ca27ea420400dfe4c5249f8ebdd37b Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Thu, 14 May 2026 01:14:14 -0400 Subject: [PATCH] feat(fusion_clock): NFC kiosk SCSS (always-dark, high-contrast) Co-Authored-By: Claude Sonnet 4.6 --- fusion_clock/__manifest__.py | 1 + fusion_clock/static/src/scss/nfc_kiosk.scss | 242 ++++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 fusion_clock/static/src/scss/nfc_kiosk.scss diff --git a/fusion_clock/__manifest__.py b/fusion_clock/__manifest__.py index 63846568..7dcc4ca7 100644 --- a/fusion_clock/__manifest__.py +++ b/fusion_clock/__manifest__.py @@ -81,6 +81,7 @@ Integrates natively with Odoo's hr.attendance module for full payroll compatibil 'assets': { 'web.assets_frontend': [ 'fusion_clock/static/src/css/portal_clock.css', + 'fusion_clock/static/src/scss/nfc_kiosk.scss', 'fusion_clock/static/src/js/fusion_clock_portal.js', 'fusion_clock/static/src/js/fusion_clock_kiosk.js', ], diff --git a/fusion_clock/static/src/scss/nfc_kiosk.scss b/fusion_clock/static/src/scss/nfc_kiosk.scss new file mode 100644 index 00000000..1e299550 --- /dev/null +++ b/fusion_clock/static/src/scss/nfc_kiosk.scss @@ -0,0 +1,242 @@ +// NFC Clock Kiosk — always-dark, high-contrast. +// Per CLAUDE.md: shop-floor kiosks need explicit hex (no var(--bs-*) which drift) +// and we deliberately do NOT branch on $o-webclient-color-scheme — this is a +// frontend bundle (not backend), and the kiosk is intentionally always dark +// regardless of the user's color scheme preference. + +$nfc-bg: #0b0d10; +$nfc-panel: #15191f; +$nfc-text: #ffffff; +$nfc-text-muted: #9ba3ad; +$nfc-success: #18a957; +$nfc-error: #d9374e; +$nfc-accent: #3b82f6; +$nfc-border: #2a3038; + +html, body { + background: $nfc-bg !important; + color: $nfc-text; + margin: 0; + padding: 0; + overflow: hidden; + height: 100vh; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} + +.o_main_navbar, header, footer { display: none !important; } + +.nfc-kiosk { + width: 100vw; + height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem; + box-sizing: border-box; + user-select: none; + -webkit-tap-highlight-color: transparent; +} + +.nfc-kiosk__company { + position: absolute; + top: 1.5rem; + left: 50%; + transform: translateX(-50%); + font-size: 1.25rem; + color: $nfc-text-muted; +} + +.nfc-kiosk__time { + position: absolute; + top: 1.5rem; + right: 2rem; + font-size: 2rem; + font-weight: 600; + color: $nfc-text; + font-variant-numeric: tabular-nums; +} + +.nfc-kiosk__date { + position: absolute; + top: 4.5rem; + right: 2rem; + font-size: 1rem; + color: $nfc-text-muted; +} + +.nfc-kiosk__location { + position: absolute; + bottom: 1.5rem; + left: 2rem; + font-size: 0.95rem; + color: $nfc-text-muted; +} + +.nfc-kiosk__settings { + position: absolute; + bottom: 1rem; + right: 1rem; + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + background: transparent; + color: $nfc-text-muted; + border: 1px solid $nfc-border; + cursor: pointer; + font-size: 1.2rem; + display: flex; + align-items: center; + justify-content: center; + + &:hover { color: $nfc-text; } +} + +.nfc-kiosk__idle { + text-align: center; +} + +.nfc-kiosk__icon { + font-size: 8rem; + color: $nfc-accent; + animation: nfc-pulse 2s ease-in-out infinite; +} + +@keyframes nfc-pulse { + 0%, 100% { opacity: 0.85; transform: scale(1); } + 50% { opacity: 1.0; transform: scale(1.06); } +} + +.nfc-kiosk__prompt { + font-size: 2rem; + font-weight: 500; + margin-top: 2rem; +} + +.nfc-kiosk__processing { + text-align: center; + font-size: 1.5rem; + color: $nfc-text-muted; +} + +.nfc-kiosk__result { + width: min(80vw, 700px); + padding: 2.5rem 3rem; + border-radius: 1rem; + display: flex; + align-items: center; + gap: 2rem; + + &--success { background: $nfc-success; } + &--error { background: $nfc-error; } +} + +.nfc-kiosk__avatar { + width: 7rem; + height: 7rem; + border-radius: 50%; + background-size: cover; + background-position: center; + background-color: rgba(255,255,255,0.2); + flex-shrink: 0; +} + +.nfc-kiosk__result-text { + flex: 1; + + .name { font-size: 2rem; font-weight: 700; } + .action { font-size: 1.5rem; margin-top: 0.5rem; } + .hours { font-size: 1.1rem; opacity: 0.9; margin-top: 0.25rem; } +} + +.nfc-kiosk__setup { + text-align: center; + max-width: 600px; + + h2 { font-size: 2rem; margin-bottom: 1rem; } + p { color: $nfc-text-muted; margin-bottom: 2rem; } + + button { + font-size: 1.5rem; + padding: 1rem 3rem; + background: $nfc-accent; + color: white; + border: none; + border-radius: 0.5rem; + cursor: pointer; + } +} + +.nfc-kiosk__enroll-overlay { + position: fixed; inset: 0; + background: rgba(0,0,0,0.85); + z-index: 1000; + display: flex; align-items: center; justify-content: center; + padding: 2rem; +} + +.nfc-kiosk__enroll-panel { + background: $nfc-panel; + border: 1px solid $nfc-border; + border-radius: 1rem; + padding: 2.5rem; + width: min(80vw, 700px); + + h2 { font-size: 1.5rem; margin: 0 0 1.5rem; } + + .numpad { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.75rem; + margin: 1rem 0; + + button { + font-size: 2rem; padding: 1.5rem 0; + background: $nfc-bg; color: $nfc-text; + border: 1px solid $nfc-border; border-radius: 0.5rem; + cursor: pointer; + } + } + + .pin-display { + font-size: 2.5rem; letter-spacing: 0.5rem; + text-align: center; margin: 1rem 0; + font-variant-numeric: tabular-nums; + } + + .employee-search { + width: 100%; + padding: 0.75rem 1rem; + font-size: 1.25rem; + background: $nfc-bg; color: $nfc-text; + border: 1px solid $nfc-border; + border-radius: 0.5rem; + margin-bottom: 1rem; + } + + .employee-list { + max-height: 40vh; + overflow-y: auto; + + .employee-row { + padding: 0.75rem 1rem; + border-bottom: 1px solid $nfc-border; + cursor: pointer; + font-size: 1.1rem; + + &:hover { background: $nfc-bg; } + } + } + + .actions { + display: flex; gap: 1rem; justify-content: flex-end; + margin-top: 1.5rem; + + button { + font-size: 1rem; padding: 0.75rem 1.5rem; + border-radius: 0.5rem; cursor: pointer; border: none; + } + .cancel { background: $nfc-border; color: $nfc-text; } + .confirm { background: $nfc-accent; color: white; } + } +}