magricambi/.specstory/history/2026-04-28_13-13-46Z-analisi-del-componente-articlelist.jsx.md

15 KiB
Raw Blame History

2026-04-28 13:13:46Z

User

Analizza il componente ArticleList.jsx e spiegami perchè pura avendo selezionatoi l magazzino e lo stabilimento non riesco ad effettuare uno scarico dell'articolo?


Agent (default)

Controllo subito src/ArticleList.jsx concentrandomi sul flusso di scarico e sulle condizioni legate a magazzino e stabilimento, poi ti spiego esattamente dove si blocca e perché.

Agent (default)

Tool use: **ripgrep_raw_search**

Parameters:

{
  "caseInsensitive": true,
  "outputMode": "content",
  "path": "d:/MagRicambi/src/ArticleList.jsx",
  "pattern": "scarico|magazzino|stabilimento|scaricare|warehouse|stabil"
}

Additional data:

{
  "isPruned": true,
  "outputMode": "content",
  "path": "d:/MagRicambi/src/ArticleList.jsx",
  "pattern": "scarico|magazzino|stabilimento|scaricare|warehouse|stabil",
  "topFiles": [
    {
      "matchCount": 100,
      "uri": "src/ArticleList.jsx"
    }
  ],
  "totalFiles": 1,
  "totalMatches": 100
}

Status: completed

Tool use: **ripgrep_raw_search**

Agent (default)

Tool use: **read_file_v2** • Read file: d:/MagRicambi/src/ArticleList.jsx

Tool use: **read_file_v2**

Agent (default)

Ho trovato in ArticleList.jsx un paio di punti critici; ora apro anche useStabilimentoMagazzino e StabilimentoMagazzinoSelector perché il blocco sembra legato a come viene davvero salvata la selezione, non solo alla UI.

Agent (default)

Tool use: **read_file_v2** • Read file: d:/MagRicambi/src/hooks/useStabilimentoMagazzino.js

Tool use: **read_file_v2**

Agent (default)

Tool use: **read_file_v2** • Read file: d:/MagRicambi/src/components/StabilimentoMagazzinoSelector.jsx

Tool use: **read_file_v2**

Agent (default)

Tool use: **read_file_v2** • Read file: d:/MagRicambi/src/modals/MovementModal.jsx

Tool use: **read_file_v2**

Agent (default)

Il problema principale non e nel backend: e nel modo in cui ArticleList legge la selezione di stabilimento/magazzino.

In ArticleList usi unistanza del hook:

const {
  selectedStabilimento,
  selectedMagazzino,
  updateStabilimento,
  updateMagazzino,
} = useStabilimentoMagazzino();

Nel selettore StabilimentoMagazzinoSelector ne usi unaltra, separata:

const {
  selectedStabilimento,
  selectedMagazzino,
  updateStabilimento,
  updateMagazzino,
  resetSelection
} = useStabilimentoMagazzino();

Quindi:

  • tu selezioni a video nel Selector, ma aggiorni il suo stato locale;
  • ArticleList continua a usare il suo selectedMagazzino/selectedStabilimento (spesso vuoti o non aggiornati in tempo reale);
  • di conseguenza lo scarico resta bloccato (disabled, controlli su !selectedStabilimento || !selectedMagazzino, giacenze non caricate sul magazzino atteso).

