2a0054aa30
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>
222 lines
7.9 KiB
Plaintext
222 lines
7.9 KiB
Plaintext
@using CommunityToolkit.Mvvm.Messaging
|
|
@using SteUp.Shared.Components.Layout.Sheet
|
|
@using SteUp.Shared.Components.SingleElements.MessageBox
|
|
@using SteUp.Shared.Core.Enum
|
|
@using SteUp.Shared.Core.Interface.LocalDb
|
|
@using SteUp.Shared.Core.Messages.Ispezione
|
|
@using SteUp.Shared.Core.Messages.Scheda
|
|
@inject INetworkService NetworkService
|
|
@inject IDialogService Dialog
|
|
@inject IMessenger Messenger
|
|
@inject IIspezioniService IspezioniService
|
|
@inject IJSRuntime Js
|
|
@implements IDisposable
|
|
|
|
@*
|
|
Navigazione flottante in basso (STILE_UI §6.15): il contenuto le scorre
|
|
sotto, le destinazioni restano a portata di pollice. Icona outline a
|
|
riposo, piena da selezionata, etichetta sempre visibile in entrambe.
|
|
Da 720px diventa una colonna laterale (vedi components.css).
|
|
*@
|
|
|
|
<nav class="navbar @(IsVisible ? "" : "navbar--hidden")" aria-label="Navigazione principale">
|
|
<NavLink class="navbar__item pressable" href="ispezioni" Match="NavLinkMatch.All">
|
|
<span class="navbar__icon">
|
|
<i class="@(IsOn("ispezioni") ? "ri-file-list-3-fill" : "ri-file-list-3-line")" aria-hidden="true"></i>
|
|
</span>
|
|
<span class="navbar__label">Ispezioni</span>
|
|
</NavLink>
|
|
|
|
<NavLink class="navbar__item pressable" href="user" Match="NavLinkMatch.All">
|
|
<span class="navbar__icon">
|
|
<i class="@(IsOn("user") ? "ri-user-fill" : "ri-user-line")" aria-hidden="true"></i>
|
|
</span>
|
|
<span class="navbar__label">Profilo</span>
|
|
</NavLink>
|
|
|
|
@if (ActionsVisible)
|
|
{
|
|
<button type="button" class="navbar__fab" @onclick="OpenActions"
|
|
aria-label="Azioni" title="Azioni">
|
|
<i class="ri-add-line" aria-hidden="true"></i>
|
|
</button>
|
|
}
|
|
</nav>
|
|
|
|
<ActionSheet @bind-Open="_sheetOpen" Title="@SheetTitle" Subtitle="@SheetSubtitle">
|
|
<div class="stack stack-xs">
|
|
@if (OnInspectionPage)
|
|
{
|
|
@if (!_isCompleted)
|
|
{
|
|
<button type="button" class="action-tile" disabled="@(!NetworkService.IsNetworkAvailable())"
|
|
@onclick="NewScheda">
|
|
<span class="action-tile__icon action-tile__icon--primary">
|
|
<i class="ri-add-line" aria-hidden="true"></i>
|
|
</span>
|
|
<span class="action-tile__label">Nuova scheda</span>
|
|
</button>
|
|
}
|
|
|
|
@if (ShowCompleteInspection)
|
|
{
|
|
<button type="button" class="action-tile" disabled="@(!NetworkService.IsNetworkAvailable())"
|
|
@onclick="CompleteInspection">
|
|
<span class="action-tile__icon action-tile__icon--success">
|
|
<i class="ri-check-double-line" aria-hidden="true"></i>
|
|
</span>
|
|
<span class="action-tile__label">Concludi ispezione</span>
|
|
</button>
|
|
}
|
|
|
|
<button type="button" class="action-tile" disabled="@(!NetworkService.IsNetworkAvailable())"
|
|
@onclick="DeleteInspection">
|
|
<span class="action-tile__icon action-tile__icon--danger">
|
|
<i class="ri-delete-bin-line" aria-hidden="true"></i>
|
|
</span>
|
|
<span class="action-tile__label">Elimina ispezione</span>
|
|
</button>
|
|
}
|
|
else
|
|
{
|
|
<button type="button" class="action-tile" disabled="@(!NetworkService.IsNetworkAvailable())"
|
|
@onclick="NewInspection">
|
|
<span class="action-tile__icon action-tile__icon--primary">
|
|
<i class="ri-add-line" aria-hidden="true"></i>
|
|
</span>
|
|
<span class="action-tile__label">Nuova ispezione</span>
|
|
</button>
|
|
}
|
|
|
|
@if (!NetworkService.IsNetworkAvailable())
|
|
{
|
|
<div class="feedback feedback--warning" role="status">
|
|
<i class="feedback__icon ri-wifi-off-line" aria-hidden="true"></i>
|
|
<span>Senza connessione queste azioni non sono disponibili. Il lavoro gia' fatto resta sul dispositivo.</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
</ActionSheet>
|
|
|
|
<ConfirmMessageBox @ref="_messageBox" NoText="Annulla" YesText="Concludi" YesColor="Color.Primary"
|
|
Message="Una volta conclusa, l'ispezione non sara' piu' modificabile e non potrai aggiungere altre schede. Procedere?"/>
|
|
|
|
@code
|
|
{
|
|
private bool IsVisible { get; set; } = true;
|
|
private bool ActionsVisible { get; set; } = true;
|
|
private bool OnInspectionPage { get; set; }
|
|
private bool ShowCompleteInspection { get; set; }
|
|
private bool _isCompleted;
|
|
private bool _sheetOpen;
|
|
|
|
private ConfirmMessageBox _messageBox = null!;
|
|
|
|
private static readonly string[] NavigableRoutes = ["ispezioni", "ispezione", "user"];
|
|
|
|
private string SheetTitle => OnInspectionPage ? "Azioni ispezione" : "Nuova ispezione";
|
|
|
|
private string? SheetSubtitle => OnInspectionPage
|
|
? SteupDataService.InspectionPageState.Ispezione.CodMdep
|
|
: null;
|
|
|
|
private string CurrentRoute =>
|
|
NavigationManager.Uri.Remove(0, NavigationManager.BaseUri.Length).Split('?')[0];
|
|
|
|
private bool IsOn(string route) => CurrentRoute.EqualsIgnoreCase(route);
|
|
|
|
protected override Task OnInitializedAsync()
|
|
{
|
|
NavigationManager.LocationChanged += OnLocationChanged;
|
|
Refresh();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private void OnLocationChanged(object? sender, LocationChangedEventArgs args)
|
|
{
|
|
// Cambiando schermata un menu aperto non ha piu' senso.
|
|
_sheetOpen = false;
|
|
Refresh();
|
|
}
|
|
|
|
private void Refresh()
|
|
{
|
|
var route = CurrentRoute;
|
|
|
|
IsVisible = NavigableRoutes.Contains(route, StringComparer.OrdinalIgnoreCase);
|
|
OnInspectionPage = route.EqualsIgnoreCase("ispezione");
|
|
ActionsVisible = IsVisible && !route.EqualsIgnoreCase("user");
|
|
|
|
if (OnInspectionPage)
|
|
{
|
|
var ispezione = SteupDataService.InspectionPageState.Ispezione;
|
|
_isCompleted = ispezione.Stato == StatusEnum.Completata;
|
|
// "Concludi" non ha senso su un'ispezione mai inviata o gia' conclusa.
|
|
ShowCompleteInspection = !ispezione.ActivityId.IsNullOrEmpty() && !_isCompleted;
|
|
|
|
// Su un'ispezione conclusa resta solo la consultazione.
|
|
if (_isCompleted) ActionsVisible = false;
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
private void OpenActions()
|
|
{
|
|
Refresh();
|
|
_sheetOpen = true;
|
|
}
|
|
|
|
private async Task NewInspection()
|
|
{
|
|
_sheetOpen = false;
|
|
|
|
if (await SteupDataService.CanOpenNewInspection())
|
|
{
|
|
_ = Dialog.OpenSelectShop();
|
|
}
|
|
else
|
|
{
|
|
await Dialog.ShowWarning(
|
|
"Per aprire una nuova ispezione devi prima concludere tutte le ispezioni aperte da piu' di 20 giorni.");
|
|
}
|
|
}
|
|
|
|
private async Task CompleteInspection()
|
|
{
|
|
_sheetOpen = false;
|
|
|
|
var result = await _messageBox.ShowAsync();
|
|
if (result is true) Messenger.Send(new CompleteInspectionMessage());
|
|
}
|
|
|
|
private async Task NewScheda()
|
|
{
|
|
_sheetOpen = false;
|
|
|
|
var ispezione = SteupDataService.InspectionPageState.Ispezione;
|
|
var modal = await Dialog.OpenFormScheda(ispezione.CodMdep, ispezione.Data, true);
|
|
|
|
if (modal is { Canceled: false }) Messenger.Send(new NewSchedaMessage());
|
|
}
|
|
|
|
private async Task DeleteInspection()
|
|
{
|
|
_sheetOpen = false;
|
|
|
|
try
|
|
{
|
|
var ispezione = SteupDataService.InspectionPageState.Ispezione;
|
|
await IspezioniService.DeleteIspezioneAsync(ispezione.CodMdep, ispezione.Data, ispezione.Rilevatore);
|
|
|
|
await Js.InvokeVoidAsync("goBack");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
await Dialog.ShowError(e.Message);
|
|
}
|
|
}
|
|
|
|
public void Dispose() => NavigationManager.LocationChanged -= OnLocationChanged;
|
|
}
|