Salvataggio come bozza: il lavoro in corso non si perde piu'

Uscendo da una scheda a meta' (tasto indietro del dispositivo o "Annulla")
il lavoro veniva scartato. Il tasto indietro non era nemmeno gestito: non
arriva alla WebView e non c'e' stack di navigazione, quindi mandava l'app
in background con i dati solo in memoria.

Nuovo stato Bozza ("Da completare"): salvata in locale, mai inviabile
finche' non viene conclusa.

- Scheda.Bozza + migration additiva (default false: le schede esistenti
  non cambiano stato).
- SchedaSyncHelper: terzo SyncState, CountBozze(). CountDaInviare()
  esclude le bozze, quindi il badge "da inviare" resta corretto.
- ModalFormScheda: SaveDraft() salva solo in locale senza validazione;
  Save() azzera il flag; Submit() avvisa quando il form e' invalido
  (prima il tocco su "Salva" sembrava non fare nulla se i campi mancanti
  erano fuori schermo); Update() prende preserveActivityId cosi' una
  bozza di scheda gia' inviata mantiene il link al server e al
  completamento lo aggiorna invece di duplicarlo.
- ConfirmUpdateActivity: il pulsante bozza sta su NoText, non CancelText
  (CancelText ritorna null, NoText ritorna false).
- BackButtonService + MainActivity.OnBackPressed: il tasto indietro passa
  dallo stesso percorso di "Annulla". Ignorato mentre e' aperto un dialog
  figlio, per non mostrare il prompt sotto un altro dialog.
- IspezionePage: "Concludi ispezione" si blocca se ci sono bozze,
  "Esporta reparto" le salta, chip "N da completare" per reparto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-16 13:24:35 +02:00
parent e3942d00cc
commit d076a4b77a
11 changed files with 433 additions and 22 deletions
@@ -40,6 +40,7 @@
<TitleContent>
@{
var daInviare = group.Value.CountDaInviare();
var daCompletare = group.Value.CountBozze();
}
<div class="header-scheda-group">
<div class="title">
@@ -54,6 +55,13 @@
@($"{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)
{
@@ -156,7 +164,25 @@
var ispezione = SteupDataService.InspectionPageState.Ispezione;
var daInviare = ispezione.Schede.Where(x => x.ActivityId == null).ToList();
// Le bozze non sono inviabili: l'ispezione non si chiude finché ci sono.
var bozze = SchedeList.CountBozze();
if (bozze > 0)
{
await InvokeAsync(() =>
{
VisibleOverlay = false;
StateHasChanged();
});
await Dialog.ShowWarning(
bozze == 1
? "C'è 1 scheda da completare. Completala o eliminala prima di concludere l'ispezione."
: $"Ci sono {bozze} schede da completare. Completale o eliminale prima di concludere l'ispezione."
);
return;
}
var daInviare = ispezione.Schede.Where(x => x.ActivityId == null && !x.Bozza).ToList();
var totale = daInviare.Count;
var indice = 0;
var fallite = 0;
@@ -341,7 +367,16 @@
{
var ispezione = SteupDataService.InspectionPageState.Ispezione;
var saveRequest = SchedeGrouped[jtbFasi].ConvertAll(x =>
// 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);
return;
}
var saveRequest = daEsportare.ConvertAll(x =>
{
return new SaveRequestDto
{
@@ -375,7 +410,7 @@
if (!apiResponse.ActivityIdSchedaList.IsNullOrEmpty())
{
foreach (var scheda in SchedeGrouped[jtbFasi])
foreach (var scheda in daEsportare)
{
var activityId = apiResponse.ActivityIdSchedaList!.Find(x =>
x.LocalId != null && x.LocalId == scheda.Id