From 4672e9770a3c9de73fbf9115cecd0456fc4fcdb1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 19 May 2026 16:35:05 +0000 Subject: [PATCH 1/4] =?UTF-8?q?Ajoute=20auto-r=C3=A9ponse=20client=20avec?= =?UTF-8?q?=20lien=20PDF=20sur=20demandes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ERIC74SONG96 --- supabase/README-NOTIFICATIONS.md | 13 ++ .../032_autoreply_form_submission_pdf.sql | 202 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 supabase/migrations/032_autoreply_form_submission_pdf.sql diff --git a/supabase/README-NOTIFICATIONS.md b/supabase/README-NOTIFICATIONS.md index 201a66d..bdffe60 100644 --- a/supabase/README-NOTIFICATIONS.md +++ b/supabase/README-NOTIFICATIONS.md @@ -3,6 +3,9 @@ À chaque nouvelle soumission (formulaire ou profil annuaire), un email récapitulatif est envoyé automatiquement à l'admin StudyAlready. +Depuis la migration `032_autoreply_form_submission_pdf.sql`, le demandeur +reçoit aussi une auto-réponse email avec un lien vers le PDF StudyAlready. + ## Architecture (sans Edge Function, 100% SQL) ``` @@ -77,6 +80,16 @@ que vous n'avez pas vérifié de domaine). 4. ✉ Dans **30 secondes max**, un email arrive sur votre boîte avec le contenu de la demande et un bouton "Ouvrir le dashboard". +## Activer l'auto-réponse client + PDF + +1. Supabase Dashboard → **SQL Editor** → **New query** +2. Exécuter `supabase/migrations/032_autoreply_form_submission_pdf.sql` +3. Faire un test depuis le formulaire de contact avec un email réel +4. Vérifier que : + - vous recevez l'alerte admin habituelle ; + - le client reçoit un email de confirmation avec le bouton + **Télécharger le PDF StudyAlready**. + ## Si l'email n'arrive pas ### Vérifier l'historique Resend diff --git a/supabase/migrations/032_autoreply_form_submission_pdf.sql b/supabase/migrations/032_autoreply_form_submission_pdf.sql new file mode 100644 index 0000000..e402ea9 --- /dev/null +++ b/supabase/migrations/032_autoreply_form_submission_pdf.sql @@ -0,0 +1,202 @@ +-- ============================================================= +-- StudyAlready — Migration 032 : auto-reponse demande + lien PDF +-- ============================================================= +-- Objectif: +-- 1) Envoyer automatiquement un email de confirmation au demandeur +-- lorsqu'une ligne est creee dans public.form_submissions. +-- 2) Inclure un lien direct vers le dossier PDF StudyAlready. +-- +-- Prerequis: +-- - Migration 005 executee (private_settings + send_admin_email + trigger) +-- - Extension pg_net activee +-- +-- Idempotent: peut etre relance sans risque. +-- ============================================================= + +-- Envoi direct vers une adresse email (utile pour les demandes anon +-- qui n'ont pas de user_id dans auth.users). +CREATE OR REPLACE FUNCTION public.send_email_to_address( + target_email text, + subject text, + html text +) RETURNS bigint +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public, extensions +AS $$ +DECLARE + api_key text; + notify_from text; + req_id bigint; + clean_email text; +BEGIN + clean_email := lower(trim(coalesce(target_email, ''))); + IF clean_email = '' OR clean_email !~ '^[^[:space:]@]+@[^[:space:]@]+\.[^[:space:]@]+$' THEN + RETURN NULL; + END IF; + + SELECT value INTO api_key FROM private_settings WHERE key = 'RESEND_API_KEY'; + SELECT value INTO notify_from FROM private_settings WHERE key = 'NOTIFY_FROM'; + + IF api_key IS NULL OR api_key = '' OR api_key = 'REMPLACER_PAR_VOTRE_CLE_RESEND' THEN + RAISE NOTICE 'send_email_to_address: RESEND_API_KEY non configuree, email ignore'; + RETURN NULL; + END IF; + + IF notify_from IS NULL OR notify_from = '' THEN + notify_from := 'StudyAlready '; + END IF; + + SELECT net.http_post( + url := 'https://api.resend.com/emails', + headers := jsonb_build_object( + 'Authorization', 'Bearer ' || api_key, + 'Content-Type', 'application/json' + ), + body := jsonb_build_object( + 'from', notify_from, + 'to', jsonb_build_array(clean_email), + 'subject', subject, + 'html', html + ) + ) INTO req_id; + + RETURN req_id; +EXCEPTION WHEN undefined_table THEN + RAISE NOTICE 'send_email_to_address: table private_settings introuvable (migration 005 requise)'; + RETURN NULL; +END; +$$; + +REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM PUBLIC; +REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM anon, authenticated; + +-- Etend la notification existante: +-- - conserve l'email admin +-- - ajoute une auto-reponse demandeur avec lien PDF +CREATE OR REPLACE FUNCTION public.on_new_submission() +RETURNS trigger +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +DECLARE + label text; + contact text; + payload_html text := ''; + k text; + v text; + client_subject text; + client_html text; + client_name text; + service_label text; +BEGIN + label := CASE NEW.form_type + WHEN 'contact' THEN 'Contact' + WHEN 'prequalification' THEN 'Pré-qualification' + WHEN 'rejoindre-reseau' THEN 'Réseau' + WHEN 'mise-en-relation' THEN 'Mise en relation' + WHEN 'rapport-admission' THEN 'Rapport admission' + WHEN 'chasseur-billet' THEN 'Chasseur de billets' + WHEN 'departs-groupes' THEN 'Départs groupés' + ELSE NEW.form_type + END; + + contact := COALESCE(NEW.nom, '—'); + IF NEW.email IS NOT NULL THEN contact := contact || ' · ' || NEW.email; END IF; + IF NEW.whatsapp IS NOT NULL THEN contact := contact || ' · ' || NEW.whatsapp; END IF; + + IF NEW.payload IS NOT NULL AND jsonb_typeof(NEW.payload) = 'object' THEN + FOR k, v IN SELECT * FROM jsonb_each_text(NEW.payload) LOOP + payload_html := payload_html || + '' || + replace(replace(k, '<', '<'), '&', '&') || + '' || + replace(replace(COALESCE(v, ''), '<', '<'), '&', '&') || + ''; + END LOOP; + END IF; + + -- Email admin (comportement conserve) + PERFORM public.send_admin_email( + '[StudyAlready] Nouvelle demande : ' || label, + '
' || + '
' || + '

