bafc776ef7
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
177 lines
9.0 KiB
HTML
177 lines
9.0 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Login — TieMeasureFlow{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="relative min-h-screen flex items-center justify-center overflow-hidden bg-[var(--bg-primary)] px-4 sm:px-6 lg:px-8">
|
|
|
|
<!-- Decorative brand background -->
|
|
<div class="pointer-events-none absolute inset-0" aria-hidden="true">
|
|
<div class="absolute -top-32 -left-32 w-96 h-96 rounded-full bg-primary-500/10 dark:bg-primary-400/10 blur-3xl"></div>
|
|
<div class="absolute -bottom-40 -right-24 w-[28rem] h-[28rem] rounded-full bg-primary-600/10 dark:bg-primary-500/10 blur-3xl"></div>
|
|
</div>
|
|
|
|
<div class="relative max-w-md w-full space-y-6">
|
|
<!-- Card -->
|
|
<div class="bg-[var(--bg-card)] border border-[var(--border-color)] shadow-lg rounded-2xl p-8">
|
|
<!-- Logo -->
|
|
<div class="text-center mb-8">
|
|
<div class="mx-auto mb-4 inline-flex justify-center text-slate-900 dark:text-white">
|
|
{% set logo_class = 'h-14 w-14' %}
|
|
{% set wordmark_class = 'text-3xl' %}
|
|
{% set logo_id = 'login' %}
|
|
{% include 'components/_app_logo.html' %}
|
|
</div>
|
|
<p class="text-sm text-[var(--text-secondary)]">
|
|
{{ _('Accedi al sistema') }}
|
|
</p>
|
|
</div>
|
|
|
|
<div x-data="pinLogin()" x-init="init()">
|
|
<!-- Vista Operatore: nome + PIN (#59) -->
|
|
<div x-show="mode === 'pin'" x-cloak>
|
|
<!-- Elenco nomi -->
|
|
<div x-show="!selected">
|
|
<p class="text-sm text-center text-[var(--text-secondary)] mb-4">{{ _('Chi sei?') }}</p>
|
|
<div class="grid grid-cols-2 gap-3" id="pin-user-list">
|
|
<template x-for="u in users" :key="u.id">
|
|
<button type="button" @click="select(u)"
|
|
class="btn btn-secondary min-h-[64px] text-base w-full" x-text="u.display_name"></button>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- Tastierino -->
|
|
<div x-show="selected" id="pin-pad">
|
|
<p class="text-center font-semibold mb-1" x-text="selected && selected.display_name"></p>
|
|
<p class="text-sm text-center text-[var(--text-secondary)] mb-3">{{ _('Inserisci il PIN') }}</p>
|
|
<div class="flex justify-center gap-3 mb-3" aria-hidden="true">
|
|
<template x-for="i in 4" :key="i">
|
|
<span class="h-4 w-4 rounded-full border-2 border-[var(--border-color)]"
|
|
:class="pin.length >= i ? 'bg-primary-600 border-primary-600' : ''"></span>
|
|
</template>
|
|
</div>
|
|
<p class="text-sm text-center text-red-600 min-h-[1.25rem] mb-2" role="alert" x-text="error"></p>
|
|
<div class="grid grid-cols-3 gap-3 max-w-xs mx-auto">
|
|
<template x-for="d in ['1','2','3','4','5','6','7','8','9']" :key="d">
|
|
<button type="button" @click="press(d)" :disabled="busy"
|
|
class="btn btn-secondary h-16 min-w-[64px] text-2xl" x-text="d"></button>
|
|
</template>
|
|
<button type="button" @click="back()" :disabled="busy"
|
|
class="btn btn-secondary h-16 min-w-[64px] text-sm">{{ _('Indietro') }}</button>
|
|
<button type="button" @click="press('0')" :disabled="busy"
|
|
class="btn btn-secondary h-16 min-w-[64px] text-2xl">0</button>
|
|
<button type="button" @click="del()" :disabled="busy"
|
|
class="btn btn-secondary h-16 min-w-[64px] text-sm">{{ _('Cancella') }}</button>
|
|
</div>
|
|
</div>
|
|
<p class="mt-6 text-center">
|
|
<a href="#" @click.prevent="mode = 'password'" class="text-sm text-primary-600 hover:underline">{{ _('Accedi con password') }}</a>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Login Form (password: admin e chi non ha PIN) -->
|
|
<form x-show="mode === 'password'" method="POST" action="{{ url_for('auth.login') }}" class="space-y-6">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<!-- Username -->
|
|
<div>
|
|
<label for="username" class="tmf-label">
|
|
{{ _('Username') }}
|
|
</label>
|
|
<div class="relative">
|
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<svg class="h-5 w-5 text-[var(--text-muted)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
</svg>
|
|
</div>
|
|
<input type="text"
|
|
id="username"
|
|
name="username"
|
|
required
|
|
autofocus
|
|
autocomplete="username"
|
|
placeholder="{{ _('Username') }}"
|
|
class="tmf-input !pl-10 !py-2.5">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Password -->
|
|
<div x-data="{ show: false }">
|
|
<label for="password" class="tmf-label">
|
|
{{ _('Password') }}
|
|
</label>
|
|
<div class="relative">
|
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<svg class="h-5 w-5 text-[var(--text-muted)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
</svg>
|
|
</div>
|
|
<input :type="show ? 'text' : 'password'"
|
|
type="password"
|
|
id="password"
|
|
name="password"
|
|
required
|
|
autocomplete="current-password"
|
|
placeholder="{{ _('Password') }}"
|
|
class="tmf-input !pl-10 !pr-12 !py-2.5">
|
|
<button type="button"
|
|
@click="show = !show"
|
|
:aria-label="show ? {{ _('Nascondi password')|tojson_attr }} : {{ _('Mostra password')|tojson_attr }}"
|
|
class="absolute inset-y-0 right-0 px-3 flex items-center text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors">
|
|
<!-- Eye (password hidden, click to show) -->
|
|
<svg x-show="!show" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"/>
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
</svg>
|
|
<!-- Eye slash (password visible, click to hide) -->
|
|
<svg x-show="show" x-cloak class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 001.934 12c1.292 4.338 5.31 7.5 10.066 7.5.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<div>
|
|
<button type="submit" class="btn btn-primary btn-lg w-full shadow-sm">
|
|
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
|
|
</svg>
|
|
{{ _('Accedi') }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<p class="mt-4 text-center" x-show="mode === 'password' && users.length" x-cloak>
|
|
<a href="#" @click.prevent="mode = 'pin'" class="text-sm text-primary-600 hover:underline">{{ _('Accedi con PIN') }}</a>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Help Text -->
|
|
<div class="mt-6 text-center">
|
|
<p class="text-xs text-[var(--text-muted)]">
|
|
{{ _('Hai dimenticato la password?') }}
|
|
<br>
|
|
{{ _('Contatta l\'amministratore') }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div class="text-center text-xs text-[var(--text-muted)]">
|
|
<p>TieMeasureFlow © {{ current_year }} - {{ _('Sistema di misurazione industriale') }}{# versione e commit (#70) #} · v{{ app_version }}{% if app_commit %} · {{ app_commit }}{% endif %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
/* Configurazione e testi per pin-pad.js (#59) */
|
|
window.TMF_PIN = {
|
|
usersUrl: {{ url_for('auth.pin_users')|tojson }},
|
|
loginUrl: {{ url_for('auth.login_pin')|tojson }},
|
|
msgNet: {{ _('Errore di connessione al server')|tojson }}
|
|
};
|
|
</script>
|
|
<script src="{{ url_for('static', filename='js/pin-pad.js') }}"></script>
|
|
{% endblock %}
|