Files
SteUP_Dotnet/SteUp.Shared/Components/Pages/LoginPage.razor
T
MarcoE 2a0054aa30 Rifacimento della UI secondo STILE_UI.md
Adotta lo stile condiviso delle app Integry: superfici piatte, raggi ampi,
spaziature a scala fissa, gerarchia data dallo spazio prima che dalla
decorazione. La palette storica di SteUP resta intatta; sono stati aggiunti
solo i ruoli che mancavano (workspace, surfaceMuted, divider, inkMuted e le
triplette dei toni semantici).

Fondamenta
- tokens.css: ruoli colore, scale di spazio/raggi/tipografia, curve di
  movimento e scala z-index semantica. Unica fonte di verita': pilotano
  anche le variabili CSS di MudBlazor.
- Inter variabile ospitata in locale (133KB) al posto di Nunito da Google
  Fonts: l'app lavora offline, un font che arriva dalla rete in campo non
  arriva.
- base.css: shell applicativa e comportamenti da app nativa — niente
  selezione testo, niente menu al tocco lungo, niente lampo grigio sul tap,
  niente rimbalzo elastico, niente zoom a doppio tocco, scroll con inerzia.
- mudblazor-overrides.css: MudBlazor appiattito (elevation 0, nessun tint,
  target 44/48, etichette in caso naturale).
- Rimossi Bootstrap (CSS, icone e JS) e i 996 righe di CSS con ambito per
  componente del vecchio sistema: erano tutti inutilizzati.

Struttura
- Navigazione flottante in basso con icona piena sulla voce attiva; da 720px
  diventa colonna laterale. Il MudFabMenu e' sostituito da un bottom sheet:
  stessa funzione, bersagli molto piu' grandi.
- Testata alta col titolo a sinistra, fusa con la pagina.
- Nel form scheda il salvataggio sta in una barra ancorata in fondo, non in
  coda a uno scroll lungo.
- Scelta di punto vendita e articoli a schermo intero con ricerca; la
  selezione multipla usa righe da 56px invece di un menu a tendina.
- Urgenza scelta a pillole invece che da una select.

Stato e riscontro
- StatusBadgeHelper: tono, icona ed etichetta di ogni stato in un punto solo,
  cosi' lo stesso stato si presenta identico ovunque.
- Scheletri al posto degli spinner; l'overlay bloccante dice sempre cosa sta
  facendo; gli stati vuoti suggeriscono la mossa successiva.
- Rimosso il ritardo artificiale di 250ms in InspectionCard, che non
  attendeva nulla.

DESIGN.md riscritto sul nuovo sistema.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:09:26 +02:00

138 lines
4.9 KiB
Plaintext

@page "/login"
@using Microsoft.Extensions.Logging
@using SteUp.Shared.Core.BarcodeReader.Contracts
@using SteUp.Shared.Core.Interface.System
@using SteUp.Shared.Core.Services
@inject IUserAccountService UserAccountService
@inject AppAuthenticationStateProvider AuthenticationStateProvider
@inject IGenericSystemService GenericSystemService
@inject IBarcodeManager BarcodeManager
@inject ILogger<LoginPage> Logger
@*
Accesso (STILE_UI §7): stessa struttura dell'app, registro diverso.
Niente accento cromatico — l'attenzione sta sul marchio; il corallo torna
dentro l'app, dove significa "qui si agisce".
*@
<div class="onboarding">
<form class="onboarding__panel" @onsubmit="SignInUser" @onsubmit:preventDefault="true">
<img src="_content/SteUp.Shared/images/logo_steup.svg" class="onboarding__logo" alt="SteUP"/>
<div class="onboarding__fields">
<div class="onboarding__field">
<MudTextField @bind-Value="UserData.Username" Label="Username"
Variant="Variant.Text" Immediate="true"
UserAttributes="@(new() { ["autocomplete"] = "username", ["enterkeyhint"] = "next", ["autocapitalize"] = "none" })"/>
</div>
<div class="onboarding__field">
<MudTextField InputType="@_passwordInput" @bind-Value="UserData.Password" Label="Password"
Variant="Variant.Text" Immediate="true"
Adornment="Adornment.End" AdornmentIcon="@_passwordInputIcon"
OnAdornmentClick="ShowPassword"
AdornmentAriaLabel="@(_isShow ? "Nascondi la password" : "Mostra la password")"
UserAttributes="@(new() { ["autocomplete"] = "current-password", ["enterkeyhint"] = "next" })"/>
</div>
<div class="onboarding__field">
<MudTextField @bind-Value="UserData.CodHash" Label="Profilo azienda"
Variant="Variant.Text" Immediate="true"
UserAttributes="@(new() { ["autocomplete"] = "organization", ["enterkeyhint"] = "go", ["autocapitalize"] = "none" })"/>
</div>
</div>
@if (_attemptFailed)
{
<p class="onboarding__error" role="alert" aria-live="assertive">@ErrorMessage</p>
}
<button type="submit" class="onboarding__submit" disabled="@(_busy || !CanSubmit)">
@if (_busy)
{
<span class="onboarding__spinner" aria-hidden="true"></span>
<span>Accesso in corso…</span>
}
else
{
<span>Accedi</span>
}
</button>
<div class="onboarding__footer">
<span>@GenericSystemService.GetCurrentAppVersion() · Powered by</span>
<img src="_content/SteUp.Shared/images/logoIntegry.svg" alt="Integry"/>
</div>
</form>
</div>
@code {
private SignIn UserData { get; } = new();
private string ErrorMessage { get; set; } = "";
private bool _attemptFailed;
private bool _busy;
private bool _isShow;
private InputType _passwordInput = InputType.Password;
private string _passwordInputIcon = Icons.Material.Rounded.VisibilityOff;
private bool CanSubmit =>
!string.IsNullOrWhiteSpace(UserData.Username) &&
!string.IsNullOrWhiteSpace(UserData.Password) &&
!string.IsNullOrWhiteSpace(UserData.CodHash);
private void ShowPassword()
{
_isShow = !_isShow;
_passwordInputIcon = _isShow ? Icons.Material.Rounded.Visibility : Icons.Material.Rounded.VisibilityOff;
_passwordInput = _isShow ? InputType.Text : InputType.Password;
}
protected override void OnInitialized()
{
UserData.CodHash = LocalStorage.GetString("codHash");
StateHasChanged();
}
private async Task SignInUser()
{
if (_busy || !CanSubmit) return;
_attemptFailed = false;
_busy = true;
StateHasChanged();
try
{
SteupDataService.RegisterAppVersion();
await UserAccountService.Login(UserData.Username!, UserData.Password!, UserData.CodHash!);
AuthenticationStateProvider.NotifyAuthenticationState(); //Chiamato per forzare il refresh
await SteupDataService.Init();
BarcodeManager.Init();
LocalStorage.SetString("codHash", UserData.CodHash!);
NavigationManager.NavigateTo("/");
}
catch (Exception e)
{
ErrorMessage = e.Message;
Logger.LogError(e, ErrorMessage);
_attemptFailed = true;
}
finally
{
_busy = false;
StateHasChanged();
}
}
public class SignIn
{
public string? Username { get; set; }
public string? Password { get; set; }
public string? CodHash { get; set; }
}
}