StudyAlready

' || + '

Nouvelle demande : ' || label || '

' || + '
' || + '
' || + '

' || contact || '

' || + '

Reçu le ' || to_char(NEW.created_at AT TIME ZONE 'Europe/Brussels', 'DD/MM/YYYY à HH24:MI') || '

' || + CASE WHEN payload_html <> '' THEN + '' || payload_html || '
' + ELSE '' END || + '

Ouvrir le dashboard →

' || + '
' || + '

Notification automatique StudyAlready · Configurable dans Supabase (table private_settings)

' || + '
' + ); + + -- Auto-reponse client + lien PDF + IF NEW.email IS NOT NULL AND length(trim(NEW.email)) > 3 THEN + client_name := replace(replace(COALESCE(NULLIF(trim(NEW.nom), ''), 'Bonjour'), '&', '&'), '<', '<'); + service_label := COALESCE(NULLIF(trim(NEW.payload ->> 'service'), ''), NULLIF(trim(NEW.subject), ''), label); + service_label := replace(replace(service_label, '&', '&'), '<', '<'); + + client_subject := CASE + WHEN NEW.form_type = 'contact' THEN 'StudyAlready — Nous avons bien reçu votre demande' + WHEN NEW.form_type = 'prequalification' THEN 'StudyAlready — Pré-qualification bien reçue' + ELSE 'StudyAlready — Votre demande a bien été reçue' + END; + + client_html := + '
' || + '
' || + '

StudyAlready

' || + '

Demande bien reçue ✅

' || + '
' || + '
' || + '

Bonjour ' || client_name || ',

' || + '

Merci, votre demande a bien été enregistrée par notre équipe.

' || + '

Service sélectionné : ' || service_label || '

' || + '
' || + '

Document demandé

' || + '

Vous pouvez télécharger notre dossier PDF ici :

' || + '

' || + 'Télécharger le PDF StudyAlready →' || + '

' || + '
' || + '

Besoin d''une réponse rapide ?

' || + '

' || + 'WhatsApp +32 465 33 94 48' || + '

' || + '

Email automatique StudyAlready — merci de ne pas répondre directement à cet email.

' || + '
' || + '
'; + + PERFORM public.send_email_to_address(NEW.email, client_subject, client_html); + END IF; + + RETURN NEW; +EXCEPTION WHEN OTHERS THEN + RAISE NOTICE 'on_new_submission failed: %', SQLERRM; + RETURN NEW; +END; +$$; + +-- Verification rapide (attendu: OK) +SELECT + 'send_email_to_address' AS element, + CASE WHEN EXISTS ( + SELECT 1 FROM pg_proc p + JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = 'public' AND p.proname = 'send_email_to_address' + ) THEN 'OK' ELSE 'KO' END AS statut +UNION ALL +SELECT + 'on_new_submission', + CASE WHEN EXISTS ( + SELECT 1 FROM pg_proc p + JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = 'public' AND p.proname = 'on_new_submission' + ) THEN 'OK' ELSE 'KO' END; From 00c55d3e179258df5522f1e23d6e0a1151c2f49f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 19 May 2026 16:39:59 +0000 Subject: [PATCH 2/4] =?UTF-8?q?Ajoute=20message=20manuel=20PDF=20et=20reti?= =?UTF-8?q?re=20auto-r=C3=A9ponse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ERIC74SONG96 --- index.html | 13 ++ supabase/README-NOTIFICATIONS.md | 13 -- .../032_autoreply_form_submission_pdf.sql | 202 ------------------ 3 files changed, 13 insertions(+), 215 deletions(-) delete mode 100644 supabase/migrations/032_autoreply_form_submission_pdf.sql diff --git a/index.html b/index.html index 415264c..80c7e8e 100644 --- a/index.html +++ b/index.html @@ -1012,6 +1012,19 @@

