# Notifications Internes du Support ## Vue d'ensemble Système de notification par email vers `paie@odentas.fr` pour alerter l'équipe support quand : 1. Un utilisateur crée un nouveau ticket 2. Un utilisateur répond à un ticket existant ## 📧 Types de notifications ### 1. Nouveau ticket créé (`support-ticket-created`) **Déclencheur :** Quand un utilisateur (non-staff) crée un nouveau ticket via l'API `POST /api/tickets` **Template Email :** - **Sujet :** `[SUPPORT] Nouveau ticket : {sujet du ticket}` - **Titre :** 🎫 Nouveau ticket support - **Bouton CTA :** "Voir le ticket" → `/staff/tickets/{id}` - **Couleur bouton :** Bleu (#3B82F6) **Informations affichées :** **InfoCard :** - Organisation - Code employeur - Créé par (nom de l'utilisateur) - Email (email de l'utilisateur) **DetailsCard :** - ID du ticket - Sujet - Catégorie (actuellement "Support général") - Message initial (avec sauts de ligne préservés) ### 2. Réponse utilisateur (`support-ticket-reply`) **Déclencheur :** Quand un utilisateur (non-staff) ajoute un message à un ticket via `POST /api/tickets/[id]/messages` **Template Email :** - **Sujet :** `[SUPPORT] Réponse au ticket : {sujet du ticket}` - **Titre :** 💬 Réponse sur un ticket - **Bouton CTA :** "Voir le ticket" → `/staff/tickets/{id}` - **Couleur bouton :** Bleu (#3B82F6) **Informations affichées :** **InfoCard :** - Organisation - Code employeur - Répondu par (nom de l'utilisateur) - Email (email de l'utilisateur) **DetailsCard :** - ID du ticket - Sujet - Statut (open, in_progress, resolved, closed) - Réponse (avec sauts de ligne préservés) ## 🔧 Implémentation technique ### Fichiers modifiés #### 1. `/lib/emailTemplateService.ts` ```typescript // Nouveaux types ajoutés | 'support-ticket-created' | 'support-ticket-reply' // Nouveaux champs dans EmailDataV2 ticketCategory?: string; ticketMessage?: string; ticketStatus?: string; userEmail?: string; userMessage?: string; ``` #### 2. `/lib/emailMigrationHelpers.ts` **Nouvelle fonction : `sendInternalTicketCreatedEmail()`** ```typescript export async function sendInternalTicketCreatedEmail( data: { ticketId: string; ticketSubject: string; ticketCategory: string; ticketMessage: string; userName: string; userEmail: string; organizationName?: string; employerCode?: string; } ) ``` - Convertit automatiquement les sauts de ligne en `
` - Envoie toujours à `paie@odentas.fr` - CTA vers `/staff/tickets/{id}` **Nouvelle fonction : `sendInternalTicketReplyEmail()`** ```typescript export async function sendInternalTicketReplyEmail( data: { ticketId: string; ticketSubject: string; ticketStatus: string; userMessage: string; userName: string; userEmail: string; organizationName?: string; employerCode?: string; } ) ``` - Convertit automatiquement les sauts de ligne en `
` - Envoie toujours à `paie@odentas.fr` - CTA vers `/staff/tickets/{id}` #### 3. `/app/api/tickets/route.ts` **Modifications dans `POST` :** ```typescript // Après la création du ticket, si !isStaff if (!isStaff) { try { // Récupérer les infos utilisateur avec createSbServiceRole() const sbAdmin = createSbServiceRole(); const { data: userData } = await sbAdmin.auth.admin.getUserById(user.id); // Récupérer org et code employeur const { data: organization } = await sb.from('organizations')... const { data: orgDetails } = await sb.from('organization_details')... // Envoyer la notification await sendInternalTicketCreatedEmail({...}); } catch (emailError) { // Ne pas bloquer la création du ticket console.error('Failed to send internal notification'); } } ``` #### 4. `/app/api/tickets/[id]/messages/route.ts` **Modifications dans `POST` :** ```typescript // Après l'insertion du message, si !isStaff if (!isStaff) { try { // Récupérer les infos du ticket const { data: ticket } = await sb.from("tickets")... // Récupérer org et code employeur const { data: organization } = await sb.from('organizations')... const { data: orgDetails } = await sb.from('organization_details')... // Récupérer les infos utilisateur avec createSbServiceRole() const sbAdmin = createSbServiceRole(); const { data: userData } = await sbAdmin.auth.admin.getUserById(user.id); // Envoyer la notification await sendInternalTicketReplyEmail({...}); } catch (emailError) { // Ne pas bloquer l'ajout du message console.error('Failed to send internal notification'); } } ``` ## 🎨 Design des emails ### Structure standardisée Les deux types d'emails utilisent le système Universal Email V2 avec : - **Header :** Logo Odentas avec fond standard - **InfoCard :** Informations sur l'organisation et l'utilisateur (fond gris clair) - **DetailsCard :** Détails du ticket/message (fond blanc) - **CTA Button :** Bouton bleu "Voir le ticket" - **Footer :** Mentions légales Odentas ### Couleurs - Header : `#171424` (violet foncé standard) - Bouton CTA : `#3B82F6` (bleu) - différent des emails clients pour distinguer les notifications internes - Texte bouton : `#FFFFFF` (blanc) ## 📊 Sources de données ### Nom de l'utilisateur ```typescript // Priorité de récupération userData?.user?.user_metadata?.display_name || userData?.user?.user_metadata?.first_name || 'Utilisateur inconnu' ``` ### Code employeur ```typescript // Depuis organization_details const { data: orgDetails } = await sb .from('organization_details') .select('code_employeur') .eq('org_id', ticket.org_id) .maybeSingle(); ``` ### Email utilisateur ```typescript user.email || 'Email non disponible' ``` ## 🔐 Permissions - Utilise `createSbServiceRole()` pour accéder à `auth.admin.getUserById()` - Les requêtes vers `organizations` et `organization_details` utilisent `createSbServer()` (permissions RLS normales) ## ⚠️ Gestion des erreurs **Important :** Les erreurs d'envoi d'email ne bloquent JAMAIS la création du ticket ou l'ajout du message. ```typescript try { await sendInternalTicketCreatedEmail({...}); } catch (emailError) { // Log uniquement, ne pas throw console.error('Failed to send internal notification:', emailError); } ``` Ceci garantit que même si AWS SES est en panne ou si les données sont incomplètes, le ticket est toujours créé/mis à jour. ## 📝 Logs Les logs suivent le format standardisé : ``` [TICKET CREATE] Sending internal notification email... [TICKET CREATE] Internal notification email sent successfully [TICKET CREATE] Failed to send internal notification email: {error} ``` ``` 📧 [POST messages] Envoi de notification interne... ✅ [POST messages] Notification interne envoyée pour le ticket {id} ❌ [POST messages] Erreur lors de l'envoi de la notification interne: {error} ``` ## 🧪 Tests suggérés 1. **Création de ticket par utilisateur :** - Créer un ticket en tant qu'utilisateur normal - Vérifier que `paie@odentas.fr` reçoit l'email - Vérifier que le bouton CTA mène vers `/staff/tickets/{id}` - Vérifier que les sauts de ligne sont préservés 2. **Réponse utilisateur :** - Répondre à un ticket en tant qu'utilisateur normal - Vérifier que `paie@odentas.fr` reçoit l'email - Vérifier que le statut est correct - Vérifier que les sauts de ligne sont préservés 3. **Pas de notification staff :** - Créer un ticket en tant que staff → Pas d'email interne - Répondre en tant que staff → Email à l'utilisateur uniquement 4. **Messages internes :** - Créer un message `internal: true` → Aucun email envoyé ## 📋 Checklist de validation - ✅ Template `support-ticket-created` ajouté à `emailTemplateService.ts` - ✅ Template `support-ticket-reply` ajouté à `emailTemplateService.ts` - ✅ Fonction `sendInternalTicketCreatedEmail()` créée - ✅ Fonction `sendInternalTicketReplyEmail()` créée - ✅ API `POST /api/tickets` modifiée pour envoyer notification - ✅ API `POST /api/tickets/[id]/messages` modifiée pour envoyer notification - ✅ Sauts de ligne convertis en `
` dans les deux fonctions - ✅ Gestion d'erreur non-bloquante implémentée - ✅ Utilisation de `createSbServiceRole()` pour getUserById - ✅ Code employeur récupéré depuis `organization_details` - ✅ Tous les fichiers compilent sans erreur TypeScript ## 🔄 Différence avec les emails clients | Fonctionnalité | Email Client | Email Interne | |----------------|--------------|---------------| | Destinataire | Utilisateur créateur du ticket | `paie@odentas.fr` | | Déclencheur | Staff répond (non-internal) | Utilisateur crée/répond | | Couleur bouton | Jaune (#EFC543) | Bleu (#3B82F6) | | Affichage staff | Nom formaté avec badge | Nom utilisateur brut | | URL CTA | `/support/{id}` | `/staff/tickets/{id}` | ## 🚀 Déploiement Aucune configuration supplémentaire nécessaire : - Les templates sont automatiquement disponibles - L'email `paie@odentas.fr` est codé en dur (pas de variable d'environnement) - AWS SES est déjà configuré via `sendUniversalEmailV2` --- **Date de création :** 14 octobre 2025 **Auteur :** Système de support Odentas **Version :** 1.0