{
  "name": "DEMO — Gestione contatti, punteggio e risposta immediata (PauSolutions)",
  "nodes": [
    {
      "parameters": {
        "content": "## Gestione contatti, punteggio e risposta immediata — DEMO\nStudio di interior design fittizio, nessuna credenziale.\n\n1. Clicca *Test workflow* per elaborare la richiesta di esempio, oppure invia un JSON {nome, email, azienda, telefono, messaggio} all'URL del webhook.\n2. Regole trasparenti classificano la richiesta e assegnano un punteggio con le motivazioni.\n3. Collega la tua email per la risposta immediata e l'avviso al titolare, e un nodo Sheets/CRM per l'elenco contatti.\n\nVersione interattiva: pausolutions.com/it/demos/lead-intake/\n\n_Tutte le date di questa demo sono esempi (\"oggi\" di esempio: 21 ott 2026). In produzione il flusso usa la data reale._",
        "height": 370,
        "width": 480
      },
      "id": "dfcd9a78-2af2-4800-a1fa-e3c1fa823fc8",
      "name": "Info su questa demo",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        -400
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "demo-gestione-contatti-it",
        "responseMode": "onReceived",
        "options": {}
      },
      "id": "d33ad12a-b48d-4800-ad0d-a893aebadc06",
      "name": "Modulo del sito (webhook)",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        100
      ],
      "webhookId": "a4e4f352-1beb-4c00-aad3-8aebbb5ce29e"
    },
    {
      "parameters": {},
      "id": "5e7c93f1-bcd6-4200-a04b-ea48301286c7",
      "name": "Test manuale",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// DATI DEMO — una richiesta fittizia, nel formato in uscita dal webhook.\nreturn [{ json: { body: {\n  \"nome\": \"Giulia Conti\",\n  \"email\": \"giulia.conti@studiodentisticoconti.example\",\n  \"azienda\": \"Studio Dentistico Conti (demo)\",\n  \"telefono\": \"+39 0432 000000\",\n  \"messaggio\": \"Buongiorno, vorrei un preventivo per rinnovare la reception del nostro studio. Budget indicativo 15.000 €. È abbastanza urgente, vorremmo iniziare questo mese.\"\n} } }];"
      },
      "id": "4d5bd9bd-5406-4000-a36c-a0044f1e6d9b",
      "name": "Richiesta di esempio (dati demo)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Stessa logica della demo interattiva su pausolutions.com/it/demos/ (regole + modelli, senza IA).\nconst TEXT = {\n  \"locale\": \"it-IT\",\n  \"firm\": \"Interni Acquachiara (demo)\",\n  \"receivedAt\": \"21/10/2026 09:12\",\n  \"rules\": [\n    {\n      \"key\": \"quote\",\n      \"category\": \"Richiesta di preventivo\",\n      \"words\": [\n        \"preventivo\",\n        \"prezzo\",\n        \"costo\",\n        \"quanto costa\",\n        \"stima\"\n      ],\n      \"points\": 30,\n      \"reason\": \"Chiede un preventivo\"\n    },\n    {\n      \"key\": \"booking\",\n      \"category\": \"Prenotazione\",\n      \"words\": [\n        \"prenot\",\n        \"appuntamento\",\n        \"sopralluogo\",\n        \"disponibil\"\n      ],\n      \"points\": 20,\n      \"reason\": \"Vuole fissare un sopralluogo\"\n    },\n    {\n      \"key\": \"support\",\n      \"category\": \"Assistenza\",\n      \"words\": [\n        \"problema\",\n        \"non funziona\",\n        \"guasto\",\n        \"errore\",\n        \"reclamo\"\n      ],\n      \"points\": 5,\n      \"reason\": \"Richiesta di assistenza\"\n    }\n  ],\n  \"general\": \"Generale\",\n  \"urgentWords\": [\n    \"urgente\",\n    \"subito\",\n    \"prima possibile\",\n    \"questa settimana\",\n    \"oggi\"\n  ],\n  \"budgetWords\": [\n    \"budget\",\n    \"€\",\n    \"eur\",\n    \"euro\"\n  ],\n  \"freeMailDomains\": [\n    \"gmail.com\",\n    \"googlemail.com\",\n    \"outlook.com\",\n    \"hotmail.com\",\n    \"hotmail.it\",\n    \"live.com\",\n    \"yahoo.com\",\n    \"yahoo.it\",\n    \"icloud.com\",\n    \"libero.it\",\n    \"virgilio.it\",\n    \"tiscali.it\",\n    \"alice.it\"\n  ],\n  \"points\": {\n    \"urgent\": 20,\n    \"budget\": 15,\n    \"company\": 10,\n    \"businessEmail\": 10,\n    \"phone\": 10,\n    \"detailed\": 10,\n    \"invalidEmail\": -20\n  },\n  \"reasons\": {\n    \"urgent\": \"Richiesta urgente\",\n    \"budget\": \"Indica un budget\",\n    \"company\": \"Nome dell'azienda indicato\",\n    \"businessEmail\": \"Email aziendale\",\n    \"phone\": \"Telefono indicato\",\n    \"detailed\": \"Messaggio dettagliato\",\n    \"invalidEmail\": \"L'indirizzo email sembra non valido\",\n    \"generalCategory\": \"Tipo di richiesta non chiaro\"\n  },\n  \"detailedWords\": 15,\n  \"bands\": {\n    \"hot\": \"Caldo\",\n    \"warm\": \"Tiepido\",\n    \"cold\": \"Freddo\"\n  },\n  \"priorities\": {\n    \"high\": \"alta\",\n    \"normal\": \"normale\"\n  },\n  \"ackSubject\": \"Abbiamo ricevuto la tua richiesta\",\n  \"ackBodies\": {\n    \"quote\": \"Ciao {first},\\n\\ngrazie per aver scritto a Interni Acquachiara. Abbiamo ricevuto la tua richiesta di preventivo e ti rispondiamo con i prossimi passi entro un giorno lavorativo.\\n\\nIl team Acquachiara (demo)\",\n    \"booking\": \"Ciao {first},\\n\\ngrazie per aver scritto a Interni Acquachiara. Ti confermiamo un orario per il sopralluogo entro un giorno lavorativo.\\n\\nIl team Acquachiara (demo)\",\n    \"support\": \"Ciao {first},\\n\\ngrazie per la segnalazione. Stiamo verificando e ti rispondiamo entro un giorno lavorativo.\\n\\nIl team Acquachiara (demo)\",\n    \"general\": \"Ciao {first},\\n\\ngrazie per aver scritto a Interni Acquachiara. Ti rispondiamo entro un giorno lavorativo.\\n\\nIl team Acquachiara (demo)\"\n  },\n  \"ackNoReply\": \"Nessuna risposta automatica: l'indirizzo email sembra non valido, quindi il contatto viene segnalato per una verifica manuale.\",\n  \"ownerSubject\": \"Nuovo contatto {band} ({score}/100): {category} — {name}\",\n  \"ownerBody\": \"{name} ({company}) — {email}, {phone}\\nCategoria: {category} · Priorità: {priority} · Punteggio: {score}/100\\nPerché: {reasons}\\n\\nMessaggio:\\n{message}\",\n  \"rowStatus\": \"Nuovo\",\n  \"rowKeys\": {\n    \"date\": \"Data\",\n    \"name\": \"Nome\",\n    \"email\": \"Email\",\n    \"company\": \"Azienda\",\n    \"category\": \"Categoria\",\n    \"score\": \"Punteggio\",\n    \"priority\": \"Priorità\",\n    \"status\": \"Stato\"\n  }\n};\n\nfunction fill(tpl, vars) {\n  return tpl.replace(/\\{(\\w+)\\}/g, (m, k) => (k in vars ? String(vars[k]) : m));\n}\n\nfunction daysBetween(fromIso, toIso) {\n  return Math.round((Date.parse(toIso + \"T00:00:00Z\") - Date.parse(fromIso + \"T00:00:00Z\")) / 86400000);\n}\n\nfunction addDays(iso, days) {\n  return new Date(Date.parse(iso + \"T00:00:00Z\") + days * 86400000).toISOString().slice(0, 10);\n}\n\nfunction longDate(iso, locale) {\n  return new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"long\", year: \"numeric\", timeZone: \"UTC\" }).format(new Date(iso + \"T00:00:00Z\"));\n}\n\nfunction money(amount, locale) {\n  return new Intl.NumberFormat(locale, { style: \"currency\", currency: \"EUR\" }).format(amount);\n}\n\nfunction cleanText(value, max, multiline) {\n  const s = String(value ?? \"\").replace(multiline ? /[\\u0000-\\u0009\\u000B-\\u001F\\u007F]/g : /[\\u0000-\\u001F\\u007F]/g, \" \");\n  const collapsed = multiline ? s.replace(/[ \\t]+/g, \" \").replace(/\\n{3,}/g, \"\\n\\n\") : s.replace(/\\s+/g, \" \");\n  return collapsed.trim().slice(0, max);\n}\n\nfunction hasTerm(text, term, whole) {\n  if (!/^[\\p{L}\\p{N}]/u.test(term)) return text.includes(term);\n  const escaped = term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n  return new RegExp(\"(^|[^\\\\p{L}\\\\p{N}])\" + escaped + (whole ? \"(?![\\\\p{L}\\\\p{N}])\" : \"\"), \"u\").test(text);\n}\n\nfunction scoreLead(raw, text) {\n  const name = cleanText(raw.name, 80, false);\n  const email = cleanText(raw.email, 120, false).toLowerCase();\n  const company = cleanText(raw.company, 100, false);\n  const phone = cleanText(raw.phone, 40, false);\n  const message = cleanText(raw.message, 1500, true);\n  const lower = message.toLowerCase();\n  const emailValid = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\n  const domain = email.split(\"@\").pop() || \"\";\n  const freeMail = text.freeMailDomains.some((d) => domain === d || domain.split(\".\")[0] === d.split(\".\")[0]);\n  const rule = text.rules.find((r) => r.words.some((w) => hasTerm(lower, w, false)));\n  const urgent = text.urgentWords.some((w) => hasTerm(lower, w, true));\n  const reasons = [];\n  if (rule) reasons.push({ label: rule.reason, points: rule.points });\n  else reasons.push({ label: text.reasons.generalCategory, points: 0 });\n  if (urgent) reasons.push({ label: text.reasons.urgent, points: text.points.urgent });\n  if (text.budgetWords.some((w) => hasTerm(lower, w, true))) reasons.push({ label: text.reasons.budget, points: text.points.budget });\n  if (company) reasons.push({ label: text.reasons.company, points: text.points.company });\n  if (emailValid && !freeMail) reasons.push({ label: text.reasons.businessEmail, points: text.points.businessEmail });\n  if (phone.replace(/\\D/g, \"\").length >= 6) reasons.push({ label: text.reasons.phone, points: text.points.phone });\n  if (message.split(/\\s+/).filter(Boolean).length >= text.detailedWords) reasons.push({ label: text.reasons.detailed, points: text.points.detailed });\n  if (!emailValid) reasons.push({ label: text.reasons.invalidEmail, points: text.points.invalidEmail });\n  const score = Math.max(0, Math.min(100, reasons.reduce((t, r) => t + r.points, 0)));\n  const band = score >= 60 ? text.bands.hot : score >= 30 ? text.bands.warm : text.bands.cold;\n  const priority = urgent || score >= 60 ? text.priorities.high : text.priorities.normal;\n  const categoryKey = rule ? rule.key : \"general\";\n  const category = rule ? rule.category : text.general;\n  const first = name.split(\" \")[0] || name;\n  const vars = { name: name || \"—\", first, email: email || \"—\", company: company || \"—\", phone: phone || \"—\", category, priority, score, band: band.toLowerCase(), message, reasons: reasons.map((r) => r.label + \" (\" + (r.points >= 0 ? \"+\" : \"\") + r.points + \")\").join(\", \") };\n  const k = text.rowKeys;\n  return {\n    name, email, company, phone, message, emailValid, categoryKey, category, urgent, score, band, priority, reasons,\n    ackSubject: emailValid ? text.ackSubject : null,\n    ackBody: emailValid ? fill(text.ackBodies[categoryKey], vars) : null,\n    ownerSubject: fill(text.ownerSubject, vars),\n    ownerBody: fill(text.ownerBody, vars),\n    row: { [k.date]: text.receivedAt, [k.name]: name, [k.email]: email, [k.company]: company, [k.category]: category, [k.score]: score, [k.priority]: priority, [k.status]: text.rowStatus },\n  };\n}\n\nreturn $input.all().map((item) => {\n  const b = item.json.body ?? item.json;\n  const input = { name: b.nome, email: b.email, company: b.azienda, phone: b.telefono, message: b.messaggio };\n  return { json: scoreLead(input, TEXT) };\n});"
      },
      "id": "7dca1c56-aa9c-4c00-a3fd-65efc3e92a7a",
      "name": "Classifica e assegna il punteggio",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        100
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "807a88b9-4d16-4800-ae4d-f1003ab58dbf",
              "leftValue": "={{ String($json.emailValid) }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "908814ea-2a3f-4000-aebf-6d530a24ea46",
      "name": "Solo indirizzi email validi",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        500,
        -60
      ],
      "notes": "Nessuna risposta automatica se l'indirizzo email sembra non valido.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "fromEmail": "info@acquachiara.example",
        "toEmail": "={{ $json.email }}",
        "subject": "={{ $json.ackSubject }}",
        "emailFormat": "text",
        "text": "={{ $json.ackBody }}",
        "options": {}
      },
      "id": "cb345a53-dfb8-4000-a503-23eaf7fea1d5",
      "name": "Risposta immediata al cliente",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        740,
        -60
      ],
      "disabled": true,
      "notes": "Disattivato nella demo. Collega il tuo account SMTP/Gmail/Outlook, poi attivalo.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "fromEmail": "info@acquachiara.example",
        "toEmail": "info@acquachiara.example",
        "subject": "={{ $json.ownerSubject }}",
        "emailFormat": "text",
        "text": "={{ $json.ownerBody }}",
        "options": {}
      },
      "id": "56208a71-6a36-4200-a817-f3c8966f7c47",
      "name": "Avviso al titolare",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        500,
        120
      ],
      "disabled": true,
      "notes": "Disattivato nella demo. Collega il tuo account SMTP/Gmail/Outlook, poi attivalo.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// Riga per l'elenco contatti (demo: restituita come dati; in produzione collega\n// qui un nodo Google Sheets \"Append row\" o il nodo del tuo CRM).\nreturn $input.all().map((i) => ({ json: i.json.row }));"
      },
      "id": "a07f9b71-6bf9-4400-ae48-e2c83d5a2347",
      "name": "Riga per l'elenco contatti",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        500,
        300
      ]
    }
  ],
  "pinData": {
    "Modulo del sito (webhook)": [
      {
        "json": {
          "body": {
            "nome": "Giulia Conti",
            "email": "giulia.conti@studiodentisticoconti.example",
            "azienda": "Studio Dentistico Conti (demo)",
            "telefono": "+39 0432 000000",
            "messaggio": "Buongiorno, vorrei un preventivo per rinnovare la reception del nostro studio. Budget indicativo 15.000 €. È abbastanza urgente, vorremmo iniziare questo mese."
          }
        }
      }
    ]
  },
  "connections": {
    "Test manuale": {
      "main": [
        [
          {
            "node": "Richiesta di esempio (dati demo)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Richiesta di esempio (dati demo)": {
      "main": [
        [
          {
            "node": "Classifica e assegna il punteggio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Modulo del sito (webhook)": {
      "main": [
        [
          {
            "node": "Classifica e assegna il punteggio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classifica e assegna il punteggio": {
      "main": [
        [
          {
            "node": "Solo indirizzi email validi",
            "type": "main",
            "index": 0
          },
          {
            "node": "Avviso al titolare",
            "type": "main",
            "index": 0
          },
          {
            "node": "Riga per l'elenco contatti",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solo indirizzi email validi": {
      "main": [
        [
          {
            "node": "Risposta immediata al cliente",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "de296281-f1e3-4800-a01e-1b3845afc7b7",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": [],
  "_demo": {
    "by": "PauSolutions",
    "note": "DEMO con dati fittizi. Nessuna credenziale inclusa. I nodi email sono disattivati finché non colleghi il tuo account.",
    "tags": [
      "demo",
      "contatti"
    ]
  }
}
