modulo 11 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Azioni UI: prestare, restituire, segnalare guasto

Come implementare nella Presentation i tre pulsanti principali del Registro con event listener che chiamano i casi d'uso senza contenere logica di dominio.

30 min Architettura del software

Azioni UI: prestare, restituire, segnalare guasto

Questa lezione appartiene al Modulo 11 — Presentation layer.

Il lavoro continua il progetto guida, il Registro del laboratorio scolastico. Implementi i tre pulsanti principali dell’interfaccia: Presta, Restituisci, Segnala Guasto — ognuno con un event listener che delega tutta la logica ai casi d’uso.

Obiettivo della lezione

Scrivi tre gestori di eventi nella Presentation che collegano i pulsanti dell’interfaccia ai casi d’uso apriPrestito, chiudiPrestito e segnalaGuasto, senza mettere logica di dominio nei gestori stessi.

Alla fine devi avere: tre addEventListener in presentation/, ognuno che legge l’input, chiama il caso d’uso, gestisce l’errore e aggiorna la UI.

Il ruolo della Presentation

La Presentation fa tre cose, e solo queste:

  • raccoglie l’input dell’utente (da form, pulsanti, selezioni)
  • chiama il caso d’uso corrispondente con i dati raccolti
  • mostra il risultato o il messaggio di errore

La Presentation non deve mai contenere questo tipo di logica:

// SBAGLIATO — regola di dominio nella UI
btnPresta.addEventListener("click", () => {
  if (strumento.stato !== "disponibile") {
    mostraMessaggio("Non disponibile");
    return;
  }
  strumento.stato = "in-prestito"; // modifica diretta dello stato
  archivio.salva(strumenti);
  mostraMessaggio("Prestito aperto");
});

Quella verifica sullo stato appartiene al dominio. La UI non deve sapere quali stati esistono.

Nel Registro del laboratorio

L’HTML della pagina principale ha tre pulsanti:

<!-- nella pagina index.html -->
<input type="text" id="inputStrumentoId" placeholder="ID strumento">
<button id="btnPresta">Presta</button>
<button id="btnRestituisci">Restituisci</button>
<button id="btnSegnalaGuasto">Segnala Guasto</button>
<p id="messaggioUscita"></p>

Il file presentation/azioni.js (o main.js) collega questi elementi ai casi d’uso.

Procedura guidata

  1. Recupera i riferimenti agli elementi HTML con document.getElementById.
  2. Scrivi la funzione mostraMessaggio(testo) che aggiorna messaggioUscita.
  3. Scrivi il gestore per “Presta” che chiama apriPrestito.
  4. Scrivi il gestore per “Restituisci” che chiama chiudiPrestito.
  5. Scrivi il gestore per “Segnala Guasto” che chiama segnalaGuasto.
  6. Ogni gestore deve gestire gli errori con un try/catch e mostrare un messaggio leggibile.
  7. Verifica manualmente nel browser: presta uno strumento, poi restituiscilo, poi segnala il guasto.

Attività in classe

Compila questa scheda prima di scrivere il codice:

## Tre azioni UI

Pulsante: Presta
Dati raccolti dall'input:
Caso d'uso chiamato:
Messaggi di successo e errore:

Pulsante: Restituisci
Dati raccolti dall'input:
Caso d'uso chiamato:
Messaggi di successo e errore:

Pulsante: Segnala Guasto
Dati raccolti dall'input:
Caso d'uso chiamato:
Messaggi di successo e errore:

Spiegazione guidata per studiare

Il confine tra Presentation e Application si vede bene guardando i gestori di eventi. Un gestore ben scritto è sempre brevissimo: prende i valori dall’input, chiama una funzione, gestisce il catch. Se il gestore cresce oltre 15 righe, probabilmente contiene logica che appartiene altrove.

Il try/catch nel gestore serve a convertire gli errori tecnici (stringhe come "STRUMENTO_NON_TROVATO") in messaggi comprensibili per l’utente. Questa conversione è responsabilità della Presentation — non del caso d’uso, che lancia errori con codici interni, e non del dominio.

Passo per passo nel progetto

  1. Crea src/presentation/azioni.js (o aggiungi a main.js). Importa i casi d’uso e gli archivi:
// presentation/azioni.js
import { apriPrestito } from "../application/apriPrestito.js";
import { chiudiPrestito } from "../application/chiudiPrestito.js";
import { segnalaGuasto } from "../application/segnalaGuasto.js";
import * as archivioStrumenti from "../infrastructure/archivioStrumentiLocalStorage.js";
import * as archivioPrestiti from "../infrastructure/archivioPrestitiLocalStorage.js";

const inputId = document.getElementById("inputStrumentoId");
const btnPresta = document.getElementById("btnPresta");
const btnRestituisci = document.getElementById("btnRestituisci");
const btnSegnalaGuasto = document.getElementById("btnSegnalaGuasto");
const messaggioUscita = document.getElementById("messaggioUscita");

function mostraMessaggio(testo) {
  messaggioUscita.textContent = testo;
}
  1. Scrivi il gestore per “Presta”:
