Ridisegna la fase 1 in card di stato e aggiunge un passo esplicito di verifica in fase 2

Fase 1: le info su DB e cartella di lavoro erano testo grigio uniforme
disallineato con bottoni squadrati senza gerarchia. Ora vivono in una
card di stato ("Database") con un indicatore verde/rosso per lo stato
di caricamento; la card "Cartella di lavoro" è stata rimossa (resta
gestibile da File → Scegli cartella di lavoro..., già presente nel
menu). Le icone sui bottoni (prima Segoe MDL2 Assets, poi disegnate a
mano via GDI+) sono state provate e infine tolte su richiesta: restano
solo testo, più semplici da mantenere per due bottoni.

Fase 2: "Verifica presenza" ora compare solo dopo un import riuscito
(dialogo o drag&drop) invece di essere sempre visibile, ed è lei —non
il solo import— a sbloccare "Avanti" (mancava la constatazione che
l'utente può ancora avanzare senza aver verificato che i file esistano
sul server). Aggiunte etichette numerate "1. Carica la distinta" /
"2. Verifica la presenza dei file sul server" accanto ai bottoni, e
rimosso il messaggio verde ridondante dopo l'import.

Doc aggiornati nello stesso commit: CLAUDE.md, docs/architettura.md,
GuidaForm.cs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5 committed 2026-09-22 15:50:29 +02:00
1 parent 3e9c78074c
commit e94331b69f
5 files changed
+166 -73

No files matched your search

