Claude: Artefatti per l’amministrazione

Image

Claude: Artefatti per l’amministrazione

Se hai già usato un LLM “classico” di solito fai una richiesta, ottieni una risposta in chat, copi-incolli altrove e inizi la danza tra editor, browser, IDE, note, documenti. Gli Artefatti di Claude nascono per spezzare quel circuito. Non sono un nuovo “tipo” di risposta più lunga, ma un diverso spazio di lavoro: quando Claude genera qualcosa che ha senso trattare come oggetto a sé (un documento, una pagina web, un componente, un diagramma, un mini-tool), lo mette in un pannello dedicato accanto alla conversazione, dove quel contenuto resta stabile, modificabile e, in molti casi, anche “eseguibile” o visualizzabile come anteprima.

La chat resta il luogo della regia (spieghi cosa vuoi, fai correzioni, cambi direzione), mentre l’artefatto è il risultato “concreto” che puoi rifinire senza perderlo nel flusso dei messaggi. Anthropic lo descrive come un modo per vedere, editare e costruire in tempo reale con Claude, proprio perché l’output non è solo testo da leggere ma materiale da trattare come bozza viva.

Cosa distingue un artefatto

La differenza più importante è che l’artefatto è pensato come contenuto autonomo. Se chiedi “scrivimi un testo”, la chat può risponderti con due righe e poi mostrarti il testo completo nell’artefatto. Se chiedi “fammi una pagina HTML”, la chat può spiegarti cosa ha fatto e l’artefatto, invece, ospita il codice (spesso con resa visuale immediata). Questo cambio di postura rende più naturale iterare: non stai più inseguendo la versione giusta tra messaggi successivi, ma stai lavorando su un oggetto persistente.

Che tipi di artefatti puoi creare

La documentazione ufficiale e guide pratiche convergono su alcune categorie ricorrenti: documenti testuali strutturati, snippet o blocchi di codice più grandi, pagine web e componenti (anche interattivi), diagrammi (per esempio in Mermaid), SVG, visualizzazioni e piccole app che reagiscono a input.

C’è poi una dimensione “sociale”, alcuni artefatti possono essere pubblicati o condivisi, così altri possono aprirli e personalizzarli (remix). Questa parte è importante perché sposta l’output dall’essere privato al diventare un oggetto riutilizzabile in team o community, un po’ come un template che gira.

Un Artefatto per l’amministrazione

Ecco un esempio concreto di “artefatto” pensato per l’amministrazione di un’azienda, un documento operativo statico (tipo template) per gestire la chiusura mensile con numeri, scadenze e responsabilità. Puoi copiarlo così com’è in un pannello Artefatto (Documenti e modelli) e usarlo ogni mese.

Plaintext
# Chiusura Mensile Amministrazione — Template Operativo

**Azienda:** ____________________  
**Mese di competenza:** ____________________  
**Responsabile amministrazione:** ____________________  
**Data avvio:** ____/____/______   **Data chiusura target:** ____/____/______  

## 1) Stato avanzamento (a colpo d’occhio)

| Area | Stato | Ultimo aggiornamento | Note |
|---|---|---:|---|
| Fatture attive (vendite) | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |
| Fatture passive (fornitori) | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |
| Prima nota / banca / cassa | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |
| Note spese e rimborsi | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |
| Payroll e contributi | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |
| IVA e liquidazione | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |
| Accantonamenti (ratei/risconti) | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |
| Reporting gestionale | ☐ Non iniziato ☐ In corso ☐ Chiuso | ____/____ | |

## 2) Checklist attività (con criteri di “done”)

### 2.1 Vendite (fatture attive)
**Done quando:** tutte le fatture del mese sono emesse, inviate e registrate; anomalie risolte o annotate.
- Riconciliazione ordini/contratti vs fatture emesse
- Verifica note di credito (emesse o da emettere)
- Controllo scarti SDI / invii PEC / errori anagrafica clienti

**Eccezioni aperte:**  
- Cliente/Pratica: ____________________  Importo: ______  Motivo: ____________________