Parlons de votre é

En envoyant ce formulaire, vous acceptez d'être recontacté concernant votre projet. Données traitées conformément à notre politique de confidentialité.

+
+

Message à envoyer au client (copier / coller)

+

Utilisez ce texte après réception d'une demande pour que le client complète et renvoie le PDF.

+ +
diff --git a/supabase/README-NOTIFICATIONS.md b/supabase/README-NOTIFICATIONS.md index bdffe60..201a66d 100644 --- a/supabase/README-NOTIFICATIONS.md +++ b/supabase/README-NOTIFICATIONS.md @@ -3,9 +3,6 @@ À chaque nouvelle soumission (formulaire ou profil annuaire), un email récapitulatif est envoyé automatiquement à l'admin StudyAlready. -Depuis la migration `032_autoreply_form_submission_pdf.sql`, le demandeur -reçoit aussi une auto-réponse email avec un lien vers le PDF StudyAlready. - ## Architecture (sans Edge Function, 100% SQL) ``` @@ -80,16 +77,6 @@ que vous n'avez pas vérifié de domaine). 4. ✉ Dans **30 secondes max**, un email arrive sur votre boîte avec le contenu de la demande et un bouton "Ouvrir le dashboard". -## Activer l'auto-réponse client + PDF - -1. Supabase Dashboard → **SQL Editor** → **New query** -2. Exécuter `supabase/migrations/032_autoreply_form_submission_pdf.sql` -3. Faire un test depuis le formulaire de contact avec un email réel -4. Vérifier que : - - vous recevez l'alerte admin habituelle ; - - le client reçoit un email de confirmation avec le bouton - **Télécharger le PDF StudyAlready**. - ## Si l'email n'arrive pas ### Vérifier l'historique Resend diff --git a/supabase/migrations/032_autoreply_form_submission_pdf.sql b/supabase/migrations/032_autoreply_form_submission_pdf.sql deleted file mode 100644 index e402ea9..0000000 --- a/supabase/migrations/032_autoreply_form_submission_pdf.sql +++ /dev/null @@ -1,202 +0,0 @@ --- ============================================================= --- StudyAlready — Migration 032 : auto-reponse demande + lien PDF --- ============================================================= --- Objectif: --- 1) Envoyer automatiquement un email de confirmation au demandeur --- lorsqu'une ligne est creee dans public.form_submissions. --- 2) Inclure un lien direct vers le dossier PDF StudyAlready. --- --- Prerequis: --- - Migration 005 executee (private_settings + send_admin_email + trigger) --- - Extension pg_net activee --- --- Idempotent: peut etre relance sans risque. --- ============================================================= - --- Envoi direct vers une adresse email (utile pour les demandes anon --- qui n'ont pas de user_id dans auth.users). -CREATE OR REPLACE FUNCTION public.send_email_to_address( - target_email text, - subject text, - html text -) RETURNS bigint -LANGUAGE plpgsql -SECURITY DEFINER -SET search_path = public, extensions -AS $$ -DECLARE - api_key text; - notify_from text; - req_id bigint; - clean_email text; -BEGIN - clean_email := lower(trim(coalesce(target_email, ''))); - IF clean_email = '' OR clean_email !~ '^[^[:space:]@]+@[^[:space:]@]+\.[^[:space:]@]+$' THEN - RETURN NULL; - END IF; - - SELECT value INTO api_key FROM private_settings WHERE key = 'RESEND_API_KEY'; - SELECT value INTO notify_from FROM private_settings WHERE key = 'NOTIFY_FROM'; - - IF api_key IS NULL OR api_key = '' OR api_key = 'REMPLACER_PAR_VOTRE_CLE_RESEND' THEN - RAISE NOTICE 'send_email_to_address: RESEND_API_KEY non configuree, email ignore'; - RETURN NULL; - END IF; - - IF notify_from IS NULL OR notify_from = '' THEN - notify_from := 'StudyAlready '; - END IF; - - SELECT net.http_post( - url := 'https://api.resend.com/emails', - headers := jsonb_build_object( - 'Authorization', 'Bearer ' || api_key, - 'Content-Type', 'application/json' - ), - body := jsonb_build_object( - 'from', notify_from, - 'to', jsonb_build_array(clean_email), - 'subject', subject, - 'html', html - ) - ) INTO req_id; - - RETURN req_id; -EXCEPTION WHEN undefined_table THEN - RAISE NOTICE 'send_email_to_address: table private_settings introuvable (migration 005 requise)'; - RETURN NULL; -END; -$$; - -REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM PUBLIC; -REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM anon, authenticated; - --- Etend la notification existante: --- - conserve l'email admin --- - ajoute une auto-reponse demandeur avec lien PDF -CREATE OR REPLACE FUNCTION public.on_new_submission() -RETURNS trigger -LANGUAGE plpgsql -SECURITY DEFINER -SET search_path = public -AS $$ -DECLARE - label text; - contact text; - payload_html text := ''; - k text; - v text; - client_subject text; - client_html text; - client_name text; - service_label text; -BEGIN - label := CASE NEW.form_type - WHEN 'contact' THEN 'Contact' - WHEN 'prequalification' THEN 'Pré-qualification' - WHEN 'rejoindre-reseau' THEN 'Réseau' - WHEN 'mise-en-relation' THEN 'Mise en relation' - WHEN 'rapport-admission' THEN 'Rapport admission' - WHEN 'chasseur-billet' THEN 'Chasseur de billets' - WHEN 'departs-groupes' THEN 'Départs groupés' - ELSE NEW.form_type - END; - - contact := COALESCE(NEW.nom, '—'); - IF NEW.email IS NOT NULL THEN contact := contact || ' · ' || NEW.email; END IF; - IF NEW.whatsapp IS NOT NULL THEN contact := contact || ' · ' || NEW.whatsapp; END IF; - - IF NEW.payload IS NOT NULL AND jsonb_typeof(NEW.payload) = 'object' THEN - FOR k, v IN SELECT * FROM jsonb_each_text(NEW.payload) LOOP - payload_html := payload_html || - '' || - replace(replace(k, '<', '<'), '&', '&') || - '' || - replace(replace(COALESCE(v, ''), '<', '<'), '&', '&') || - ''; - END LOOP; - END IF; - - -- Email admin (comportement conserve) - PERFORM public.send_admin_email( - '[StudyAlready] Nouvelle demande : ' || label, - '
' || - '
' || - '

