Skip to content

Commit c29be46

Browse files
feat: add qualified lead email flow (#45)
* feat(contact): qualify leads with automatic email flow * fix(contact): clarify quote deliverables and pricing
1 parent cfaa690 commit c29be46

13 files changed

Lines changed: 401 additions & 77 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ server/public
55
vite.config.ts.*
66
*.tar.gz
77
.env
8+
.dev.vars
89
*.pem

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Questo progetto è sotto licenza MIT. Consulta il file `LICENSE` per maggiori in
7575

7676
## 📞 Contatti
7777

78-
PrimeBuild PC - [primebuild.official@gmail.com](mailto:primebuild.official@gmail.com)
78+
PrimeBuild PC - [preventivi@primebuild.website](mailto:preventivi@primebuild.website)
7979

8080
Project Link: [https://github.com/PrimeBuild-pc/webapp](https://github.com/PrimeBuild-pc/webapp)
8181

client/public/llms.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
- [Chi siamo](https://primebuild.website/chi-siamo/)
2323
- Sito ufficiale: https://primebuild.website/
2424
- Sede: Montegrotto Terme, 35036 (PD), Italia
25-
- Email: primebuild.official@gmail.com
25+
- Email: preventivi@primebuild.website
2626
- Instagram: https://www.instagram.com/prime_build_/
2727
- GitHub: https://github.com/PrimeBuild-pc
2828
- Discord: https://discord.gg/ERUwSxE79q

client/src/components/ContactSection.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,12 @@ const ContactSection = () => {
144144
trackFormSubmit('contact', true);
145145
setFormData(initialState);
146146
resetTurnstile();
147-
toast({ title: "Messaggio inviato", description: "Ti contatteremo al piu presto." });
147+
toast({
148+
title: "Richiesta ricevuta",
149+
description: json.confirmationSent === false
150+
? "La conferma automatica non è partita: non inviare di nuovo, abbiamo comunque ricevuto il messaggio."
151+
: "Controlla la tua email per il prossimo passo e il questionario preventivo.",
152+
});
148153
} catch (err) {
149154
console.error('Network/contact error', err);
150155
trackFormSubmit('contact', false);
@@ -157,8 +162,8 @@ const ContactSection = () => {
157162

158163
// Obfuscate email to prevent spam harvesters
159164
const getEmail = () => {
160-
const user = "primebuild.official";
161-
const domain = "gmail.com";
165+
const user = "preventivi";
166+
const domain = "primebuild.website";
162167
return `${user}@${domain}`;
163168
};
164169

@@ -198,7 +203,7 @@ const ContactSection = () => {
198203
<SectionHeader
199204
title="Parliamo del"
200205
highlight="tuo prossimo PC"
201-
subtitle="Raccontaci budget, giochi, software e obiettivi. Ti rispondiamo con una proposta chiara e personalizzata."
206+
subtitle="Inviaci un primo contatto. Riceverai via email il questionario per qualificare la richiesta e scegliere il livello di preventivo."
202207
alignment="left"
203208
/>
204209

@@ -289,7 +294,7 @@ const ContactSection = () => {
289294
disabled={isSubmitting}
290295
className="button-primary disabled:cursor-not-allowed disabled:opacity-50"
291296
>
292-
{isSubmitting ? "Invio in corso..." : "Invia Messaggio"}
297+
{isSubmitting ? "Invio in corso..." : "Invia richiesta"}
293298
</button>
294299
<p className="text-xs text-neutral-400">
295300
Inviando questo modulo, dichiari di aver letto la nostra{" "}

client/src/lib/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const buildSchema = (location: string) => {
1313
url: `${SITE_URL}/`,
1414
logo: { "@type": "ImageObject", url: assetUrl("/logo.png"), width: 302, height: 302 },
1515
image: assetUrl("/og-image.png"),
16-
email: "primebuild.official@gmail.com",
16+
email: "preventivi@primebuild.website",
1717
description: "Assemblaggio PC gaming su misura, assistenza tecnica e ottimizzazione a Montegrotto Terme, Padova e in tutta Italia.",
1818
address: {
1919
"@type": "PostalAddress",

client/src/pages/About.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const About = () => {
4747
<h2 className="text-3xl font-bold">Identità e contatti ufficiali</h2>
4848
<ul className="mt-5 space-y-3 text-neutral-300">
4949
<li>Sede operativa: Montegrotto Terme, 35036 (PD), Italia</li>
50-
<li>Email: <a className="text-primary underline" href="mailto:primebuild.official@gmail.com">primebuild.official@gmail.com</a></li>
50+
<li>Email: <a className="text-primary underline" href="mailto:preventivi@primebuild.website">preventivi@primebuild.website</a></li>
5151
<li><a className="text-primary underline" href="https://www.instagram.com/prime_build_/" target="_blank" rel="noopener noreferrer">Instagram</a></li>
5252
<li><a className="text-primary underline" href="https://github.com/PrimeBuild-pc" target="_blank" rel="noopener noreferrer">GitHub</a></li>
5353
<li><a className="text-primary underline" href="https://discord.gg/ERUwSxE79q" target="_blank" rel="noopener noreferrer">Community Discord</a></li>

client/src/pages/Privacy.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const Privacy = () => {
2929
<div className="prose prose-invert prose-neutral max-w-none space-y-8">
3030
<section>
3131
<p className="text-neutral-300 text-sm">
32-
Ultimo aggiornamento: 7 Dicembre 2025
32+
Ultimo aggiornamento: 24 luglio 2026
3333
</p>
3434
</section>
3535

@@ -39,7 +39,7 @@ const Privacy = () => {
3939
Il titolare del trattamento dei dati personali e Prime Build, con sede a Padova, Italia.
4040
</p>
4141
<p className="text-neutral-300">
42-
Email di contatto: <a href="mailto:primebuild.official@gmail.com" className="text-primary hover:underline">primebuild.official@gmail.com</a>
42+
Email di contatto: <a href="mailto:preventivi@primebuild.website" className="text-primary hover:underline">preventivi@primebuild.website</a>
4343
</p>
4444
</section>
4545

@@ -53,6 +53,7 @@ const Privacy = () => {
5353
<li>Nome</li>
5454
<li>Indirizzo email</li>
5555
<li>Oggetto e contenuto del messaggio</li>
56+
<li>Informazioni tecniche e di budget fornite nel questionario preventivo</li>
5657
</ul>
5758

5859
<h3 className="text-xl font-medium text-white mb-2 mt-6">2.2 Dati raccolti automaticamente (con consenso)</h3>
@@ -75,7 +76,8 @@ const Privacy = () => {
7576
<h2 className="text-2xl font-semibold text-white mb-4">3. Finalita del Trattamento</h2>
7677
<p className="text-neutral-300 mb-4">I tuoi dati vengono utilizzati per:</p>
7778
<ul className="list-disc list-inside text-neutral-300 space-y-1 ml-4">
78-
<li><strong>Rispondere alle richieste</strong> inviate tramite il modulo di contatto</li>
79+
<li><strong>Rispondere e inviare conferme operative</strong> per le richieste trasmesse tramite il modulo</li>
80+
<li><strong>Qualificare le richieste di preventivo</strong> e preparare la stima scelta dall'utente</li>
7981
<li><strong>Migliorare il sito web</strong> analizzando statistiche anonime di utilizzo</li>
8082
<li><strong>Garantire la sicurezza</strong> del sito prevenendo abusi e spam</li>
8183
</ul>
@@ -112,15 +114,16 @@ const Privacy = () => {
112114
I tuoi dati non vengono venduti a terzi. Potrebbero essere condivisi solo con:
113115
</p>
114116
<ul className="list-disc list-inside text-neutral-300 space-y-1 ml-4 mt-4">
115-
<li><strong>Google LLC</strong> - Per l'elaborazione delle statistiche Analytics (soggetto alle loro policy privacy)</li>
116-
<li><strong>Cloudflare Inc.</strong> - Per l'hosting e la protezione del sito</li>
117+
<li><strong>Google LLC</strong> - Per Google Analytics e, se compilato, per il questionario Google Forms</li>
118+
<li><strong>Cloudflare Inc.</strong> - Per hosting, protezione anti-spam e gestione tecnica delle richieste</li>
119+
<li><strong>Resend</strong> - Per l'invio delle email operative e delle conferme automatiche</li>
117120
</ul>
118121
</section>
119122

120123
<section>
121124
<h2 className="text-2xl font-semibold text-white mb-4">6. Conservazione dei Dati</h2>
122125
<ul className="list-disc list-inside text-neutral-300 space-y-1 ml-4">
123-
<li>I messaggi inviati tramite il form di contatto vengono conservati per il tempo necessario a evadere la richiesta</li>
126+
<li>I messaggi e le risposte al questionario vengono conservati per il tempo necessario a valutare ed evadere la richiesta</li>
124127
<li>I dati di Google Analytics vengono conservati per 14 mesi, poi automaticamente eliminati</li>
125128
</ul>
126129
</section>
@@ -138,8 +141,8 @@ const Privacy = () => {
138141
</ul>
139142
<p className="text-neutral-300 mt-4">
140143
Per esercitare questi diritti, contattaci a{" "}
141-
<a href="mailto:primebuild.official@gmail.com" className="text-primary hover:underline">
142-
primebuild.official@gmail.com
144+
<a href="mailto:preventivi@primebuild.website" className="text-primary hover:underline">
145+
preventivi@primebuild.website
143146
</a>
144147
</p>
145148
</section>
@@ -169,7 +172,7 @@ const Privacy = () => {
169172
puoi contattarci a:
170173
</p>
171174
<ul className="list-none text-neutral-300 space-y-1 mt-4">
172-
<li>Email: <a href="mailto:primebuild.official@gmail.com" className="text-primary hover:underline">primebuild.official@gmail.com</a></li>
175+
<li>Email: <a href="mailto:preventivi@primebuild.website" className="text-primary hover:underline">preventivi@primebuild.website</a></li>
173176
<li>Instagram: <a href="https://www.instagram.com/prime_build_/" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">@prime_build_</a></li>
174177
<li>Discord: <a href="https://discord.gg/ERUwSxE79q" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">Prime Build Community</a></li>
175178
</ul>

docs/email-flow-setup.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Flusso email professionale Prime Build
2+
3+
## Architettura scelta
4+
5+
```text
6+
Modulo sito + Turnstile
7+
|
8+
v
9+
Cloudflare Pages Function /contact
10+
|-- Resend --> [PRIMO CONTATTO] alla casella interna
11+
`-- Resend --> conferma al cliente + link Google Form
12+
|
13+
v
14+
Google Form di qualificazione
15+
|
16+
v
17+
notifica/risposta qualificata
18+
```
19+
20+
- **Indirizzo pubblico e Reply-To:** `preventivi@primebuild.website`.
21+
- **Destinazione reale:** la casella Gmail esistente, nascosta dietro Cloudflare Email Routing.
22+
- **Mittente automatico:** `Prime Build Preventivi <preventivi@primebuild.website>`.
23+
- Non usare `no-reply@`: impedisce una risposta naturale e comunica distanza proprio quando il cliente deve fidarsi.
24+
- `info@` è generico e riceve più spam; `supporto@` va riservato in futuro ai clienti con un servizio già attivo.
25+
26+
Il primo messaggio resta disponibile, ma arriva con oggetto `[PRIMO CONTATTO]`: in Gmail può essere archiviato automaticamente sotto l'etichetta `Da qualificare`. La posta prioritaria sarà quindi la notifica di una risposta completa al Google Form.
27+
28+
## Perché il Google Form va nella prima risposta automatica
29+
30+
Il link inviato per email aggiunge un piccolo impegno e verifica che l'indirizzo sia raggiungibile. Chi compila il secondo passaggio ha già letto differenze e prezzo dei due preventivi. Non mostrare subito il link sul sito: annullerebbe questo filtro.
31+
32+
Il form dovrebbe richiedere almeno:
33+
34+
1. email usata nel primo contatto;
35+
2. servizio scelto: Base gratuito oppure Completo €25;
36+
3. budget massimo e indicazione se include monitor/periferiche;
37+
4. giochi, software e utilizzo principale;
38+
5. risoluzione e refresh rate desiderati;
39+
6. componenti già posseduti;
40+
7. preferenze o vincoli (dimensioni, rumore, Wi-Fi, estetica);
41+
8. tempistiche;
42+
9. acquisto/assemblaggio autonomo oppure servizio Prime Build;
43+
10. conferma di aver compreso cosa include il livello scelto.
44+
45+
Evita upload di file e domande non necessarie. Non richiedere l'accesso Google: l'apertura del link dalla casella email è già un filtro sufficiente e il login farebbe perdere lead validi.
46+
47+
## Setup manuale Cloudflare Email Routing
48+
49+
Email Routing risulta già attivo sul dominio: sono presenti gli MX Cloudflare e l'SPF `include:_spf.mx.cloudflare.net`. Non eliminarli.
50+
51+
1. Apri **Cloudflare → Email → Email Routing → Destination addresses**.
52+
2. Verifica la casella Gmail di destinazione, se non è già verificata.
53+
3. In **Routing rules → Custom addresses**, crea:
54+
- `preventivi@primebuild.website` → Gmail esistente;
55+
- facoltativo: `dmarc@primebuild.website` → Gmail esistente, per ricevere i report DMARC.
56+
4. Invia un messaggio esterno a `preventivi@primebuild.website` e verifica ricezione e risposta.
57+
5. Non attivare un catch-all: aumenta lo spam senza migliorare il servizio.
58+
59+
### Regola legacy `no-reply@`
60+
61+
`no-reply@primebuild.website` (con trattino) non compare nel codice né negli altri repository Prime Build. La versione precedente del sito usava soltanto `noreply@primebuild.website` (senza trattino) come mittente in uscita, e questa PR la sostituisce con `preventivi@primebuild.website`. Una regola Email Routing non è necessaria per spedire: serve solo a ricevere risposte. Si può quindi disattivare la regola `no-reply@`, attendere 7–14 giorni e poi eliminarla se non emergono messaggi inattesi.
62+
63+
Non eliminare invece i record DNS MX/TXT di `noreply.primebuild.website`: sono il Return-Path tecnico verificato di Resend e non rappresentano una casella o una regola di inoltro.
64+
65+
## Setup manuale Resend e DNS
66+
67+
Sono già presenti il DKIM `resend._domainkey.primebuild.website` e il Return-Path tecnico `noreply.primebuild.website` con SPF/MX Amazon SES. Devono restare invariati e il dominio deve risultare **Verified** nella dashboard Resend. La creazione dell'indirizzo inoltrato `dmarc@primebuild.website` non crea il record DNS DMARC: sono due configurazioni distinte.
68+
69+
1. Apri **Resend → Domains** e aggiungi o seleziona `primebuild.website`.
70+
2. Copia in **Cloudflare → DNS** esattamente i record DKIM, SPF e MX mostrati da Resend.
71+
3. Non sostituire gli MX Cloudflare all'apice e non creare un secondo SPF all'apice. In questa configurazione Resend usa `noreply.primebuild.website` come Return-Path, separato dall'indirizzo `no-reply@primebuild.website` e dall'SPF di Email Routing.
72+
4. Lascia i record email in modalità **DNS only** quando applicabile.
73+
5. Premi **Verify DNS Records** in Resend e attendi lo stato `Verified`.
74+
6. Aggiungi il record iniziale:
75+
76+
```text
77+
Type: TXT
78+
Name: _dmarc
79+
Value: v=DMARC1; p=none; rua=mailto:dmarc@primebuild.website; adkim=r; aspf=r; pct=100
80+
```
81+
82+
Dopo almeno 2–4 settimane di report senza mittenti legittimi non allineati, valuta `p=quarantine`. Non partire direttamente con `reject`.
83+
7. In **Resend → API Keys**, crea una chiave con sola autorizzazione di invio. Copiala una volta e non inserirla nel repository.
84+
8. Lascia disattivato il tracking di aperture/click salvo reale necessità: riduce trattamento dati e complessità privacy.
85+
86+
## Variabili Cloudflare Pages
87+
88+
Apri **Cloudflare → Workers & Pages → website → Settings → Variables and Secrets** e configura Production (e Preview solo per i test):
89+
90+
| Nome | Tipo | Valore |
91+
|---|---|---|
92+
| `RESEND_API_KEY` | Secret | `re_...` |
93+
| `QUALIFICATION_FORM_URL` | Variable | URL HTTPS completo del Google Form |
94+
| `TURNSTILE_SECRET_KEY` | Secret | chiave Turnstile già in uso |
95+
| `MAIL_TO` | Variable/Secret | casella Gmail interna |
96+
| `MAIL_FROM` | Variable | `preventivi@primebuild.website` |
97+
| `MAIL_REPLY_TO` | Variable | `preventivi@primebuild.website` |
98+
| `EMAIL_STRICT` | Variable | `1` |
99+
| `ALLOWED_ORIGINS` | Variable | `https://primebuild.website` |
100+
101+
La chiave Resend deve esistere soltanto lato Cloudflare. Non usare mai un nome `VITE_RESEND_API_KEY`: le variabili `VITE_*` finiscono nel JavaScript pubblico.
102+
103+
Alternativa CLI per i secret:
104+
105+
```bash
106+
npx wrangler pages secret put RESEND_API_KEY --project-name website
107+
npx wrangler pages secret put TURNSTILE_SECRET_KEY --project-name website
108+
```
109+
110+
Per sviluppo locale crea `.dev.vars` (ignorato da Git):
111+
112+
```dotenv
113+
RESEND_API_KEY=re_xxx
114+
TURNSTILE_SECRET_KEY=xxx
115+
QUALIFICATION_FORM_URL=https://docs.google.com/forms/d/e/xxx/viewform
116+
MAIL_TO=casella-interna@example.com
117+
MAIL_FROM=preventivi@primebuild.website
118+
MAIL_REPLY_TO=preventivi@primebuild.website
119+
```
120+
121+
Dopo ogni variazione alle impostazioni, avvia un nuovo deploy Cloudflare.
122+
123+
## Configurazione Google Form e Gmail
124+
125+
1. Collega il Google Form a un Google Sheet.
126+
2. Attiva **Ricevi notifiche email per le nuove risposte** dal menu delle risposte/Sheet.
127+
3. Nel testo finale del Form usa:
128+
129+
> Grazie, la richiesta è completa. Il team Prime Build potrà ora valutarla. L'invio del questionario non comporta acquisti automatici; eventuali passaggi successivi saranno comunicati via email.
130+
131+
4. In Gmail crea un filtro per gli oggetti che contengono `[PRIMO CONTATTO]`: applica l'etichetta `Da qualificare` e, se desiderato, ignora la Posta in arrivo.
132+
5. Lascia in Posta in arrivo le notifiche del Google Form: saranno i lead che hanno completato il percorso.
133+
134+
## Copy della conferma automatica
135+
136+
**Oggetto:** `Abbiamo ricevuto la tua richiesta | Prime Build`
137+
138+
> Ciao {{nome}},
139+
>
140+
> abbiamo ricevuto il tuo primo contatto.
141+
>
142+
> Per preparare una proposta adatta alle tue esigenze, completa il questionario: ci aiuterà a valutare budget, utilizzo, giochi o software, risoluzione, componenti già disponibili, preferenze e tempistiche.
143+
>
144+
> **Preventivo Base — gratuito**
145+
> Include l'analisi delle tue esigenze, una stima del costo complessivo e l'indicazione della fascia di configurazione più adatta. È sufficiente per capire che tipo di PC e quale livello di prestazioni potresti ricevere affidando a Prime Build la realizzazione e l'assemblaggio.
146+
>
147+
> **Preventivo Completo — €25**
148+
> Include la stessa analisi, con in più il modello esatto di ogni componente, i link di acquisto, i prezzi consigliati rilevati al momento della ricerca e una checklist completa di compatibilità. È pensato per chi vuole poter acquistare e assemblare la build in autonomia.
149+
>
150+
> Se dopo aver acquistato il Preventivo Completo decidi di affidare comunque a Prime Build l'assemblaggio, i €25 già pagati verranno sottratti dal costo della manodopera.
151+
>
152+
> I preventivi riflettono prezzi e disponibilità presenti nel momento in cui vengono preparati. Il mercato dei componenti può cambiare rapidamente e Prime Build non vende direttamente i singoli componenti. Se rileviamo un prezzo particolarmente alto, te lo comunichiamo con trasparenza e, quando ci sono indicazioni concrete di un possibile calo a breve, possiamo consigliarti di attendere.
153+
>
154+
> Se ci hai contattato per assistenza relativa a un servizio già in corso, puoi rispondere direttamente a questa email senza compilare il questionario.
155+
>
156+
> Team Prime Build
157+
158+
Il codice in `functions/contact.js` genera sia HTML sia testo semplice e inserisce il pulsante verso `QUALIFICATION_FORM_URL`.
159+
160+
## Test prima del merge
161+
162+
1. Configura routing, DNS, API key e URL del Form.
163+
2. Invia il modulo con una casella diversa da quella interna.
164+
3. Verifica:
165+
- Turnstile blocca invii privi di token;
166+
- arriva la notifica interna con Reply-To del cliente;
167+
- il cliente riceve HTML e testo semplice;
168+
- il pulsante apre il Form corretto;
169+
- rispondendo alla conferma si raggiunge `preventivi@primebuild.website`;
170+
- SPF, DKIM e DMARC risultano `PASS` nelle intestazioni del messaggio.
171+
4. In Cloudflare crea facoltativamente una regola di rate limiting su `POST /contact` (per esempio 5 richieste ogni 10 minuti per IP), senza sostituire Turnstile.

0 commit comments

Comments
 (0)