### 2.2 Fornitori (fatture passive)
**Done quando:** tutte le fatture ricevute sono contabilizzate e abbinate a centro di costo/commessa.
- Verifica fatture da ricevere (servizi ricorrenti, canoni, consulenze)
- Controllo plafond/spese “non deducibili” e note interne
- Segnalazione DDT/beni non fatturati (se rilevante)

**Eccezioni aperte:**  
- Fornitore: ____________________  Importo: ______  Motivo: ____________________

### 2.3 Banca e cassa (riconciliazione)
**Done quando:** estratti conto riconciliati al 100% o con differenze spiegate.
- Import estratti conto / verifica movimenti duplicati
- Riconciliazione incassi clienti e pagamenti fornitori
- Commissioni, interessi, addebiti ricorrenti verificati

**Differenze da spiegare:** ________________________________

### 2.4 Note spese e carte aziendali
**Done quando:** tutte le spese hanno giustificativo, categoria e approvazione.
- Controllo ricevute mancanti
- Spese personali da rimborsare o stornare
- Riconciliazione carte aziendali vs giustificativi

**Mancanti:** ________________________________

### 2.5 Payroll (se applicabile)
**Done quando:** cedolini registrati e quadrati con costo del personale.
- Registrazione costo del lavoro (stipendi, contributi, TFR)
- Verifica F24 e scadenze contributive
- Premi/bonus/una tantum: documentati e allocati

## 3) Numeri chiave del mese (bozza)

| Voce | Importo (€) | Fonte / Report | Note |
|---|---:|---|---|
| Ricavi (fatturato) | ______ | Report vendite / contabilità | |
| Costi diretti | ______ | Contabilità / commesse | |
| Margine lordo | ______ | Calcolo | |
| Opex (costi operativi) | ______ | Contabilità | |
| EBITDA (stimato) | ______ | Calcolo | |
| IVA a debito/credito (stimata) | ______ | Liquidazione IVA | |

**Commento gestionale (5 righe):**  
____________________________________________________________________  
____________________________________________________________________  

## 4) Scadenze e responsabilità

| Scadenza | Attività | Owner | Stato | Note |
|---:|---|---|---|---|
| ____/____ | Invio documenti al commercialista | ______ | ☐ | |
| ____/____ | Liquidazione IVA (stima) | ______ | ☐ | |
| ____/____ | Reporting a Direzione | ______ | ☐ | |
| ____/____ | Pagamenti principali | ______ | ☐ | |

## 5) Allegati e link utili
- Cartella mese: ____________________
- Report vendite: ____________________
- Estratti conto: ____________________
- Registro IVA: ____________________
- Note spese: ____________________

## 6) Log decisioni / anomalie (per non perdere pezzi)
**Data:** ____/____  **Tema:** ____________________  
**Decisione:** ____________________________________  
**Impatto (costi/ricavi/IVA):** _____________________  
**Follow-up:** _____________________________________

Ora realizziamo un artefatto più interattivo in vibe coding, prestate attenzione però al debito tecnologico! Copiate e incollate nel pannello Artefatto (Applicazioni e siti web).