StudyAlready

' || - '

Nouvelle demande : ' || label || '

' || - '
' || - '
' || - '

' || contact || '

' || - '

Reçu le ' || to_char(NEW.created_at AT TIME ZONE 'Europe/Brussels', 'DD/MM/YYYY à HH24:MI') || '

' || - CASE WHEN payload_html <> '' THEN - '' || payload_html || '
' - ELSE '' END || - '

Ouvrir le dashboard →

' || - '
' || - '

Notification automatique StudyAlready · Configurable dans Supabase (table private_settings)

' || - '
' - ); - - -- Auto-reponse client + lien PDF - IF NEW.email IS NOT NULL AND length(trim(NEW.email)) > 3 THEN - client_name := replace(replace(COALESCE(NULLIF(trim(NEW.nom), ''), 'Bonjour'), '&', '&'), '<', '<'); - service_label := COALESCE(NULLIF(trim(NEW.payload ->> 'service'), ''), NULLIF(trim(NEW.subject), ''), label); - service_label := replace(replace(service_label, '&', '&'), '<', '<'); - - client_subject := CASE - WHEN NEW.form_type = 'contact' THEN 'StudyAlready — Nous avons bien reçu votre demande' - WHEN NEW.form_type = 'prequalification' THEN 'StudyAlready — Pré-qualification bien reçue' - ELSE 'StudyAlready — Votre demande a bien été reçue' - END; - - client_html := - '
' || - '
' || - '

StudyAlready

' || - '

Demande bien reçue ✅

' || - '
' || - '
' || - '

Bonjour ' || client_name || ',

' || - '

Merci, votre demande a bien été enregistrée par notre équipe.

' || - '

Service sélectionné : ' || service_label || '

' || - '
' || - '

Document demandé

' || - '

Vous pouvez télécharger notre dossier PDF ici :

' || - '

' || - 'Télécharger le PDF StudyAlready →' || - '

' || - '
' || - '

Besoin d''une réponse rapide ?

' || - '

' || - 'WhatsApp +32 465 33 94 48' || - '

' || - '

Email automatique StudyAlready — merci de ne pas répondre directement à cet email.

