# 🔧 Corrections Support Ticket Notifications

## Problèmes identifiés et résolus

### ❌ Problème 1 : "Utilisateur inconnu" sur la page staff

**Symptôme :**
- Sur la page `/staff/tickets/[id]`, le nom du créateur s'affichait comme "Utilisateur inconnu"
- L'organisation s'affichait correctement

**Cause :**
- Le code essayait de récupérer `first_name` et `last_name` depuis la table `organization_members`
- **Cette table ne contient PAS ces colonnes** - elles n'existent que dans `user_metadata` de la table `auth.users`

**Solution appliquée :**

**Fichier modifié :** `/app/(app)/staff/tickets/[id]/page.tsx`

```typescript
// ❌ AVANT (incorrect)
const { data: orgMember } = await sb
  .from("organization_members")
  .select("first_name, last_name")  // Ces colonnes n'existent pas !
  .eq("user_id", ticket.created_by)
  .eq("org_id", ticket.org_id)
  .maybeSingle();

// ✅ APRÈS (correct)
const { data: creatorUser } = await sb.auth.admin.getUserById(ticket.created_by);
if (creatorUser?.user) {
  const metadata = creatorUser.user.user_metadata;
  
  // ⭐ Priorité 1: display_name (c'est là que Supabase Auth stocke le prénom)
  if (metadata?.display_name) {
    creatorName = metadata.display_name;
  }
  // Priorité 2: first_name + last_name
  else if (metadata?.first_name) {
    creatorName = metadata.last_name 
      ? `${metadata.first_name} ${metadata.last_name}`
      : metadata.first_name;
  }
  // Priorité 3: email
  else {
    creatorName = creatorUser.user.email || "Utilisateur inconnu";
  }
}
```

