1. Module
| Attribut | Valeur |
| Agent | agent-developer |
| Module | seal-urgent-button (C8) |
| Story | PD-284 |
| Projet | app (ProbatioVault-app) |
2. Fichiers produits
| Fichier | Action | Justification |
src/components/seal/UrgentSealButton.tsx | Cree | Composant bouton de declenchement scellement urgent |
3. Couverture des invariants
| Invariant | Mecanisme implemente | Fichier:ligne |
| INV-284-01 | computeEligibility() : visible si accountType !== "minor", enabled si quota > 0 | UrgentSealButton.tsx:55-70 |
| INV-284-02 | Retour null (aucun noeud UI) si accountType === "minor" | UrgentSealButton.tsx:57-59, UrgentSealButton.tsx:105-107 |
| INV-284-04 | Tooltips contractuels exacts importes depuis types/seal.ts (TOOLTIP_QUOTA_EXHAUSTED, TOOLTIP_URGENT_ACTIVE) | UrgentSealButton.tsx:61,66 |
4. Couverture des tests contractuels
| Test ID | Couvert | Observable |
| TC-NOM-01 | Oui | accountType="minor" → composant retourne null, aucun noeud rendu |
| TC-NOM-02 | Oui | accountType="standard", quota > 0 → bouton visible et actif, quota affiche strictement egal au payload serveur |
| TC-NOM-03 | Oui | quota = 0 → bouton desactive + raison explicite "Quota epuise ce mois" |
| TC-ERR-05 | Oui | accountType hors enum → traite comme minor (non eligible, aucun bouton) |
5. Decisions architecturales
D1 — Eligibilite en fonction pure
- Decision :
computeEligibility() est une fonction pure extraite hors du composant - Rationale : Testable en isolation, pas d'effet de bord, memoisation triviale via
useMemo - Alternatives : Inline dans le JSX (rejete : non testable), hook custom (rejete : over-engineering pour logique pure)
- Trade-offs : Une allocation objet par render (negligeable)
D2 — Selectors granulaires Zustand (CA-284-14)
- Decision :
selectIsLoading et selectActiveSealId importes depuis useSealStore - Rationale : CA-284-14 exige un selecteur par champ pour eviter les re-renders en cascade
- Alternatives : Destructuring du store entier (rejete : viole CA-284-14)
- Trade-offs : Deux appels
useSealStore() au lieu d'un, mais chacun ne re-rend que sur son champ
D3 — TouchableOpacity + ActivityIndicator pendant le chargement
- Decision :
TouchableOpacity au lieu de Pressable, avec ActivityIndicator pendant isLoading - Rationale : Feedback visuel immediat conforme TC-NOM-04 ("feedback immediat de chargement visible")
- Alternatives :
Pressable avec style conditionnel (rejete : pas de feedback de chargement natif) - Trade-offs : Dependance
TouchableOpacity (standard React Native, pas de lib externe)
D4 — Props typees avec branded types et AccountType enum
- Decision :
accountType: AccountType au lieu de string, documentId: DocumentId au lieu de string - Rationale : Branded types de
types/seal.ts empechent inversion accidentelle des IDs (cf. regle "Refined/branded types obligatoires") - Alternatives :
string brut (rejete : viole la convention codebase) - Trade-offs : Le parent doit cast explicitement via
asDocumentId()
6. Hypotheses et points ouverts
| ID | Hypothese | Impact si invalide |
| H1 | Le parent (ecran detail document) fournit accountType, urgentQuotaRemaining et hasActiveUrgentSeal depuis le payload serveur | Le bouton ne peut pas evaluer l'eligibilite sans appel API interne |
| H2 | Le callback onTriggerUrgentSeal est connecte au SealOrchestrator.triggerUrgentSeal (C7) par le parent | Le debounce SEC-02 ne serait pas applique si connecte a un autre handler |
| H3 | has_active_urgent_seal du payload backend est optionnel avec default false (HT-09 du plan) | Le bouton est actif par defaut — acceptable car PD-80 POST est idempotent |
7. Fichiers hors perimetre identifies
Aucun fichier hors perimetre n'a ete modifie. Interactions identifiees :
src/types/seal.ts : lecture seule (types, tooltips) — deja existant src/store/useSealStore.ts : lecture seule (selecteurs) — deja existant src/seal/orchestrator.ts : le parent doit connecter onTriggerUrgentSeal au C7
8. Matrice de couverture test → fichier
TC-NOM-01 → src/components/seal/UrgentSealButton.tsx (computeEligibility: accountType="minor" → visible=false → null)
TC-NOM-02 → src/components/seal/UrgentSealButton.tsx (computeEligibility: quota > 0 → visible+enabled, quota affiché)
TC-NOM-03 → src/components/seal/UrgentSealButton.tsx (computeEligibility: quota=0 → disabled + TOOLTIP_QUOTA_EXHAUSTED)
TC-ERR-05 → src/components/seal/UrgentSealButton.tsx (isValidAccountType: hors enum → traité comme minor)
Le composant respecte les contraintes suivantes du plan d'implementation :
- SEC-02 : Le debounce 2s est gere en amont par le C7 orchestrator (pas duplique ici). Le composant desactive le bouton via
isLoading pendant l'orchestration. - CA-284-14 : Selecteurs granulaires Zustand (un par champ) pour eviter re-renders cascades.
- INV-284-10 : Aucun artefact sensible n'est manipule par ce composant. Les tooltips sont des constantes publiques.
- TC-NOM-04 : Le
ActivityIndicator fournit le feedback immediat de chargement exige par le test.