' || - '
' || - '
'; - - PERFORM public.send_email_to_address(NEW.email, client_subject, client_html); - END IF; - - RETURN NEW; -EXCEPTION WHEN OTHERS THEN - RAISE NOTICE 'on_new_submission failed: %', SQLERRM; - RETURN NEW; -END; -$$; - --- Verification rapide (attendu: OK) -SELECT - 'send_email_to_address' AS element, - CASE WHEN EXISTS ( - SELECT 1 FROM pg_proc p - JOIN pg_namespace n ON n.oid = p.pronamespace - WHERE n.nspname = 'public' AND p.proname = 'send_email_to_address' - ) THEN 'OK' ELSE 'KO' END AS statut -UNION ALL -SELECT - 'on_new_submission', - CASE WHEN EXISTS ( - SELECT 1 FROM pg_proc p - JOIN pg_namespace n ON n.oid = p.pronamespace - WHERE n.nspname = 'public' AND p.proname = 'on_new_submission' - ) THEN 'OK' ELSE 'KO' END; From 73fb408fcb89317b4c1effaa4ae32894447914db Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 19 May 2026 16:44:18 +0000 Subject: [PATCH 3/4] =?UTF-8?q?Ajoute=20email=20client=20automatique=20ave?= =?UTF-8?q?c=20suite=20des=20=C3=A9tapes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ERIC74SONG96 --- index.html | 1 + supabase/README-NOTIFICATIONS.md | 13 ++ ...2_autoreply_form_submission_next_steps.sql | 191 ++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 supabase/migrations/032_autoreply_form_submission_next_steps.sql diff --git a/index.html b/index.html index 80c7e8e..56fd56d 100644 --- a/index.html +++ b/index.html @@ -1011,6 +1011,7 @@

Parlons de votre é Envoyer ma demande +

Après envoi, vous recevez aussi un email de confirmation avec la suite des étapes.

En envoyant ce formulaire, vous acceptez d'être recontacté concernant votre projet. Données traitées conformément à notre politique de confidentialité.

Message à envoyer au client (copier / coller)

diff --git a/supabase/README-NOTIFICATIONS.md b/supabase/README-NOTIFICATIONS.md index 201a66d..3f599df 100644 --- a/supabase/README-NOTIFICATIONS.md +++ b/supabase/README-NOTIFICATIONS.md @@ -3,6 +3,10 @@ À chaque nouvelle soumission (formulaire ou profil annuaire), un email récapitulatif est envoyé automatiquement à l'admin StudyAlready. +Optionnel: avec la migration `032_autoreply_form_submission_next_steps.sql`, +le client reçoit aussi un email automatique qui explique la suite +(analyse du dossier + lien PDF a completer et renvoyer). + ## Architecture (sans Edge Function, 100% SQL) ``` @@ -79,6 +83,15 @@ que vous n'avez pas vérifié de domaine). ## Si l'email n'arrive pas +## Activer l'email client "suite des etapes" + +1. Supabase Dashboard → **SQL Editor** → **New query** +2. Exécuter `supabase/migrations/032_autoreply_form_submission_next_steps.sql` +3. Tester un formulaire avec une vraie adresse email +4. Vérifier : + - l'admin reçoit toujours l'alerte "Nouvelle demande" + - le client reçoit l'email "Demande bien recue, voici la suite" + ### Vérifier l'historique Resend https://resend.com → **Emails** → vous voyez la liste des envois, diff --git a/supabase/migrations/032_autoreply_form_submission_next_steps.sql b/supabase/migrations/032_autoreply_form_submission_next_steps.sql new file mode 100644 index 0000000..20e707a --- /dev/null +++ b/supabase/migrations/032_autoreply_form_submission_next_steps.sql @@ -0,0 +1,191 @@ +-- ============================================================= +-- StudyAlready - Migration 032 : email client automatique (suite) +-- ============================================================= +-- But: +-- - Conserver l'email admin "nouvelle demande" +-- - Envoyer aussi un email automatique au client avec la suite +-- apres une soumission dans public.form_submissions. +-- +-- Prerequis: +-- - migration 005 executee (private_settings + send_admin_email) +-- - extension pg_net activee +-- +-- Idempotent: peut etre relance sans risque. +-- ============================================================= + +-- Envoi direct a une adresse (utile pour les formulaires sans user_id) +CREATE OR REPLACE FUNCTION public.send_email_to_address( + target_email text, + subject text, + html text +) RETURNS bigint +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public, extensions +AS $$ +DECLARE + api_key text; + notify_from text; + req_id bigint; + clean_email text; +BEGIN + clean_email := lower(trim(coalesce(target_email, ''))); + IF clean_email = '' OR clean_email !~ '^[^[:space:]@]+@[^[:space:]@]+\.[^[:space:]@]+$' THEN + RETURN NULL; + END IF; + + SELECT value INTO api_key FROM private_settings WHERE key = 'RESEND_API_KEY'; + SELECT value INTO notify_from FROM private_settings WHERE key = 'NOTIFY_FROM'; + + IF api_key IS NULL OR api_key = '' OR api_key = 'REMPLACER_PAR_VOTRE_CLE_RESEND' THEN + RAISE NOTICE 'send_email_to_address: RESEND_API_KEY non configuree, envoi ignore'; + RETURN NULL; + END IF; + + IF notify_from IS NULL OR notify_from = '' THEN + notify_from := 'StudyAlready '; + END IF; + + SELECT net.http_post( + url := 'https://api.resend.com/emails', + headers := jsonb_build_object( + 'Authorization', 'Bearer ' || api_key, + 'Content-Type', 'application/json' + ), + body := jsonb_build_object( + 'from', notify_from, + 'to', jsonb_build_array(clean_email), + 'subject', subject, + 'html', html + ) + ) INTO req_id; + + RETURN req_id; +EXCEPTION WHEN undefined_table THEN + RAISE NOTICE 'send_email_to_address: private_settings introuvable (migration 005 requise)'; + RETURN NULL; +END; +$$; + +REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM PUBLIC; +REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM anon, authenticated; + +-- Etend la fonction existante de notification soumission +CREATE OR REPLACE FUNCTION public.on_new_submission() +RETURNS trigger +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +DECLARE + label text; + contact text; + payload_html text := ''; + k text; + v text; + client_subject text; + client_html text; + client_name text; +BEGIN + label := CASE NEW.form_type + WHEN 'contact' THEN 'Contact' + WHEN 'prequalification' THEN 'Pre-qualification' + WHEN 'rejoindre-reseau' THEN 'Reseau' + WHEN 'mise-en-relation' THEN 'Mise en relation' + WHEN 'rapport-admission' THEN 'Rapport admission' + WHEN 'chasseur-billet' THEN 'Chasseur de billets' + WHEN 'departs-groupes' THEN 'Departs groupes' + ELSE NEW.form_type + END; + + contact := COALESCE(NEW.nom, '-'); + IF NEW.email IS NOT NULL THEN contact := contact || ' · ' || NEW.email; END IF; + IF NEW.whatsapp IS NOT NULL THEN contact := contact || ' · ' || NEW.whatsapp; END IF; + + IF NEW.payload IS NOT NULL AND jsonb_typeof(NEW.payload) = 'object' THEN + FOR k, v IN SELECT * FROM jsonb_each_text(NEW.payload) LOOP + payload_html := payload_html || + '' || + replace(replace(k, '<', '<'), '&', '&') || + '' || + replace(replace(COALESCE(v, ''), '<', '<'), '&', '&') || + ''; + END LOOP; + END IF; + + -- Email admin (inchangé) + PERFORM public.send_admin_email( + '[StudyAlready] Nouvelle demande : ' || label, + '
' || + '
' || + '

