Rimetti i reparti richiudibili e il "+" sul reparto

Le schede di un reparto erano un elenco fisso: con quattro reparti aperti
la pagina diventava un rotolo, e per aggiungere una scheda a un reparto
preciso bisognava passare dal comando generale e ridichiarare il reparto.

La testata del reparto torna a essere un comando: si tocca per richiudere
il gruppo, e la freccia dice da che parte sta. I conteggi salgono nella
testata ("3 schede · 1 da inviare · 1 da completare") invece di stare nel
corpo: un reparto compattato non deve nascondere quello che gli manca.

Accanto torna il "+", che apre la scheda nuova con il reparto gia'
scelto. E' l'unica differenza con il "+" della barra, ed e' quella che
toglie un passaggio a chi sta lavorando su un reparto alla volta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 16:45:50 +02:00
parent b9de075f45
commit 70a0d0f5fa
2 changed files with 177 additions and 33 deletions
@@ -151,49 +151,64 @@
}
else
{
@* Un reparto per volta: la testata si tocca per richiudere il
gruppo, e i conteggi restano scritti li' anche da chiuso — un
reparto compattato non deve nascondere quello che gli manca. *@
@foreach (var group in SchedeGrouped)
{
var reparto = group.Key;
var chiuso = IsCollapsed(reparto);
var daInviare = group.Value.CountDaInviare();
var daCompletare = group.Value.CountBozze();
<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>
<section class="reparto @(chiuso ? "reparto--collapsed" : "")">
<div class="reparto__head">
<button type="button" class="reparto__toggle" @onclick="@(() => Toggle(reparto))"
aria-expanded="@((!chiuso).ToString().ToLowerInvariant())">
<i class="reparto__chevron ri-arrow-down-s-line" aria-hidden="true"></i>
<span class="reparto__text">
<span class="reparto__title">@reparto.Descrizione</span>
<span class="reparto__meta">@Conteggi(group.Value)</span>
</span>
</button>
<div class="reparto__actions">
@if (!IsCompleted)
{
@* Da qui la scheda nasce gia' con il suo reparto: e'
l'unica differenza con il "+" della barra. *@
<button type="button" class="reparto__add"
disabled="@(!NetworkService.IsNetworkAvailable())"
aria-label="@($"Nuova scheda nel reparto {reparto.Descrizione}")"
title="@($"Nuova scheda in {reparto.Descrizione}")"
@onclick="@(() => CreateNewScheda(reparto))">
<i class="ri-add-line" aria-hidden="true"></i>
</button>
@if (NetworkService.IsNetworkAvailable() && daInviare > 0)
{
<MudButton Variant="Variant.Outlined" Size="Size.Small"
StartIcon="@Icons.Material.Rounded.CloudUpload"
aria-label="@($"Invia le schede del reparto {reparto.Descrizione}")"
OnClick="@(() => ExportReparto(reparto))">
Invia @daInviare
</MudButton>
}
}
</div>
@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)
@if (!chiuso)
{
<div class="badge-row" style="margin-bottom: var(--space-10);">
<Badge Spec="@SyncState.Bozza.ToBadge()" Text="@($"{daCompletare} da completare")"/>
<div class="card-list reparto__body">
@foreach (var scheda in group.Value)
{
<SchedaCard Scheda="scheda" Ispezione="Ispezione"
OnSchedaDeleted="OnSchedaDeleted"
OnSchedaModified="OnSchedaModified"
OnBusyChanged="@(b => VisibleOverlay = b)"/>
}
</div>
}
<div class="card-list">
@foreach (var scheda in group.Value)
{
<SchedaCard Scheda="scheda" Ispezione="Ispezione"
OnSchedaDeleted="OnSchedaDeleted"
OnSchedaModified="OnSchedaModified"
OnBusyChanged="@(b => VisibleOverlay = b)"/>
}
</div>
</section>
}
}
@@ -296,6 +311,43 @@
if (modal is { Canceled: false }) LoadSchede();
}
/// Stessa scheda nuova, ma con il reparto gia' scelto: partendo da un
/// reparto non ha senso richiederlo.
private async Task CreateNewScheda(JtbFasiDto reparto)
{
var modal = await Dialog.OpenFormScheda(
Ispezione.CodMdep, Ispezione.Data, true, new Scheda { Reparto = reparto }
);
if (modal is { Canceled: false }) LoadSchede();
}
// I reparti richiusi stanno qui e non nell'entita': e' uno stato della
// schermata, non del documento.
private readonly HashSet<string> _collapsed = new(StringComparer.OrdinalIgnoreCase);
private bool IsCollapsed(JtbFasiDto reparto) =>
reparto.CodJfas is not null && _collapsed.Contains(reparto.CodJfas);
private void Toggle(JtbFasiDto reparto)
{
if (reparto.CodJfas is null) return;
if (!_collapsed.Remove(reparto.CodJfas)) _collapsed.Add(reparto.CodJfas);
}
private static string Conteggi(List<Scheda> schede)
{
var parti = new List<string> { schede.Count == 1 ? "1 scheda" : $"{schede.Count} schede" };
var daInviare = schede.CountDaInviare();
if (daInviare > 0) parti.Add($"{daInviare} da inviare");
var bozze = schede.CountBozze();
if (bozze > 0) parti.Add($"{bozze} da completare");
return string.Join(" · ", parti);
}
private async Task ConfirmCompleteInspection()
{
_actionsOpen = false;
+92
View File
@@ -140,6 +140,98 @@
.section-header__aside { flex: 0 0 auto; }
/* -----------------------------------------------------------------------------
Reparto — gruppo di schede richiudibile
La testata e' un comando a tutta riga: si tocca ovunque per chiudere il
gruppo. I conteggi stanno nella testata e non nel corpo, cosi' restano
leggibili anche da chiuso.
-------------------------------------------------------------------------- */
.reparto__head {
display: flex;
align-items: center;
gap: var(--space-xs);
margin-bottom: var(--space-sm);
}
.reparto__toggle {
flex: 1 1 auto;
min-width: 0;
display: flex;
align-items: center;
gap: var(--space-4);
min-height: var(--touch-min);
padding: 0;
border: none;
background: none;
font-family: inherit;
text-align: left;
color: var(--ink);
}
.reparto__chevron {
flex: 0 0 auto;
font-size: 24px;
color: var(--ink-muted);
transition: transform var(--duration-base) var(--ease-out);
}
.reparto--collapsed .reparto__chevron { transform: rotate(-90deg); }
.reparto__text {
flex: 1 1 auto;
min-width: 0;
}
.reparto__title {
display: block;
font-size: var(--title-lg-size);
line-height: var(--title-lg-height);
font-weight: 700;
letter-spacing: -0.01em;
}
.reparto__meta {
display: block;
margin-top: var(--space-2);
font-size: var(--body-md-size);
line-height: var(--body-md-height);
font-weight: 400;
color: var(--ink-muted);
}
.reparto__actions {
flex: 0 0 auto;
display: flex;
align-items: center;
gap: var(--space-6);
}
/* Il "+" del reparto non porta l'accento: l'accento e' del comando principale
della schermata, e qui i bottoni sono due in fila. */
.reparto__add {
display: flex;
align-items: center;
justify-content: center;
width: var(--touch-min);
height: var(--touch-min);
flex: 0 0 auto;
border: 1px solid var(--divider);
border-radius: var(--radius-pill);
background-color: var(--surface);
color: var(--ink);
font-size: 22px;
transition: transform var(--duration-fast) var(--ease-out),
background-color var(--duration-fast) var(--ease-out);
}
.reparto__add:active { transform: scale(0.94); background-color: var(--surface-muted); }
.reparto__add:disabled { color: var(--ink-muted); background-color: var(--surface-muted); }
@media (prefers-reduced-motion: reduce) {
.reparto__add:active { transform: none; }
}
/* -----------------------------------------------------------------------------
§6.4 Badge — stato in pillola
Mai il solo colore: sempre icona + etichetta.