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>
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
@using Microsoft.Extensions.Logging
|
||||
@using SteUp.Shared.Components.Layout
|
||||
@using SteUp.Shared.Components.Layout.Overlay
|
||||
@using SteUp.Shared.Components.SingleElements
|
||||
@using SteUp.Shared.Components.SingleElements.Card
|
||||
@using SteUp.Shared.Components.SingleElements.MessageBox
|
||||
@using SteUp.Shared.Core.Dto
|
||||
@using SteUp.Shared.Core.Entities
|
||||
@using SteUp.Shared.Core.Enum
|
||||
@using SteUp.Shared.Core.Helpers
|
||||
@using SteUp.Shared.Core.Interface.IntegryApi
|
||||
@using SteUp.Shared.Core.Interface.LocalDb
|
||||
@using SteUp.Shared.Core.Interface.System
|
||||
@@ -21,98 +23,133 @@
|
||||
@inject ILogger<IspezionePage> Logger
|
||||
@implements IDisposable
|
||||
|
||||
<HeaderLayout Title="Ispezione" BackTo="Indietro" Back="true"/>
|
||||
<HeaderLayout Title="@Titolo" Subtitle="@Sottotitolo" Back="true"/>
|
||||
|
||||
<div class="container content pb-safe-area ispezione">
|
||||
<InspectionCard Ispezione="SteupDataService.InspectionPageState.Ispezione"/>
|
||||
<div class="app-scroll">
|
||||
<div class="page-width page-body stack stack-lg">
|
||||
|
||||
@if (!SchedeGrouped.IsNullOrEmpty())
|
||||
{
|
||||
<MudExpansionPanels MultiExpansion="true">
|
||||
@{
|
||||
var panelIndex = 0;
|
||||
@* §6.6 Pannello di riepilogo: primo elemento della pagina, superficie
|
||||
tenue, con lo stato del documento e i conteggi sempre a vista. *@
|
||||
<section class="panel panel--info panel--lg">
|
||||
<div class="summary__head">
|
||||
<div class="section-header__text">
|
||||
<h2 class="summary__title">@Titolo</h2>
|
||||
<p class="summary__subtitle">@Ispezione.Data.ToString("d MMMM yyyy")</p>
|
||||
</div>
|
||||
<Badge Spec="@Ispezione.Stato.ToBadge()"/>
|
||||
</div>
|
||||
|
||||
<div class="summary__metrics">
|
||||
<div class="summary__metric">
|
||||
<i class="summary__metric-icon ri-file-list-3-line" aria-hidden="true"></i>
|
||||
<div class="summary__metric-value">@SchedeList.Count</div>
|
||||
<div class="summary__metric-label">@(SchedeList.Count == 1 ? "scheda" : "schede")</div>
|
||||
</div>
|
||||
<div class="summary__metric">
|
||||
<i class="summary__metric-icon ri-smartphone-line" aria-hidden="true"></i>
|
||||
<div class="summary__metric-value">@DaInviare</div>
|
||||
<div class="summary__metric-label">da inviare</div>
|
||||
</div>
|
||||
<div class="summary__metric">
|
||||
<i class="summary__metric-icon ri-edit-line" aria-hidden="true"></i>
|
||||
<div class="summary__metric-value">@Bozze</div>
|
||||
<div class="summary__metric-label">da completare</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (SchedeList.Count > 0)
|
||||
{
|
||||
<div class="summary__progress-head">
|
||||
<span class="summary__progress-count">@Inviate di @SchedeList.Count inviate</span>
|
||||
<span class="summary__progress-pct">@PercentualeInviate%</span>
|
||||
</div>
|
||||
<div class="progress-track" role="progressbar" aria-valuenow="@PercentualeInviate"
|
||||
aria-valuemin="0" aria-valuemax="100" aria-label="Schede inviate al server">
|
||||
<div class="progress-fill" style="--progress: @Progresso"></div>
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
|
||||
@if (Bozze > 0)
|
||||
{
|
||||
@* Feedback inline: resta a video finche' il problema esiste, a
|
||||
differenza del toast che sparisce. *@
|
||||
<div class="feedback feedback--warning" role="status" aria-live="polite">
|
||||
<i class="feedback__icon ri-edit-line" aria-hidden="true"></i>
|
||||
<span>
|
||||
@(Bozze == 1 ? "1 scheda è rimasta a metà" : $"{Bozze} schede sono rimaste a metà").
|
||||
Completale o eliminale prima di concludere l'ispezione.
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (SchedeGrouped.Count == 0)
|
||||
{
|
||||
<div class="empty-state">
|
||||
<i class="empty-state__icon ri-clipboard-line" aria-hidden="true"></i>
|
||||
<p class="empty-state__title">Nessuna scheda</p>
|
||||
@if (!IsCompleted)
|
||||
{
|
||||
<p class="empty-state__text">
|
||||
Tocca <b>+</b> nella barra in basso per creare la prima scheda di questa ispezione.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var group in SchedeGrouped)
|
||||
{
|
||||
var isFirstPanel = panelIndex == 0;
|
||||
panelIndex++;
|
||||
<MudExpansionPanel Class="expansion-panel" Expanded="isFirstPanel" Text="@group.Key.Descrizione">
|
||||
<TitleContent>
|
||||
@{
|
||||
var daInviare = group.Value.CountDaInviare();
|
||||
var daCompletare = group.Value.CountBozze();
|
||||
}
|
||||
<div class="header-scheda-group">
|
||||
<div class="title">
|
||||
<MudText Typo="Typo.h6"><b>@group.Key.Descrizione</b></MudText>
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Default">
|
||||
@($"{group.Value.Count} sched{(group.Value.Count == 1 ? "a" : "e")}")
|
||||
</MudChip>
|
||||
@if (daInviare > 0)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Variant="Variant.Filled"
|
||||
Color="Color.Primary" Icon="@Icons.Material.Rounded.PhonelinkOff">
|
||||
@($"{daInviare} da inviare")
|
||||
</MudChip>
|
||||
}
|
||||
@if (daCompletare > 0)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Variant="Variant.Filled"
|
||||
Color="Color.Warning" Icon="@Icons.Material.Rounded.EditNote">
|
||||
@($"{daCompletare} da completare")
|
||||
</MudChip>
|
||||
}
|
||||
</div>
|
||||
@if (SteupDataService.InspectionPageState.Ispezione.Stato != StatusEnum.Completata)
|
||||
{
|
||||
<div class="action-scheda-group">
|
||||
<MudFab StartIcon="@Icons.Material.Rounded.Add"
|
||||
Color="Color.Default" Size="Size.Medium"
|
||||
Disabled="!NetworkService.IsNetworkAvailable()"
|
||||
aria-label="@($"Nuova scheda su {group.Key.Descrizione}")"
|
||||
OnClick="@(() => CreateNewScheda(group.Key))"/>
|
||||
var daInviare = group.Value.CountDaInviare();
|
||||
var daCompletare = group.Value.CountBozze();
|
||||
|
||||
@if (NetworkService.IsNetworkAvailable() && daInviare > 0)
|
||||
{
|
||||
<MudFab StartIcon="@Icons.Material.Rounded.CloudUpload" Label="Esporta reparto"
|
||||
Color="Color.Primary" Size="Size.Medium"
|
||||
aria-label="@($"Esporta il reparto {group.Key.Descrizione} al server")"
|
||||
OnClick="@(() => ExportReparto(group.Key))"/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<section>
|
||||
<div class="section-header">
|
||||
<div class="section-header__text">
|
||||
<h2 class="section-header__title">@group.Key.Descrizione</h2>
|
||||
<p class="section-header__desc">
|
||||
@(group.Value.Count == 1 ? "1 scheda" : $"{group.Value.Count} schede")
|
||||
</p>
|
||||
</div>
|
||||
</TitleContent>
|
||||
<ChildContent>
|
||||
|
||||
@if (!IsCompleted && NetworkService.IsNetworkAvailable() && daInviare > 0)
|
||||
{
|
||||
<div class="section-header__aside">
|
||||
<MudButton Variant="Variant.Outlined" Size="Size.Small"
|
||||
StartIcon="@Icons.Material.Rounded.CloudUpload"
|
||||
aria-label="@($"Invia le schede del reparto {group.Key.Descrizione}")"
|
||||
OnClick="@(() => ExportReparto(group.Key))">
|
||||
Invia @daInviare
|
||||
</MudButton>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (daCompletare > 0)
|
||||
{
|
||||
<div class="badge-row" style="margin-bottom: var(--space-10);">
|
||||
<Badge Spec="@SyncState.Bozza.ToBadge()" Text="@($"{daCompletare} da completare")"/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card-list">
|
||||
@foreach (var scheda in group.Value)
|
||||
{
|
||||
<SchedaCard Scheda="scheda" OnSchedaDeleted="OnSchedaDeleted"
|
||||
<SchedaCard Scheda="scheda" Ispezione="Ispezione"
|
||||
OnSchedaDeleted="OnSchedaDeleted"
|
||||
OnSchedaModified="OnSchedaModified"
|
||||
Ispezione="@SteupDataService.InspectionPageState.Ispezione"/>
|
||||
OnBusyChanged="@(b => VisibleOverlay = b)"/>
|
||||
}
|
||||
</ChildContent>
|
||||
</MudExpansionPanel>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
</MudExpansionPanels>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="empty-state">
|
||||
<MudIcon Icon="@Icons.Material.Rounded.AssignmentLate" Class="empty-state-icon" Size="Size.Large"/>
|
||||
<p class="empty-state-title">Nessuna scheda ancora</p>
|
||||
@if (SteupDataService.InspectionPageState.Ispezione.Stato != StatusEnum.Completata)
|
||||
{
|
||||
<p class="empty-state-text">
|
||||
Tocca <b>+</b> in basso per creare la prima scheda di questa ispezione.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SpinnerOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="_success" Message="@_progressMessage"/>
|
||||
<ConfirmMessageBox @ref="_exportRepartoBox" NoText="Annulla" YesText="Esporta" YesColor="Color.Primary"
|
||||
Message="Le schede di questo reparto verranno inviate al server. Procedere?"/>
|
||||
<ConfirmMessageBox @ref="_exportRepartoBox" NoText="Annulla" YesText="Invia" YesColor="Color.Primary"
|
||||
Message="Le schede complete di questo reparto verranno inviate al server. Procedere?"/>
|
||||
|
||||
@code {
|
||||
private List<Scheda> SchedeList { get; set; } = [];
|
||||
@@ -125,6 +162,28 @@
|
||||
|
||||
private ConfirmMessageBox _exportRepartoBox = null!;
|
||||
|
||||
private Ispezione Ispezione => SteupDataService.InspectionPageState.Ispezione;
|
||||
private bool IsCompleted => Ispezione.Stato == StatusEnum.Completata;
|
||||
|
||||
private int DaInviare => SchedeList.CountDaInviare();
|
||||
private int Bozze => SchedeList.CountBozze();
|
||||
private int Inviate => SchedeList.Count(s => s.GetSyncState() == SyncState.Inviato);
|
||||
|
||||
private int PercentualeInviate => SchedeList.Count == 0
|
||||
? 0
|
||||
: (int)Math.Round(Inviate * 100.0 / SchedeList.Count);
|
||||
|
||||
private string Progresso => (PercentualeInviate / 100.0).ToString("0.###",
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
private string Titolo => SteupDataService.PuntiVenditaList
|
||||
.Find(x => x.CodMdep != null && x.CodMdep.EqualsIgnoreCase(Ispezione.CodMdep))?
|
||||
.Descrizione is { Length: > 0 } descrizione
|
||||
? descrizione
|
||||
: Ispezione.CodMdep;
|
||||
|
||||
private string Sottotitolo => Ispezione.CodMdep;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
NewScheda.OnNewScheda += LoadSchede;
|
||||
@@ -135,7 +194,7 @@
|
||||
|
||||
private void LoadSchede()
|
||||
{
|
||||
var ispezione = SteupDataService.InspectionPageState.Ispezione;
|
||||
var ispezione = Ispezione;
|
||||
|
||||
InvokeAsync(async () =>
|
||||
{
|
||||
@@ -162,7 +221,7 @@
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
var ispezione = SteupDataService.InspectionPageState.Ispezione;
|
||||
var ispezione = Ispezione;
|
||||
|
||||
// Le bozze non sono inviabili: l'ispezione non si chiude finché ci sono.
|
||||
var bozze = SchedeList.CountBozze();
|
||||
@@ -277,7 +336,7 @@
|
||||
StatusEnum.Completata
|
||||
);
|
||||
|
||||
SteupDataService.InspectionPageState.Ispezione.Stato = StatusEnum.Completata;
|
||||
ispezione.Stato = StatusEnum.Completata;
|
||||
|
||||
// Riscontro al momento più importante: la spunta di successo prima di chiudere l'overlay.
|
||||
await InvokeAsync(() =>
|
||||
@@ -339,18 +398,6 @@
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task CreateNewScheda(JtbFasiDto jtbFasi)
|
||||
{
|
||||
var modal = await Dialog.OpenFormScheda(
|
||||
SteupDataService.InspectionPageState.Ispezione.CodMdep,
|
||||
SteupDataService.InspectionPageState.Ispezione.Data,
|
||||
true,
|
||||
new Scheda { Reparto = jtbFasi }
|
||||
);
|
||||
|
||||
if (modal is { Canceled: false }) LoadSchede();
|
||||
}
|
||||
|
||||
private async Task ExportReparto(JtbFasiDto jtbFasi)
|
||||
{
|
||||
// Guardia anti doppio-tap: un secondo tocco durante l'invio non ri-esporta il batch.
|
||||
@@ -365,14 +412,14 @@
|
||||
|
||||
try
|
||||
{
|
||||
var ispezione = SteupDataService.InspectionPageState.Ispezione;
|
||||
var ispezione = Ispezione;
|
||||
|
||||
// Le bozze restano sul dispositivo: si esporta solo ciò che è completo.
|
||||
var daEsportare = SchedeGrouped[jtbFasi].FindAll(x => !x.Bozza);
|
||||
|
||||
if (daEsportare.Count == 0)
|
||||
{
|
||||
Snackbar.Add("Nessuna scheda completa da esportare in questo reparto", Severity.Info);
|
||||
Snackbar.Add("Nessuna scheda completa da inviare in questo reparto", Severity.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -398,11 +445,11 @@
|
||||
|
||||
if (apiResponse == null)
|
||||
{
|
||||
Snackbar.Add("Esportazione non riuscita. Le schede restano sul dispositivo, riprova.", Severity.Error);
|
||||
Snackbar.Add("Invio non riuscito. Le schede restano sul dispositivo, riprova.", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
SteupDataService.InspectionPageState.Ispezione.ActivityId = apiResponse.ActivityIdIspezione;
|
||||
ispezione.ActivityId = apiResponse.ActivityIdIspezione;
|
||||
|
||||
await IspezioniService.UpdateActivityIdIspezioneAsync(ispezione.CodMdep, ispezione.Data,
|
||||
UserSession.User.Username, apiResponse.ActivityIdIspezione
|
||||
@@ -423,7 +470,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
Snackbar.Add("Reparto esportato", Severity.Success);
|
||||
Snackbar.Add(daEsportare.Count == 1 ? "Scheda inviata" : $"{daEsportare.Count} schede inviate",
|
||||
Severity.Success);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -453,4 +501,4 @@
|
||||
CompleteInspection.OnComplete -= HandleCompleteInspection;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/* Tinta di fondo tenue: le card e i pannelli bianchi (Carta) risaltano sul Grigio Campo */
|
||||
.ispezione {
|
||||
background-color: var(--light-card-background);
|
||||
}
|
||||
|
||||
.ispezione ::deep .expansion-panel .mud-expand-panel-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.action-scheda-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: .75rem;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: .75rem;
|
||||
margin-top: 3rem;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
.empty-state .empty-state-icon {
|
||||
color: var(--mud-palette-primary);
|
||||
opacity: .85;
|
||||
}
|
||||
|
||||
.empty-state .empty-state-title {
|
||||
margin: 0;
|
||||
font-weight: 800;
|
||||
font-size: 1.25rem;
|
||||
color: var(--mud-palette-text-primary);
|
||||
}
|
||||
|
||||
.empty-state .empty-state-text {
|
||||
margin: 0;
|
||||
max-width: 34ch;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
color: var(--mud-palette-gray-darker);
|
||||
}
|
||||
@@ -1,47 +1,68 @@
|
||||
@page "/ispezioni"
|
||||
@attribute [Authorize]
|
||||
@using SteUp.Shared.Components.Layout
|
||||
@using SteUp.Shared.Components.Layout.Overlay
|
||||
@using SteUp.Shared.Components.SingleElements
|
||||
@using SteUp.Shared.Components.SingleElements.Card
|
||||
@using SteUp.Shared.Core.Entities
|
||||
@using SteUp.Shared.Core.Interface.LocalDb
|
||||
@inject IIspezioniService IspezioniService
|
||||
|
||||
<HeaderLayout Title="Ispezioni"/>
|
||||
<HeaderLayout Title="Ispezioni" Subtitle="@Subtitle"/>
|
||||
|
||||
<div class="container ispezioni pb-safe-area">
|
||||
@if (Ispezioni.IsNullOrEmpty())
|
||||
{
|
||||
<NoDataAvailable Text="Nessuna ispezione effettuata"
|
||||
ImageSource="_content/SteUp.Shared/images/undraw_file-search_cbur.svg"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Virtualize Items="Ispezioni" Context="ispezione">
|
||||
<InspectionCard Class="no-margin" Ispezione="ispezione" CompactView="true" OnClick="@OnClickIspezione"/>
|
||||
</Virtualize>
|
||||
}
|
||||
<div class="app-scroll">
|
||||
<div class="page-width page-body">
|
||||
@if (_loading)
|
||||
{
|
||||
@* Scheletri, non uno spinner in mezzo al contenuto: la pagina ha
|
||||
gia' la forma che avra', e non salta quando i dati arrivano. *@
|
||||
<div class="card-list" aria-hidden="true">
|
||||
@for (var i = 0; i < 3; i++)
|
||||
{
|
||||
<div class="list-card">
|
||||
<div class="skeleton skeleton--line-short"></div>
|
||||
<div class="skeleton skeleton--title" style="margin-top: 10px;"></div>
|
||||
<div class="skeleton skeleton--line" style="margin-top: 12px;"></div>
|
||||
<div class="skeleton skeleton--badge" style="margin-top: 14px;"></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else if (Ispezioni.Count == 0)
|
||||
{
|
||||
<div class="empty-state">
|
||||
<i class="empty-state__icon ri-clipboard-line" aria-hidden="true"></i>
|
||||
<p class="empty-state__title">Nessuna ispezione</p>
|
||||
<p class="empty-state__text">
|
||||
Tocca <b>+</b> nella barra in basso per aprire la prima ispezione su un punto vendita.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="card-list">
|
||||
<Virtualize Items="Ispezioni" Context="ispezione" OverscanCount="4">
|
||||
<InspectionCard Ispezione="ispezione" OnClick="@OnClickIspezione"/>
|
||||
</Virtualize>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SpinnerOverlay VisibleOverlay="VisibleOverlay"/>
|
||||
|
||||
@code{
|
||||
private List<Ispezione> Ispezioni { get; set; } = [];
|
||||
private bool _loading = true;
|
||||
|
||||
private bool VisibleOverlay { get; set; } = true;
|
||||
private string? Subtitle => _loading || Ispezioni.Count == 0
|
||||
? null
|
||||
: Ispezioni.Count == 1
|
||||
? "1 ispezione"
|
||||
: $"{Ispezioni.Count} ispezioni";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadData();
|
||||
|
||||
VisibleOverlay = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task LoadData()
|
||||
{
|
||||
Ispezioni = await IspezioniService.GetAllIspezioniWithSchedeAsync(UserSession.User.Username);
|
||||
|
||||
_loading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void OnClickIspezione(Ispezione ispezione)
|
||||
@@ -49,5 +70,4 @@
|
||||
SteupDataService.InspectionPageState.Ispezione = ispezione;
|
||||
NavigationManager.NavigateTo("/ispezione");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.ispezioni{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
@page "/login"
|
||||
@using Microsoft.Extensions.Logging
|
||||
@using SteUp.Shared.Components.Layout.Spinner
|
||||
@using SteUp.Shared.Core.BarcodeReader.Contracts
|
||||
@using SteUp.Shared.Core.Interface.System
|
||||
@using SteUp.Shared.Core.Services
|
||||
@@ -10,68 +9,83 @@
|
||||
@inject IBarcodeManager BarcodeManager
|
||||
@inject ILogger<LoginPage> Logger
|
||||
|
||||
@if (Spinner)
|
||||
{
|
||||
<SpinnerLayout/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="login-page">
|
||||
<div class="container container-top-logo">
|
||||
<img src="_content/SteUp.Shared/images/logo_steup.svg" class="logo" alt="sales book">
|
||||
</div>
|
||||
<div class="container container-login">
|
||||
<div class="login-form-container">
|
||||
<div class="input-group">
|
||||
<MudTextField @bind-Value="UserData.Username" Label="Username" Variant="Variant.Outlined"/>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<MudTextField InputType="@_passwordInput" @bind-Value="UserData.Password" Label="Password" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentIcon="@_passwordInputIcon" OnAdornmentClick="ShowPassword" AdornmentAriaLabel="Show Password"/>
|
||||
</div>
|
||||
<div class="input-group mb-2">
|
||||
<MudTextField @bind-Value="UserData.CodHash" Label="Profilo azienda" Variant="Variant.Outlined"/>
|
||||
</div>
|
||||
@*
|
||||
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".
|
||||
*@
|
||||
|
||||
<MudButton OnClick="@SignInUser" Color="Color.Primary" Variant="Variant.Filled">Login</MudButton>
|
||||
@if (_attemptFailed)
|
||||
{
|
||||
<MudAlert Class="my-3" Dense="true" Severity="Severity.Error" Variant="Variant.Filled">@ErrorMessage</MudAlert>
|
||||
}
|
||||
<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="my-4 login-footer">
|
||||
<span>@GenericSystemService.GetCurrentAppVersion() | Powered by</span>
|
||||
<img src="_content/SteUp.Shared/images/logoIntegry.svg" class="img-fluid" alt="Integry">
|
||||
<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>
|
||||
</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 bool Spinner { get; set; }
|
||||
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()
|
||||
{
|
||||
if (_isShow)
|
||||
{
|
||||
_isShow = false;
|
||||
_passwordInputIcon = Icons.Material.Rounded.VisibilityOff;
|
||||
_passwordInput = InputType.Password;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isShow = true;
|
||||
_passwordInputIcon = Icons.Material.Rounded.Visibility;
|
||||
_passwordInput = InputType.Text;
|
||||
}
|
||||
_isShow = !_isShow;
|
||||
_passwordInputIcon = _isShow ? Icons.Material.Rounded.Visibility : Icons.Material.Rounded.VisibilityOff;
|
||||
_passwordInput = _isShow ? InputType.Text : InputType.Password;
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
@@ -82,36 +96,34 @@ else
|
||||
|
||||
private async Task SignInUser()
|
||||
{
|
||||
if (_busy || !CanSubmit) return;
|
||||
|
||||
_attemptFailed = false;
|
||||
if (!string.IsNullOrEmpty(UserData.Username) && !string.IsNullOrEmpty(UserData.Password) && !string.IsNullOrEmpty(UserData.CodHash))
|
||||
_busy = true;
|
||||
StateHasChanged();
|
||||
|
||||
try
|
||||
{
|
||||
Spinner = true;
|
||||
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();
|
||||
|
||||
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("/");
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ErrorMessage = e.Message;
|
||||
Logger.LogError(e, ErrorMessage);
|
||||
Console.WriteLine(e.Message);
|
||||
|
||||
Spinner = false;
|
||||
StateHasChanged();
|
||||
|
||||
_attemptFailed = true;
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,4 +134,4 @@ else
|
||||
public string? CodHash { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
.login-page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--mud-palette-surface);
|
||||
}
|
||||
|
||||
.container-top-logo > .logo {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.container-login > span {
|
||||
font-size: large;
|
||||
font-weight: 900;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container-top-logo {
|
||||
height: 35vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.login-form-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.container-login {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 4px 16px 16px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-footer span {
|
||||
font-size: 9px;
|
||||
color: var(--mud-palette-gray-darker);
|
||||
}
|
||||
|
||||
.login-footer img {
|
||||
height: 15px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
@@ -2,5 +2,19 @@
|
||||
@using SteUp.Shared.Components.Layout
|
||||
@layout MainLayout
|
||||
|
||||
<h3>Not Found</h3>
|
||||
<p>Sorry, the content you are looking for does not exist.</p>
|
||||
<HeaderLayout Title="Pagina non trovata" Back="true"/>
|
||||
|
||||
<div class="app-scroll">
|
||||
<div class="page-width page-body">
|
||||
<div class="empty-state">
|
||||
<i class="empty-state__icon ri-compass-3-line" aria-hidden="true"></i>
|
||||
<h1 class="empty-state__title">Qui non c'è niente</h1>
|
||||
<p class="empty-state__text">
|
||||
La schermata che cercavi non esiste. Torna all'elenco delle ispezioni per riprendere il lavoro.
|
||||
</p>
|
||||
<div class="empty-state__action">
|
||||
<MudButton Variant="Variant.Outlined" Href="/ispezioni">Vai alle ispezioni</MudButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@using SteUp.Shared.Components.Layout
|
||||
@using SteUp.Shared.Components.Layout.Overlay
|
||||
@using SteUp.Shared.Components.SingleElements
|
||||
@using SteUp.Shared.Components.SingleElements.MessageBox
|
||||
@using SteUp.Shared.Core.Authorization.Enum
|
||||
@using SteUp.Shared.Core.Dto
|
||||
@using SteUp.Shared.Core.Interface.IntegryApi
|
||||
@@ -10,109 +11,94 @@
|
||||
@using SteUp.Shared.Core.Services
|
||||
@using SteUp.Shared.Core.Utility
|
||||
@inject AppAuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject INetworkService NetworkService
|
||||
@inject IGenericSystemService GenericSystemService
|
||||
@inject IFileManager FileManager
|
||||
@inject IIntegryApiService IntegryApiService
|
||||
|
||||
<HeaderLayout Title="Profilo"/>
|
||||
|
||||
@if (IsLoggedIn)
|
||||
{
|
||||
<div class="container content pb-safe-area">
|
||||
<div class="container-primary-info mud-elevation-1">
|
||||
<div class="section-primary-info">
|
||||
<MudAvatar Style="height: 70px; width: 70px; font-size: 2rem; font-weight: bold"
|
||||
Color="Color.Secondary">
|
||||
@UtilityString.ExtractInitials(UserSession.User.Fullname)
|
||||
</MudAvatar>
|
||||
<div class="app-scroll">
|
||||
<div class="page-width page-body stack stack-md">
|
||||
|
||||
<div class="personal-info">
|
||||
<span class="info-nome">@UserSession.User.Fullname</span>
|
||||
@if (UserSession.User.KeyGroup is not null)
|
||||
{
|
||||
<span
|
||||
class="info-section">@(((KeyGroupEnum)UserSession.User.KeyGroup).ConvertToHumanReadable())</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="section-info">
|
||||
<div class="section-personal-info">
|
||||
<div>
|
||||
<span class="info-title">Profilo azienda</span>
|
||||
<span class="info-text">@CodHash</span>
|
||||
@if (IsLoggedIn)
|
||||
{
|
||||
@* §6.16 Intestazione profilo *@
|
||||
<section class="panel panel--lg">
|
||||
<div class="profile-head">
|
||||
<div class="profile-avatar" aria-hidden="true">
|
||||
@UtilityString.ExtractInitials(UserSession.User.Fullname)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-personal-info">
|
||||
<div>
|
||||
<span class="info-title">Status</span>
|
||||
@if (NetworkService.ConnectionAvailable)
|
||||
<div class="section-header__text">
|
||||
<h2 class="summary__title">@UserSession.User.Fullname</h2>
|
||||
@if (UserSession.User.KeyGroup is not null)
|
||||
{
|
||||
<div class="status online">
|
||||
<i class="ri-wifi-line"></i>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="status offline">
|
||||
<i class="ri-wifi-off-line"></i>
|
||||
<span>Offline</span>
|
||||
</div>
|
||||
<p class="summary__subtitle">
|
||||
@(((KeyGroupEnum)UserSession.User.KeyGroup).ConvertToHumanReadable())
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-button ripple-container mud-elevation-1">
|
||||
<MudButton Class="button-settings red-icon"
|
||||
FullWidth="true"
|
||||
StartIcon="@Icons.Material.Rounded.UploadFile"
|
||||
Size="Size.Medium"
|
||||
OnClick="@ExportLog"
|
||||
Variant="Variant.Outlined">
|
||||
Esporta log
|
||||
</MudButton>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="container-button ripple-container mud-elevation-1">
|
||||
<MudButton Class="button-settings green-icon"
|
||||
FullWidth="true"
|
||||
StartIcon="@Icons.Material.Outlined.Sync"
|
||||
Size="Size.Medium"
|
||||
OnClick="@UpdateDb"
|
||||
Variant="Variant.Outlined">
|
||||
Sincronizza ispezioni esportate
|
||||
</MudButton>
|
||||
</div>
|
||||
<section class="panel">
|
||||
<div class="nav-list__item" style="pointer-events: none;">
|
||||
<i class="nav-list__icon ri-building-line" aria-hidden="true"></i>
|
||||
<span class="nav-list__label">Profilo azienda</span>
|
||||
<span class="t-meta">@CodHash</span>
|
||||
</div>
|
||||
|
||||
<div class="container-button ripple-container mud-elevation-1">
|
||||
<MudButton Class="button-settings exit"
|
||||
FullWidth="true"
|
||||
Color="Color.Error"
|
||||
Size="Size.Medium"
|
||||
OnClick="@Logout"
|
||||
Variant="Variant.Outlined">
|
||||
Esci
|
||||
</MudButton>
|
||||
</div>
|
||||
|
||||
<AppVersion/>
|
||||
<div class="nav-list__item" style="pointer-events: none;">
|
||||
<i class="nav-list__icon ri-cloud-line" aria-hidden="true"></i>
|
||||
<span class="nav-list__label">Connessione</span>
|
||||
<Badge Spec="@ConnessioneBadge"/>
|
||||
</div>
|
||||
|
||||
<div class="nav-list__item" style="pointer-events: none;">
|
||||
<i class="nav-list__icon ri-information-line" aria-hidden="true"></i>
|
||||
<span class="nav-list__label">Versione</span>
|
||||
<span class="t-meta">@GenericSystemService.GetCurrentAppVersion()</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<button type="button" class="nav-list__item" @onclick="UpdateDb">
|
||||
<i class="nav-list__icon ri-refresh-line" aria-hidden="true"></i>
|
||||
<span class="nav-list__label">Sincronizza ispezioni esportate</span>
|
||||
<i class="nav-list__chevron ri-arrow-right-s-line" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<button type="button" class="nav-list__item" @onclick="ExportLog">
|
||||
<i class="nav-list__icon ri-file-upload-line" aria-hidden="true"></i>
|
||||
<span class="nav-list__label">Invia i log allo sviluppo</span>
|
||||
<i class="nav-list__chevron ri-arrow-right-s-line" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
@* Le voci distruttive portano l'icona nel tono danger e non hanno chevron. *@
|
||||
<button type="button" class="nav-list__item nav-list__item--danger" @onclick="ConfirmLogout">
|
||||
<i class="nav-list__icon ri-logout-box-r-line" aria-hidden="true"></i>
|
||||
<span class="nav-list__label">Esci</span>
|
||||
</button>
|
||||
</section>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<SpinnerOverlay VisibleOverlay="VisibleOverlay"/>
|
||||
<SpinnerOverlay VisibleOverlay="VisibleOverlay" Message="@_progressMessage"/>
|
||||
<ConfirmMessageBox @ref="_logoutBox" NoText="Annulla" YesText="Esci" YesColor="Color.Error"
|
||||
Message="Uscendo servirà di nuovo la connessione per rientrare. Le ispezioni salvate sul dispositivo restano al loro posto."/>
|
||||
|
||||
@code {
|
||||
private bool IsLoggedIn { get; set; }
|
||||
private bool VisibleOverlay { get; set; }
|
||||
private string? _progressMessage;
|
||||
private string? CodHash { get; set; } = "";
|
||||
|
||||
private ConfirmMessageBox _logoutBox = null!;
|
||||
|
||||
private Core.Helpers.StatusBadgeHelper.BadgeSpec ConnessioneBadge => NetworkService.ConnectionAvailable
|
||||
? new Core.Helpers.StatusBadgeHelper.BadgeSpec("success", "ri-cloud-line", "Online")
|
||||
: new Core.Helpers.StatusBadgeHelper.BadgeSpec("danger", "ri-wifi-off-line", "Offline");
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
IsLoggedIn = await UserSession.IsLoggedIn();
|
||||
@@ -122,46 +108,72 @@
|
||||
|
||||
private async Task ExportLog()
|
||||
{
|
||||
_progressMessage = "Invio dei log…";
|
||||
VisibleOverlay = true;
|
||||
StateHasChanged();
|
||||
|
||||
var profiloAzienda = LocalStorage.GetString("codHash");
|
||||
|
||||
var email = new SendEmailDto
|
||||
|
||||
try
|
||||
{
|
||||
FromName = "Integry Log",
|
||||
To = "developer@integry.it",
|
||||
Subject = $"SteUP - Log del {DateTime.Today:d} di {UserSession.User.Username}",
|
||||
IsHtml = true,
|
||||
MsgText = $"Username: <b>{UserSession.User.Username}</b><br/>" +
|
||||
$"Profilo azienda: <b>{profiloAzienda}</b><br/>" +
|
||||
$"ProfileDb: <b>{UserSession.ProfileDb}</b><br/>" +
|
||||
$"Versione app: <b>{GenericSystemService.GetCurrentAppVersion()}</b>",
|
||||
Attachments = FileManager.GetFileForExport()
|
||||
};
|
||||
|
||||
await IntegryApiService.SendEmail(email);
|
||||
|
||||
VisibleOverlay = false;
|
||||
StateHasChanged();
|
||||
var profiloAzienda = LocalStorage.GetString("codHash");
|
||||
|
||||
var email = new SendEmailDto
|
||||
{
|
||||
FromName = "Integry Log",
|
||||
To = "developer@integry.it",
|
||||
Subject = $"SteUP - Log del {DateTime.Today:d} di {UserSession.User.Username}",
|
||||
IsHtml = true,
|
||||
MsgText = $"Username: <b>{UserSession.User.Username}</b><br/>" +
|
||||
$"Profilo azienda: <b>{profiloAzienda}</b><br/>" +
|
||||
$"ProfileDb: <b>{UserSession.ProfileDb}</b><br/>" +
|
||||
$"Versione app: <b>{GenericSystemService.GetCurrentAppVersion()}</b>",
|
||||
Attachments = FileManager.GetFileForExport()
|
||||
};
|
||||
|
||||
await IntegryApiService.SendEmail(email);
|
||||
Snackbar.Add("Log inviati allo sviluppo", Severity.Success);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Snackbar.Add("Invio dei log non riuscito. Controlla la connessione e riprova.", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
VisibleOverlay = false;
|
||||
_progressMessage = null;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateDb()
|
||||
{
|
||||
_progressMessage = "Sincronizzazione…";
|
||||
VisibleOverlay = true;
|
||||
StateHasChanged();
|
||||
|
||||
await SteupDataService.CheckAndUpdateStatus();
|
||||
|
||||
VisibleOverlay = false;
|
||||
StateHasChanged();
|
||||
try
|
||||
{
|
||||
await SteupDataService.CheckAndUpdateStatus();
|
||||
Snackbar.Add("Ispezioni aggiornate", Severity.Success);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Snackbar.Add("Sincronizzazione non riuscita. Controlla la connessione e riprova.", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
VisibleOverlay = false;
|
||||
_progressMessage = null;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Logout()
|
||||
private async Task ConfirmLogout()
|
||||
{
|
||||
var result = await _logoutBox.ShowAsync();
|
||||
if (result is not true) return;
|
||||
|
||||
await AuthenticationStateProvider.SignOut();
|
||||
IsLoggedIn = await UserSession.IsLoggedIn();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
.container-primary-info {
|
||||
/*background: var(--light-card-background);*/
|
||||
width: 100%;
|
||||
margin-bottom: 2rem;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.container-primary-info .divider {
|
||||
margin: 0 0 0 7rem;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.section-primary-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
padding: .8rem 1.2rem .4rem;
|
||||
}
|
||||
|
||||
.personal-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.info-nome {
|
||||
color: var(--mud-palette-text-primary);
|
||||
font-weight: 800;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
color: var(--mud-palette-gray-default);
|
||||
font-size: medium;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
padding: .4rem 1.2rem .8rem;
|
||||
}
|
||||
|
||||
.section-personal-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.section-personal-info > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: normal;
|
||||
margin: .25rem 0;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
color: var(--mud-palette-gray-darker);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
color: var(--mud-palette-text-secondary);
|
||||
font-weight: 700;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.content ::deep .user-button { border: 1px solid var(--card-border-color) !important; }
|
||||
|
||||
.user-button > i { font-size: large; }
|
||||
|
||||
.user-button > span {
|
||||
font-size: medium;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status { font-weight: 700; }
|
||||
|
||||
.status.online { color: var(--mud-palette-success); }
|
||||
|
||||
.status.offline { color: var(--mud-palette-error); }
|
||||
Reference in New Issue
Block a user