StudyAlready

' || + '

Nouvelle demande : ' || label || '

' || + '
' || + '
' || + '

' || contact || '

' || + '

Recu le ' || to_char(NEW.created_at AT TIME ZONE 'Europe/Brussels', 'DD/MM/YYYY a HH24:MI') || '

' || + CASE WHEN payload_html <> '' THEN + '' || payload_html || '
' + ELSE '' END || + '

Ouvrir le dashboard ->

' || + '
' || + '

Notification automatique StudyAlready

' || + '
' + ); + + -- Email client (nouveau): explique la suite + IF NEW.email IS NOT NULL AND length(trim(NEW.email)) > 3 THEN + client_name := replace(replace(COALESCE(NULLIF(trim(NEW.nom), ''), 'Bonjour'), '&', '&'), '<', '<'); + client_subject := 'StudyAlready - Demande bien recue, voici la suite'; + + client_html := + '
' || + '
' || + '

StudyAlready

' || + '

Demande bien recuee

' || + '
' || + '
' || + '

Bonjour ' || client_name || ',

' || + '

Merci pour votre demande. Notre equipe va l''analyser et vous repondre sous 48 h ouvrees.

' || + '
' || + '

Prochaine etape

' || + '

Merci de telecharger, remplir puis renvoyer ce document PDF :

' || + '

' || + 'Telecharger le PDF StudyAlready ->' || + '

' || + '
' || + '

Une fois complete, vous pouvez nous le renvoyer :

' || + '' || + '

Email automatique StudyAlready.

' || + '
' || + '
'; + + PERFORM public.send_email_to_address(NEW.email, client_subject, client_html); + END IF; + + RETURN NEW; +EXCEPTION WHEN OTHERS THEN + RAISE NOTICE 'on_new_submission failed: %', SQLERRM; + RETURN NEW; +END; +$$; + +-- Verification rapide (attendu: OK) +SELECT + 'send_email_to_address' AS element, + CASE WHEN EXISTS ( + SELECT 1 FROM pg_proc p + JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = 'public' AND p.proname = 'send_email_to_address' + ) THEN 'OK' ELSE 'KO' END AS statut +UNION ALL +SELECT + 'on_new_submission', + CASE WHEN EXISTS ( + SELECT 1 FROM pg_proc p + JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = 'public' AND p.proname = 'on_new_submission' + ) THEN 'OK' ELSE 'KO' END; From 5708cfc0f6d0b385590e330d007028f02a0b0441 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 19 May 2026 16:55:27 +0000 Subject: [PATCH 4/4] Reste en confirmation site uniquement Co-authored-by: ERIC74SONG96 --- assets/js/main.js | 2 +- index.html | 1 - supabase/README-NOTIFICATIONS.md | 13 -- ...2_autoreply_form_submission_next_steps.sql | 191 ------------------ 4 files changed, 1 insertion(+), 206 deletions(-) delete mode 100644 supabase/migrations/032_autoreply_form_submission_next_steps.sql diff --git a/assets/js/main.js b/assets/js/main.js index 6e0ff47..b3a0a6e 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -268,7 +268,7 @@ document.addEventListener('DOMContentLoaded', function () { formId: 'contactForm', statusId: 'formStatus', formType: 'contact', - successMessage: 'Merci ! Votre message est bien arrivé. Réponse sous 48 h.' + successMessage: 'Merci ! Votre message est bien arrivé. Suite : téléchargez, remplissez puis renvoyez le PDF StudyAlready : https://www.studyalready.com/assets/docs/StudyAlready-Dossier-FWB.pdf' }); SA.bind({ formId: 'prequalificationForm', diff --git a/index.html b/index.html index 56fd56d..80c7e8e 100644 --- a/index.html +++ b/index.html @@ -1011,7 +1011,6 @@

