tenant-admin — Design Specification¶
Module: tenant-admin (TENANT)
Sides: [ADMIN] (Papillon platform staff) · [OPERATOR] (Cabinet admin self-service)
Date: 2026-05-22
Design tokens: docs/standards/design-system.md
Scaffold base: frontend-operator/ — OperatorShell · Sidebar · TopBar · AuthBoundary · Badge · Button · DataTable (all live)
Stub to evolve: frontend-operator/src/pages/admin/AdminPage.tsx (tenant list stub)
Sidebar routing: /admin already gated to PLATFORM_ADMIN role in Sidebar.tsx
0. Design Decisions Log¶
These decisions were resolved by the DESIGNER. Each decision references the UX principle that drove it.
D1 — App packaging: same shell, separate route subtrees¶
Both [ADMIN] and [OPERATOR] cabinet-admin views live inside frontend-operator. Platform admin routes: /admin/tenants/*. Cabinet admin self-service: /tenant/settings/*. Same OperatorShell, same design system. Role-gated via existing AuthBoundary pattern. The two audiences never overlap; a CABINET_ADMIN never sees /admin/*.
Why not a separate micro-frontend? Overkill for ~10 Papillon staff. The PLATFORM_ADMIN role guard is already wired in Sidebar.tsx; extending it to sub-routes is the minimal delta.
UX principle: Hick's Law — fewer entry points, lower cognitive cost. Don Norman's constraint — impossible actions are invisible.
D2 — Tenant creation: 5-step wizard, PENDING tenant persisted after step 1¶
~25 fields + conditional BYO-DB block + mandatory DPA upload + mandatory NOTIF sender IDs. A single long form (Miller's 7±2 violation) would cause abandonment. The wizard groups by concern: identity → contact/address → isolation → compliance/notifications → recap+activate.
Save strategy: Submitting step 1 creates the PENDING tenant (POST). Steps 2–4 PATCH. Step 5 triggers provisioning. If the admin closes mid-wizard, the PENDING tenant appears in the list — they can resume from the "Documents" tab on the tenant detail page.
Activation gate: The "Activer" button in step 5 is disabled until the checklist (DPA ✓ · SMS Sender ID ✓ · WA Business ID ✓) is green. The checklist is visible at the top of step 5, not buried in an error message after submit.
UX principle: Forgiving interface — forms never lose data on accidental close. Progressive disclosure — step 3 (BYO-DB) only asks for JDBC credentials when the "BYO-DB" radio is selected.
D3 — Provisioning pipeline: live 5-step stepper with per-step retry¶
The provisioning pipeline (TR-002) has 5 named steps. Each can fail independently. Idempotency means "retry" skips completed steps. Showing a single spinner would hide this structure and make failures opaque. The live stepper makes idempotency legible: a ✅ step won't re-run; a ❌ step shows the French error code and a "Relancer" button.
UX principle: Feedback visibility (Don Norman's feedback principle). The operator needs to know WHICH step failed, not just "an error occurred".
D4 — Suspend: confirmation modal with radio reason + prominent consequence warning¶
Suspension is irreversible in the short term (it revokes Keycloak access within 30 s). Two-layer friction: (a) radio button for reason code (makes the admin think about WHY, not just click through), (b) explicit danger Button labelled "Confirmer la suspension de [nom cabinet]". No "type the name to confirm" — that pattern is English-origin and feels foreign in French professional UI. The modal opens from a "Suspendre" button on the tenant detail action bar, not from the list (prevents fat-finger on wrong row).
UX principle: Don Norman's error prevention. The cost of confirming matches the cost of the action.
D5 — Tenant list: status filter tabs + proactive DPA expiry banner¶
The most frequent operator action is "show me suspended tenants" or "show me pending ones". Status tabs (Tous / En attente / Actifs / Suspendus / Supprimés) put this one click away without a filter dropdown. "Supprimés" tab is visually de-emphasized (lighter weight) — rare enough to be secondary. DPA expiry warning displays as a global InlineBanner above the table when ≥1 tenant has DPA expiring in ≤30 days — proactive signal, not discoverable only per-row.
UX principle: Fitt's Law — most-used filters at top. "Design the unhappy paths first" — the DPA warning is an unhappy path that must be unmissable.
D6 — Tenant detail: tabbed layout (Identité | Documents | Provisionnement | Agences | Paramètres)¶
A tenant has ~50 data points across 4 concerns. Tabs organise by concern without depth (no nested navigation). Both PLATFORM_ADMIN and CABINET_ADMIN land on the "Identité" tab; field editability differs by role (locked fields for cabinet admin, editable by platform admin). This avoids duplicating the page tree.
Cabinet admin route /tenant/settings shows only 3 tabs: Identité | Agences | Paramètres. The platform-admin-only tabs (Documents, Provisionnement) are absent from the cabinet admin view.
D7 — Agency "disable last": preventive UI (disabled button + tooltip)¶
When activeCount === 1, the "Désactiver" action button for that agency is disabled with aria-disabled="true" and a tooltip "Au moins une agence active est requise par cabinet." The button is rendered but inert. This is preferable to allowing the action and returning a 400 — error prevention > error recovery.
UX principle: Don Norman's constraints. Make impossible actions impossible, not just error-prone.
D8 — Cabinet-admin locked fields: distinct "verrou" visual state¶
Identity fields (legal name, NCC, agrément, country) that CABINET_ADMIN cannot edit render as read-only Input components with a 🔒 icon and helper text "Modifiable par le support Papillon uniquement." They look like inputs but are not interactive. This communicates "this is data, not a label" while explaining why editing is blocked.
D9 — BYO-DB password: write-only, never echoed back¶
The db_password field uses type="password" with show/hide toggle during entry. After save, the field renders as a placeholder string "••••••••" with a label "Mot de passe (chiffré — non récupérable)". A "Modifier" button replaces it with a fresh input. The real value is never sent to the UI from the API (API returns "***" for this field).
D10 — Settings page: compliance floors surfaced inline with [LEGAL-REVIEW] label¶
The retention overrides section in Paramètres shows the hard floor next to each input. A [LEGAL-REVIEW] badge (clickable tooltip) reads: "Durées minimales légales — délibération ARTCI sectorielle assurance CI en cours. Ne pas descendre sous ce seuil sans confirmation juridique." This surfaces the TV-03 compliance assumption at the point of use.
1. User Flow Diagrams¶
1.1 [ADMIN] Platform Admin — Tenant Lifecycle¶
flowchart TD
Start([Admin connecté /admin/tenants]) --> List[ADM-001 Liste des cabinets]
List -->|+ Nouveau cabinet| Wizard[ADM-002 Wizard création]
Wizard --> W1[Étape 1 — Identité légale\nPOST → tenant PENDING]
W1 -->|Suivant| W2[Étape 2 — Contact & Adresse\nPATCH]
W2 -->|Suivant| W3[Étape 3 — Isolation BYO / Shared\nPATCH]
W3 -->|Suivant| W4[Étape 4 — Conformité & Notifications\nPATCH + DPA upload]
W4 -->|Suivant| W5[Étape 5 — Récapitulatif]
W5 -->|Activer — checklist verte| Prov[ADM-003 onglet Provisionnement\nPipeline live 5 steps]
Prov -->|Succès| Active[Tenant ACTIF → toast → retour liste]
Prov -->|Échec step N| Retry{Relancer step N ?}
Retry -->|Oui| Prov
Retry -->|Non| PendingDetail[ADM-003 Détail tenant PENDING]
W1 -->|Fermer avant fin| PendingList[Tenant PENDING dans liste — reprise possible]
List -->|Clic sur ligne| Detail[ADM-003 Détail tenant]
Detail -->|Onglet Identité| Identity[Voir et Modifier identité]
Detail -->|Onglet Documents| Docs[Gestion documents versionnés]
Docs -->|Upload nouveau PDF| DocUpload[Nouvelle version — ancienne archivée]
Docs -->|DPA expirant 30j| DPAWarn[InlineBanner avertissement]
Detail -->|Onglet Provisionnement| ProvTab[Statut pipeline]
ProvTab -->|Relancer si PENDING ou FAILED| Prov
Detail -->|Onglet Agences| AgencyTab[Liste agences]
AgencyTab -->|+ Nouvelle agence| AgencyModal[Modal création agence]
AgencyTab -->|Désactiver — active_count > 1| AgencyDisable[AgencyDisabled]
AgencyTab -->|Désactiver — active_count = 1| AgencyBlocked[Bouton désactivé + tooltip]
Detail -->|Onglet Paramètres| SettingsTab[Paramètres tenant]
Detail -->|Status ACTIVE → Suspendre| SuspModal[ADM-004 Modal suspension]
SuspModal -->|Annuler| Detail
SuspModal -->|Confirmer| Suspended[Tenant SUSPENDU]
Suspended -->|Réactiver| ReactModal[ADM-005 Modal réactivation]
ReactModal -->|Confirmer| Active2[Tenant ACTIF à nouveau]
List -->|Onglet Suspendus| SuspList[Tenants suspendus]
SuspList -->|Grace restante affichée| GraceInfo[X jours avant suppression auto]
SuspList -->|90j dépassés — job nuit| AutoDeleted[Tenant SUPPRIMÉ soft]
AutoDeleted -->|Onglet Supprimés| DeletedView[Lecture seule — audit trail accessible]
1.2 [OPERATOR] Cabinet Admin — Self-Service¶
flowchart TD
Start([Cabinet admin connecté]) --> Settings[/tenant/settings — Identité]
Settings -->|Modifier logo| LogoUpload[FileUpload PNG/SVG ≤200Ko]
LogoUpload -->|Fichier valide| LogoSaved[Logo enregistré → TenantSettingsChanged]
LogoUpload -->|Invalide taille/format/dims| LogoError[Erreur inline validation]
Settings -->|Modifier couleur hex| ColorPicker[Input hex + aperçu swatch]
Settings -->|Modifier support_email| EmailField[Input email]
Settings -->|Sauvegarder| Saved[Toast succès]
Settings -->|Onglet Agences| Agencies[/tenant/settings/agences]
Agencies -->|+ Nouvelle agence| AgencyCreate[Modal — code/nom/ville requis]
AgencyCreate -->|Code unique| AgencyCreated[Agence créée — AgencyCreated]
AgencyCreate -->|Code dupliqué| AgencyDupError[Erreur — Code agence déjà utilisé]
Agencies -->|Modifier ligne| AgencyEdit[Modal pré-remplie]
Agencies -->|Désactiver — n>1 active| AgencyDisabled[Soft-disable → AgencyDisabled]
Agencies -->|Désactiver — n=1 active| AgencyBlocked[Bouton disabled + tooltip]
Agencies -->|Réactiver| AgencyReactivated[Status ACTIVE]
Settings -->|Onglet Paramètres| Params[/tenant/settings/parametres]
Params -->|Fenêtre renouvellement hors 7-90| BoundError[Erreur borne inline]
Params -->|Canal préféré| ChannelRadio[WHATSAPP / SMS RadioGroup]
Params -->|Template WA| WAInput[Texte libre]
Params -->|Rétention sous plancher légal| RetentionError[Erreur plancher + LEGAL-REVIEW]
Params -->|Sauvegarder| SettingsSaved[TenantSettingsChanged]
2. Screen Inventory¶
[ADMIN] — Platform staff (/admin/tenants/*)¶
| ID | Route | Stories | UI-bearing |
|---|---|---|---|
| ADM-001 | /admin/tenants |
US-01 (list) | ✅ |
| ADM-002 | /admin/tenants/nouveau |
US-01, US-02, US-03 (wizard) | ✅ |
| ADM-003 | /admin/tenants/:id |
US-01..06 (detail + tabs) | ✅ |
| ADM-004 | Modal sur ADM-003 | US-04 (suspend) | ✅ |
| ADM-005 | Modal sur ADM-003 | US-04 (reactivate) | ✅ |
[OPERATOR] — Cabinet admin (/tenant/settings/*)¶
| ID | Route | Stories | UI-bearing |
|---|---|---|---|
| OP-001 | /tenant/settings |
US-07 (branding) | ✅ |
| OP-002 | /tenant/settings/agences |
US-08 (agencies) | ✅ |
| OP-003 | /tenant/settings/parametres |
US-09 (settings) | ✅ |
3. Wireframes¶
All wireframes are desktop-first (1440px). Responsive deltas noted per screen. Components referenced by name from
docs/standards/design-system.md. New components spec'd in §8.
ADM-001 — Liste des cabinets¶
Shaped by: D5 (status tabs + DPA banner)
┌─────────────────────────────────────────────────────────────────────┐
│ OperatorShell (Sidebar 240px + TopBar) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Administration plateforme [+ Nouveau cabinet] (primary)│ │
│ │ Gestion des tenants (cabinets) │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ ⚠ 2 DPA expirent dans moins de 30 jours. → Voir les cabinets │ │ ← InlineBanner warning (conditionnel)
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ [🔍 Rechercher un cabinet…] [Plan : Tous ▾] │ │
│ │ │ │
│ │ [Tous (12)] [En attente (3)] [Actifs (7)] [Suspendus (2)] [Supprimés (0)] │ │ ← Tabs, pill variant
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ 12 cabinets │ │
│ │ │ │
│ │ Raison sociale ↕ Plan ↕ Pays Statut Activé le ↕ Actions │ │
│ │ ─────────────────────────────────────────────────────────────── │ │
│ │ Cabinet Akwaba GROWTH CI ● Actif 12/01/2026 [Voir] │ │
│ │ Broker Plus STARTER CI ● Actif 08/03/2026 [Voir] │ │
│ │ Société Nsia CI ENTERPRISE CI ● Actif 01/02/2026 [Voir] │ │
│ │ Mutuelle Abidjan STARTER CI ⏳ En attente — [Voir] │ │
│ │ AssurAfrique SARL GROWTH CI ⚠ Suspendu 23/11/2025 [Voir] │ │
│ │ │ │
│ │ ◄ 1 2 ► Afficher 20 ▾ │ │ ← Pagination
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Status → Badge mapping:
| Statut | Badge variant | Icône | Label |
|---|---|---|---|
| PENDING | info |
⏳ | En attente |
| ACTIVE | success |
● | Actif |
| SUSPENDED | warning |
⚠ | Suspendu |
| DELETED | neutral |
✕ | Supprimé |
Supprimés tab: rows rendered with opacity: 0.6, italic raison sociale, no "Voir" button — replaced by "Audit" (ghost variant, opens read-only audit trail modal).
ADM-002 — Wizard création tenant (5 étapes)¶
Shaped by: D2 (wizard, PENDING after step 1), D9 (BYO-DB pw write-only)
┌──────────────────────────────────────────────────────────────────────┐
│ ← Retour à la liste Nouveau cabinet │
├──────────────────────────────────────────────────────────────────────┤
│ ① Identité ── ② Contact ── ③ Isolation ── ④ Conformité ── ⑤ Récap │ ← StepIndicator
├──────────────────────────────────────────────────────────────────────┤
Étape 1 — Identité légale
│ ┌─ Identité légale ──────────────────────────────────────────────┐ │
│ │ Raison sociale * [________________________________] │ │
│ │ Nom commercial * [________________________________] │ │
│ │ Pays * [Côte d'Ivoire (CI) ▾ — désactivé] │ │ ← Select disabled, V1 lock
│ │ ⓘ V1 : Côte d'Ivoire uniquement │ │
│ │ Numéro d'agrément CIMA * [________________________________] │ │
│ │ NCC * [________________________________] │ │
│ │ ⓘ Requis pour la facturation FNE │ │
│ │ Plan tarifaire * [STARTER ▾] │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ [Annuler] [Suivant ▸] │
Étape 2 — Contact & Adresse
│ ┌─ Contact principal ────────────────────────────────────────────┐ │
│ │ Nom complet * [________________________________] │ │
│ │ Email * [________________________________] │ │
│ │ Téléphone * [+225 ▾] [_____________________] │ │ ← préfixe +225 verrouillé V1
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌─ Adresse postale ──────────────────────────────────────────────┐ │
│ │ Adresse (ligne 1) * [________________________________] │ │
│ │ Adresse (ligne 2) [________________________________] │ │
│ │ Ville * [________________________________] │ │
│ │ Code postal [________________________________] │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ [◂ Précédent] [Suivant ▸] │
Étape 3 — Isolation base de données
│ ┌─ Profil d'isolation ───────────────────────────────────────────┐ │
│ │ ● Base partagée Papillon (recommandé) │ │
│ │ Isolation par tenant_id. Aucune configuration requise. │ │
│ │ │ │
│ │ ○ BYO-DB (clause contractuelle requise) │ │
│ │ Fournissez votre propre base PostgreSQL. │ │
│ │ │ │
│ │ ┌─ Connexion BYO-DB (visible seulement si BYO sélectionné) ─┐ │ │
│ │ │ JDBC URL * [jdbc:postgresql://host:5432/db] │ │ │
│ │ │ Utilisateur * [______________________________] │ │ │
│ │ │ Mot de passe * [••••••••••••••••] [👁 Afficher] │ │ │ ← type=password + toggle
│ │ │ Taille du pool * [10] │ │ │
│ │ │ ⚠ Les identifiants sont chiffrés (KEK). Ils ne seront │ │ │
│ │ │ jamais affichés après enregistrement. │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ [◂ Précédent] [Suivant ▸] │
Étape 4 — Conformité & Notifications
│ ┌─ Documents de conformité ──────────────────────────────────────┐ │
│ │ DPA signé (PDF) * │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ 📂 Glissez votre PDF ici ou cliquez pour sélectionner │ │ │ ← FileUpload
│ │ │ Taille max : 25 Mo — PDF uniquement │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ Date de signature * [JJ/MM/AAAA] │ │
│ │ Date d'expiration [JJ/MM/AAAA] ⓘ Optionnel │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌─ Identifiants notifications ───────────────────────────────────┐ │
│ │ ⚠ Requis pour l'activation du cabinet. │ │
│ │ SMS Sender ID * [______________________________] │ │
│ │ ⓘ Identifiant expéditeur SMS approuvé│ │
│ │ WA Business Account ID *[______________________________] │ │
│ │ ⓘ Trouvable dans Meta Business Manager│ │
│ │ Template WhatsApp [______________________________] │ │
│ │ ⓘ ID du template Meta approuvé │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ [◂ Précédent] [Suivant ▸] │
Étape 5 — Récapitulatif & Activation
│ ┌─ Checklist d'activation ───────────────────────────────────────┐ │
│ │ ✅ DPA signé uploadé │ │
│ │ ✅ SMS Sender ID renseigné │ │
│ │ ✅ WA Business Account ID renseigné │ │
│ │ ✅ Identité légale complète │ │
│ │ ✅ Contact principal renseigné │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌─ Résumé ───────────────────────────────────────────────────────┐ │
│ │ Cabinet Akwaba Assurances · GROWTH · CI │ │
│ │ Agrément CIMA : IB-CI-2019-0042 · NCC : 1234567 X │ │
│ │ Contact : Jean Konan · [email protected] · +225 07 08 09 10 │ │
│ │ Adresse : 12 av. du Commerce, Abidjan, CI │ │
│ │ Isolation : Base partagée Papillon │ │
│ │ DPA : dpa-akwaba-2026.pdf (signé le 15/01/2026) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ⓘ L'activation créera l'agence PRINCIPAL et enverra un lien │
│ d'activation à [email protected]. │
│ [◂ Précédent] [✓ Activer le cabinet] │ ← primary, disabled si checklist incomplète
ADM-003 — Détail tenant (page onglets)¶
Shaped by: D6 (tabs), D8 (locked fields), D3 (provisioning stepper)
En-tête commun à tous les onglets:
┌──────────────────────────────────────────────────────────────────────┐
│ ← Cabinets Cabinet Akwaba Assurances ● Actif GROWTH │
│ [Modifier] [Suspendre ▾] (danger) │
├──────────────────────────────────────────────────────────────────────┤
│ [Identité] [Documents] [Provisionnement] [Agences] [Paramètres] │ ← Tabs, underline variant
├──────────────────────────────────────────────────────────────────────┤
Onglet Identité (lecture):
│ ┌─ Identité légale ─────────────────────────────────────────────┐ │
│ │ Raison sociale Cabinet Akwaba Assurances │ │
│ │ Nom commercial Akwaba Assurances │ │
│ │ Pays 🇨🇮 Côte d'Ivoire (CI) │ │
│ │ Agrément CIMA IB-CI-2019-0042 │ │
│ │ NCC 1234567 X │ │
│ │ Plan GROWTH │ │
│ │ Slug cabinet-akwaba-assurances │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌─ Contact principal ───────────────────────────────────────────┐ │
│ │ Nom Jean Konan │ │
│ │ Email [email protected] │ │
│ │ Téléphone +225 07 08 09 10 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌─ Identité visuelle ───────────────────────────────────────────┐ │
│ │ Logo [Aperçu 64×64] [email protected] │ │
│ │ Couleur ██ #3B7A57 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ Créé le 12/01/2026 · Activé le 12/01/2026 · Modifié le 03/05/2026 │
Onglet Documents:
│ ⚠ Le DPA expire dans 18 jours (15/06/2026). Pensez à le renouveler.│ ← InlineBanner warning (conditionnel)
│ │
│ ┌─ DPA signé ──────────────────────── v2 (active) ─────────────┐ │
│ │ Uploadé le 10/01/2026 par Admin Papillon │ │
│ │ SHA-256 : a3f2...9c14 │ │
│ │ [📥 Télécharger] [↑ Nouvelle version] │ │
│ │ ▼ Historique (1 version archivée) │ │
│ │ v1 — 12/01/2025 — a1b2… [📥 Télécharger] │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌─ Agrément CIMA ──────────────────── v1 (active) ─────────────┐ │
│ │ Uploadé le 12/01/2026 SHA-256 : b4e1…2f09 │ │
│ │ [📥 Télécharger] [↑ Nouvelle version] │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌─ Enregistrement FNE ───────────────── Non uploadé ───────────┐ │
│ │ ⓘ Document facultatif pour la facturation FNE │ │
│ │ [↑ Uploader] │ │
│ └───────────────────────────────────────────────────────────────┘ │
Onglet Provisionnement:
│ Dernier provisionnement : 12/01/2026 à 14:32 — ✅ Succès │
│ │
│ ✅ 1. Profil pays (CI) 0.1 s │
│ ✅ 2. Base de données (Shared — aucune conf.) 0.0 s │
│ ✅ 3. Keycloak — client créé 2.3 s │
│ ✅ 4. Agence PRINCIPAL créée 0.4 s │
│ ✅ 5. Invitation admin envoyée (via AUTH) 0.2 s │
│ │
│ État d'échec (exemple) : │
│ ✅ 1. Profil pays (CI) 0.1 s │
│ ✅ 2. Base de données (BYO) 8.4 s │
│ ❌ 3. Keycloak — KEYCLOAK_UNREACHABLE │
│ Le service d'authentification est indisponible. Réessayez. │
│ [Relancer à partir de l'étape 3] │
│ ⊘ 4. Agence PRINCIPAL (ignorée — step 3 bloqué) │
│ ⊘ 5. Invitation admin (ignorée — step 3 bloqué) │
Onglet Agences:
│ [+ Nouvelle agence] │
│ Code ↕ Nom ↕ Ville ↕ Statut Créée le Actions │
│ ───────────────────────────────────────────────────────────────── │
│ PRINCIPAL Akwaba Assur. Abidjan ● Active 12/01/26 [Modifier] [Désactiver 🔒] ← disabled si seule active
│ ABJ-NORD Agence Nord Cocody ● Active 03/03/26 [Modifier] [Désactiver]
│ ABJ-SUD Agence Sud Marcory ⊘ Inactive 15/04/26 [Modifier] [Réactiver]
Onglet Paramètres (identique à OP-003, mais tous champs éditables par platform admin):
│ (cf. wireframe OP-003 — seule différence : platform admin peut │
│ modifier renewal_window_days et les rétentions sans restriction │
│ de rôle ; CABINET_ADMIN voit les mêmes champs mais bounded) │
Tenant SUSPENDU — bannière dans tous les onglets:
│ ⚠ Ce cabinet est suspendu depuis le 15/04/2026. │
│ Sans réactivation, il sera supprimé le 14/07/2026. │
│ Les données d'audit et factures seront conservées 10 ans. │
│ Les données personnelles des assurés seront supprimées. │
Tenant DELETED — bannière + tous les champs read-only:
│ ✕ Ce cabinet a été supprimé le 14/07/2026. │
│ Les données personnelles ont été purgées. Seul l'audit subsiste. │
ADM-004 — Modal Suspension¶
Shaped by: D4 (reason radio + danger confirm)
┌──────────────────────────────────────────────────────────────────────┐
│ ✕ Suspendre Cabinet Akwaba Assurances │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ╔══════════════════════════════════════════════════════════════╗ │
│ ║ ⛔ Cette action révoque immédiatement l'accès de tous les ║ │
│ ║ opérateurs du cabinet et bloque les nouveaux paiements. ║ │
│ ╚══════════════════════════════════════════════════════════════╝ │
│ │
│ Motif de suspension * │
│ ● Facturation impayée │
│ ○ Raison légale ou réglementaire │
│ ○ Demande du cabinet │
│ ○ Autre │
│ │
│ Détail (facultatif) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ [Annuler] [⛔ Confirmer la suspension] (danger) │
└──────────────────────────────────────────────────────────────────────┘
ADM-005 — Modal Réactivation¶
┌──────────────────────────────────────────────────────────────────────┐
│ ✕ Réactiver Cabinet Akwaba Assurances │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ✅ La réactivation rétablira l'accès des opérateurs │
│ et relancera les campagnes planifiées. │
│ │
│ Suspendu depuis : 15/04/2026 (37 jours) │
│ Motif initial : Facturation impayée │
│ │
│ [Annuler] [✅ Réactiver le cabinet] (success) │
└──────────────────────────────────────────────────────────────────────┘
OP-001 — Identité du cabinet (cabinet admin)¶
Shaped by: D8 (locked fields), D1 (cabinet admin subtree)
┌──────────────────────────────────────────────────────────────────────┐
│ OperatorShell (Sidebar — "Mon cabinet" nav item) │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Paramètres du cabinet │ │
│ │ [Identité] [Agences] [Paramètres] │ │ ← Tabs, 3 onglets seulement
│ ├──────────────────────────────────────────────────────────────────┤ │
│ │ ┌─ Identité légale — lecture seule ──────────────────────────┐ │ │
│ │ │ 🔒 Raison sociale Cabinet Akwaba Assurances │ │ │ ← Input read-only + icône verrou
│ │ │ 🔒 Nom commercial Akwaba Assurances │ │ │
│ │ │ 🔒 Pays 🇨🇮 Côte d'Ivoire │ │ │
│ │ │ 🔒 Agrément CIMA IB-CI-2019-0042 │ │ │
│ │ │ 🔒 NCC 1234567 X │ │ │
│ │ │ ⓘ Modifiable par le support Papillon uniquement │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ ┌─ Identité visuelle ────────────────────────────────────────┐ │ │
│ │ │ Logo du cabinet │ │ │
│ │ │ ┌────────────────────────────────────────────────────────┐│ │ │
│ │ │ │ [Aperçu logo actuel 96×96 px] ││ │ │
│ │ │ │ 📂 Glissez votre logo ici ou cliquez pour sélectionner││ │ │ ← FileUpload
│ │ │ │ PNG ou SVG · ≤ 200 Ko · min 64×64 px ││ │ │
│ │ │ └────────────────────────────────────────────────────────┘│ │ │
│ │ │ Couleur principale [██ #3B7A57] [__________] Aperçu ▶ │ │ │
│ │ │ Email d'assistance [[email protected] ] │ │ │
│ │ │ ⓘ Affiché dans le portail client │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ [Sauvegarder] │ │ ← sticky bottom on tablet
│ └──────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
OP-002 — Gestion des agences¶
Shaped by: D7 (preventive disable last agency)
┌──────────────────────────────────────────────────────────────────────┐
│ Paramètres du cabinet │
│ [Identité] [Agences] [Paramètres] │
├──────────────────────────────────────────────────────────────────────┤
│ 3 agences [+ Nouvelle agence] │
│ Code ↕ Nom ↕ Ville ↕ Statut Créée le Actions │
│ ───────────────────────────────────────────────────────────────── │
│ PRINCIPAL Akwaba Assur. Abidjan ● Active 12/01/26 [Modifier] [Désactiver — disabled 🔒 si n=1]
│ ABJ-NORD Agence Nord Cocody ● Active 03/03/26 [Modifier] [Désactiver]
│ ABJ-SUD Agence Sud Marcory ⊘ Inactive 15/04/26 [Modifier] [Réactiver]
Modal Nouvelle agence / Modifier agence:
┌──────────────────────────────────────────────────────────────────┐
│ ✕ Nouvelle agence │
├──────────────────────────────────────────────────────────────────┤
│ Code * [________________________] │
│ ⓘ Identifiant unique au sein du cabinet │
│ Nom * [________________________] │
│ Ville * [________________________] │
│ Adresse [________________________] │
│ Téléphone [+225 ▾] [_______________] │
│ Responsable [________________________] │
│ │
│ [Annuler] [Créer l'agence] (primary) │
└──────────────────────────────────────────────────────────────────┘
OP-003 — Paramètres du cabinet¶
Shaped by: D10 ([LEGAL-REVIEW] inline)
┌──────────────────────────────────────────────────────────────────────┐
│ Paramètres du cabinet │
│ [Identité] [Agences] [Paramètres] │
├──────────────────────────────────────────────────────────────────────┤
│ ┌─ Renouvellement ──────────────────────────────────────────────┐ │
│ │ Fenêtre de renouvellement [30] jours │ │
│ │ ⓘ Entre 7 et 90 jours │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌─ Notifications ───────────────────────────────────────────────┐ │
│ │ Canal par défaut ● WhatsApp ○ SMS │ │
│ │ Template WA [HX_template_renewal_v2 ] │ │
│ │ Plafond quotidien [500] messages/jour │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌─ Niveaux d'authentification ─────────────────────────────────┐ │
│ │ Niveau requis [1 — Lien signé ▾] ⓘ Niveaux 2/3 en V2 │ │ ← Select disabled
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌─ Rétention des données [LEGAL-REVIEW ⓘ] ───────────────────┐ │
│ │ Dossiers sources ≥ [90 ] jours (plancher : 90 jours) │ │
│ │ Journaux d'audit ≥ [10 ] années (plancher : 10 ans) │ │
│ │ Quittances ≥ [10 ] années (plancher : 10 ans) │ │
│ │ ⚠ Durées minimales légales — délibération ARTCI en cours. │ │
│ │ Ne modifiez pas sans avis juridique. │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ Papillon agit en sous-traitant. [Voir la liste des sous-traitants ↗]│ ← Compliance link §9.2
│ [Sauvegarder] │
└──────────────────────────────────────────────────────────────────────┘
4. Interaction Specs — 5 états par écran¶
ADM-001 — Liste des cabinets¶
| État | Comportement UI |
|---|---|
| loading | 5 rangées Skeleton dans DataTable (colonnes proportionnelles). Bouton "+ Nouveau cabinet" reste actif. Onglets affichés, compteurs en Skeleton. |
| empty | Onglet "Tous" : EmptyState + "Aucun cabinet enregistré. Créez le premier cabinet pour démarrer." + [+ Créer le premier cabinet]. Onglet filtré : "Aucun cabinet {statut} pour le moment." sans illustration. |
| error | InlineBanner error : "Impossible de charger les cabinets. Vérifiez votre connexion." + [Réessayer] (Button ghost). Filtre et bouton création restent actifs. |
| low-network | Toast warning : "Connexion lente — données en cours de chargement." DataTable affiche données en cache TanStack Query (stale-while-revalidate) avec bandeau de données non actualisées. |
| success | État nominal : tableau données fraîches, compteurs onglets à jour, InlineBanner DPA visible si applicable. |
ADM-002 — Wizard création¶
| État | Comportement UI |
|---|---|
| loading | Soumission étape : bouton "Suivant" → variant loading (spinner), formulaire disabled. Durée typique < 1 s (PATCH). |
| empty | N/A — wizard démarre toujours vide. |
| error | Champ : Input error + message inline (ex. "NCC déjà utilisé : Cabinet Konan SARL. Contactez le superviseur."). Réseau : Toast error + formulaire non démonté (données préservées). Bouton "Suivant" redevient cliquable. Similar name : Modal confirmation "Cabinets similaires détectés : {names}. Continuer ?" |
| low-network | InlineBanner warning : "Connexion instable — votre saisie est conservée." Step 4 (upload DPA) bloque si hors ligne : "Upload impossible hors ligne. Reconnectez-vous." |
| success | Step 5 → Activer → ProvisioningPipeline s'ouvre inline → toast "Cabinet Akwaba Assurances activé." + redirect ADM-003. |
ADM-003 — Détail tenant¶
| État | Comportement UI |
|---|---|
| loading | Skeleton dans chaque section de l'onglet actif. Tabs rendus immédiatement (structure visible). En-tête (raison sociale + badge) chargé en priorité. |
| empty | Onglet Documents : card FNE "Non uploadé". Onglet Agences vide : EmptyState "L'agence PRINCIPAL est créée automatiquement à l'activation." |
| error | InlineBanner error dans l'onglet actif + [Réessayer]. L'en-tête reste visible. |
| low-network | Toast warning passif. Formulaire modification bloqué avec InlineBanner "Modification impossible hors ligne. Reconnectez-vous." |
| success | Données fraîches. Toast si action vient d'être effectuée (suspension, réactivation, upload doc). |
ADM-004 / ADM-005 — Modals Suspend / Réactiver¶
| État | Comportement UI |
|---|---|
| loading | Bouton confirmer → variant loading. Modal reste ouverte pendant l'API call. |
| empty | N/A |
| error | InlineBanner error dans la modal : "Impossible de suspendre. Réessayez." Bouton débloqué. |
| low-network | Bouton confirmer bloqué : "Connexion requise pour cette action." |
| success | Modal se ferme. Toast "Cabinet suspendu." / "Cabinet réactivé." Badge sur la page détail mis à jour. |
OP-001 — Identité cabinet¶
| État | Comportement UI |
|---|---|
| loading | Skeleton pour aperçu logo + champs. |
| empty | Logo non uploadé : placeholder gris avec initiales + "Aucun logo — uploadez un PNG ou SVG." |
| error | Logo : erreur inline sous FileUpload. Save : Toast error "Impossible de sauvegarder. Réessayez." |
| low-network | InlineBanner warning. Save mis en file offline queue avec indicateur sync en attente. |
| success | Toast success "Identité visuelle mise à jour." Aperçu logo rafraîchi. |
OP-002 — Agences¶
| État | Comportement UI |
|---|---|
| loading | Skeleton 3 rangées dans DataTable. |
| empty | EmptyState "Aucune agence créée. L'agence PRINCIPAL est créée automatiquement lors de l'activation." |
| error | Modal : Input error sous "Code" si doublon. Submit réseau : Toast error. |
| low-network | Création d'agence bloquée : InlineBanner "Création impossible hors ligne." (validation serveur requise immédiatement). |
| success | Toast success "Agence ABJ-NORD créée." Modal se ferme, DataTable rafraîchit. |
OP-003 — Paramètres¶
| État | Comportement UI |
|---|---|
| loading | Skeleton dans chaque section. |
| empty | N/A — paramètres toujours pré-remplis avec valeurs par défaut. |
| error | Champ hors borne → Input error + message inline immédiat (client-side). Réseau → Toast error. |
| low-network | InlineBanner warning. Save mis en file offline queue. |
| success | Toast success "Paramètres enregistrés." |
5. Comportement responsive¶
TENANT est operator-side. Desktop-primary. Cabinet admin peut accéder depuis tablette.
| Breakpoint | [ADMIN] (platform admin) | [OPERATOR] (cabinet admin) |
|---|---|---|
| 360–767 (mobile) | InlineBanner "Veuillez utiliser un écran plus grand pour l'administration plateforme." Navigation encore fonctionnelle. | Tabs scroll horizontal. Card layout 1 col. Formulaires 1 col. FileUpload full-width. Bouton sauvegarder sticky bottom. |
| 768–1023 (tablet) | Sidebar collapsed par défaut. DataTable scroll horizontal. Wizard en colonne unique. Tabs scroll horizontal. | Full experience. 2-col field grid. Sidebar collapsed. |
| 1024–1439 (desktop) | Sidebar expanded. DataTable toutes colonnes. Wizard 2-col fields (étapes 1, 2). | Confortable. 2-col field grid. |
| 1440+ (large desktop) | Contenu centré, max-width: var(--content-max-width). |
Idem. |
Règle Tabs : overflow-x: auto avec gradient-fade droit si overflow. Touch target minimum 48 px height.
Règle Modal : Width 560 px sur desktop, 100 % viewport minus 32 px sur mobile, centré vertically.
6. Comportement connectivité (operator-side online-first)¶
TENANT n'est pas soumis aux règles customer low-bandwidth. Néanmoins : les admins en déplacement utilisent parfois une 4G instable.
| Scénario | Comportement |
|---|---|
| Chargement lent | TanStack Query stale-while-revalidate : données cache affichées immédiatement. Toast warning "Données non actualisées — chargement en cours." |
| Perte de connexion mid-wizard | Données formulaire préservées (état React local). InlineBanner "Connexion perdue. Votre saisie est conservée." Bouton "Suivant" bloqué. Retry automatique sur navigator.onLine = true. |
| Upload DPA lent | Progress bar dans FileUpload (chunked 5 Mo). "Envoi en cours… 60 %". Annulation possible. Sur erreur réseau : "Échec de l'envoi. Réessayez (votre fichier est conservé)" — fichier ne disparaît pas. |
| Provisionnement > 30 s | ProvisioningPipeline affiche le step en cours avec spinner. Bouton "Annuler" (ghost danger) — n'annule pas les steps complétés (idempotent). |
| BYO-DB validation lente | Step 2 du pipeline affiche "Test de connexion… 12 s". Pas de timeout UI avant 60 s (NFR-03). |
| Reconnexion | Toast success "Connexion rétablie." Formulaires débloqués. Offline queue flushée. |
7. Micro-copy française — Référence complète¶
ADM-001 — Liste¶
| Élément | Copie |
|---|---|
| Page title | Administration plateforme |
| Subtitle | Gestion des tenants (cabinets) |
| Bouton créer | + Nouveau cabinet |
| DPA banner | ⚠ {n} DPA expirent dans moins de 30 jours. → Voir les cabinets concernés |
| Search placeholder | Rechercher par raison sociale, NCC… |
| Plan filter | Plan : Tous |
| Tab Tous | Tous ({n}) |
| Tab En attente | En attente ({n}) |
| Tab Actifs | Actifs ({n}) |
| Tab Suspendus | Suspendus ({n}) |
| Tab Supprimés | Supprimés ({n}) |
| Compteur | {n} cabinet(s) |
| Empty global | Aucun cabinet enregistré. Créez le premier cabinet pour démarrer. |
| Empty filtre | Aucun cabinet {statut} pour le moment. |
| Error loading | Impossible de charger les cabinets. Vérifiez votre connexion. |
| Badge PENDING | En attente |
| Badge ACTIVE | Actif |
| Badge SUSPENDED | Suspendu |
| Badge DELETED | Supprimé |
| Action voir | Voir |
| Action audit (deleted) | Audit |
ADM-002 — Wizard¶
| Étape / Élément | Copie |
|---|---|
| Titre page | Nouveau cabinet |
| Back link | ← Retour à la liste |
| Étape 1 | Identité légale |
| Étape 2 | Contact & Adresse |
| Étape 3 | Isolation |
| Étape 4 | Conformité |
| Étape 5 | Récapitulatif |
| Bouton Suivant | Suivant ▸ |
| Bouton Précédent | ◂ Précédent |
| Bouton Annuler | Annuler |
| Bouton Activer | ✓ Activer le cabinet |
| Tooltip Activer disabled | Checklist incomplète : {liste des éléments manquants} |
| Label raison sociale | Raison sociale * |
| Label nom commercial | Nom commercial * |
| Label pays | Pays * |
| Tooltip pays locked | V1 : Côte d'Ivoire uniquement. D'autres pays CIMA disponibles en V2. |
| Label agrément | Numéro d'agrément CIMA * |
| Label NCC | NCC (Numéro Compte Contribuable) * |
| Helper NCC | Requis pour la facturation électronique FNE. |
| Label plan | Plan tarifaire * |
| Label contact nom | Nom complet du contact principal * |
| Label contact email | Email * |
| Label contact tel | Téléphone * |
| Préfixe tel | +225 |
| Label adresse 1 | Adresse (ligne 1) * |
| Label adresse 2 | Adresse (ligne 2) |
| Label ville | Ville * |
| Label cp | Code postal |
| Radio shared | Base partagée Papillon (recommandé) |
| Radio shared desc | Isolation par tenant_id. Aucune configuration requise. |
| Radio BYO | BYO-DB (clause contractuelle requise) |
| Radio BYO desc | Fournissez votre propre base PostgreSQL. |
| Label jdbc | URL JDBC * |
| Label db user | Utilisateur base de données * |
| Label db pass | Mot de passe * |
| Label pool | Taille du pool de connexions * |
| Warning BYO creds | Les identifiants sont chiffrés (KEK). Ils ne seront jamais affichés après enregistrement. |
| Label DPA upload | DPA signé (PDF) * |
| FileUpload hint | Glissez votre PDF ici ou cliquez pour sélectionner. Taille max : 25 Mo — PDF uniquement. |
| Label DPA signed_at | Date de signature * |
| Label DPA expires_at | Date d'expiration (optionnel) |
| Label SMS sender | SMS Sender ID * |
| Helper SMS sender | Identifiant expéditeur approuvé par l'opérateur mobile (ex. AKWABA). |
| Label WA business | WA Business Account ID * |
| Helper WA business | Trouvable dans Meta Business Manager → Comptes WhatsApp Business. |
| Label WA template | Template WhatsApp (ID Meta) |
| Helper WA template | ID du template Meta approuvé pour les rappels de renouvellement. |
| Warning notif required | ⚠ Le SMS Sender ID et le WA Business Account ID sont requis pour l'activation. |
| Checklist DPA | DPA signé uploadé |
| Checklist SMS | SMS Sender ID renseigné |
| Checklist WA | WA Business Account ID renseigné |
| Checklist identity | Identité légale complète |
| Checklist contact | Contact principal renseigné |
| Recap note | L'activation créera l'agence PRINCIPAL et enverra un lien d'activation à {email}. |
| Error NCC dup | Un cabinet avec ce NCC existe déjà : {legal_name}. Contactez le superviseur. |
| Error pays locked | Pays non disponible en V1 (Côte d'Ivoire uniquement). |
| Warn name similar | Cabinets similaires détectés : {names}. Ces cabinets existent déjà. Continuer quand même ? |
| Error champ requis | Ce champ est obligatoire. |
| Error email invalide | Adresse email invalide. |
| Error phone invalide | Numéro de téléphone invalide. Format attendu : +225 XX XX XX XX XX |
| Error file too large | Le fichier dépasse la taille maximale de 25 Mo. |
| Error file type | Format non supporté. Seuls les fichiers PDF sont acceptés. |
ADM-003 — Détail¶
| Élément | Copie |
|---|---|
| Back link | ← Cabinets |
| Tab Identité | Identité |
| Tab Documents | Documents |
| Tab Provisionnement | Provisionnement |
| Tab Agences | Agences |
| Tab Paramètres | Paramètres |
| Bouton Modifier | Modifier |
| Bouton Suspendre | Suspendre |
| Bouton Réactiver | Réactiver |
| Infos readonly | Créé le {date} · Activé le {date} · Modifié le {date} |
| Locked field helper | Modifiable par le support Papillon uniquement. |
| DPA expiry warning | ⚠ Le DPA expire dans {n} jours ({date}). Pensez à le renouveler. |
| Doc version active | v{n} (active) |
| Doc uploaded | Uploadé le {date} par {admin_name} |
| Doc dl button | 📥 Télécharger |
| Doc replace button | ↑ Nouvelle version |
| Doc history | ▼ Historique ({n} version(s) archivée(s)) |
| Doc not uploaded | Non uploadé |
| Provision last run OK | Dernier provisionnement : {date} à {heure} — ✅ Succès |
| Provision last run KO | Dernier provisionnement : {date} à {heure} — ❌ Échec |
| Pipeline step OK | ✅ {n}. {label} — {duration} |
| Pipeline step FAIL | ❌ {n}. {label} — {error_code} |
| Pipeline step RUNNING | ⏳ {n}. {label} — En cours… |
| Pipeline step SKIPPED | ⊘ {n}. {label} (ignoré — déjà complété) |
| Bouton retry step | Relancer à partir de l'étape {n} |
| Error CP_NOT_SEEDED | Profil pays CI introuvable. Contactez l'équipe Papillon. |
| Error BYO_DB_UNREACHABLE | Connexion à la base BYO impossible. Vérifiez l'URL et le mot de passe. |
| Error BYO_DB_MIGRATION | Migration Flyway échouée. Vérifiez les droits (ALTER, CREATE TABLE). |
| Error BYO_DB_SMOKE | Test d'isolation échoué. Vérifiez que le schéma est vide et les droits DML. |
| Error KEYCLOAK_UNREACHABLE | Service d'authentification indisponible. Réessayez dans quelques minutes. |
| Agency tooltip last active | Au moins une agence active est requise par cabinet. |
| Banner SUSPENDED | ⚠ Ce cabinet est suspendu depuis le {date}. Sans réactivation, il sera supprimé le {date+90j}. Les données d'audit et factures seront conservées 10 ans. Les données personnelles des assurés seront supprimées. |
| Banner DELETED | ✕ Ce cabinet a été supprimé le {date}. Les données personnelles ont été purgées. Seul l'audit subsiste. |
ADM-004 / ADM-005 — Modals¶
| Élément | Copie |
|---|---|
| Suspend title | Suspendre {legal_name} ? |
| Suspend warning | ⛔ Cette action révoque immédiatement l'accès de tous les opérateurs du cabinet et bloque les nouveaux paiements. |
| Suspend reason label | Motif de suspension * |
| Radio MANUAL_BILLING | Facturation impayée |
| Radio MANUAL_LEGAL | Raison légale ou réglementaire |
| Radio MANUAL_TENANT_REQUEST | Demande du cabinet |
| Radio MANUAL_OTHER | Autre |
| Suspend detail label | Détail (facultatif) |
| Suspend confirm btn | ⛔ Confirmer la suspension |
| Reactivate title | Réactiver {legal_name} ? |
| Reactivate info | ✅ La réactivation rétablira l'accès des opérateurs et relancera les campagnes planifiées. |
| Reactivate since | Suspendu depuis : {date} ({n} jours) · Motif : {reason} |
| Reactivate confirm btn | ✅ Réactiver le cabinet |
| Bouton annuler | Annuler |
OP-001 — Identité / Branding¶
| Élément | Copie |
|---|---|
| Page title | Paramètres du cabinet |
| Section légal | Identité légale — lecture seule |
| Locked helper | Modifiable par le support Papillon uniquement. |
| Section branding | Identité visuelle |
| Label logo | Logo du cabinet |
| FileUpload hint | Glissez votre logo ici ou cliquez. PNG ou SVG · ≤ 200 Ko · min 64×64 px |
| Error logo size | Le fichier dépasse 200 Ko. |
| Error logo format | Format non supporté. Utilisez PNG ou SVG. |
| Error logo dims | Dimensions insuffisantes. Le logo doit faire au minimum 64×64 pixels. |
| Label couleur | Couleur principale |
| Label support email | Email d'assistance |
| Helper support email | Affiché dans le portail client et les notifications envoyées aux assurés. |
| Bouton sauvegarder | Sauvegarder |
| Success toast | Identité visuelle mise à jour. |
OP-002 — Agences¶
| Élément | Copie |
|---|---|
| Section title | Agences du cabinet |
| Bouton créer | + Nouvelle agence |
| Compteur | {n} agence(s) |
| Empty | Aucune agence créée. L'agence PRINCIPAL est créée automatiquement à l'activation. |
| Modal title créer | Nouvelle agence |
| Modal title éditer | Modifier {agency_name} |
| Label code | Code * |
| Helper code | Identifiant unique au sein du cabinet (ex. ABJ-SUD). Lettres, chiffres et tirets uniquement. |
| Label nom | Nom * |
| Label ville | Ville * |
| Label adresse | Adresse |
| Label phone | Téléphone |
| Label manager | Responsable |
| Bouton créer modal | Créer l'agence |
| Bouton modifier modal | Enregistrer les modifications |
| Error code dup | Ce code est déjà utilisé par une autre agence du cabinet. |
| Success créer | Agence {code} créée. |
| Success modifier | Agence {code} mise à jour. |
| Tooltip last active | Au moins une agence active est requise par cabinet. |
| Success disable | Agence {code} désactivée. |
| Success reactivate | Agence {code} réactivée. |
OP-003 — Paramètres¶
| Élément | Copie |
|---|---|
| Section renouvellement | Renouvellement |
| Label window | Fenêtre de renouvellement |
| Helper window | Nombre de jours avant l'échéance où les rappels sont envoyés. Entre 7 et 90 jours. |
| Error window | La fenêtre doit être comprise entre 7 et 90 jours. |
| Section notif | Notifications |
| Label canal | Canal de notification par défaut |
| Radio WA | |
| Radio SMS | SMS |
| Label template | Template WhatsApp (ID Meta) |
| Label daily cap | Plafond d'envoi quotidien |
| Helper daily cap | Nombre maximum de messages envoyés par jour pour ce cabinet. |
| Section auth | Niveaux d'authentification |
| Auth level V1 | 1 — Lien signé (niveaux 2 et 3 disponibles en V2) |
| Section retention | Rétention des données |
| LEGAL-REVIEW tooltip | Durées minimales légales basées sur une analyse préliminaire — délibération ARTCI sectorielle assurance CI en cours. Ne pas descendre sous ces seuils sans confirmation juridique. |
| Label source retention | Dossiers sources |
| Helper source floor | Plancher légal : 90 jours |
| Error source below | La durée minimale de rétention des dossiers sources est 90 jours. |
| Label audit retention | Journaux d'audit |
| Helper audit floor | Plancher légal : 10 ans |
| Error audit below | La durée minimale de rétention des journaux d'audit est 10 ans. |
| Label quittance retention | Quittances |
| Helper quittance floor | Plancher légal : 10 ans |
| Error quittance below | La durée minimale de rétention des quittances est 10 ans. |
| Bouton sauvegarder | Sauvegarder |
| Success toast | Paramètres enregistrés. |
| Compliance link | Papillon agit en sous-traitant. Voir la liste des sous-traitants ↗ |
États globaux¶
| État | Copie |
|---|---|
| Loading générique | Chargement en cours… |
| Error générique | Une erreur est survenue. Réessayez ou contactez le support Papillon. |
| Offline | Connexion perdue — votre saisie est conservée. |
| Low-network | Connexion instable — chargement en cours. |
| Reconnected | Connexion rétablie. |
| Sync pending | Synchronisation en attente ({n} opération(s)) |
8. Spécifications nouvelles composantes¶
Composantes à créer dans
frontend-operator/src/components/ui/avant les pages TENANT. Priorités mises à jour dansdocs/standards/design-system.md.
8.1 StepIndicator — Priorité HIGH¶
Location: components/ui/StepIndicator.tsx + StepIndicator.module.css
interface StepIndicatorProps {
steps: { label: string }[];
currentStep: number; // 0-indexed
completedSteps: number[]; // indices
}
Rendu visuel:
- Dot pending : cercle outline, --color-border, label --color-text-secondary
- Dot active : cercle plein --color-primary, label --font-weight-semibold, --color-text-primary
- Dot completed : cercle plein --color-success, checkmark SVG inline
- Connector : ligne --color-border (pending) ou --color-success (completed)
Responsive: labels tronqués sur tablet (max 8 chars + ellipsis), masqués sur mobile (dots uniquement, aria-label maintenu).
Accessibility: role="list", chaque step role="listitem", aria-current="step" sur active, aria-label="{label} — étape {n} sur {total}".
8.2 InlineBanner — Priorité HIGH¶
Location: components/ui/InlineBanner.tsx + InlineBanner.module.css
interface InlineBannerProps {
variant: 'info' | 'warning' | 'error' | 'success';
children: ReactNode;
action?: { label: string; onClick: () => void };
dismissible?: boolean;
className?: string;
}
Tokens:
- info : --color-info teinté, icône ℹ
- warning : --color-warning teinté, icône ⚠
- error : --color-error teinté, icône ⛔
- success : --color-success teinté, icône ✅
Accessibility: role="alert" pour error, role="status" pour les autres, aria-live="polite".
8.3 FileUpload — Priorité HIGH¶
Location: components/ui/FileUpload.tsx + FileUpload.module.css
interface FileUploadProps {
accept: string; // ".pdf" | ".png,.svg"
maxSizeKb: number;
minDimensions?: { w: number; h: number };
onFile: (file: File) => void;
onClear?: () => void;
currentFileLabel?: string;
progress?: number; // 0–100 pendant upload
error?: string;
disabled?: boolean;
}
États:
1. idle : zone drag-drop, icône 📂, hint text
2. drag-over : bordure --color-primary, fond --color-primary-subtle, transition: --duration-fast
3. has-file : nom fichier + taille + [✕ Supprimer] + [📂 Remplacer]
4. uploading : progress bar --color-primary, pourcentage, [Annuler]
5. error : InlineBanner error sous la zone (fichier sélectionné préservé)
Validation client-side immédiate: taille, MIME type, dimensions (canvas check pour images).
8.4 Tabs — Priorité HIGH¶
Location: components/ui/Tabs.tsx + Tabs.module.css
interface TabsProps {
tabs: { id: string; label: string; disabled?: boolean }[];
activeTab: string;
onTabChange: (id: string) => void;
variant?: 'underline' | 'pill';
}
underline (défaut, detail pages): ligne sous l'onglet actif --color-primary, texte --color-primary, --font-weight-semibold.
pill (filtres liste): fond --color-primary sur actif, texte blanc; fond transparent inactif.
Accessibility: role="tablist", role="tab", aria-selected, keyboard Arrow left/right, Home, End.
Responsive: overflow-x: auto avec gradient-fade droit. Touch target 48 px height minimum.
8.5 Skeleton — Priorité HIGH¶
Location: components/ui/Skeleton.tsx + Skeleton.module.css
interface SkeletonProps {
width?: string;
height?: string;
variant?: 'text' | 'rect' | 'circle';
className?: string;
}
Animation: shimmer background: linear-gradient(90deg, var(--color-bg-tertiary) 25%, var(--color-bg-secondary) 50%, var(--color-bg-tertiary) 75%), background-size: 200%, animation: shimmer 1.5s infinite.
prefers-reduced-motion: remplace l'animation par un fond statique --color-bg-tertiary.
8.6 EmptyState — Priorité HIGH¶
Location: components/ui/EmptyState.tsx + EmptyState.module.css
interface EmptyStateProps {
title: string;
description?: string;
action?: { label: string; onClick: () => void };
icon?: ReactNode; // SVG ≤ 48×48, `aria-hidden="true"`
}
Centré verticalement dans son conteneur. Icône en --color-text-tertiary. Titre --font-size-h3. Description --color-text-secondary. Action = Button variant="secondary".
8.7 Switch — Priorité MEDIUM¶
Location: components/ui/Switch.tsx + Switch.module.css
interface SwitchProps {
label: string;
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
helperText?: string;
}
Accessibility: role="switch", aria-checked, label cliquable (htmlFor), touch target 48 px.
8.8 ProvisioningPipeline — Module-specific¶
Location: modules/tenant-admin/components/ProvisioningPipeline.tsx
type StepStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
interface PipelineStep {
id: string;
label: string;
status: StepStatus;
durationMs?: number;
errorCode?: string;
errorMessage?: string; // French, from server
onRetry?: () => void;
}
interface ProvisioningPipelineProps {
steps: PipelineStep[];
lastRunAt?: string;
overallStatus: 'idle' | 'running' | 'success' | 'failed';
onRetryAll?: () => void;
}
Polling: TanStack Query refetchInterval: 2000 tant que overallStatus === 'running'. Arrêt sur success | failed.
prefers-reduced-motion: remplace spinner SVG par "En cours…" texte.
8.9 Mise à jour Component Inventory¶
Les composantes suivantes passent de la liste "Needed" à "Available" une fois développées pour TENANT:
| Composante | Priorité mise à jour | Emplacement final |
|---|---|---|
| StepIndicator | HIGH (était LOW) | components/ui/ |
| InlineBanner | HIGH (était MEDIUM) | components/ui/ |
| FileUpload | HIGH (inchangé) | components/ui/ |
| Tabs | HIGH (était MEDIUM) | components/ui/ |
| Skeleton | HIGH (était MEDIUM) | components/ui/ |
| EmptyState | HIGH (était MEDIUM) | components/ui/ |
| Switch | MEDIUM (inchangé) | components/ui/ |
9. Compliance UI¶
9.1 DPA obligatoire avant activation (AC-06.3 · Loi 2013-450 art. 1)¶
FileUpload DPA dans wizard étape 4, mention contextuelle :
« Le Contrat de Traitement des Données (DPA) est obligatoire pour l'activation du cabinet, conformément à la loi ivoirienne n°2013-450 relative à la protection des données personnelles. Le cabinet est responsable de traitement ; Papillon agit en sous-traitant. [Modèle DPA ↗] »
Le bouton "Activer" (étape 5) reste disabled si DPA absent, tooltip : "DPA signé manquant — uploadez le PDF avant activation."
9.2 Liste des sous-traitants (Loi 2013-450 art. 42)¶
Bas de page OP-003 (Paramètres) :
« Papillon agit en tant que sous-traitant pour votre cabinet. Consultez [la liste de nos sous-traitants ↗] (Twilio, Meta WhatsApp Cloud API, Cinetpay, Orange Money CI, Wave CI). »
Lien → page statique /legal/sous-traitants.
9.3 Rétention sectorielle [LEGAL-REVIEW] (TV-03)¶
Badge [LEGAL-REVIEW] (InlineBanner warning pliée) dans ADM-003 onglet Paramètres et OP-003, tooltip :
« Durées minimales légales basées sur une analyse préliminaire — délibération ARTCI sectorielle assurance CI en cours. Ne pas descendre sous ces seuils sans confirmation juridique (TV-03). »
9.4 Agrément CIMA — capture (TV-04, Règl. 01-24)¶
Étape 1 wizard, helper sous "Numéro d'agrément CIMA" :
« Numéro d'agrément délivré par la Commission Régionale de Contrôle des Assurances (CRCA) pour les intermédiaires. Conservé conformément à la Réglementation CIMA 01-24. »
9.5 Information suspension → purge 90j¶
InlineBanner warning dans tous les onglets d'un tenant SUSPENDU (§7, ADM-003) — texte complet dans la micro-copy.
9.6 Restriction pays V1¶
Étape 1 wizard et OP-001 : Select "Pays" désactivé. Tooltip : "V1 : Côte d'Ivoire uniquement. Sénégal, Bénin, Cameroun et autres pays CIMA disponibles en V2."
10. Route Map & Structure module¶
Nouvelles routes frontend-operator¶
| Route | Composant | Rôle requis | Note |
|---|---|---|---|
/admin/tenants |
TenantListPage |
PLATFORM_ADMIN |
Remplace stub AdminPage |
/admin/tenants/nouveau |
TenantCreationWizardPage |
PLATFORM_ADMIN |
|
/admin/tenants/:id |
TenantDetailPage |
PLATFORM_ADMIN |
|
/tenant/settings |
CabinetBrandingPage |
CABINET_ADMIN |
|
/tenant/settings/agences |
CabinetAgenciesPage |
CABINET_ADMIN |
|
/tenant/settings/parametres |
CabinetSettingsPage |
CABINET_ADMIN |
Sidebar update — nav item à ajouter¶
// Dans NAV_ITEMS de Sidebar.tsx
{ to: '/tenant/settings', label: 'Mon cabinet', icon: '🏢', requiredRole: 'CABINET_ADMIN' }
Structure module¶
frontend-operator/src/modules/tenant-admin/
├── components/
│ └── ProvisioningPipeline.tsx
├── hooks/
│ ├── useTenantList.ts // GET /api/control/tenants
│ ├── useTenant.ts // GET /api/control/tenants/:id
│ ├── useTenantCreate.ts // POST (wizard step 1)
│ ├── useTenantProvision.ts // POST /provision + polling
│ ├── useTenantSuspend.ts // POST /suspend
│ ├── useTenantReactivate.ts // POST /reactivate
│ ├── useComplianceDocs.ts // GET/POST compliance-docs
│ ├── useAgencies.ts // GET/POST/PATCH/disable/reactivate agencies
│ └── useTenantSettings.ts // GET/PATCH me/settings
├── pages/
│ ├── admin/
│ │ ├── TenantListPage.tsx
│ │ ├── TenantCreationWizardPage.tsx
│ │ └── TenantDetailPage.tsx
│ └── cabinet/
│ ├── CabinetBrandingPage.tsx
│ ├── CabinetAgenciesPage.tsx
│ └── CabinetSettingsPage.tsx
├── types/
│ └── tenant.ts // TenantRow, AgencyRow, TenantSettings, etc.
└── i18n/
└── fr.json // toutes les clés de micro-copy ci-dessus
11. Stories UI-bearing — Inputs pour /wireframe¶
Pour chaque story, invoquer
/wireframe tenant-admin TENANT-NNNavec les inputs ci-dessous.
TENANT-001 [V0.0.0] — ADM-001 + ADM-002 + ADM-003 onglet Identité¶
Stories couvertes: TENANT-US-01, TENANT-US-02 (étapes 1–3 + activation). ACs: AC-01.1, AC-01.2, AC-01.3, AC-01.4, AC-02.1, AC-02.2, AC-02.3. Screens: ADM-001 (list + skeleton + empty + error + DPA banner), ADM-002 étapes 1–3 + 5 (recap+checklist), ADM-003 onglet Identité (read mode + ProvisioningPipeline success + failed with retry). Brand context: [OPERATOR] (back-office platform admin, desktop-primary). 5 states: loading (skeleton table), empty (first tenant), error (NCC dup + similar name modal), low-network (banner + form preserved), success (PENDING créé → pipeline → ACTIVE).
TENANT-002 [V0.0.2] — OP-001 + OP-002 + OP-003¶
Stories couvertes: TENANT-US-07, TENANT-US-08, TENANT-US-09. ACs: AC-07.1, AC-07.2, AC-07.3, AC-08.1, AC-08.2, AC-08.3, AC-09.1, AC-09.2, AC-09.3. Screens: OP-001 (branding + locked fields + FileUpload), OP-002 (agency list + modal create + modal edit + disable-last prevention), OP-003 (settings + LEGAL-REVIEW badges + V1 auth lock). Brand context: [OPERATOR] (cabinet admin, desktop/tablet). 5 states: loading, empty (no logo / no agencies beyond PRINCIPAL), error (logo format/size, code dup, bound violation), low-network, success.
TENANT-003 [V0.0.4] — ADM-002 étape 3 BYO-DB + ProvisioningPipeline¶
Stories couvertes: TENANT-US-02, TENANT-US-03. ACs: AC-03.1, AC-03.2, AC-03.3, AC-02.2 (retry from failed step). Screens: ADM-002 étape 3 (BYO-DB radio sélectionné, JDBC fields visibles, password show/hide), ProvisioningPipeline (running → BYO_DB_UNREACHABLE → retry → BYO_DB_MIGRATION → retry → success). Brand context: [OPERATOR] (platform admin). 5 states: loading (pipeline polling), empty (no run yet), error (BYO_DB_UNREACHABLE, BYO_DB_MIGRATION, KEYCLOAK_UNREACHABLE chacun), low-network (slow JDBC test), success (all steps green).
Fin du document de design — tenant-admin (TENANT) · [ADMIN] + [OPERATOR] · 2026-05-22