btnPresta.addEventListener("click", () => {
  const strumentoId = inputId.value.trim();
  if (!strumentoId) {
    mostraMessaggio("Inserisci l'ID dello strumento.");
    return;
  }
  try {
    const prestito = apriPrestito(
      { strumentoId },
      archivioStrumenti,
      archivioPrestiti
    );
    mostraMessaggio(`Prestito aperto. ID prestito: ${prestito.id}`);
  } catch (errore) {
    const messaggi = {
      "STRUMENTO_NON_TROVATO": "Strumento non trovato.",
      "STRUMENTO_NON_DISPONIBILE": "Lo strumento non è disponibile.",
      "ARCHIVIO_NON_DISPONIBILE": "Errore nel salvataggio. Riprova."
    };
    mostraMessaggio(messaggi[errore.message] ?? "Operazione non riuscita.");
  }
});
  1. Scrivi il gestore per “Restituisci”:
btnRestituisci.addEventListener("click", () => {
  const strumentoId = inputId.value.trim();
  if (!strumentoId) {
    mostraMessaggio("Inserisci l'ID dello strumento.");
    return;
  }
  try {
    chiudiPrestito({ strumentoId }, archivioStrumenti, archivioPrestiti);
    mostraMessaggio("Prestito chiuso. Strumento restituito.");
  } catch (errore) {
    const messaggi = {
      "PRESTITO_NON_TROVATO": "Nessun prestito aperto per questo strumento.",
      "STRUMENTO_NON_TROVATO": "Strumento non trovato."
    };
    mostraMessaggio(messaggi[errore.message] ?? "Operazione non riuscita.");
  }
});
  1. Scrivi il gestore per “Segnala Guasto”:
btnSegnalaGuasto.addEventListener("click", () => {
  const strumentoId = inputId.value.trim();
  if (!strumentoId) {
    mostraMessaggio("Inserisci l'ID dello strumento.");
    return;
  }
  try {
    segnalaGuasto({ strumentoId }, archivioStrumenti);
    mostraMessaggio("Guasto segnalato. Lo strumento è ora fuori uso.");
  } catch (errore) {
    const messaggi = {
      "STRUMENTO_NON_TROVATO": "Strumento non trovato.",
      "STRUMENTO_GIA_GUASTO": "Lo strumento è già segnalato come guasto."
    };
    mostraMessaggio(messaggi[errore.message] ?? "Operazione non riuscita.");
  }
});

Esempio JS — il caso d’uso segnalaGuasto

Per completare l’esempio, ecco come segnalaGuasto potrebbe essere implementato in application/:

// application/segnalaGuasto.js
export function segnalaGuasto({ strumentoId }, archivio) {
  const strumenti = archivio.caricaTutti();
  const strumento = strumenti.find(s => s.id === strumentoId);
  if (!strumento) throw new Error("STRUMENTO_NON_TROVATO");
  if (strumento.stato === "guasto") throw new Error("STRUMENTO_GIA_GUASTO");
  strumento.stato = "guasto";
  archivio.salva(strumenti);
}

Il gestore nella UI non sa nulla degli stati "disponibile" o "guasto" — sa solo chiamare segnalaGuasto e gestire le eccezioni con messaggi leggibili.

Prova di comprensione

Rispondi per iscritto:

  • Perché il controllo if (strumento.stato !== "disponibile") non deve stare nel gestore del click?
  • Cosa fa esattamente messaggi[errore.message] ?? "Operazione non riuscita."?
  • Se aggiungessi un quarto pulsante “Metti in manutenzione”, cosa cambieresti nella Presentation e cosa nell’Application?
  • Come verificheresti che il click su “Restituisci” non modifica lo stato se lo strumento non ha prestiti aperti?

Errori da evitare

  • scrivere condizioni if (strumento.stato === ...) nei gestori degli eventi;
  • chiamare direttamente archivio.salva() dalla Presentation;
  • mostrare all’utente messaggi come "Error: STRUMENTO_NON_TROVATO";
  • dimenticare il controllo sull’input vuoto prima di chiamare il caso d’uso;
  • mettere tutta la logica in main.js invece di separare la Presentation.

Prodotto da consegnare

I tre gestori di eventi funzionanti in presentation/azioni.js. Come verifica: nel browser, presta uno strumento, poi restituiscilo, poi segnala il guasto su un altro strumento — tutti e tre i flussi devono funzionare con messaggi chiari.

Checklist di chiusura

  • i tre addEventListener chiamano solo casi d’uso, senza logica di dominio;
  • ogni gestore ha un try/catch con messaggi in italiano;
  • l’input viene validato (non vuoto) prima di chiamare il caso d’uso;
  • nessun gestore chiama direttamente l’archivio;
  • ho verificato tutti e tre i flussi nel browser.

Risultato atteso

Ho implementato i tre gestori nella Presentation.
Presta chiama apriPrestito, Restituisci chiama chiudiPrestito, Segnala Guasto chiama segnalaGuasto.
I messaggi di errore sono in italiano e leggibili.
Ho verificato manualmente tutti e tre i flussi nel browser.
Il prossimo passo: aggiornare la lista strumenti dopo ogni azione.