Ridisegna la schermata di accesso
Ogni campo era un riquadro dentro il riquadro, con l'etichetta che saltava sopra il testo al primo tocco: un modulo web, non una schermata. Ora i tre campi stanno in un pannello solo diviso da una linea, come le liste del resto dell'app, e l'etichetta sta ferma sopra il campo. Lo sfondo passa alla superficie di lavoro, la stessa dello splash e della prima schermata: l'avvio non cambia colore tre volte. L'errore di accesso usa il riquadro di avviso gia' in uso dentro l'app invece di uno stile suo. Non verificata sul terminale: la sessione sul dispositivo e' aperta e non l'ho chiusa per non far perdere l'accesso. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,32 +19,48 @@
|
|||||||
<form class="onboarding__panel" @onsubmit="SignInUser" @onsubmit:preventDefault="true">
|
<form class="onboarding__panel" @onsubmit="SignInUser" @onsubmit:preventDefault="true">
|
||||||
<img src="_content/SteUp.Shared/images/logo_steup.svg" class="onboarding__logo" alt="SteUP"/>
|
<img src="_content/SteUp.Shared/images/logo_steup.svg" class="onboarding__logo" alt="SteUP"/>
|
||||||
|
|
||||||
|
@* Un solo pannello con tre righe divise da una linea, come le liste
|
||||||
|
del resto dell'app. Prima ogni campo era un riquadro dentro il
|
||||||
|
riquadro, con l'etichetta che saltava sopra il testo al primo tocco:
|
||||||
|
un gesto da modulo web, non da applicazione. Qui l'etichetta sta
|
||||||
|
ferma sopra il campo e non si muove mai. *@
|
||||||
<div class="onboarding__fields">
|
<div class="onboarding__fields">
|
||||||
<div class="onboarding__field">
|
<label class="onboarding__field">
|
||||||
<MudTextField @bind-Value="UserData.Username" Label="Username"
|
<span class="onboarding__label">Username</span>
|
||||||
Variant="Variant.Text" Immediate="true"
|
<input class="onboarding__input" type="text" autocomplete="username"
|
||||||
UserAttributes="@(new() { ["autocomplete"] = "username", ["enterkeyhint"] = "next", ["autocapitalize"] = "none" })"/>
|
autocapitalize="none" autocorrect="off" enterkeyhint="next"
|
||||||
</div>
|
value="@UserData.Username" @oninput="@(e => UserData.Username = e.Value?.ToString())"/>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="onboarding__field">
|
<label class="onboarding__field">
|
||||||
<MudTextField InputType="@_passwordInput" @bind-Value="UserData.Password" Label="Password"
|
<span class="onboarding__label">Password</span>
|
||||||
Variant="Variant.Text" Immediate="true"
|
<span class="onboarding__input-row">
|
||||||
Adornment="Adornment.End" AdornmentIcon="@_passwordInputIcon"
|
<input class="onboarding__input" type="@(_isShow ? "text" : "password")"
|
||||||
OnAdornmentClick="ShowPassword"
|
autocomplete="current-password" enterkeyhint="next"
|
||||||
AdornmentAriaLabel="@(_isShow ? "Nascondi la password" : "Mostra la password")"
|
value="@UserData.Password" @oninput="@(e => UserData.Password = e.Value?.ToString())"/>
|
||||||
UserAttributes="@(new() { ["autocomplete"] = "current-password", ["enterkeyhint"] = "next" })"/>
|
<button type="button" class="onboarding__reveal" @onclick="ShowPassword"
|
||||||
</div>
|
aria-label="@(_isShow ? "Nascondi la password" : "Mostra la password")">
|
||||||
|
<i class="@(_isShow ? "ri-eye-off-line" : "ri-eye-line")" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="onboarding__field">
|
<label class="onboarding__field">
|
||||||
<MudTextField @bind-Value="UserData.CodHash" Label="Profilo azienda"
|
<span class="onboarding__label">Profilo azienda</span>
|
||||||
Variant="Variant.Text" Immediate="true"
|
<input class="onboarding__input" type="text" autocomplete="organization"
|
||||||
UserAttributes="@(new() { ["autocomplete"] = "organization", ["enterkeyhint"] = "go", ["autocapitalize"] = "none" })"/>
|
autocapitalize="none" autocorrect="off" enterkeyhint="go"
|
||||||
</div>
|
value="@UserData.CodHash" @oninput="@(e => UserData.CodHash = e.Value?.ToString())"/>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (_attemptFailed)
|
@if (_attemptFailed)
|
||||||
{
|
{
|
||||||
<p class="onboarding__error" role="alert" aria-live="assertive">@ErrorMessage</p>
|
@* Stesso riquadro di avviso usato dentro l'app: l'errore di accesso
|
||||||
|
non e' un caso speciale che merita un suo stile. *@
|
||||||
|
<div class="feedback feedback--danger onboarding__error" role="alert" aria-live="assertive">
|
||||||
|
<i class="feedback__icon ri-error-warning-line" aria-hidden="true"></i>
|
||||||
|
<span>@ErrorMessage</span>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<button type="submit" class="onboarding__submit" disabled="@(_busy || !CanSubmit)">
|
<button type="submit" class="onboarding__submit" disabled="@(_busy || !CanSubmit)">
|
||||||
@@ -73,20 +89,13 @@
|
|||||||
private bool _busy;
|
private bool _busy;
|
||||||
|
|
||||||
private bool _isShow;
|
private bool _isShow;
|
||||||
private InputType _passwordInput = InputType.Password;
|
|
||||||
private string _passwordInputIcon = Icons.Material.Rounded.VisibilityOff;
|
|
||||||
|
|
||||||
private bool CanSubmit =>
|
private bool CanSubmit =>
|
||||||
!string.IsNullOrWhiteSpace(UserData.Username) &&
|
!string.IsNullOrWhiteSpace(UserData.Username) &&
|
||||||
!string.IsNullOrWhiteSpace(UserData.Password) &&
|
!string.IsNullOrWhiteSpace(UserData.Password) &&
|
||||||
!string.IsNullOrWhiteSpace(UserData.CodHash);
|
!string.IsNullOrWhiteSpace(UserData.CodHash);
|
||||||
|
|
||||||
private void ShowPassword()
|
private void ShowPassword() => _isShow = !_isShow;
|
||||||
{
|
|
||||||
_isShow = !_isShow;
|
|
||||||
_passwordInputIcon = _isShow ? Icons.Material.Rounded.Visibility : Icons.Material.Rounded.VisibilityOff;
|
|
||||||
_passwordInput = _isShow ? InputType.Text : InputType.Password;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -881,7 +881,7 @@
|
|||||||
padding: var(--space-lg);
|
padding: var(--space-lg);
|
||||||
padding-top: calc(var(--space-lg) + env(safe-area-inset-top, 0px));
|
padding-top: calc(var(--space-lg) + env(safe-area-inset-top, 0px));
|
||||||
padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom, 0px));
|
padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom, 0px));
|
||||||
background-color: var(--surface-muted);
|
background-color: var(--workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
.onboarding__panel {
|
.onboarding__panel {
|
||||||
@@ -892,40 +892,80 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.onboarding__logo {
|
.onboarding__logo {
|
||||||
height: 128px;
|
height: 104px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Un pannello solo, tre righe divise da una linea: la stessa forma delle
|
||||||
|
liste dell'app, invece di tre riquadri che galleggiano separati. */
|
||||||
.onboarding__fields {
|
.onboarding__fields {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-md);
|
|
||||||
margin-top: var(--space-xl);
|
margin-top: var(--space-xl);
|
||||||
|
background-color: var(--surface);
|
||||||
|
border-radius: var(--radius-surface);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.onboarding__field {
|
.onboarding__field {
|
||||||
background-color: var(--surface);
|
display: block;
|
||||||
border: 1px solid var(--divider);
|
padding: var(--space-sm) var(--space-md);
|
||||||
border-radius: var(--radius-control);
|
transition: background-color var(--duration-fast) var(--ease-out);
|
||||||
padding: var(--space-xs) var(--space-md);
|
|
||||||
transition: border-color var(--duration-fast) var(--ease-out),
|
|
||||||
box-shadow var(--duration-fast) var(--ease-out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.onboarding__field:focus-within {
|
.onboarding__field + .onboarding__field { border-top: 1px solid var(--divider); }
|
||||||
border-color: var(--primary-dark);
|
|
||||||
box-shadow: inset 0 0 0 1px var(--primary-dark);
|
.onboarding__field:focus-within { background-color: var(--surface-muted); }
|
||||||
|
|
||||||
|
/* L'etichetta sta sopra il campo e non si muove: niente testo che scivola
|
||||||
|
quando appoggi il dito. */
|
||||||
|
.onboarding__label {
|
||||||
|
display: block;
|
||||||
|
font-size: var(--label-md-size);
|
||||||
|
line-height: 1.25;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--ink-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.onboarding__error {
|
.onboarding__input-row {
|
||||||
margin-top: var(--space-lg);
|
display: flex;
|
||||||
text-align: center;
|
align-items: center;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding__input {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 34px;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
outline: none;
|
||||||
|
font-family: inherit;
|
||||||
font-size: var(--body-lg-size);
|
font-size: var(--body-lg-size);
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: var(--danger-ink);
|
color: var(--ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.onboarding__reveal {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: var(--touch-min);
|
||||||
|
height: var(--touch-min);
|
||||||
|
margin-right: calc(var(--space-xs) * -1);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: none;
|
||||||
|
color: var(--ink-muted);
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding__reveal:active { background-color: var(--surface-muted); }
|
||||||
|
|
||||||
|
.onboarding__error { margin-top: var(--space-md); }
|
||||||
|
|
||||||
/* Bottone a pillola, alto 52: e' l'unico gesto della schermata. */
|
/* Bottone a pillola, alto 52: e' l'unico gesto della schermata. */
|
||||||
.onboarding__submit {
|
.onboarding__submit {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user