9.1 KiB
Notifications Internes du Support
Vue d'ensemble
Système de notification par email vers paie@odentas.fr pour alerter l'équipe support quand :
- Un utilisateur crée un nouveau ticket
- 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
// 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()
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
<br> - Envoie toujours à
paie@odentas.fr - CTA vers
/staff/tickets/{id}
Nouvelle fonction : sendInternalTicketReplyEmail()
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
<br> - Envoie toujours à
paie@odentas.fr - CTA vers
/staff/tickets/{id}
3. /app/api/tickets/route.ts
Modifications dans POST :
// 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 :
// 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
// Priorité de récupération
userData?.user?.user_metadata?.display_name ||
userData?.user?.user_metadata?.first_name ||
'Utilisateur inconnu'
Code employeur
// Depuis organization_details
const { data: orgDetails } = await sb
.from('organization_details')
.select('code_employeur')
.eq('org_id', ticket.org_id)
.maybeSingle();
Email utilisateur
user.email || 'Email non disponible'
🔐 Permissions
- Utilise
createSbServiceRole()pour accéder àauth.admin.getUserById() - Les requêtes vers
organizationsetorganization_detailsutilisentcreateSbServer()(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.
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
-
Création de ticket par utilisateur :
- Créer un ticket en tant qu'utilisateur normal
- Vérifier que
paie@odentas.frreç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
-
Réponse utilisateur :
- Répondre à un ticket en tant qu'utilisateur normal
- Vérifier que
paie@odentas.frreçoit l'email - Vérifier que le statut est correct
- Vérifier que les sauts de ligne sont préservés
-
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
-
Messages internes :
- Créer un message
internal: true→ Aucun email envoyé
- Créer un message
📋 Checklist de validation
- ✅ Template
support-ticket-createdajouté àemailTemplateService.ts - ✅ Template
support-ticket-replyajouté àemailTemplateService.ts - ✅ Fonction
sendInternalTicketCreatedEmail()créée - ✅ Fonction
sendInternalTicketReplyEmail()créée - ✅ API
POST /api/ticketsmodifiée pour envoyer notification - ✅ API
POST /api/tickets/[id]/messagesmodifiée pour envoyer notification - ✅ Sauts de ligne convertis en
<br>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.frest 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