+13 -9
View File
@@ -73,16 +73,20 @@ che conta per capire il prodotto.
designer visuale):
1. **Database** — carica/aggiorna `cad_index.db` (`Indexer.ScanAsync`, radice disco di
default `X:\` hardcoded in `RadiceDiscoDefault`); mostra anche la **cartella di lavoro**
(destinazione di CSV/`raw/`/`step/`), proposta dall'ultima usata o dalla cartella del DB se
non c'è ancora nulla di salvato — cambiabile da "File → Scegli cartella di lavoro..." (non
più un bottone in fase 1: azione occasionale, non un passo del flusso) — vedi
`docs/db-cad-index.md` per il dettaglio di schema e risoluzione del percorso DB.
default `X:\` hardcoded in `RadiceDiscoDefault`); un pallino verde/rosso accanto al titolo
indica se un DB è caricato. La **cartella di lavoro** (destinazione di CSV/`raw/`/`step/`)
non è più mostrata qui — proposta dall'ultima usata o dalla cartella del DB se non c'è
ancora nulla di salvato, cambiabile in qualunque momento da "File → Scegli cartella di
lavoro..." — vedi `docs/db-cad-index.md` per il dettaglio di schema e risoluzione del
percorso DB.
2. **Importa Distinta Base** — `DistintaBaseImporter.ConvertiInCsv` genera il CSV
componente;descrizione nella cartella di lavoro; se il file segue la convenzione del
gestionale (`DBP_<codice>_<timestamp>`), aggiunge l'assieme generale come riga in più (da
quel momento è un componente come tutti gli altri, nessun caso speciale nel resto del
codice).
componente;descrizione nella cartella di lavoro (da file scelto o trascinato); se il file
segue la convenzione del gestionale (`DBP_<codice>_<timestamp>`), aggiunge l'assieme
generale come riga in più (da quel momento è un componente come tutti gli altri, nessun
caso speciale nel resto del codice). Il bottone "Verifica presenza" compare solo dopo un
import riuscito e cerca i componenti nell'indice (`VerificaCodiciNelDb`, righe colorate in
verde/rosso in griglia): è quello — non il solo import — a sbloccare "Avanti"
(`AvantiConsentito`).
3. **Crea cartella raw** — `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela, vedi
`docs/copia-raw-parallela.md`) copiano in `raw/` i file CAD dei componenti del CSV trovati
nell'indice.
+100 -45
View File
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Drawing.Drawing2D;
using Microsoft.Data.Sqlite;
namespace PrepStep;
@@ -10,11 +11,13 @@ public partial class Form1 : Form
private readonly Button _btnCreaRaw = new();
private readonly Button _btnTrovaPrt = new();
private readonly Button _btnVerifica = new();
private readonly FlowLayoutPanel _rigaVerifica = new();
private readonly ListView _lstAnteprimaAsm = new();
private readonly Label _lblStatus = new();
private readonly Label _lblDb = new();
private readonly Label _lblUltimoAggiornamento = new();
private readonly Label _lblCartellaLavoro = new();
private readonly Panel _indicatoreDb = new();
private bool _dbCaricato;
private readonly DataGridView _gridImport = new();
private readonly DataGridView _gridRaw = new();
private readonly Label _lblImportStatus = new();
@@ -250,7 +253,7 @@ public partial class Form1 : Form
private bool AvantiConsentito(int fase) => fase switch
{
0 => _dbPath is not null && _cartellaLavoro is not null,
1 => _righeCorrenti is { Count: > 0 },
1 => _righeCorrenti is { Count: > 0 } && _ultimaVerifica is not null,
2 => _ultimaCartellaRaw is not null,
3 => true, // risoluzione ricorsiva opzionale: non tutti gli assiemi hanno sotto-componenti
_ => true,
@@ -278,6 +281,7 @@ public partial class Form1 : Form
_gridRaw.Rows.Clear();
_lstAnteprimaAsm.Items.Clear();
ImpostaStatoImport("", Color.Black);
_rigaVerifica.Visible = false;
VaiAllaFase(1);
}
@@ -301,57 +305,78 @@ public partial class Form1 : Form
Dock = DockStyle.Top,
AutoSize = true,
ForeColor = SystemColors.GrayText,
Margin = new Padding(0, 0, 0, 12),
Text = "L'indice CAD viene caricato automaticamente all'avvio, se già presente. "
+ "Usa 'Apri DB...' per selezionarne un altro, oppure 'Aggiorna indice' per "
+ "scansionare di nuovo il disco (X:\\).",
Margin = new Padding(0, 0, 0, 16),
Text = "L'indice CAD viene caricato automaticamente all'avvio, se già presente.",
};
contenuto.Controls.Add(lblIstruzioni);
var cardDb = UiStile.Card();
var contenutoCardDb = new TableLayoutPanel
{
Dock = DockStyle.Top,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
ColumnCount = 1,
};
contenutoCardDb.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
var rigaTitoloDb = new FlowLayoutPanel
{
AutoSize = true,
FlowDirection = FlowDirection.LeftToRight,
Margin = new Padding(0, 0, 0, 6),
};
_indicatoreDb.Size = new Size(10, 10);
_indicatoreDb.Margin = new Padding(0, 5, 8, 0);
_indicatoreDb.Paint += (_, e) =>
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var colore = _dbCaricato ? Color.FromArgb(30, 160, 90) : Color.FromArgb(200, 60, 60);
using var pennello = new SolidBrush(colore);
e.Graphics.FillEllipse(pennello, 0, 0, _indicatoreDb.Width - 1, _indicatoreDb.Height - 1);
};
rigaTitoloDb.Controls.Add(_indicatoreDb);
var lblTitoloDb = new Label
{
AutoSize = true,
Font = new Font(Font, FontStyle.Bold),
Text = "Database",
};
rigaTitoloDb.Controls.Add(lblTitoloDb);
contenutoCardDb.Controls.Add(rigaTitoloDb);
_lblDb.Dock = DockStyle.Top;
_lblDb.AutoSize = true;
_lblDb.ForeColor = SystemColors.GrayText;
_lblDb.Margin = new Padding(0, 0, 0, 4);
contenuto.Controls.Add(_lblDb);
_lblDb.ForeColor = SystemColors.ControlText;
_lblDb.Margin = new Padding(0, 0, 0, 2);
contenutoCardDb.Controls.Add(_lblDb);
_lblUltimoAggiornamento.AutoSize = true;
_lblUltimoAggiornamento.ForeColor = SystemColors.GrayText;
_lblUltimoAggiornamento.Margin = new Padding(0, 0, 0, 12);
contenuto.Controls.Add(_lblUltimoAggiornamento);
_lblUltimoAggiornamento.Margin = new Padding(0, 0, 0, 14);
contenutoCardDb.Controls.Add(_lblUltimoAggiornamento);
var bottoni = new FlowLayoutPanel { AutoSize = true, FlowDirection = FlowDirection.LeftToRight };
var bottoniDb = new FlowLayoutPanel { AutoSize = true, FlowDirection = FlowDirection.LeftToRight };
_btnApriDb.Text = "Apri DB...";
_btnApriDb.Margin = new Padding(0, 0, 8, 0);
UiStile.Secondario(_btnApriDb);
UiStile.Arrotonda(_btnApriDb);
_btnApriDb.Click += (_, _) => ScegliDatabase();
bottoni.Controls.Add(_btnApriDb);
bottoniDb.Controls.Add(_btnApriDb);
_btnAggiorna.Text = "Aggiorna indice";
UiStile.Secondario(_btnAggiorna);
UiStile.Arrotonda(_btnAggiorna);
_btnAggiorna.Click += (_, _) => AggiornaIndice();
bottoni.Controls.Add(_btnAggiorna);
bottoniDb.Controls.Add(_btnAggiorna);
contenuto.Controls.Add(bottoni);
var lblIstruzioniCartella = new Label
{
Dock = DockStyle.Top,
AutoSize = true,
ForeColor = SystemColors.GrayText,
Margin = new Padding(0, 20, 0, 4),
Text = "Cartella di lavoro: qui vengono scritti il CSV della distinta e la "
+ "cartella 'raw' (gli STEP esportati con Creo vanno invece in 'step', "
+ "dentro la stessa cartella). Proposta automaticamente (ricordata da un "
+ "avvio all'altro), modificabile da 'File → Scegli cartella di lavoro...'.",
};
contenuto.Controls.Add(lblIstruzioniCartella);
_lblCartellaLavoro.Dock = DockStyle.Top;
_lblCartellaLavoro.AutoSize = true;
_lblCartellaLavoro.ForeColor = SystemColors.GrayText;
_lblCartellaLavoro.Margin = new Padding(0, 0, 0, 4);
contenuto.Controls.Add(_lblCartellaLavoro);
contenutoCardDb.Controls.Add(bottoniDb);
cardDb.Controls.Add(contenutoCardDb);
contenuto.Controls.Add(cardDb);
var panel = new Panel();
panel.Controls.Add(contenuto);
@@ -382,20 +407,47 @@ public partial class Form1 : Form
};
contenuto.Controls.Add(lblIstruzioni);
var rigaImporta = new FlowLayoutPanel
{
AutoSize = true,
FlowDirection = FlowDirection.LeftToRight,
Margin = new Padding(0, 0, 0, 8),
};
var btnSfoglia = new Button
{
Text = "Scegli file distinta...",
Margin = new Padding(0, 0, 0, 8),
Margin = new Padding(0, 0, 8, 0),
};
UiStile.Secondario(btnSfoglia);
btnSfoglia.Click += (_, _) => ScegliFileDistinta();
contenuto.Controls.Add(btnSfoglia);
rigaImporta.Controls.Add(btnSfoglia);
rigaImporta.Controls.Add(new Label
{
AutoSize = true,
ForeColor = SystemColors.GrayText,
Margin = new Padding(0, 9, 0, 0),
Text = "1. Carica la distinta",
});
contenuto.Controls.Add(rigaImporta);
_rigaVerifica.AutoSize = true;
_rigaVerifica.FlowDirection = FlowDirection.LeftToRight;
_rigaVerifica.Margin = new Padding(0, 0, 0, 8);
_rigaVerifica.Visible = false; // compare solo dopo un import riuscito (file o drag&drop)
_btnVerifica.Text = "Verifica presenza";
_btnVerifica.Margin = new Padding(0, 0, 0, 8);
_btnVerifica.Margin = new Padding(0, 0, 8, 0);
UiStile.Secondario(_btnVerifica);
_btnVerifica.Click += (_, _) => VerificaCodiciNelDb();
contenuto.Controls.Add(_btnVerifica);
_rigaVerifica.Controls.Add(_btnVerifica);
_rigaVerifica.Controls.Add(new Label
{
AutoSize = true,
ForeColor = SystemColors.GrayText,
Margin = new Padding(0, 9, 0, 0),
Text = "2. Verifica la presenza dei file sul server",
});
contenuto.Controls.Add(_rigaVerifica);
_lblImportStatus.Dock = DockStyle.Top;
_lblImportStatus.AutoSize = false;
@@ -601,7 +653,7 @@ public partial class Form1 : Form
{
try
{
var (csv, righe) = DistintaBaseImporter.ConvertiInCsv(percorso, _cartellaLavoro!);
var (_, righe) = DistintaBaseImporter.ConvertiInCsv(percorso, _cartellaLavoro!);
_percorsoInputCorrente = percorso;
_righeCorrenti = righe;
@@ -614,7 +666,8 @@ public partial class Form1 : Form
_ultimaCartellaRaw = null;
_ultimaVerifica = null;
ImpostaStatoImport($"CSV generato ({righe.Count} righe): {csv}", Color.DarkGreen);
ImpostaStatoImport("", Color.Black);
_rigaVerifica.Visible = true;
}
catch (Exception ex)
{
@@ -622,6 +675,7 @@ public partial class Form1 : Form
_righeCorrenti = null;
_gridImport.Rows.Clear();
ImpostaStatoImport($"Errore durante la conversione: {ex.Message}", Color.DarkRed);
_rigaVerifica.Visible = false;
}
AggiornaStatoAvanti();
}
@@ -649,6 +703,7 @@ public partial class Form1 : Form
? Color.FromArgb(198, 239, 206)
: Color.FromArgb(255, 199, 206);
}
AggiornaStatoAvanti();
}
private void CreaCartellaRaw()
@@ -1091,14 +1146,13 @@ public partial class Form1 : Form
/// c'è ancora una preferenza salvata (o punta a una cartella cancellata), la cartella dove si
/// trova il DB (più sensata dell'eseguibile: quella è dove l'utente tiene il proprio lavoro,
/// non dov'è installata la app). Richiede quindi che InizializzaDatabase sia già stata
/// chiamata. Sempre modificabile con "Cambia cartella..." in fase 1.</summary>
/// chiamata. Sempre modificabile con "File → Scegli cartella di lavoro...".</summary>
private void InizializzaCartellaLavoro()
{
var ultima = LeggiUltimaCartellaLavoro();
_cartellaLavoro = ultima is not null && Directory.Exists(ultima)
? ultima
: (_dbPath is not null ? Path.GetDirectoryName(_dbPath) : null) ?? AppContext.BaseDirectory;
AggiornaLabelCartellaLavoro();
AggiornaStatoAvanti();
}
@@ -1114,13 +1168,10 @@ public partial class Form1 : Form
string percorso = dialog.SelectedPath;
_cartellaLavoro = percorso;
AggiornaLabelCartellaLavoro();
SalvaUltimaCartellaLavoro(percorso);
AggiornaStatoAvanti();
}
private void AggiornaLabelCartellaLavoro() => _lblCartellaLavoro.Text = $"Cartella di lavoro: {_cartellaLavoro}";
private static string? LeggiUltimaCartellaLavoro()
{
try
@@ -1161,6 +1212,8 @@ public partial class Form1 : Form
_lblStatus.Text = "Database non trovato. Usa 'Apri DB...' per selezionarlo oppure 'Aggiorna indice' per crearlo.";
_lblDb.Text = "Nessun database caricato. Premi 'Aggiorna indice' per crearlo da X:\\, oppure 'Apri DB...' se ne hai gi\u00e0 uno.";
_lblUltimoAggiornamento.Text = "";
_dbCaricato = false;
_indicatoreDb.Invalidate();
AggiornaStatoAvanti();
return;
}
@@ -1205,6 +1258,8 @@ public partial class Form1 : Form
MessageBox.Show(this, $"Impossibile aprire il database:\n{ex.Message}", "Errore",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
_dbCaricato = _dbPath is not null;
_indicatoreDb.Invalidate();
AggiornaStatoAvanti();
}
+9 -6
View File
@@ -17,14 +17,17 @@ public class GuidaForm : Form
"""),
("1. Database", """
Carica o aggiorna l'indice "cad_index.db" a partire dalle cartelle di primo
livello del disco/share (radice di default X:\). Mostra anche la cartella di
lavoro corrente, dove finiscono CSV, "raw" e "step" (cambiabile da
"File ▸ Scegli cartella di lavoro...").
livello del disco/share (radice di default X:\). Un pallino verde/rosso indica
se un database è caricato. La cartella di lavoro, dove finiscono CSV, "raw" e
"step", si cambia da "File ▸ Scegli cartella di lavoro...".
"""),
("2. Importa Distinta Base", """
Converte il file della distinta (trascinabile nella finestra) in un CSV
componente;descrizione nella cartella di lavoro. Se il file segue la
convenzione del gestionale, aggiunge anche l'assieme generale come riga.
Converte il file della distinta (trascinabile nella finestra, o scelto col
bottone "1. Carica la distinta") in un CSV componente;descrizione nella cartella
di lavoro. Se il file segue la convenzione del gestionale, aggiunge anche
l'assieme generale come riga. Poi "2. Verifica la presenza dei file sul server"
cerca i componenti nell'indice: è questo passo, non il solo import, a sbloccare
"Avanti".
"""),
("3. Crea cartella raw", """
Cerca nell'indice i file CAD dei componenti del CSV e li copia nella
+42 -11
View File
@@ -12,6 +12,12 @@ internal static class UiStile
/// l'RGB.</summary>
public static readonly Color ColorePrimario = Color.FromArgb(0, 99, 177);
/// <summary>Sfondo/bordo delle card di stato (es. riepilogo Database/cartella di lavoro in
/// fase 1): un grigio-azzurro appena percettibile rispetto al bianco del resto della
/// pagina, sufficiente a separare "stato attuale" da "istruzioni" senza ombre/gradienti.</summary>
public static readonly Color ColoreSfondoCard = Color.FromArgb(247, 249, 251);
public static readonly Color ColoreBordoCard = Color.FromArgb(226, 231, 236);
// AutoSize+Padding invece di una Size fissa in pixel: il bottone si dimensiona sempre
// al proprio testo (a qualunque lunghezza/font), niente più testo che va a capo e viene
// tagliato in verticale quando non ci sta nella larghezza indovinata a mano.
@@ -48,22 +54,23 @@ internal static class UiStile
b.Cursor = Cursors.Hand;
}
/// <summary>Arrotonda gli angoli del bottone. Si aggancia a SizeChanged invece di calcolare
/// la Region una volta sola: con AutoSize la dimensione finale si conosce solo a layout
/// avvenuto (dopo Text/Padding), non subito dopo la creazione del controllo.</summary>
public static void Arrotonda(Button b, int raggio = 6)
/// <summary>Arrotonda gli angoli del controllo (bottone o card). Si aggancia a SizeChanged
/// invece di calcolare la Region una volta sola: con AutoSize la dimensione finale si
/// conosce solo a layout avvenuto (dopo Text/Padding), non subito dopo la creazione del
/// controllo.</summary>
public static void Arrotonda(Control c, int raggio = 6)
{
b.SizeChanged += (_, _) => AggiornaRegione(b, raggio);
AggiornaRegione(b, raggio);
c.SizeChanged += (_, _) => AggiornaRegione(c, raggio);
AggiornaRegione(c, raggio);
}
private static void AggiornaRegione(Button b, int raggio)
private static void AggiornaRegione(Control c, int raggio)
{
if (b.Width <= 0 || b.Height <= 0)
if (c.Width <= 0 || c.Height <= 0)
return;
int d = raggio * 2;
var rect = new Rectangle(Point.Empty, b.Size);
var rect = new Rectangle(Point.Empty, c.Size);
using var percorso = new GraphicsPath();
percorso.AddArc(rect.X, rect.Y, d, d, 180, 90);
percorso.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
@@ -71,7 +78,31 @@ internal static class UiStile
percorso.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
percorso.CloseFigure();
b.Region?.Dispose();
b.Region = new Region(percorso);
c.Region?.Dispose();
c.Region = new Region(percorso);
}
/// <summary>Card di stato: sfondo/bordo leggermente distinti dalla pagina, angoli arrotondati,
/// padding interno. Il bordo va disegnato a mano (Paint) perché Region ritaglia il
/// controllo ma non lascia spazio a un Border WinForms nativo sugli angoli arrotondati.</summary>
public static Panel Card()
{
var card = new Panel
{
Dock = DockStyle.Top,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
BackColor = ColoreSfondoCard,
Padding = new Padding(18, 16, 18, 16),
Margin = new Padding(0, 0, 0, 16),
};
card.Paint += (_, e) =>
{
using var penna = new Pen(ColoreBordoCard);
e.Graphics.DrawRectangle(penna, 0, 0, card.Width - 1, card.Height - 1);
};
Arrotonda(card, 8);
return card;
}
}
+2 -2
View File
@@ -92,8 +92,8 @@ trascinano i `.asm` su Creo Distributed Batch, che scrive in `step/`.
| # | Fase | Bottone → metodo | Cosa fa |
|---|---|---|---|
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync` | Crea/aggiorna `cad_index.db` scansionando la share; mostra anche la cartella di lavoro corrente (cambiabile da "File → Scegli cartella di lavoro...") |
| 2 | Importa Distinta Base | selezione file → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro |
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync` | Crea/aggiorna `cad_index.db` scansionando la share; un pallino verde/rosso accanto al titolo indica se un DB è caricato. La cartella di lavoro non è mostrata qui (cambiabile in qualunque momento da "File → Scegli cartella di lavoro...") |
| 2 | Importa Distinta Base | selezione file/drag&drop → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv`, poi "Verifica presenza" → `VerificaCodiciNelDb` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro; "Verifica presenza" (visibile solo dopo un import riuscito) cerca i componenti nell'indice e colora le righe della griglia — è quella che sblocca "Avanti", non il solo import |
| 3 | Crea cartella raw | "Crea cartella raw" → `CreaCartellaRaw` | `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela) → `raw/` dentro la cartella di lavoro |
| 4 | Trova componenti referenziati | "Trova componenti referenziati" → `TrovaPrtReferenziati` → `RisolviAlberoComponenti` | Legge i `.asm` in `raw/`, risolve ricorsivamente sotto-assiemi/parti nell'indice e li copia anch'essi in `raw/`, insieme ai componenti soppressi e ai modelli citati per riferimento esterno che servono a Creo per aprire l'assieme |
| 5 | Verifica completezza | (ingresso in fase) → `AggiornaAnteprimaAsm` | Mostra le icone trascinabili dei `.asm` della distinta già in `raw/`, da trascinare su Creo per l'esportazione STEP (Distributed Batch) |