diff --git a/CLAUDE.md b/CLAUDE.md index f718dcf..913948b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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__`), 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__`), 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. diff --git a/PrepStep/Form1.cs b/PrepStep/Form1.cs index 020057f..e9af493 100644 --- a/PrepStep/Form1.cs +++ b/PrepStep/Form1.cs @@ -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. + /// chiamata. Sempre modificabile con "File → Scegli cartella di lavoro...". 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(); } diff --git a/PrepStep/GuidaForm.cs b/PrepStep/GuidaForm.cs index 65af454..80dc7d5 100644 --- a/PrepStep/GuidaForm.cs +++ b/PrepStep/GuidaForm.cs @@ -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 diff --git a/PrepStep/UiStile.cs b/PrepStep/UiStile.cs index 5f3a428..1bc4314 100644 --- a/PrepStep/UiStile.cs +++ b/PrepStep/UiStile.cs @@ -12,6 +12,12 @@ internal static class UiStile /// l'RGB. public static readonly Color ColorePrimario = Color.FromArgb(0, 99, 177); + /// 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. + 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; } - /// 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. - public static void Arrotonda(Button b, int raggio = 6) + /// 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. + 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); } + + /// 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. + 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; + } + } diff --git a/docs/architettura.md b/docs/architettura.md index 94e28e9..14ce54e 100644 --- a/docs/architettura.md +++ b/docs/architettura.md @@ -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) |