# 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