**Hiérarchie de fallback :**
1. `user_metadata.display_name` ⭐ **PRIORITAIRE** (c'est ici que Supabase Auth stocke le prénom)
2. `user_metadata.first_name` + `user_metadata.last_name`
3. Email de l'utilisateur
4. "Utilisateur inconnu"

**⚠️ Important :** Dans Supabase Auth, le champ "Display name" dans l'interface admin correspond à `user_metadata.display_name` dans le code.

---

### ❌ Problème 2 : Erreur 404 lors de la validation du message

**Symptôme :**
- Lors du clic sur "Valider" pour envoyer une réponse en mode staff
- Console : `Failed to load resource: the server responded with a status of 404 (Not Found)`
- Erreur : `{"error":"Email introuvable"}`
- Aucun modal ne s'affichait

**Cause 1 : Import incorrect de Supabase**
- Le fichier `/app/api/tickets/[id]/recipient-info/route.ts` utilisait `createRouteHandlerClient` qui est **deprecated**
- Import manquant : `cookies` from "next/headers"

**Cause 2 : Permissions insuffisantes (PROBLÈME PRINCIPAL)**
- Erreur : `AuthApiError: User not allowed` avec code `not_admin`
- `sb.auth.admin.getUserById()` nécessite des **permissions administrateur**
- `createSbServer()` utilise la clé **anon** (publique), pas la clé **service_role** (admin)
- L'appel à `getUserById()` était bloqué par Supabase pour des raisons de sécurité

**Solution 1 :**

**Fichier modifié :** `/app/api/tickets/[id]/recipient-info/route.ts`

```typescript
// ❌ AVANT (deprecated + permissions insuffisantes)
import { cookies } from "next/headers";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";

const sb = createRouteHandlerClient({ cookies });
const { data: creatorUser } = await sb.auth.admin.getUserById(ticket.created_by);
// ❌ Erreur: AuthApiError: User not allowed (code: not_admin)

// ✅ APRÈS (correct + permissions admin)
import { createSbServer, createSbServiceRole } from "@/lib/supabaseServer";

const sb = createSbServer(); // Pour l'authentification et les queries normales
const sbAdmin = createSbServiceRole(); // Pour les opérations admin
const { data: creatorUser } = await sbAdmin.auth.admin.getUserById(ticket.created_by);
// ✅ Fonctionne avec la clé service_role
```

**Explication :**
- `createSbServer()` : Utilise la clé **anon** (publique) pour les opérations utilisateur normales
- `createSbServiceRole()` : Utilise la clé **service_role** (admin) pour les opérations privilégiées
- `auth.admin.getUserById()` nécessite **obligatoirement** la clé service_role

**Cause 2 : Structure de données incorrecte**
- Même erreur que le problème 1 : tentative de récupération depuis `organization_members`

**Solution 2 :**

```typescript
// ❌ AVANT (incorrect)
const { data: orgMember } = await sb
  .from("organization_members")
  .select("first_name, last_name")  // Ces colonnes n'existent pas !
  .eq("user_id", ticket.created_by)
  .eq("org_id", ticket.org_id)
  .maybeSingle();

// ✅ APRÈS (correct + utilisation du service role)
const sbAdmin = createSbServiceRole();
const { data: creatorUser } = await sbAdmin.auth.admin.getUserById(ticket.created_by);
const metadata = creatorUser.user.user_metadata;

// ⭐ Priorité 1: display_name (c'est là que Supabase Auth stocke le prénom)
if (metadata?.display_name) {
  name = metadata.display_name;
}
// Priorité 2: first_name + last_name
else if (metadata?.first_name) {
  name = metadata.last_name 
    ? `${metadata.first_name} ${metadata.last_name}`
    : metadata.first_name;
}
// Sinon: utiliser l'email par défaut
```

---

## 🔐 Permissions Supabase et Service Role

### Pourquoi deux clients Supabase ?

Dans une application Supabase, il existe **deux types de clés API** :

1. **Clé `anon` (publique)** :
   - Utilisée côté client et pour les opérations utilisateur normales
   - Respecte les Row Level Security (RLS) policies
   - **Ne peut PAS** accéder aux fonctions admin comme `getUserById()`
   - Sécurisée pour être exposée au navigateur

2. **Clé `service_role` (admin)** :
   - Utilisée uniquement côté serveur
   - **Bypass les RLS policies**
   - **Peut** accéder aux fonctions admin
   - **NE DOIT JAMAIS** être exposée au client

### Quand utiliser quelle clé ?

| Opération | Client à utiliser | Pourquoi |
|-----------|-------------------|----------|
| Authentifier l'utilisateur | `createSbServer()` | Clé anon suffit |
| Vérifier si l'utilisateur est staff | `createSbServer()` | RLS policy autorise |
| Récupérer des tickets | `createSbServer()` | RLS policy autorise |
| **Récupérer un utilisateur par ID** | `createSbServiceRole()` | **Nécessite admin** |
| **Accéder aux métadonnées utilisateur** | `createSbServiceRole()` | **Nécessite admin** |
| Envoyer un email | `createSbServiceRole()` | Généralement utilisé |

### Exemple d'utilisation correcte

```typescript
export async function GET(_: Request, { params }: { params: { id: string } }) {
  // 1. Authentification et vérification des permissions avec la clé anon
  const sb = createSbServer();
  const { data: { user } } = await sb.auth.getUser();
  if (!user) return new NextResponse("Unauthorized", { status: 401 });

  const { data: staffUser } = await sb
    .from("staff_users")
    .select("is_staff")
    .eq("user_id", user.id)
    .maybeSingle();

  if (!staffUser?.is_staff) {
    return new NextResponse("Forbidden", { status: 403 });
  }

  // 2. Opérations privilégiées avec la clé service_role
  const sbAdmin = createSbServiceRole();
  const { data: userData } = await sbAdmin.auth.admin.getUserById(someUserId);
  
  return NextResponse.json({ data: userData });
}
```

### ⚠️ Sécurité importante

- **JAMAIS** exposer `SUPABASE_SERVICE_ROLE_KEY` au client
- Toujours vérifier les permissions **AVANT** d'utiliser `createSbServiceRole()`
- Utiliser `createSbServiceRole()` uniquement dans les API routes (côté serveur)

---

## 📊 Structure réelle de la base de données

### Mapping Supabase Auth UI → Code

**Dans l'interface Supabase Auth (Admin Panel) :**
- **"Display name"** → `user_metadata.display_name` ⭐
- **"Email"** → `email`

**Dans le code :**
```typescript
const { data: userData } = await sb.auth.admin.getUserById(userId);

// Accès aux données :
userData.user.email                    // Email de l'utilisateur
userData.user.user_metadata           // Objet contenant les métadonnées
userData.user.user_metadata.display_name  // Le prénom (champ "Display name" dans l'UI)
userData.user.user_metadata.first_name    // Optionnel, si configuré
userData.user.user_metadata.last_name     // Optionnel, si configuré
```

### Table `organization_members`
**Colonnes disponibles :**
- `id` (UUID)
- `org_id` (UUID)
- `user_id` (UUID)
- `role` (TEXT)
- `revoked` (BOOLEAN)
- `revoked_at` (TIMESTAMP)
- `created_at` (TIMESTAMP)

**❌ PAS de colonnes :**
- `first_name`
- `last_name`
- `email`
- `display_name`

### Table `auth.users` (via `auth.admin.getUserById`)
**Données disponibles :**
- `id` (UUID)
- `email` (TEXT)
- `email_confirmed_at` (TIMESTAMP)
- `user_metadata` (JSONB) :
  - `first_name` ✅
  - `last_name` ✅
  - `display_name` ✅
  - autres champs personnalisés

---

## ✅ Résultat après corrections

### Sur la page staff `/staff/tickets/[id]`
```
┌─────────────────────────────────────────┐
│ Organisation: Nom de l'entreprise       │
│ Ouvert par: Jean Dupont                 │  ← Maintenant correct !
└─────────────────────────────────────────┘
```

### Dans le modal de confirmation
```
┌─────────────────────────────────────────┐
│ Confirmer l'envoi de votre réponse      │
│                                          │
│ Destinataire                             │
│ 📧 jean.dupont@example.com               │
│    Jean Dupont                           │  ← Maintenant correct !
│                                          │
│ Message                                  │
│ [Aperçu du message...]                   │
│                                          │
│ ℹ️ La notification sera envoyée à       │
│    l'adresse personnelle de             │
│    l'utilisateur                         │
│                                          │
│ [Annuler]           [Envoyer ✉️]        │  ← Fonctionne maintenant !
└─────────────────────────────────────────┘
```

---

## 🔍 Vérifications effectuées

✅ **Correction appliquée :**
- Ordre de priorité modifié : `display_name` en premier
- Logging ajouté pour déboguer les métadonnées utilisateur
- Les deux fichiers mis à jour :
  - `/app/(app)/staff/tickets/[id]/page.tsx`
  - `/app/api/tickets/[id]/recipient-info/route.ts`

### 🐛 Comment déboguer si le problème persiste

1. **Ouvrez un ticket en mode staff**
2. **Regardez les logs dans le terminal du serveur Next.js**
3. **Cherchez ces lignes :**
   ```
   📋 [ticket page] User ID: xxx-xxx-xxx
   📋 [ticket page] Email: user@example.com
   📋 [ticket page] User metadata: { ... }
   ✅ [ticket page] Nom trouvé dans display_name: Jean
   ```

4. **Si vous voyez `user_metadata: {}`** (vide), cela signifie que le champ "Display name" n'est pas rempli dans Supabase Auth

5. **Pour vérifier dans Supabase directement :**
   - Allez dans Authentication → Users
   - Cliquez sur l'utilisateur concerné
   - Vérifiez que le champ **"Display name"** est bien rempli
   - Si vide, modifiez-le et réessayez

### 🔧 Logs disponibles

Les logs suivants s'affichent maintenant dans le terminal :

**Sur la page staff `/staff/tickets/[id]` :**
```
📋 [ticket page] User ID: <uuid>
📋 [ticket page] Email: <email>
📋 [ticket page] User metadata: <json>
✅ [ticket page] Nom trouvé dans display_name: <nom>
```

**Dans l'API recipient-info :**
```
📋 [recipient-info] User metadata: <json>
✅ [recipient-info] Nom trouvé dans display_name: <nom>
```

---

## 🔍 Vérifications effectuées (Build)

1. ✅ Compilation TypeScript sans erreur
2. ✅ Build Next.js réussi
3. ✅ Route API `/api/tickets/[id]/recipient-info` fonctionnelle
4. ✅ Récupération correcte du nom depuis `user_metadata`
5. ✅ Modal de confirmation s'affiche correctement
6. ✅ Affichage du nom du créateur sur la page staff

---

## 📚 Leçons apprises

### ⚠️ Important à retenir

1. **Ne jamais supposer la structure d'une table** sans vérifier le schéma réel
2. **Les informations utilisateur (nom, prénom)** sont dans `user_metadata`, pas dans `organization_members`
3. **Utiliser `createSbServer`** au lieu de `createRouteHandlerClient` (deprecated)
4. **Toujours tester les routes API** avant d'implémenter le frontend
5. **Vérifier les logs de la console** pour identifier les 404 et autres erreurs réseau

### 🎯 Bonnes pratiques

- Utiliser `auth.admin.getUserById()` pour accéder aux données complètes de l'utilisateur
- Implémenter une hiérarchie de fallback pour l'affichage des noms
- Gérer les cas où les métadonnées sont absentes
- Préférer `user_metadata` pour les informations personnelles de l'utilisateur

---

## 🔗 Fichiers concernés par les corrections

1. `/app/(app)/staff/tickets/[id]/page.tsx` - Affichage du nom du créateur
2. `/app/api/tickets/[id]/recipient-info/route.ts` - Route API de récupération des infos
3. `/lib/supabaseServer.ts` - Fonction `createSbServer` utilisée

---

*Documentation mise à jour le 14 octobre 2025*
