diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index f7def69..f3e3595 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -49,6 +49,8 @@ jobs: run: node --test test/workshop.test.mjs - name: Test del modulo del rider tecnico completo run: node --test test/rider-richiesta.test.mjs + - name: Test del modulo di Produzione eventi + run: node --test test/produzione-richiesta.test.mjs - name: Test funzioni condivise Supabase run: deno test --lock=deno.lock --frozen supabase/functions/_shared/*.test.ts - name: Type-check Edge Functions @@ -87,7 +89,7 @@ jobs: robots.txt sitemap.xml llms.txt 4d1fdc8b5b357ff5353a9e928c187bff.txt \ favicon.ico favicon.svg favicon-48.png favicon-96.png \ apple-touch-icon.png icon-192.png icon-512.png logo-512.png preview.png \ - accedi alternative biglietteria consulenza guida img orchestre privacy programma richiesta rider-tecnico-completo stage-plot termini vendor voce workshop \ + accedi alternative biglietteria consulenza guida img orchestre privacy produzione-eventi programma richiesta rider-tecnico-completo stage-plot termini vendor voce workshop \ ./_site/ - name: Timbra il build ID (SHA del commit) negli asset pubblicati (audit L-03) # il placeholder sta nella shell dell'editor (app/index.html), non nella landing diff --git a/.github/workflows/pr-controlli.yml b/.github/workflows/pr-controlli.yml index 9e6e52f..4068708 100644 --- a/.github/workflows/pr-controlli.yml +++ b/.github/workflows/pr-controlli.yml @@ -41,6 +41,8 @@ jobs: run: node --test test/workshop.test.mjs - name: Test del modulo del rider tecnico completo run: node --test test/rider-richiesta.test.mjs + - name: Test del modulo di Produzione eventi + run: node --test test/produzione-richiesta.test.mjs - name: Test funzioni condivise Supabase run: deno test --lock=deno.lock --frozen supabase/functions/_shared/*.test.ts - name: Type-check Edge Functions diff --git a/consulenza/index.html b/consulenza/index.html index 25623d3..b89e86c 100644 --- a/consulenza/index.html +++ b/consulenza/index.html @@ -749,6 +749,7 @@

Produzione tecnica

Revisione: pagamento sicuro con Stripe (carta, Apple Pay, Google Pay), fattura su richiesta. Rider tecnico completo: nessun pagamento con la richiesta. Produzione tecnica: nessun pagamento prima del preventivo.

+

Dovete organizzare un evento intero, con service, permessi e orari? C’è Produzione eventi: documenti, preventivi confrontati e cronoprogramma, servizio in prova da 290 €.

Una domanda prima di richiederla? Scrivi direttamente a Simone: risposta senza impegno.

diff --git a/guida/rider-tecnico/index.html b/guida/rider-tecnico/index.html index 4d00fc3..7f5b38f 100644 --- a/guida/rider-tecnico/index.html +++ b/guida/rider-tecnico/index.html @@ -151,7 +151,7 @@

Parti dallo stage plot

Fatti revisionare da un sound engineer -

Non avete tempo di farlo? Il rider tecnico completo lo prepariamo noi: ci mandate quello che avete e in 72 ore ricevete il PDF controllato da un sound engineer, 59 €.

+

Non avete tempo di farlo? Il rider tecnico completo lo prepariamo noi: ci mandate quello che avete e in 72 ore ricevete il PDF controllato da un sound engineer, 59 €. Per un evento intero, con più fornitori e permessi, c'è Produzione eventi.

Domande frequenti

diff --git a/llms.txt b/llms.txt index d6d7bdf..a6e9939 100644 --- a/llms.txt +++ b/llms.txt @@ -33,6 +33,7 @@ Uno stage plot (o stage plan) è lo schema in scala del palco che mostra la disp - [Stage plot per formazione](https://stageplot.it/stage-plot/): indice delle undici formazioni (band, orchestra, coro, big band, jazz, acustico, tributo, chiesa, matrimonio, DJ set, festival) - [Rider tecnico completo per band](https://stageplot.it/rider-tecnico-completo/): servizio a 59 € — la band manda il materiale che ha (vecchio rider, lista, foto, descrizione), il rider lo prepariamo noi e lo controlla un sound engineer; in 72 ore (giorni lavorativi) arrivano il PDF (stage plot in scala, channel list, ascolti, corrente, luci di base, ospitalità, orari, contatti) e il progetto modificabile su stageplot.it. Una correzione compresa; per band fino a 12 musicisti; nessun pagamento con la richiesta - [Esempio di rider tecnico completo in PDF](https://stageplot.it/rider-tecnico-completo/esempio-rider-tecnico-completo.pdf): band inventata di 7 elementi (pop e funk), 6 pagine A4 — in breve, pianta in scala 1:50, channel list da 19 canali, ascolti (6 spie + 1 in-ear), luci, orari e ospitalità +- [Produzione eventi](https://stageplot.it/produzione-eventi/): servizio in prova per Comuni, Pro Loco, associazioni, aziende e location senza un tecnico — tre livelli (documenti da 59 €; coordinamento a distanza da 290 €: richieste ai fornitori, confronto dei preventivi a parità di condizioni, cronoprogramma, elenco dei permessi con chi li firma; produzione in loco da 790 €, con Simone Castellan sul posto); non firma sicurezza, strutture, impianti né SCIA e non prende commissioni dai fornitori; tre esempi con nomi inventati - [Revisione e produzione tecnica](https://stageplot.it/consulenza/): gratis l'app genera palco, lista canali, monitor, elettrico e rider PDF; revisione 29 € (un tecnico corregge direttamente il tuo progetto e lo restituisce entro 48 ore, senza PDF); da 149 € lo disegniamo noi da zero, su preventivo - [Privacy policy](https://stageplot.it/privacy/) - [Termini di utilizzo](https://stageplot.it/termini/) diff --git a/produzione-eventi/esempio-azienda-copertina.jpg b/produzione-eventi/esempio-azienda-copertina.jpg new file mode 100644 index 0000000..073eb94 Binary files /dev/null and b/produzione-eventi/esempio-azienda-copertina.jpg differ diff --git a/produzione-eventi/esempio-azienda-cronoprogramma.jpg b/produzione-eventi/esempio-azienda-cronoprogramma.jpg new file mode 100644 index 0000000..42e0860 Binary files /dev/null and b/produzione-eventi/esempio-azienda-cronoprogramma.jpg differ diff --git a/produzione-eventi/esempio-location-copertina.jpg b/produzione-eventi/esempio-location-copertina.jpg new file mode 100644 index 0000000..ec6e46f Binary files /dev/null and b/produzione-eventi/esempio-location-copertina.jpg differ diff --git a/produzione-eventi/esempio-location-palco.jpg b/produzione-eventi/esempio-location-palco.jpg new file mode 100644 index 0000000..f36576f Binary files /dev/null and b/produzione-eventi/esempio-location-palco.jpg differ diff --git a/produzione-eventi/esempio-piazza-copertina.jpg b/produzione-eventi/esempio-piazza-copertina.jpg new file mode 100644 index 0000000..0ca25c2 Binary files /dev/null and b/produzione-eventi/esempio-piazza-copertina.jpg differ diff --git a/produzione-eventi/esempio-piazza-preventivi.jpg b/produzione-eventi/esempio-piazza-preventivi.jpg new file mode 100644 index 0000000..c92fa40 Binary files /dev/null and b/produzione-eventi/esempio-piazza-preventivi.jpg differ diff --git a/produzione-eventi/index.html b/produzione-eventi/index.html new file mode 100644 index 0000000..3ea42ac --- /dev/null +++ b/produzione-eventi/index.html @@ -0,0 +1,358 @@ + + + + + + + + Produzione eventi: la parte tecnica, già organizzata + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ SPstageplot.it + + Descrivi l'evento +
+ +
+ + + Servizio in prova +

La parte tecnica del vostro evento, già organizzata

+

Preventivi dei fornitori confrontati a parità di condizioni, piante in scala, cronoprogramma e permessi con chi li firma. Per Comuni, Pro Loco, associazioni, aziende e location che non hanno un tecnico in casa.

+
da 59 €solo documenti
da 290 €coordinamento a distanza
da 790 €con Simone sul posto
+

Prezzi di partenza, in prova: il preventivo scritto indica importo finale e IVA, e lo ricevete prima di decidere.

+ + + +
+

In breve: «Produzione eventi» è il servizio con cui organizziamo la parte tecnica di un concerto, di una festa aziendale o di una rassegna per chi non ha un tecnico: documenti in scala, richieste ai fornitori e confronto dei preventivi, cronoprogramma, elenco dei permessi con chi li firma e per quando e, se serve, un sound engineer sul posto. Non vendiamo né noleggiamo palchi e impianti e non prendiamo commissioni dai fornitori. Ogni consegna la controlla Simone Castellan.

+
+ +

Vi riconoscete in una di queste?

+ + +

Cosa ricevete

+
+
+ Livello 1 +

Documenti

+

da 59 €

+
    +
  • Stage plot in scala (A3, 1:50)
  • +
  • Channel list e output list
  • +
  • Rider tecnico o scheda del vostro palco
  • +
  • PDF con versione e data, e progetto da riaprire su stageplot.it
  • +
+

59 € per una band fino a 12 musicisti; 149 € per la scheda di una location o per organici maggiori. In 3 giorni lavorativi dal materiale completo.

+
+
+ Livello 2 +

Coordinamento a distanza

+

da 290 €

+
    +
  • I documenti del livello 1
  • +
  • Richiesta di preventivo a 2-3 fornitori e confronto a parità di condizioni
  • +
  • Cronoprogramma e piano di produzione
  • +
  • Elenco dei documenti e dei permessi, con chi li firma e per quando
  • +
  • Mail ai fornitori, checklist per chi organizza
  • +
+

Da 290 € per un palco, un fornitore, una giornata; da 490 € con 2-3 preventivi e piano di comunicazione. Primo riscontro entro 1 giorno lavorativo, documenti definitivi 7 giorni prima dell'evento.

+
+
+ Livello 3 +

Produzione in loco

+

da 790 €

+
    +
  • Il livello 2
  • +
  • Sopralluogo e controllo del montaggio
  • +
  • Simone Castellan sul posto come responsabile tecnico e sound engineer
  • +
  • Foglio del giorno: orari, contatti, checklist
  • +
+

Da 790 € a 1.200 € a evento secondo durata e distanza. L'impianto proprio, se serve, si indica a parte.

+
+
+
+

Cosa non facciamo

+

Coordiniamo, non firmiamo. Vi diciamo quali carte servono, chi le firma, con quali dati e per quando, e controlliamo che arrivino.

+
    +
  • Non firmiamo relazioni di sicurezza né certificazioni di palchi, strutture o impianti elettrici, e non presentiamo la SCIA né il parere della Commissione di vigilanza: li firma un tecnico abilitato.
  • +
  • Non presentiamo domande al posto vostro, non curiamo la SIAE, non scegliamo gli artisti e non trattiamo cachet.
  • +
  • Non noleggiamo né vendiamo palchi, impianti e luci e non fatturiamo per conto dei fornitori: non prendiamo commissioni da nessuno, per questo possiamo dirvi quale offerta conviene.
  • +
+
+ +

Tre esempi

+

Sono pacchetti dimostrativi con nomi, luoghi e cifre inventati, costruiti con la stessa sostanza tecnica dei concerti veri: li guardate qui, senza scaricare niente. Se volete vederne uno intero, chiedetelo nel modulo.

+
+
+
+

Natale in piazza

+

Coro, archi, pianoforte e solista per 350 persone, organizzato da una Pro Loco.

+

Stage plot e planimetria della piazza, cronoprogramma di due giorni, permessi con chi li firma, e tre preventivi confrontati: il più basso a prima vista era il secondo più caro una volta messe le stesse voci.

+

9 pagine · livello 3

+
+
+
Prima pagina del pacchetto d'esempio «Natale in piazza»: scheda dell'evento, numeri chiave e cosa contiene il pacchetto
La scheda dell'evento: tutto quello che il service guarda per primo
+
Tabella del confronto di tre preventivi di service a parità di condizioni, con le voci escluse evidenziate
Il confronto dei preventivi a parità di condizioni (importi inventati)
+
+
+
+
+

Festa di fine anno di un'azienda

+

130 ospiti, saluti, cena e una band di sette elementi; il referente interno non è un tecnico.

+

Stage plot, channel list da 23 canali, cronoprogramma della serata con i momenti sonori, cosa deve contenere un preventivo e una checklist per il referente.

+

7 pagine · livello 2

+
+
+
Prima pagina del pacchetto d'esempio «Festa di fine anno»: scheda dell'evento e numeri chiave
La scheda dell'evento e i numeri chiave
+
Cronoprogramma della serata con orari di montaggio, prova suoni, cena e concerto, e chi fa cosa
Il cronoprogramma: chi fa cosa e a che ora
+
+
+
+
+

Una location con una rassegna

+

Un agriturismo con sei serate musicali e un palco fisso.

+

La scheda tecnica del palco, cosa trova e cosa deve portare chi suona, il modulo di richiesta per gli artisti e una serata tipo: da riusare a ogni data.

+

6 pagine · livelli 1-2

+
+
+
Prima pagina della scheda d'esempio di una location con rassegna: scheda del luogo e numeri chiave
La scheda del luogo
+
Pianta in scala del palco fisso della location d'esempio
Il palco fisso in scala 1:50
+
+
+
+

Avete un evento di cui potete parlare? Scriveteci: con il vostro permesso scritto lo mostriamo qui al posto di un esempio.

+ +

Come funziona

+
    +
  1. Descrivete l'eventoData, luogo, tipo, persone, cosa c'è già, scadenze. Il modulo non chiede soldi. Rispondiamo entro un giorno lavorativo con due o tre domande e il preventivo scritto.
  2. +
  3. Prepariamo e controlliamoDocumenti e richieste ai fornitori con gli strumenti di stageplot.it e le regole che uso sui palchi veri. Prima di ogni consegna e di ogni mail a un fornitore passa da Simone.
  4. +
  5. Decidete voi, noi seguiamoVi presentiamo le offerte sulla stessa lista di voci, cronoprogramma e documenti con le scadenze; solleciti, contatti del giorno e, dopo l'evento, un recap con cosa cambiare la prossima volta.
  6. +
+ +

Per chi è

+ + +

Perché fidarsi

+
+
+

Gli stessi documenti dei concerti con orchestra. Sono Simone Castellan, sound engineer: ogni anno preparo stage plot, channel list, cronoprogrammi e richieste ai service per decine di concerti. Qui lo faccio per chi non ha un tecnico.

+

Indipendenti dai fornitori. Nessun palco da vendere, nessuna commissione: le offerte si confrontano su una lista di voci uguale per tutti.

+

Il controllo è umano. Una parte del lavoro è automatica, ma nessun documento e nessuna mail a un fornitore parte senza che li abbia guardati io. Se trovo un errore nostro, lo correggo: una correzione dei documenti è compresa.

+
+
+

Servizio in prova. Stiamo seguendo le prime pratiche e aggiustiamo prezzi e tempi con quello che impariamo: per questo i prezzi sono «da» e il preventivo scritto arriva prima di ogni impegno. Preferite solo il rider della band? C'è il rider tecnico completo a 59 €; o fate da soli con l'editor gratuito.

+ +

Descrivete il vostro evento

+

Bastano cinque minuti. Se avete una pianta del luogo, un programma o un vecchio rider, potete mandarli dopo, rispondendo alla nostra mail.

+
+
+ L'evento +
+
+
Anche solo indicativa.
+
+
+
+
+
+
+
Chi suona o parla, quanti sono, cosa vi preoccupa, cosa avete già deciso. Anche in poche righe.
+
Preventivi da chiedere, documenti richiesti da qualcuno, date da rispettare.
+
+
+ Il servizio +
+
+
Facoltativo.
+
+
+
+ A chi rispondiamo +
+
+
+
+
+
+
+
+ + + +
+ +

Nessun pagamento adesso. Preferite la mail? Scrivete a produzione@stageplot.it.

+
+ + +

Domande frequenti

+
+
Quanto costa?

Da 59 € per i soli documenti (una band fino a 12 musicisti), da 290 € per il coordinamento a distanza, da 790 € con Simone sul posto il giorno dell'evento. Sono prezzi di partenza, in prova: dopo la vostra richiesta ricevete un preventivo scritto con importo finale e IVA, e si parte solo dopo il vostro sì.

+
Cosa serve per cominciare?

Data, luogo, tipo di evento, numero di persone e cosa c'è già (palco, impianto, corrente). Se avete una pianta del luogo, un programma o un vecchio rider, meglio; altrimenti le domande ve le facciamo noi.

+
Fate anche i permessi?

Non firmiamo né presentiamo domande al posto vostro. Vi diciamo quali carte servono, chi le firma (un tecnico abilitato, il palchista, l'installatore, il Comune), con quali dati e per quando, e controlliamo che arrivino. Le regole cambiano da Comune a Comune: per ogni evento si confermano con l'ufficio competente, in genere lo sportello unico per le attività produttive.

+
Perché non vendete anche il service?

Perché così possiamo dirvi quale offerta conviene. Se volete, vi suggeriamo fornitori a cui chiedere; né loro né noi riceviamo commissioni, e lo scriviamo anche nella richiesta di preventivo.

+
Posso modificare i documenti dopo?

Sì. Lo stage plot si riapre su stageplot.it con «Importa», gratis e senza account, e lo cambiate da soli quando cambia il palco. Una correzione dei documenti consegnati è compresa.

+
Quanto tempo prima devo scrivervi?

Per un evento in piazza meglio otto settimane prima: di solito i service si interpellano da quattro a otto settimane prima e le carte chiedono tempo. Per una festa aziendale quattro-cinque settimane; per i soli documenti bastano tre giorni lavorativi dal materiale completo. Con meno tempo scriveteci lo stesso: vi diciamo cosa si riesce a fare.

+
Lavorate fuori dal Veneto?

I livelli 1 e 2 sì, a distanza. Il livello 3, con Simone sul posto, si valuta in base alla distanza e al giorno.

+
Cosa significa «servizio in prova»?

Che stiamo seguendo le prime pratiche e misurando tempi e lavoro per aggiustare prezzi e consegne. Per voi cambia una cosa: i prezzi sono di partenza e li confermiamo per iscritto prima di cominciare.

+
Chi c'è dietro?

Simone Castellan, sound engineer di Castelfranco Veneto: prepara ogni anno stage plot, channel list e richieste ai service per concerti anche con orchestra e ha creato stageplot.it. Ogni documento e ogni mail ai fornitori passa da lui prima di partire.

+
+ + + + +
+ + + + + diff --git a/produzione-eventi/richiesta.js b/produzione-eventi/richiesta.js new file mode 100644 index 0000000..bd99d8f --- /dev/null +++ b/produzione-eventi/richiesta.js @@ -0,0 +1,220 @@ +/* Produzione eventi — il modulo di richiesta (produzione-eventi/, 13/11/2026). + Servizio «Produzione eventi» di stageplot.it (livelli 1-3, prezzi «da», in prova): chi organizza un evento descrive + cosa gli serve, Simone Castellan risponde entro un giorno lavorativo con 2-3 domande e il preventivo. + + COME ARRIVA. Come per il rider (rider-tecnico-completo/richiesta.js): nessun server nuovo, la richiesta passa dalla + Edge Function del box segnalazioni (submit-feedback). Il messaggio (5-1000 caratteri, l'unica parte che la mail di avviso + mostra) inizia con «RICHIESTA PRODUZIONE EVENTO» e porta il RIASSUNTO; il tech_context (fino a 32 KB) la richiesta intera, + campo per campo. La mail di avviso va dove la manda la funzione (variabile NOTIFY_EMAIL): la casella produzione@stageplot.it + è indicata in pagina come alternativa, non è il destinatario di questo modulo. + + PAGAMENTO. Nessuno: il modulo non chiede soldi; il preventivo scritto lo manda Simone dopo la richiesta. + + Il file ha due metà: funzioni PURE esportate su globalThis.ProduzioneRichiesta e, in Node, su module.exports (le prova + test/produzione-richiesta.test.mjs); sotto, il collegamento al modulo vero, che parte solo nel browser. */ +(function (root) { + "use strict"; + + /* ================================================================== 1. FUNZIONI PURE */ + + var MSG_MIN = 5, MSG_MAX = 1000; /* gli stessi limiti del server (feedback-validation.ts) */ + var TECH_MAX = 32768; /* tech_context oltre questo il server lo butta: si resta molto sotto */ + var INTESTAZIONE = "RICHIESTA PRODUZIONE EVENTO"; + var SERVIZIO = "produzione-eventi"; + var URL_INVIO = "https://vsodplqkuvnsdiikvmjb.supabase.co/functions/v1/submit-feedback"; + + /* I campi del modulo: nome nel form · lunghezza massima · obbligatorio. Una lista sola per pulizia, controlli e test. */ + var CAMPI = [ + ["tipo", 40, true], ["data", 10, false], ["luogo", 100, true], ["persone", 40, false], + ["presente", 40, false], ["livello", 40, false], ["budget", 60, false], + ["descrizione", 1500, true], ["scadenze", 400, false], + ["ente", 100, false], ["nome", 80, true], ["email", 120, true], ["telefono", 30, false], + ["privacy", 3, true] + ]; + var TIPI = ["concerto-piazza", "rassegna", "festa-azienda", "convention", "evento-band", "serata-location", "altro"]; + var PERSONE = ["fino-100", "100-300", "300-1000", "oltre-1000", "non-so"]; + var PRESENTE = ["niente", "palco", "palco-impianto", "non-so"]; + var LIVELLI = ["documenti", "distanza", "in-loco", "non-so"]; + var CAMPI_A_CAPO = { descrizione: 1, scadenze: 1 }; + + /* Testo su una riga o a capo, senza caratteri di controllo, tagliato alla misura del campo */ + function pulito(v, max, aCapo) { + var s = String(v == null ? "" : v).replace(/\r\n?/g, "\n"); + // eslint-disable-next-line no-control-regex + s = s.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f\u200b\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, " "); + s = aCapo ? s.replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim() : s.replace(/\s+/g, " ").trim(); + return s.slice(0, max); + } + + function scelta(v, ammessi) { return ammessi.indexOf(v) < 0 ? "" : v; } + + /* Da un oggetto qualsiasi (FormData letta, test) ai soli campi noti, puliti */ + function datiPuliti(o) { + o = o || {}; + var d = {}; + for (var i = 0; i < CAMPI.length; i++) { + var k = CAMPI[i][0], max = CAMPI[i][1]; + d[k] = pulito(o[k], max, !!CAMPI_A_CAPO[k]); + } + d.tipo = scelta(d.tipo, TIPI); + d.persone = scelta(d.persone, PERSONE); + d.presente = scelta(d.presente, PRESENTE); + d.livello = scelta(d.livello, LIVELLI); + d.privacy = (o.privacy === true || o.privacy === "si" || o.privacy === "on") ? "si" : ""; + if (!/^\d{4}-\d{2}-\d{2}$/.test(d.data)) d.data = ""; + d.email = d.email.toLowerCase(); + return d; + } + + function emailValida(e) { return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(String(e || "")); } + + /* {campo: "frase"} per ciò che manca o non va; {} = si può inviare */ + function errori(d) { + var out = {}; + if (!d.tipo) out.tipo = "Scegli che tipo di evento è."; + if (!d.luogo) out.luogo = "Scrivi dove si svolge (anche solo il comune)."; + if (d.descrizione.length < 10) out.descrizione = "Raccontaci in breve cosa deve succedere."; + if (!d.nome) out.nome = "Scrivi il tuo nome."; + if (!emailValida(d.email)) out.email = "Serve un indirizzo email valido: rispondiamo lì."; + if (d.privacy !== "si") out.privacy = "Serve il consenso per poterti rispondere."; + return out; + } + + var ETICHETTE = { + tipo: { + "concerto-piazza": "concerto in piazza", "rassegna": "rassegna o più serate", "festa-azienda": "festa aziendale", + "convention": "convention o cena di gala", "evento-band": "evento di una band", "serata-location": "serata in una location", + "altro": "altro" + }, + persone: { "fino-100": "fino a 100", "100-300": "100-300", "300-1000": "300-1000", "oltre-1000": "oltre 1000", "non-so": "da definire" }, + presente: { "niente": "non c'è niente", "palco": "c'è il palco", "palco-impianto": "palco e impianto", "non-so": "da verificare" }, + livello: { "documenti": "documenti (1)", "distanza": "coordinamento a distanza (2)", "in-loco": "produzione in loco (3)", "non-so": "da capire" } + }; + function data(d) { return d ? d.slice(8, 10) + "/" + d.slice(5, 7) + "/" + d.slice(0, 4) : ""; } + + /* Il riassunto per la mail (≤ 1000 caratteri). Prima i contatti e il tipo di evento: se qualcosa va tagliato, + si taglia la descrizione, mai a chi rispondere. */ + function messaggio(d) { + var testa = [ + INTESTAZIONE, + "Evento: " + ETICHETTE.tipo[d.tipo] + " · " + d.luogo + (d.data ? " · " + data(d.data) : ""), + "Contatto: " + d.nome + (d.ente ? " (" + d.ente + ")" : "") + " · " + d.email + (d.telefono ? " · " + d.telefono : ""), + [d.persone ? "persone: " + ETICHETTE.persone[d.persone] : "", d.presente ? "sul posto: " + ETICHETTE.presente[d.presente] : "", + d.livello ? "interessa: " + ETICHETTE.livello[d.livello] : "", d.budget ? "budget: " + d.budget : ""].filter(Boolean).join(" · ") + ].filter(function (r) { return r.trim(); }).join("\n"); + var righe = d.descrizione.split("\n").map(function (r) { return r.trim(); }).filter(Boolean); + var coda = "\nDescrizione: " + righe.join(" / ") + (d.scadenze ? "\nScadenze: " + d.scadenze.replace(/\s+/g, " ") : ""); + var tutto = testa + coda; + if (tutto.length <= MSG_MAX) return tutto; + var resto = MSG_MAX - testa.length - 30; + var codaCorta = resto > 0 ? coda.slice(0, resto).replace(/\s+\S*$/, "") : ""; + return (testa.slice(0, MSG_MAX - 30) + codaCorta + "\n[continua nella richiesta]").slice(0, MSG_MAX); + } + + /* Il corpo per submit-feedback (campi di feedback-validation.ts). Niente progetto, niente schermata, niente account: + l'email di chi scrive viaggia nel messaggio e nel tech_context, perché la risposta va a lei. */ + function corpo(d, ctx) { + ctx = ctx || {}; + return { + message: messaggio(d), + hint: null, + honeypot: String(ctx.honeypot || ""), + tech_context: { servizio: SERVIZIO, prova: true, richiesta: d }, + meta: { + app_version: "servizio/" + SERVIZIO, + page_url: "https://stageplot.it/" + SERVIZIO + "/", + user_agent: String(ctx.userAgent || "").slice(0, 500), + viewport: String(ctx.viewport || "").slice(0, 40), + language: String(ctx.lingua || "").slice(0, 20) + }, + screenshot: null + }; + } + + /* La risposta del server, in parole */ + function esitoInvio(status, j) { + if (status === 200 && j && j.ok) return { ok: true }; + if (status === 429) return { ok: false, testo: "Sono arrivate troppe richieste da qui: riprova fra un'ora, o scrivi a produzione@stageplot.it." }; + if (status === 400) return { ok: false, testo: "Qualcosa nel modulo non va: controlla i campi e riprova." }; + return { ok: false, testo: "La richiesta non è partita: controlla la connessione e riprova, o scrivi a produzione@stageplot.it." }; + } + + var PR = { + CAMPI: CAMPI, MSG_MIN: MSG_MIN, MSG_MAX: MSG_MAX, TECH_MAX: TECH_MAX, INTESTAZIONE: INTESTAZIONE, URL_INVIO: URL_INVIO, + TIPI: TIPI, PERSONE: PERSONE, PRESENTE: PRESENTE, LIVELLI: LIVELLI, + pulito: pulito, datiPuliti: datiPuliti, emailValida: emailValida, errori: errori, messaggio: messaggio, corpo: corpo, + esitoInvio: esitoInvio + }; + root.ProduzioneRichiesta = PR; + if (typeof module === "object" && module && module.exports) module.exports = PR; + + /* ================================================================== 2. IL MODULO VERO (solo browser) */ + + if (typeof document === "undefined") return; + + function avvia() { + var form = document.getElementById("richiesta-form"); + if (!form) return; + var esito = document.getElementById("richiesta-esito"); + var bottone = form.querySelector("button[type=submit]"); + + function leggi() { + var o = {}; + for (var i = 0; i < CAMPI.length; i++) { + var k = CAMPI[i][0], el = form.elements[k]; + if (!el) continue; + o[k] = el.type === "checkbox" ? (el.checked ? "si" : "") : el.value; + } + return o; + } + function mostraErrori(err) { + var primo = null; + for (var i = 0; i < CAMPI.length; i++) { + var k = CAMPI[i][0], el = form.elements[k], box = document.getElementById("err-" + k); + if (!el) continue; + if (err[k]) { el.setAttribute("aria-invalid", "true"); if (!primo) primo = el; } + else el.removeAttribute("aria-invalid"); + if (box) box.textContent = err[k] || ""; + } + if (primo) primo.focus(); + } + function scrivi(testo, tipo) { + esito.textContent = testo; + esito.className = "esito " + (tipo || ""); + esito.hidden = !testo; + } + + form.addEventListener("submit", function (ev) { + ev.preventDefault(); + var d = datiPuliti(leggi()); + var err = errori(d); + mostraErrori(err); + if (Object.keys(err).length) { scrivi("Mancano alcune cose: le trovi segnate in rosso.", "ko"); return; } + var body = corpo(d, { + honeypot: form.elements.sito ? form.elements.sito.value : "", + userAgent: navigator.userAgent, viewport: innerWidth + "x" + innerHeight, lingua: navigator.language + }); + bottone.disabled = true; + scrivi("Invio in corso…", ""); + var ctrl = typeof AbortController === "function" ? new AbortController() : null; + var timer = ctrl ? setTimeout(function () { ctrl.abort(); }, 20000) : null; /* una richiesta che non torna mai: dopo 20 s si dice */ + fetch(URL_INVIO, { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), signal: ctrl ? ctrl.signal : undefined + }).then(function (r) { + return r.json().catch(function () { return null; }).then(function (j) { return esitoInvio(r.status, j); }); + }, function () { return esitoInvio(0, null); }).then(function (e) { + if (timer) clearTimeout(timer); + bottone.disabled = false; + if (e.ok) { + form.hidden = true; + scrivi("Richiesta arrivata, grazie. Ti rispondiamo entro un giorno lavorativo all'indirizzo " + + d.email + " con due o tre domande e il preventivo: controlla anche lo spam. Se hai una pianta del luogo, un programma o un vecchio rider, allegali rispondendo a quella mail.", "ok"); + esito.focus(); + } else scrivi(e.testo, "ko"); + }); + }); + } + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", avvia); + else avvia(); +})(typeof globalThis !== "undefined" ? globalThis : this); diff --git a/rider-tecnico-completo/index.html b/rider-tecnico-completo/index.html index 118455f..d46c3d7 100644 --- a/rider-tecnico-completo/index.html +++ b/rider-tecnico-completo/index.html @@ -155,6 +155,7 @@

Chi lo controlla

Per chi è: band e gruppi fino a 12 musicisti, una formazione, un palco tipo. Orchestre, cori ed eventi con più palchi o più scene si fanno su preventivo. Non comprende noleggi, contatti col service al posto vostro e sopralluoghi.

+

Per un evento intero: se vi serve anche chi confronta i preventivi dei service, prepara il cronoprogramma e vi dice quali permessi servono e chi li firma, c'è Produzione eventi, in prova.

Preferite farlo da soli? L'editor di stageplot.it è gratis e lo resta. Per capire cosa ci va dentro c'è la guida al rider tecnico e quella alla scheda tecnica della band.

Richiedi il rider

diff --git a/sitemap.xml b/sitemap.xml index 247b0d2..1caae51 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -12,6 +12,12 @@ monthly 0.8 + + https://stageplot.it/produzione-eventi/ + 2026-11-13 + monthly + 0.7 + https://stageplot.it/consulenza/ 2026-11-11 diff --git a/supabase/functions/_shared/produzione-richiesta.test.ts b/supabase/functions/_shared/produzione-richiesta.test.ts new file mode 100644 index 0000000..6cd8625 --- /dev/null +++ b/supabase/functions/_shared/produzione-richiesta.test.ts @@ -0,0 +1,42 @@ +// Contratto fra il modulo di «Produzione eventi» (produzione-eventi/richiesta.js, 13/11/2026) e il server. +// Il modulo non ha una funzione sua: passa da submit-feedback (come il rider). Se un giorno la validazione cambia +// (lunghezze, tetti, campi), questo test lo dice prima che le richieste di Comuni e aziende vengano rifiutate in silenzio. +import { assert, assertEquals } from "jsr:@std/assert@1"; +import { validateFeedback } from "./feedback-validation.ts"; +import "../../../produzione-eventi/richiesta.js"; + +// deno-lint-ignore no-explicit-any +const PR = (globalThis as any).ProduzioneRichiesta; + +const BASE = { + tipo: "festa-azienda", data: "2027-12-10", luogo: "Borgo Salice (inventato)", persone: "100-300", presente: "palco", + livello: "distanza", budget: "3.000 €", descrizione: "Cena con saluti e una band di sette elementi.", + scadenze: "Preventivi entro novembre", ente: "Azienda di prova", nome: "Mario Prova", email: "prova@esempio.it", + telefono: "000 000 0000", privacy: "on", +}; + +Deno.test("la richiesta normale passa la validazione del server com'è", () => { + const b = PR.corpo(PR.datiPuliti(BASE), { userAgent: "UA", viewport: "390x844", lingua: "it-IT" }); + const v = validateFeedback(b); + assert(v.ok, JSON.stringify(v)); + if (!v.ok) return; + assertEquals(v.value.message, b.message); + assertEquals(v.value.tech_context, b.tech_context, "il contesto arriva intero: è lì la richiesta completa"); + assertEquals(v.value.hint, null); +}); + +Deno.test("anche la richiesta più lunga possibile passa intera", () => { + // deno-lint-ignore no-explicit-any + const pieno = Object.fromEntries(PR.CAMPI.map(([k, max]: any) => [k, "é".repeat(max)])); + const b = PR.corpo(PR.datiPuliti({ ...pieno, tipo: "altro", email: "prova@esempio.it", privacy: "on" })); + const v = validateFeedback(b); + assert(v.ok, JSON.stringify(v)); + if (!v.ok) return; + assertEquals(v.value.tech_context, b.tech_context); +}); + +Deno.test("il campo trappola compilato è spam per il server", () => { + const b = PR.corpo(PR.datiPuliti(BASE), { honeypot: "http://spam" }); + const v = validateFeedback(b); + assert(!v.ok && v.error === "spam"); +}); diff --git a/test/produzione-richiesta.test.mjs b/test/produzione-richiesta.test.mjs new file mode 100644 index 0000000..81794ad --- /dev/null +++ b/test/produzione-richiesta.test.mjs @@ -0,0 +1,148 @@ +/* Produzione eventi — il modulo di richiesta e la pagina (produzione-eventi/, 13/11/2026). + Parti pure di richiesta.js (pulizia dei campi, controlli, riassunto per la mail ≤ 1000 caratteri con i contatti sempre + dentro, corpo per submit-feedback) e la pagina che le usa: un campo per ogni voce di CAMPI, la CSP che lascia uscire + solo la richiesta, schema e FAQ coerenti con quanto si vede, prezzi «da» uguali in pagina e nei dati strutturati, nessun + PDF scaricabile (decisione PE-05), nessun testimonial. Il contratto col server sta in + supabase/functions/_shared/produzione-richiesta.test.ts. + node --test test/produzione-richiesta.test.mjs */ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { createRequire } from "node:module"; +import { readFileSync, existsSync, readdirSync, statSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const req = createRequire(import.meta.url); +const PR = req(join(root, "produzione-eventi/richiesta.js")); +const leggi = (p) => readFileSync(join(root, p), "utf8"); + +/* dati finti: il repo è pubblico */ +const BASE = { + tipo: "festa-azienda", data: "2027-12-10", luogo: "Borgo Salice (inventato)", persone: "100-300", presente: "palco", + livello: "distanza", budget: "3.000 €", descrizione: "Cena con saluti e una band di sette elementi.\nIl referente non è un tecnico.", + scadenze: "Preventivi entro novembre", ente: "Azienda di prova", nome: "Mario Prova", email: "Prova@Esempio.IT", telefono: "000 000 0000", privacy: "on" +}; + +test("un modulo compilato bene non ha errori", () => { + const d = PR.datiPuliti(BASE); + assert.deepEqual(PR.errori(d), {}); + assert.equal(d.email, "prova@esempio.it"); + assert.equal(d.privacy, "si"); +}); + +test("mancano tipo, luogo, descrizione, nome, email o consenso: si dice campo per campo", () => { + const d = PR.datiPuliti({ ...BASE, tipo: "boh", luogo: " ", descrizione: "poco", nome: "", email: "non-una-mail", privacy: "" }); + assert.deepEqual(Object.keys(PR.errori(d)).sort(), ["descrizione", "email", "luogo", "nome", "privacy", "tipo"]); +}); + +test("solo i campi noti, scelte solo fra quelle previste, data solo AAAA-MM-GG", () => { + const d = PR.datiPuliti({ ...BASE, persone: "tantissime