Parlons de votre é Envoyer ma demande -

Après envoi, vous recevez aussi un email de confirmation avec la suite des étapes.

En envoyant ce formulaire, vous acceptez d'être recontacté concernant votre projet. Données traitées conformément à notre politique de confidentialité.

Message à envoyer au client (copier / coller)

diff --git a/supabase/README-NOTIFICATIONS.md b/supabase/README-NOTIFICATIONS.md index 3f599df..201a66d 100644 --- a/supabase/README-NOTIFICATIONS.md +++ b/supabase/README-NOTIFICATIONS.md @@ -3,10 +3,6 @@ À chaque nouvelle soumission (formulaire ou profil annuaire), un email récapitulatif est envoyé automatiquement à l'admin StudyAlready. -Optionnel: avec la migration `032_autoreply_form_submission_next_steps.sql`, -le client reçoit aussi un email automatique qui explique la suite -(analyse du dossier + lien PDF a completer et renvoyer). - ## Architecture (sans Edge Function, 100% SQL) ``` @@ -83,15 +79,6 @@ que vous n'avez pas vérifié de domaine). ## Si l'email n'arrive pas -## Activer l'email client "suite des etapes" - -1. Supabase Dashboard → **SQL Editor** → **New query** -2. Exécuter `supabase/migrations/032_autoreply_form_submission_next_steps.sql` -3. Tester un formulaire avec une vraie adresse email -4. Vérifier : - - l'admin reçoit toujours l'alerte "Nouvelle demande" - - le client reçoit l'email "Demande bien recue, voici la suite" - ### Vérifier l'historique Resend https://resend.com → **Emails** → vous voyez la liste des envois, diff --git a/supabase/migrations/032_autoreply_form_submission_next_steps.sql b/supabase/migrations/032_autoreply_form_submission_next_steps.sql deleted file mode 100644 index 20e707a..0000000 --- a/supabase/migrations/032_autoreply_form_submission_next_steps.sql +++ /dev/null @@ -1,191 +0,0 @@ --- ============================================================= --- StudyAlready - Migration 032 : email client automatique (suite) --- ============================================================= --- But: --- - Conserver l'email admin "nouvelle demande" --- - Envoyer aussi un email automatique au client avec la suite --- apres une soumission dans public.form_submissions. --- --- Prerequis: --- - migration 005 executee (private_settings + send_admin_email) --- - extension pg_net activee --- --- Idempotent: peut etre relance sans risque. --- ============================================================= - --- Envoi direct a une adresse (utile pour les formulaires sans user_id) -CREATE OR REPLACE FUNCTION public.send_email_to_address( - target_email text, - subject text, - html text -) RETURNS bigint -LANGUAGE plpgsql -SECURITY DEFINER -SET search_path = public, extensions -AS $$ -DECLARE - api_key text; - notify_from text; - req_id bigint; - clean_email text; -BEGIN - clean_email := lower(trim(coalesce(target_email, ''))); - IF clean_email = '' OR clean_email !~ '^[^[:space:]@]+@[^[:space:]@]+\.[^[:space:]@]+$' THEN - RETURN NULL; - END IF; - - SELECT value INTO api_key FROM private_settings WHERE key = 'RESEND_API_KEY'; - SELECT value INTO notify_from FROM private_settings WHERE key = 'NOTIFY_FROM'; - - IF api_key IS NULL OR api_key = '' OR api_key = 'REMPLACER_PAR_VOTRE_CLE_RESEND' THEN - RAISE NOTICE 'send_email_to_address: RESEND_API_KEY non configuree, envoi ignore'; - RETURN NULL; - END IF; - - IF notify_from IS NULL OR notify_from = '' THEN - notify_from := 'StudyAlready '; - END IF; - - SELECT net.http_post( - url := 'https://api.resend.com/emails', - headers := jsonb_build_object( - 'Authorization', 'Bearer ' || api_key, - 'Content-Type', 'application/json' - ), - body := jsonb_build_object( - 'from', notify_from, - 'to', jsonb_build_array(clean_email), - 'subject', subject, - 'html', html - ) - ) INTO req_id; - - RETURN req_id; -EXCEPTION WHEN undefined_table THEN - RAISE NOTICE 'send_email_to_address: private_settings introuvable (migration 005 requise)'; - RETURN NULL; -END; -$$; - -REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM PUBLIC; -REVOKE EXECUTE ON FUNCTION public.send_email_to_address(text, text, text) FROM anon, authenticated; - --- Etend la fonction existante de notification soumission -CREATE OR REPLACE FUNCTION public.on_new_submission() -RETURNS trigger -LANGUAGE plpgsql -SECURITY DEFINER -SET search_path = public -AS $$ -DECLARE - label text; - contact text; - payload_html text := ''; - k text; - v text; - client_subject text; - client_html text; - client_name text; -BEGIN - label := CASE NEW.form_type - WHEN 'contact' THEN 'Contact' - WHEN 'prequalification' THEN 'Pre-qualification' - WHEN 'rejoindre-reseau' THEN 'Reseau' - WHEN 'mise-en-relation' THEN 'Mise en relation' - WHEN 'rapport-admission' THEN 'Rapport admission' - WHEN 'chasseur-billet' THEN 'Chasseur de billets' - WHEN 'departs-groupes' THEN 'Departs groupes' - ELSE NEW.form_type - END; - - contact := COALESCE(NEW.nom, '-'); - IF NEW.email IS NOT NULL THEN contact := contact || ' · ' || NEW.email; END IF; - IF NEW.whatsapp IS NOT NULL THEN contact := contact || ' · ' || NEW.whatsapp; END IF; - - IF NEW.payload IS NOT NULL AND jsonb_typeof(NEW.payload) = 'object' THEN - FOR k, v IN SELECT * FROM jsonb_each_text(NEW.payload) LOOP - payload_html := payload_html || - '' || - replace(replace(k, '<', '<'), '&', '&') || - '' || - replace(replace(COALESCE(v, ''), '<', '<'), '&', '&') || - ''; - END LOOP; - END IF; - - -- Email admin (inchangé) - PERFORM public.send_admin_email( - '[StudyAlready] Nouvelle demande : ' || label, - '
' || - '
' || - '