HTML
<!doctype html>
<html lang="it">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Cruscotto Chiusura Mensile — Amministrazione</title>
  <style>
    :root { color-scheme: light dark; }
    body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 0; }
    header { padding: 18px 18px 10px; border-bottom: 1px solid rgba(127,127,127,.25); position: sticky; top: 0; background: Canvas; z-index: 2; }
    h1 { margin: 0 0 6px; font-size: 18px; }
    .sub { opacity: .8; font-size: 13px; }
    main { padding: 16px 18px 40px; max-width: 1100px; margin: 0 auto; }
    .grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 14px; }
    @media (max-width: 980px) { .grid { grid-template-columns: 1fr; } header { position: static; } }
    .card { border: 1px solid rgba(127,127,127,.25); border-radius: 14px; padding: 14px; background: color-mix(in oklab, Canvas, CanvasText 2%); }
    .card h2 { margin: 0 0 10px; font-size: 14px; letter-spacing: .2px; }
    label { display: block; font-size: 12px; opacity: .85; margin-bottom: 6px; }
    input, select, textarea, button { width: 100%; box-sizing: border-box; padding: 10px 10px; border-radius: 10px; border: 1px solid rgba(127,127,127,.35); background: Canvas; color: CanvasText; }
    textarea { min-height: 90px; resize: vertical; }
    .row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
    .row3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
    .kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
    @media (max-width: 720px) { .kpis { grid-template-columns: 1fr; } .row, .row3 { grid-template-columns: 1fr; } }
    .kpi { padding: 12px; border-radius: 12px; border: 1px solid rgba(127,127,127,.25); }
    .kpi .v { font-size: 18px; margin-top: 6px; font-weight: 650; }
    .muted { opacity: .75; font-size: 12px; }
    .tasks { display: grid; gap: 8px; }
    .task { display: grid; grid-template-columns: 18px 1fr auto; gap: 10px; align-items: center; padding: 10px; border-radius: 12px; border: 1px solid rgba(127,127,127,.25); }
    .pill { font-size: 12px; padding: 5px 9px; border-radius: 999px; border: 1px solid rgba(127,127,127,.35); }
    .pill.ok { background: color-mix(in oklab, #2ecc71 18%, Canvas); }
    .pill.mid { background: color-mix(in oklab, #f1c40f 18%, Canvas); }
    .pill.no { background: color-mix(in oklab, #e74c3c 18%, Canvas); }
    .actions { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; margin-top: 10px; }
    @media (max-width: 720px) { .actions { grid-template-columns: 1fr; } }
    button { cursor: pointer; font-weight: 600; }
    button.secondary { background: color-mix(in oklab, Canvas, CanvasText 6%); }
    button.danger { background: color-mix(in oklab, #e74c3c 18%, Canvas); }
    .small { font-size: 12px; opacity: .8; margin-top: 8px; line-height: 1.35; }
    .table { width: 100%; border-collapse: collapse; overflow: hidden; border-radius: 12px; border: 1px solid rgba(127,127,127,.25); }
    .table th, .table td { padding: 10px; border-bottom: 1px solid rgba(127,127,127,.2); text-align: left; font-size: 13px; }
    .table th { font-size: 12px; opacity: .8; }
    .table tr:last-child td { border-bottom: none; }
    .right { text-align: right; }
    .toast { position: fixed; bottom: 16px; right: 16px; padding: 10px 12px; border-radius: 12px; border: 1px solid rgba(127,127,127,.35); background: Canvas; box-shadow: 0 6px 18px rgba(0,0,0,.12); display: none; max-width: 360px; }
    .toast.show { display: block; }
    @media print {
      header, .actions, .small { display: none !important; }
      body { margin: 0; }
      .card { break-inside: avoid; }
    }
  </style>
</head>
<body>
  <header>
    <h1>Cruscotto Chiusura Mensile — Amministrazione</h1>
    <div class="sub">Template interattivo: KPI automatici, checklist, note e export. (I dati restano nel browser, in locale.)</div>
  </header>

  <main>
    <div class="grid">
      <section class="card">
        <h2>Contesto</h2>
        <div class="row3">
          <div>
            <label for="azienda">Azienda</label>
            <input id="azienda" placeholder="Es. ACME S.r.l." />
          </div>
          <div>
            <label for="mese">Mese di competenza</label>
            <input id="mese" placeholder="Es. Marzo 2026" />
          </div>
          <div>
            <label for="responsabile">Responsabile</label>
            <input id="responsabile" placeholder="Nome e cognome" />
          </div>
        </div>
        <div class="row" style="margin-top:10px;">
          <div>
            <label for="dataAvvio">Data avvio</label>
            <input id="dataAvvio" type="date" />
          </div>
          <div>
            <label for="dataTarget">Chiusura target</label>
            <input id="dataTarget" type="date" />
          </div>
        </div>

        <div style="margin-top:14px;">
          <h2>Numeri del mese</h2>
          <div class="row3">
            <div>
              <label for="ricavi">Ricavi (fatturato) €</label>
              <input id="ricavi" inputmode="decimal" placeholder="0,00" />
            </div>
            <div>
              <label for="costiDiretti">Costi diretti €</label>
              <input id="costiDiretti" inputmode="decimal" placeholder="0,00" />
            </div>
            <div>
              <label for="opex">Opex (costi operativi) €</label>
              <input id="opex" inputmode="decimal" placeholder="0,00" />
            </div>
          </div>
          <div class="row3" style="margin-top:10px;">
            <div>
              <label for="ivaDebito">IVA a debito €</label>
              <input id="ivaDebito" inputmode="decimal" placeholder="0,00" />
            </div>
            <div>
              <label for="ivaCredito">IVA a credito €</label>
              <input id="ivaCredito" inputmode="decimal" placeholder="0,00" />
            </div>
            <div>
              <label for="altri">Altri accantonamenti €</label>
              <input id="altri" inputmode="decimal" placeholder="0,00" />
            </div>
          </div>

          <div class="kpis" style="margin-top:12px;">
            <div class="kpi">
              <div class="muted">Margine lordo</div>
              <div class="v" id="kpiMargine">€ 0,00</div>
              <div class="muted" id="kpiMarginePct">0,0% sui ricavi</div>
            </div>
            <div class="kpi">
              <div class="muted">EBITDA stimato</div>
              <div class="v" id="kpiEbitda">€ 0,00</div>
              <div class="muted">Ricavi − Costi diretti − Opex</div>
            </div>
            <div class="kpi">
              <div class="muted">IVA netta stimata</div>
              <div class="v" id="kpiIvaNetta">€ 0,00</div>
              <div class="muted">IVA a debito − IVA a credito</div>
            </div>
          </div>

          <div style="margin-top:12px;">
            <label for="commento">Commento gestionale (max 6–8 righe)</label>
            <textarea id="commento" placeholder="Cosa spiega i numeri di questo mese? Eventi straordinari, incassi attesi, criticità, ecc."></textarea>
          </div>
        </div>
      </section>

      <aside class="card">
        <h2>Checklist chiusura</h2>
        <div class="tasks" id="tasks"></div>

        <div style="margin-top:12px;">
          <label for="noteAnomalie">Log anomalie / decisioni</label>
          <textarea id="noteAnomalie" placeholder="Data, tema, decisione, impatto e follow-up."></textarea>
        </div>

        <div class="actions">
          <button id="btnSalva" class="secondary">Salva in locale</button>
          <button id="btnExport" class="secondary">Export JSON</button>
          <button id="btnStampa">Stampa / PDF</button>
        </div>

        <div class="actions" style="margin-top:10px;">
          <button id="btnReset" class="danger">Reset (cancella)</button>
          <button id="btnCsv" class="secondary">CSV KPI</button>
          <button id="btnEsempio" class="secondary">Carica esempio</button>
        </div>

        <div class="small">
          Suggerimento: usa “Carica esempio” per vedere come funziona, poi modifica. “Salva in locale” memorizza i dati nel browser (localStorage).
        </div>
      </aside>
    </div>

    <section class="card" style="margin-top:14px;">
      <h2>Scadenze e responsabilità</h2>
      <table class="table" id="tabScadenze">
        <thead>
          <tr>
            <th style="width:160px;">Scadenza</th>
            <th>Attività</th>
            <th style="width:170px;">Owner</th>
            <th style="width:140px;">Stato</th>
          </tr>
        </thead>
        <tbody id="scadenzeBody"></tbody>
      </table>
      <div class="actions" style="grid-template-columns: 1fr 1fr; margin-top:10px;">
        <button id="btnAddScadenza" class="secondary">Aggiungi riga</button>
        <button id="btnClearScadenze" class="secondary">Svuota scadenze</button>
      </div>
    </section>

    <div class="toast" id="toast"></div>
  </main>

  <script>
    const STORAGE_KEY = "chiusura_mensile_admin_v1";

    const TASKS = [
      { id: "attive", label: "Fatture attive (vendite)" },
      { id: "passive", label: "Fatture passive (fornitori)" },
      { id: "banca", label: "Prima nota / banca / cassa" },
      { id: "spese", label: "Note spese e rimborsi" },
      { id: "payroll", label: "Payroll e contributi" },
      { id: "iva", label: "IVA e liquidazione" },
      { id: "ratei", label: "Accantonamenti (ratei/risconti)" },
      { id: "report", label: "Reporting gestionale" }
    ];

    const $ = (id) => document.getElementById(id);

    function toast(msg) {
      const el = $("toast");
      el.textContent = msg;
      el.classList.add("show");
      setTimeout(() => el.classList.remove("show"), 2200);
    }

    function parseEuro(raw) {
      if (raw == null) return 0;
      const s = String(raw).trim()
        .replaceAll("", "")
        .replaceAll(/\s/g, "")
        .replaceAll(".", "")      // 1.234,56 -> 1234,56
        .replaceAll(",", ".");    // -> 1234.56
      const n = Number(s);
      return Number.isFinite(n) ? n : 0;
    }

    function formatEuro(n) {
      const v = Number.isFinite(n) ? n : 0;
      return new Intl.NumberFormat("it-IT", { style: "currency", currency: "EUR" }).format(v);
    }

    function formatPct(n) {
      const v = Number.isFinite(n) ? n : 0;
      return new Intl.NumberFormat("it-IT", { style: "percent", minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(v);
    }

    function calcKpi() {
      const ricavi = parseEuro($("ricavi").value);
      const costiDiretti = parseEuro($("costiDiretti").value);
      const opex = parseEuro($("opex").value);
      const ivaDebito = parseEuro($("ivaDebito").value);
      const ivaCredito = parseEuro($("ivaCredito").value);

      const margine = ricavi - costiDiretti;
      const ebitda = ricavi - costiDiretti - opex;
      const ivaNetta = ivaDebito - ivaCredito;
      const marginePct = ricavi > 0 ? (margine / ricavi) : 0;

      $("kpiMargine").textContent = formatEuro(margine);
      $("kpiEbitda").textContent = formatEuro(ebitda);
      $("kpiIvaNetta").textContent = formatEuro(ivaNetta);
      $("kpiMarginePct").textContent = `${formatPct(marginePct)} sui ricavi`;
    }

    function taskStatusPill(done, started) {
      if (done) return { text: "Chiuso", cls: "ok" };
      if (started) return { text: "In corso", cls: "mid" };
      return { text: "Non iniziato", cls: "no" };
    }

    function renderTasks(state) {
      const wrap = $("tasks");
      wrap.innerHTML = "";
      TASKS.forEach(t => {
        const st = state.tasks?.[t.id] || { started: false, done: false };
        const pill = taskStatusPill(st.done, st.started);

        const row = document.createElement("div");
        row.className = "task";

        const chk = document.createElement("input");
        chk.type = "checkbox";
        chk.checked = !!st.done;
        chk.title = "Segna come chiuso";

        const label = document.createElement("div");
        label.innerHTML = `<div style="font-weight:600">${t.label}</div>
          <div class="muted">Spunta per “chiuso”. Usa il selettore per impostare “in corso”.</div>`;

        const sel = document.createElement("select");
        sel.innerHTML = `
          <option value="no">Non iniziato</option>
          <option value="mid">In corso</option>
          <option value="ok">Chiuso</option>
        `;
        sel.value = st.done ? "ok" : (st.started ? "mid" : "no");

        const pillEl = document.createElement("span");
        pillEl.className = `pill ${pill.cls}`;
        pillEl.textContent = pill.text;

        const right = document.createElement("div");
        right.style.display = "grid";
        right.style.gap = "6px";
        right.style.justifyItems = "end";
        right.appendChild(pillEl);

        row.appendChild(chk);
        row.appendChild(label);
        row.appendChild(right);

        // seconda riga "controllo" sotto (selettore)
        const row2 = document.createElement("div");
        row2.style.gridColumn = "1 / -1";
        row2.style.display = "grid";
        row2.style.gridTemplateColumns = "1fr 170px";
        row2.style.gap = "10px";
        row2.style.alignItems = "center";
        row2.style.marginTop = "8px";

        const hint = document.createElement("div");
        hint.className = "muted";
        hint.textContent = "Stato lavorazione:";

        row2.appendChild(hint);
        row2.appendChild(sel);

        const container = document.createElement("div");
        container.style.border = "1px solid rgba(127,127,127,.25)";
        container.style.borderRadius = "12px";
        container.style.padding = "10px";
        container.appendChild(row);
        container.appendChild(row2);

        function updateFromUI() {
          const v = sel.value;
          const started = (v === "mid" || v === "ok");
          const done = (v === "ok") || chk.checked;
          // Se spunti, forza "Chiuso"
          if (chk.checked) sel.value = "ok";

          const pill = taskStatusPill(done, started);
          pillEl.className = `pill ${pill.cls}`;
          pillEl.textContent = pill.text;

          state.tasks = state.tasks || {};
          state.tasks[t.id] = { started, done };
        }

        chk.addEventListener("change", () => {
          if (chk.checked) sel.value = "ok";
          updateFromUI();
        });

        sel.addEventListener("change", () => {
          chk.checked = (sel.value === "ok");
          updateFromUI();
        });

        wrap.appendChild(container);
      });
    }

    function newScadenzaRow(row = { date: "", activity: "", owner: "", status: "Da fare" }) {
      const tr = document.createElement("tr");

      const tdDate = document.createElement("td");
      const inDate = document.createElement("input");
      inDate.type = "date";
      inDate.value = row.date || "";
      tdDate.appendChild(inDate);

      const tdAct = document.createElement("td");
      const inAct = document.createElement("input");
      inAct.placeholder = "Es. Invio documenti al commercialista";
      inAct.value = row.activity || "";
      tdAct.appendChild(inAct);

      const tdOwner = document.createElement("td");
      const inOwner = document.createElement("input");
      inOwner.placeholder = "Nome";
      inOwner.value = row.owner || "";
      tdOwner.appendChild(inOwner);

      const tdStatus = document.createElement("td");
      const sel = document.createElement("select");
      ["Da fare", "In corso", "Fatto"].forEach(s => {
        const o = document.createElement("option");
        o.value = s; o.textContent = s;
        sel.appendChild(o);
      });
      sel.value = row.status || "Da fare";
      tdStatus.appendChild(sel);

      tr.appendChild(tdDate);
      tr.appendChild(tdAct);
      tr.appendChild(tdOwner);
      tr.appendChild(tdStatus);

      // handle
      tr._getData = () => ({
        date: inDate.value,
        activity: inAct.value.trim(),
        owner: inOwner.value.trim(),
        status: sel.value
      });

      return tr;
    }

    function readScadenze() {
      const rows = Array.from($("scadenzeBody").children);
      return rows.map(r => r._getData()).filter(r => r.date || r.activity || r.owner);
    }

    function renderScadenze(data) {
      const body = $("scadenzeBody");
      body.innerHTML = "";
      (data?.length ? data : [ {} ]).forEach(r => body.appendChild(newScadenzaRow(r)));
    }

    function getStateFromUI() {
      return {
        azienda: $("azienda").value.trim(),
        mese: $("mese").value.trim(),
        responsabile: $("responsabile").value.trim(),
        dataAvvio: $("dataAvvio").value,
        dataTarget: $("dataTarget").value,
        numeri: {
          ricavi: parseEuro($("ricavi").value),
          costiDiretti: parseEuro($("costiDiretti").value),
          opex: parseEuro($("opex").value),
          ivaDebito: parseEuro($("ivaDebito").value),
          ivaCredito: parseEuro($("ivaCredito").value),
          altri: parseEuro($("altri").value)
        },
        commento: $("commento").value,
        noteAnomalie: $("noteAnomalie").value,
        tasks: state.tasks || {},
        scadenze: readScadenze(),
        savedAt: new Date().toISOString()
      };
    }

    function applyStateToUI(s) {
      $("azienda").value = s.azienda || "";
      $("mese").value = s.mese || "";
      $("responsabile").value = s.responsabile || "";
      $("dataAvvio").value = s.dataAvvio || "";
      $("dataTarget").value = s.dataTarget || "";

      $("ricavi").value = s.numeri?.ricavi != null ? s.numeri.ricavi.toString().replace(".", ",") : "";
      $("costiDiretti").value = s.numeri?.costiDiretti != null ? s.numeri.costiDiretti.toString().replace(".", ",") : "";
      $("opex").value = s.numeri?.opex != null ? s.numeri.opex.toString().replace(".", ",") : "";
      $("ivaDebito").value = s.numeri?.ivaDebito != null ? s.numeri.ivaDebito.toString().replace(".", ",") : "";
      $("ivaCredito").value = s.numeri?.ivaCredito != null ? s.numeri.ivaCredito.toString().replace(".", ",") : "";
      $("altri").value = s.numeri?.altri != null ? s.numeri.altri.toString().replace(".", ",") : "";

      $("commento").value = s.commento || "";
      $("noteAnomalie").value = s.noteAnomalie || "";

      state.tasks = s.tasks || {};
      renderTasks(state);

      renderScadenze(s.scadenze || []);
      calcKpi();
    }

    function download(filename, content, mime="application/json") {
      const blob = new Blob([content], { type: mime });
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      a.remove();
      setTimeout(() => URL.revokeObjectURL(a.href), 2500);
    }

    function exportJson() {
      const s = getStateFromUI();
      const safeName = (s.azienda || "azienda").toLowerCase().replaceAll(/[^a-z0-9]+/gi, "_").replaceAll(/^_+|_+$/g,"");
      const safeMonth = (s.mese || "mese").toLowerCase().replaceAll(/[^a-z0-9]+/gi, "_").replaceAll(/^_+|_+$/g,"");
      download(`chiusura_${safeName}_${safeMonth}.json`, JSON.stringify(s, null, 2));
    }

    function exportCsvKpi() {
      const ricavi = parseEuro($("ricavi").value);
      const costiDiretti = parseEuro($("costiDiretti").value);
      const opex = parseEuro($("opex").value);
      const ivaDebito = parseEuro($("ivaDebito").value);
      const ivaCredito = parseEuro($("ivaCredito").value);

      const margine = ricavi - costiDiretti;
      const ebitda = ricavi - costiDiretti - opex;
      const ivaNetta = ivaDebito - ivaCredito;

      const rows = [
        ["Azienda", $("azienda").value.trim()],
        ["Mese", $("mese").value.trim()],
        ["Ricavi", ricavi],
        ["CostiDiretti", costiDiretti],
        ["Opex", opex],
        ["MargineLordo", margine],
        ["EBITDA", ebitda],
        ["IVADebito", ivaDebito],
        ["IVACredito", ivaCredito],
        ["IVANetta", ivaNetta],
      ];

      const csv = rows.map(r => r.map(v => `"${String(v).replaceAll('"','""')}"`).join(",")).join("\n");
      download("kpi_chiusura_mensile.csv", csv, "text/csv");
    }

    function saveLocal() {
      const s = getStateFromUI();
      localStorage.setItem(STORAGE_KEY, JSON.stringify(s));
      toast("Salvato in locale.");
    }

    function loadLocal() {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return null;
      try { return JSON.parse(raw); } catch { return null; }
    }

    function resetAll() {
      localStorage.removeItem(STORAGE_KEY);
      applyStateToUI({
        azienda: "", mese: "", responsabile: "", dataAvvio: "", dataTarget: "",
        numeri: { ricavi: 0, costiDiretti: 0, opex: 0, ivaDebito: 0, ivaCredito: 0, altri: 0 },
        commento: "", noteAnomalie: "", tasks: {}, scadenze: [ {} ]
      });
      toast("Reset completato.");
    }

    function loadExample() {
      const today = new Date();
      const yyyy = today.getFullYear();
      const mm = String(today.getMonth() + 1).padStart(2, "0");
      const dd = String(today.getDate()).padStart(2, "0");
      const iso = `${yyyy}-${mm}-${dd}`;

      applyStateToUI({
        azienda: "ACME S.r.l.",
        mese: "Marzo 2026",
        responsabile: "Ufficio Amministrazione",
        dataAvvio: iso,
        dataTarget: iso,
        numeri: {
          ricavi: 185000,
          costiDiretti: 74000,
          opex: 56000,
          ivaDebito: 26500,
          ivaCredito: 21400,
          altri: 3500
        },
        commento: "Ricavi in crescita per chiusura di due contratti annuali. Opex più alto per campagna marketing e consulenza legale. Prevista normalizzazione il prossimo mese.",
        noteAnomalie: "05/04: nota di credito cliente X da emettere (errore quantità). Impatto stimato: -2.450 € ricavi, IVA correlata.\n06/04: fattura fornitore Y in contestazione, accantonamento prudenziale 1.200 €.",
        tasks: {
          attive: { started: true, done: true },
          passive: { started: true, done: false },
          banca: { started: true, done: false },
          spese: { started: false, done: false },
          payroll: { started: true, done: true },
          iva: { started: true, done: false },
          ratei: { started: true, done: false },
          report: { started: false, done: false }
        },
        scadenze: [
          { date: iso, activity: "Invio documenti al commercialista", owner: "Admin", status: "In corso" },
          { date: iso, activity: "Stima liquidazione IVA", owner: "Admin", status: "Da fare" },
          { date: iso, activity: "Reporting a Direzione", owner: "Finance", status: "Da fare" }
        ]
      });
      toast("Esempio caricato.");
    }

    // stato globale minimo
    const state = { tasks: {} };

    // init
    renderTasks(state);
    renderScadenze([{}]);
    calcKpi();

    // listeners KPI
    ["ricavi","costiDiretti","opex","ivaDebito","ivaCredito","altri"].forEach(id => {
      $(id).addEventListener("input", calcKpi);
    });

    // autosave leggero (non aggressivo)
    ["azienda","mese","responsabile","dataAvvio","dataTarget","commento","noteAnomalie"].forEach(id => {
      $(id).addEventListener("change", () => saveLocal());
    });
    ["ricavi","costiDiretti","opex","ivaDebito","ivaCredito","altri"].forEach(id => {
      $(id).addEventListener("change", () => saveLocal());
    });

    // buttons
    $("btnSalva").addEventListener("click", saveLocal);
    $("btnExport").addEventListener("click", exportJson);
    $("btnStampa").addEventListener("click", () => window.print());
    $("btnReset").addEventListener("click", resetAll);
    $("btnCsv").addEventListener("click", exportCsvKpi);
    $("btnEsempio").addEventListener("click", loadExample);

    $("btnAddScadenza").addEventListener("click", () => {
      $("scadenzeBody").appendChild(newScadenzaRow({}));
      saveLocal();
    });

    $("btnClearScadenze").addEventListener("click", () => {
      renderScadenze([{}]);
      saveLocal();
      toast("Scadenze svuotate.");
    });

    // ripristino automatico
    const saved = loadLocal();
    if (saved) {
      applyStateToUI(saved);
      toast("Dati locali ripristinati.");
    }
  </script>
</body>
</html>

Quando conviene usarli

Gli artefatti brillano quando l’output è “corposo” e deve vivere oltre il singolo messaggio, ad esempio un documento che vuoi rivedere, un layout che vuoi vedere, un diagramma che vuoi far maturare. Se invece ti serve una risposta rapida (una definizione, un chiarimento, due righe di troubleshooting), la chat pura resta più veloce e meno ingombrante.

Qui potete trovare il catalogo ufficiale degli artefatti di Claude AI: https://claude.ai/catalog/artifacts

Se hai trovato utile questo articolo restiamo in contatto su linkedin a https://www.linkedin.com/in/andreatonin/

#ClaudeArtifacts #ClaudeAI #Anthropic #AItools #GenAI #Prototyping #NoCode #DeveloperTools #Productivity #TechMagazine

Banner

Releated Posts

Prompt che mettono la AI in discussione

Il problema raramente è “non avere informazioni”, ma averle giuste, coerenti e verificabili. Una fattura letta male, una…

DiByAndrea Tonin Apr 15, 2026

Bologna Children’s Book Fair: dove il libro incontra licenze, creatività e il digitale

Anche quest’anno siamo tornati alla Bologna Children’s Book Fair con quello sguardo un po’ doppio di chi ha…

DiByAndrea Tonin Apr 15, 2026

ComfyUI – Reference Conditioning

In ComfyUI (soprattutto con modelli come Flux) il Reference Conditioning è un nodo che permette di usare una…

DiByAndrea Tonin Apr 9, 2026

ComfyUI per generare anime: NewBie Image Exp0.1

NewBie Image (spesso indicato come NewBie-image-Exp0.1) è un modello text-to-image in stile anime/ACG pensato per generare illustrazioni con…

DiByAndrea Tonin Apr 9, 2026