---
name: project-communication
description: "Page Communication — tickets support + notifications : architecture, comportements UI, jobs backend"
metadata: 
  node_type: memory
  type: project
  originSessionId: 19a3ce4d-7c1a-418b-9ca2-65b6157249db
---

# Page Communication (`frontend/src/pages/Communication.jsx`)

## Architecture générale
3 volets : sidebar (volet 1), liste (volet 2), détail (volet 3).
Onglets : `support` (tickets) | `notifications`.

## Filtres chips (volet 2)
- **Tickets** : multi-select via `useState(new Set(['open', 'pending']))` ; si aucune chip active → liste vide (pas "tout afficher")
- **Notifications** : multi-select `useState(new Set(['unread']))` au démarrage ; même règle liste vide si 0 chip active
- Chips actives = fond `var(--primary)` + checkmark SVG blanc ; cumulatives

## Comportements notifications
- Pas de présélection automatique au chargement ni au changement d'onglet
- La notification sélectionnée reste jusqu'à clic explicite
- Menu ⋮ dans le volet 3 : "Marquer comme lu" / "Tout considérer comme lu" / "Tout supprimer"

## Dropdowns portal (pattern obligatoire)
Tous les dropdowns custom (TagSelect, TypeDropdown, RecipientDropdown) utilisent `createPortal(content, document.body)` + `position: fixed` + `getBoundingClientRect()` pour échapper aux stacking contexts.

## Composant RecipientDropdown
Dropdown searchable pour les broadcasts. Trois niveaux :
- "Tous les utilisateurs" (value `''`)
- "Tous les administrateurs" (value `'admins'`) — routé vers `WHERE role='admin'` côté backend
- Utilisateurs individuels filtrés par `display_name` / `email` avec avatars initiales

## Broadcast payload
```js
payload.user_id = bcUserId === 'admins' ? 'admins' : Number(bcUserId);
```

## Ticket detail header (volet 3)
Structure colonne :
- Ligne 1 : chip Type + titre (flex:1) + StatusBadge
- Ligne 2 : "Ouvert par **Nom** · date [— assigné à **Nom**]" à gauche + chips pages à droite
- `margin-top: 25px` entre les deux lignes

## Droits
- Utilisateur : voit uniquement ses tickets et ses notifications
- Admin : voit tous les tickets ; voit ses notifs + notifs `notifyAdmins`
- Actions admin-only : Assigner à, Mettre en attente, Réouvrir, Clôturer
- "Modifier la catégorisation" : accessible à tous (owner + admin)
- "Fermer" (panneau) : accessible à tous

## Pièges React connus
- `{msg.is_admin && <span>}` → si `is_admin = 0` (SQLite), affiche "0". Toujours `{!!msg.is_admin && ...}`
- Images dans messages : classe `msg-inline-img` + wrapper `msg-img-wrap` ajouté par useEffect. Margin `7px` sur `.msg-img-wrap` ET `.msg-inline-img` pour couvrir les deux cas.

## Jobs backend
- `autoTicketStatus.js` : open→pending (2j sans réponse), pending→resolved (5j sans réponse créateur) — toutes les heures
- `autoCleanNotifs.js` : supprime notifications >7 jours sauf type `security` — toutes les heures

**Why:** Nettoyage automatique pour éviter l'accumulation infinie de notifications lues.
**How to apply:** Si on ajoute un nouveau type de notification critique à ne jamais purger, l'ajouter à la clause `type != 'security'` dans `autoCleanNotifs.js`.