StudyAlready

' || - '

Nouvelle demande : ' || label || '

' || - '
' || - '
' || - '

' || contact || '

' || - '

Recu le ' || to_char(NEW.created_at AT TIME ZONE 'Europe/Brussels', 'DD/MM/YYYY a HH24:MI') || '

' || - CASE WHEN payload_html <> '' THEN - '' || payload_html || '
' - ELSE '' END || - '

Ouvrir le dashboard ->

' || - '
' || - '

Notification automatique StudyAlready

' || - '
' - ); - - -- Email client (nouveau): explique la suite - IF NEW.email IS NOT NULL AND length(trim(NEW.email)) > 3 THEN - client_name := replace(replace(COALESCE(NULLIF(trim(NEW.nom), ''), 'Bonjour'), '&', '&'), '<', '<'); - client_subject := 'StudyAlready - Demande bien recue, voici la suite'; - - client_html := - '
' || - '
' || - '

StudyAlready

' || - '

Demande bien recuee

' || - '
' || - '
' || - '

Bonjour ' || client_name || ',

' || - '

Merci pour votre demande. Notre equipe va l''analyser et vous repondre sous 48 h ouvrees.

' || - '
' || - '

Prochaine etape

' || - '

Merci de telecharger, remplir puis renvoyer ce document PDF :

' || - '

' || - 'Telecharger le PDF StudyAlready ->' || - '

' || - '
' || - '

Une fois complete, vous pouvez nous le renvoyer :

' || - '' || - '

Email automatique StudyAlready.

' || - '
' || - '
'; - - PERFORM public.send_email_to_address(NEW.email, client_subject, client_html); - END IF; - - RETURN NEW; -EXCEPTION WHEN OTHERS THEN - RAISE NOTICE 'on_new_submission failed: %', SQLERRM; - RETURN NEW; -END; -$$; - --- Verification rapide (attendu: OK) -SELECT - 'send_email_to_address' AS element, - CASE WHEN EXISTS ( - SELECT 1 FROM pg_proc p - JOIN pg_namespace n ON n.oid = p.pronamespace - WHERE n.nspname = 'public' AND p.proname = 'send_email_to_address' - ) THEN 'OK' ELSE 'KO' END AS statut -UNION ALL -SELECT - 'on_new_submission', - CASE WHEN EXISTS ( - SELECT 1 FROM pg_proc p - JOIN pg_namespace n ON n.oid = p.pronamespace - WHERE n.nspname = 'public' AND p.proname = 'on_new_submission' - ) THEN 'OK' ELSE 'KO' END;