In piu, nellonSelectionChangediArticleList` fai solo log e non sincronizzi niente:

onSelectionChange={(stabilimento, magazzino) => {
  // La selezione viene gestita automaticamente dal hook
  console.log("ArticleList: Selezione aggiornata", {
    stabilimento,
    magazzino,
  });
}}

Per questo “sembra selezionato”, ma lo scarico non parte.

Secondario ma importante: MovementModal valida su movimento.articolo.quantita (totale), mentre ArticleList valida per magazzino (giacenzePerMagazzino). Questo puo creare incoerenze nei messaggi, ma il blocco principale e la doppia istanza del hook.

Se vuoi, nel prossimo step ti preparo la patch corretta (stato unico in ArticleList passato al Selector via props, oppure Context condiviso) così lo scarico funziona subito.


User

applica uno stato unico in ArticleList passato al Selector via props..


Agent (default)

Perfetto, applico subito la versione “controllata”: stato unico in ArticleList e StabilimentoMagazzinoSelector pilotato via props, senza hook interno duplicato. Poi faccio un controllo lint rapido sui file toccati.

Agent (default)

Tool use: **read_file_v2** • Read file: d:/MagRicambi/src/ArticleList.jsx

Tool use: **read_file_v2**

Agent (default)

Tool use: **read_file_v2** • Read file: d:/MagRicambi/src/components/StabilimentoMagazzinoSelector.jsx

Tool use: **read_file_v2**

Agent (default)

Sto applicando ora le modifiche ai due file: Selector controllato via props e collegamento diretto delle callback in ArticleList (updateStabilimento, updateMagazzino).

Agent (default)

Tool use: **edit_file_v2** • Edit file: d:/MagRicambi/src/components/StabilimentoMagazzinoSelector.jsx

Tool use: **edit_file_v2**

Agent (default)

Tool use: **edit_file_v2** • Edit file: d:/MagRicambi/src/ArticleList.jsx

Tool use: **edit_file_v2**

Agent (default)

Tool use: **read_lints**

Parameters:

{
  "paths": [
    "d:/MagRicambi/src/ArticleList.jsx",
    "d:/MagRicambi/src/components/StabilimentoMagazzinoSelector.jsx"
  ]
}

Result:

{
  "linterErrorsByFile": [
    {
      "relativeWorkspacePath": "d:/MagRicambi/src/ArticleList.jsx"
    },
    {
      "relativeWorkspacePath": "d:/MagRicambi/src/components/StabilimentoMagazzinoSelector.jsx"
    }
  ]
}

Status: completed

Tool use: **read_lints**

Agent (default)

Fatto: ho applicato lo stato unico in ArticleList e reso StabilimentoMagazzinoSelector un componente controllato via props.

Cosa ho cambiato:

  • In src/components/StabilimentoMagazzinoSelector.jsx:
    • rimosso useStabilimentoMagazzino interno;
    • aggiunte props controllate: selectedStabilimento, selectedMagazzino;
    • aggiunte callback: onStabilimentoChange, onMagazzinoChange, onReset;
    • il componente ora usa solo i valori/callback passati dal parent.
  • In src/ArticleList.jsx:
    • uso del solo hook locale (useStabilimentoMagazzino) come source of truth;
    • passato al selector:
      • selectedStabilimento={selectedStabilimento}
      • selectedMagazzino={selectedMagazzino}
      • onStabilimentoChange={updateStabilimento}
      • onMagazzinoChange={updateMagazzino}
      • onReset={resetSelection}

Verifica:

  • nessun errore lint nei file modificati.

Adesso la selezione che fai nel selector ela stessa letta daconfermaMovimento, quindi lo scarico non resta piu bloccato per disallineamento di stato.


User

Da ora lavora, analizza e agisci come un web developer full stack senior di esperienza decennale. Analizza il componente AricleList.js e spiegami il funzionamento in modo dettagliato punto per punto, grazie.


Agent (default)

Certo. Ti spiego ArticleList come farei in una code review senior, in modo funzionale e operativo.

Visione generale

src/ArticleList.jsx è un componente “orchestratore” di magazzino:

  • carica e mostra articoli/famiglie;
  • gestisce ricerca, ordinamento, paginazione;
  • gestisce i movimenti di carico/scarico;
  • coordina il contesto di selezione stabilimento/magazzino;
  • apre modali per aggiunta, modifica, movimento, barcode, info articolo.

Non è solo una lista UI: contiene una parte consistente di logica business lato frontend.


1) Input (props) e responsabilità

Riceve da parent:

  • stabilimenti, magazzini (anagrafiche già caricate fuori dal componente),
  • currentUser,
  • callback come onArticleUpdate,
  • filtri esterni (familyFilterId, understockOnly, onClearUnderstock),
  • showHeader.

Questo design separa bene: anagrafiche da parent, operazioni specifiche in ArticleList.


2) Stato locale principale

Mantiene molti state con useState, raggruppabili in 5 aree:

  • Dati base: articles, famiglie
  • UI listato: search, searchCode, sortBy, sortOrder, page
  • Workflow modali: addingArticle, editingArticle, movimento, barcodeArticle, infoArticolo
  • Movimentazione: qtyMovimento
  • Giacenze:
    • giacenzeArticoli: totale per articolo
    • giacenzePerMagazzino: mappa per magazzino+articolo (chiave annidata)

In più usa barcodeRef per generazione barcode nel DOM.


3) Selezione stabilimento/magazzino (core del flusso)

ArticleList usa useStabilimentoMagazzino() come source of truth:

  • selectedStabilimento
  • selectedMagazzino
  • updateStabilimento
  • updateMagazzino
  • resetSelection

E passa questi valori/callback al componente StabilimentoMagazzinoSelector (ora controllato da props).
Questa parte è fondamentale perché tutte le abilitazioni scarico/carico dipendono da questa selezione.


4) Caricamento dati iniziale e refresh

Funzione centrale: loadData():

  • Promise.all([fetchFamiglie(), fetchArticles()])
  • aggiorna famiglie e articles.

useEffect:

  • al mount: carica dati iniziali;
  • su cambio pagina: ricarica (scelta discutibile, ma presente);
  • espone window.articleListRef = { loadData } quando cè onArticleUpdate (ponte verso componenti esterni).

5) Caricamento giacenze per magazzino selezionato

Quando cambia selectedMagazzino:

  • chiama giacenzeApi.getAll({ id_magazzino: selectedMagazzino })
  • costruisce:
    • giacenzePerMagazzinoMap[id_magazzino][articolo_id] = { quantita }
    • giacenzeTotaliMap[articolo_id] += quantita
  • aggiorna gli state di giacenza.

Questo è il dato usato poi per:

  • abilitare/disabilitare scarico,
  • mostrare giacenza specifica del magazzino scelto.

6) Aggiunta articolo (confermaAggiuntaArticolo)

Flusso:

  1. valida campi minimi (descrizione, id_famiglia);
  2. crea articolo via addArticle(...);
  3. se quantità iniziale > 0, crea anche movimento automatico "in" con causale "Carico iniziale" (tracciabilità);
  4. reset form + chiusura modal + loadData() + onArticleUpdate?.().

Quindi linserimento articolo è “event-sourced light”: non solo record articolo ma anche movimento associato.


7) Modifica articolo (confermaModificaArticolo)

Flusso:

  1. recupera articolo originale da articles;
  2. calcola delta quantità tra nuovo e vecchio;
  3. aggiorna anagrafica articolo (updateArticle);
  4. se delta ≠ 0 crea movimento automatico:
    • "in" se aumenta,
    • "out" se diminuisce,
    • quantità assoluta.

Include campi magazzino/stabilimento di appoggio (id_magazzino_destinazione, fallback id_magazzino), con molti log di debug.


8) Movimento manuale carico/scarico (confermaMovimento)

Validazioni:

  • blocca se mancano selectedStabilimento o selectedMagazzino;
  • blocca quantità non valida;
  • per scarico (out) verifica disponibilità sulla giacenza del magazzino selezionato.

Payload movimento:

  • id_articolo, tipo_movimento, quantita, causale,
  • id_stabilimento, id_magazzino,
  • campi multi-warehouse:
    • id_magazzino_origine per out,
    • id_magazzino_destinazione per in.

Dopo la chiamata:

  • aggiorna ottimisticamente giacenzePerMagazzino,
  • resetta modal/stato quantità,
  • notifica successo e fa loadData().

9) Ricerca, filtri, ordinamento

Pipeline dati:

  1. filteredArticles:
    • match descrizione (search)
    • match codice/barcode (searchCode)
    • filtro sottoscorta (understockOnly)
    • filtro famiglia (familyFilterId)
  2. sortedArticles:
    • quantita ordinata numericamente
    • altri campi ordinati lessicograficamente
  3. paginazione:
    • paginatedArticles = sortedArticles.slice(...)

Quando cambiano filtri/ricerche resetta page a 1.


10) Rendering item articolo e UX operativa

Per ogni articolo:

  • calcola giacenzaMag dal magazzino selezionato;
  • abilita drag & drop solo se giacenzaMag > 0 e cè magazzino selezionato;
  • mostra doppia quantità:
    • totale articolo,
    • quantità nel magazzino selezionato;
  • pulsanti azione:
    • info,
    • modifica,
    • carico (📥),
    • scarico (📤, disabilitato se giacenza magazzino <= 0),
    • barcode.

Il titolo tooltip cambia in base allo stato (es. “Seleziona Stabilimento e Magazzino”, “Giacenza insufficiente”).


11) Modali usate

  • ArticoliModal: aggiunta/modifica articolo
  • MovementModal: conferma carico/scarico con causale/quantità
  • BarcodeModal: visualizzazione/stampa barcode
  • ArticoloInfoModal: dettaglio informativo

Il componente funge da “state owner” per tutte.


12) Osservazioni tecniche da senior (importanti)

  • Componente molto grande (oltre 1200 righe): conviene modularizzare (custom hooks + subcomponenti) per manutenibilità/testabilità.
  • Tanti console.log: utili in debug, ma da ridurre/astrarre (logger con livelli).
  • Doppia fonte quantità (article.quantita vs giacenzePerMagazzino): da mantenere coerente sempre, altrimenti UX ambigua.
  • Refresh su cambio pagina: ricaricare tutto a ogni pagina può costare; meglio paginazione client pura o server-side coerente.
  • Tipizzazione assente (JS puro): TypeScript ridurrebbe errori su payload movimenti/giacenze.

Se vuoi, nel prossimo step posso farti una mappa tecnica completa delle dipendenze di ArticleList (API chiamate, payload attesi, modali collegate, punti esatti da rifattorizzare) e proporti una versione più pulita in 3 hook dedicati.