Aller au contenu

Levée communautaire (mobile investisseur)

Documentation du parcours levée de fonds communautaire 2026 dans front-mobile-app : feature flags, routing V1/V2, data layer partagée, progression en polling, modales d'intention et d'achat (wallet, Checkout, PEA-PME).

Contrat API : module company-internal-fundraising.

Feature flags et bascule V1 / V2

Switch Où Effet
DISPLAY_COMPANY_FUNDRAISING API feature flags → FeatureFlagsProvider Coupe toute la levée côté app (home, provider, polls, route /fundraising).
FUNDRAISING_V2_ENABLED featureSwitch.ts (const compile-time) true → écran et modales V2 ; false → module legacy fundraising (V1).

La route fundraising.tsx exporte withUnmountUnfocusedScreen(FUNDRAISING_V2_ENABLED ? FundraisingV2Screen : FundraisingScreen).

Architecture modules

  • fundraising — data layer : hooks React Query (useCurrentCompanyFundraisingCampaign, intentions, achats wallet), types, utils (getCampaignState, applyRaisedAmountToCurrentCampaign), modales V1, écran V1, provider global.
  • fundraising-v2 — UI « Levée 2026 » : page présentation (FundraisingV2Screen), dock, sections scroll, cartes d'entrée home (CampaignSnippetCard), modales FundraisingV2IntentionModal et FundraisingV2PurchaseModal (dont flux PEA-PME dédié).

Ne pas dupliquer les appels API dans V2 : réutiliser les services du module fundraising.

Provider global

FundraisingCampaignProvider (layout authentifié) :

  • Fetch campagne courante + horloge locale (useFundraisingCampaignClock).
  • Lance le poll raised-amount via useCurrentCompanyFundraisingRaisedAmount.
  • Deep link ?invest-fundraising=true → ouvre la modale d'achat (V1 ou V2 selon FUNDRAISING_V2_ENABLED) si le flag display est actif et droits KYC OK.
  • Explainer V1 : modale FundraisingExplainerModal au premier passage sur une campagne ouverte non investie — désactivée quand V2 est actif (pas de pré-modale explainer en V2).

Progression : polling, pas de SSE

La barre de progression et les compteurs investisseurs ne passent pas par Server-Sent Events. Le front appelle :

GET /customers/company-internal-fundraising/campaigns/current/raised-amount

Hook : useCurrentCompanyFundraisingRaisedAmount.ts.

Condition campagne Intervalle
isOpen ou earlyAccess.hasStarted 3 s (OPEN_RAISED_AMOUNT_POLL_INTERVAL_MS)
Campagne planifiée (progression « live » mais pas encore ouverte) 10 s (SCHEDULED_RAISED_AMOUNT_POLL_INTERVAL_MS)
Campagne clôturée (isClosed) Poll désactivé (isCampaignProgressLive = false)

Chaque réponse valide patche le cache GET …/campaigns/current via applyRaisedAmountToCurrentCampaign (alignement lifecycleStatus, montants, passage scheduled → open → closed). Erreurs poll : silencieuses (showErrorNotification: false).

Entrées utilisateur

Entrée Comportement
Home — carousel projets Si flag display + campagne en cache : CampaignSnippetCard en tête de liste → router.navigate('/(authenticated)/fundraising').
Route /fundraising Présentation V2 ou wizard V1 selon le switch.
Query invest-fundraising=true Ouvre modale achat (provider).
Actionnariat ShareholdingScreen — gated par le même flag display.

Modale intention (V2)

FundraisingV2IntentionModal — intention déclarative avant ouverture officielle :

  • Montant : multiple de 10 € ; le champ peut être vidé (RHF : ne pas repasser par undefined seul au clear — voir IntentionAmountStep).
  • Suppression d'intention : action dans le corps de la modale (pas dans le header).
  • Écriture API : PUT|DELETE …/declarative-intention (voir doc API).

Modale achat (V2)

FundraisingV2PurchaseModal :

  • Wallet : quote + confirm partagés avec V1 (useBricksTotalBalance), Checkout si money-in requis ; après succès Checkout, le front enchaîne le confirm (pas un second quote).
  • PEA-PME : chemin séparé usePeaPmePurchase → POST …/purchase/pea-pme (pas de quote, pas de WT). Compte individual uniquement. Référence virement BRICKS-PEA-XXXXXX ; bannière / modale dossier PEA sur la page présentation si intention waiting ou confirmed.
  • Erreur pea-pme-already-exists (409) : affichage succès avec dossier existant (autre appareil / cache stale).

États campagne (UI)

Utilitaires : campaignVisibility.ts.

  • getCampaignState : future | ongoing | closed (combine isOpen, isClosed, isNewInvestmentAllowed).
  • Cartes home et dock V2 s'appuient sur earlyAccess, isOpen, montants pollés et intention déclarative.

Fichiers clés

Rôle Chemin
Query keys src/modules/fundraising/services/queryKeys.ts
Campagne courante useCurrentCompanyFundraisingCampaign.ts
Poll progression useCurrentCompanyFundraisingRaisedAmount.ts
Patch cache poll utils/applyRaisedAmountToCurrentCampaign.ts
Écran V2 fundraising-v2/screens/FundraisingV2Screen/
Switch V2 fundraising-v2/featureSwitch.ts

Pièges courants

  • Modifier V2 sans toucher les hooks fundraising/services → risque de double fetch ou clés query incohérentes.
  • Confondre isOpen (GET current) et lifecycleStatus du poll : pendant l'avant-première, le poll peut être scheduled alors que le front patche quand même les montants confirmés.
  • Oublier DISPLAY_COMPANY_FUNDRAISING : l'UI disparaît mais les query keys peuvent rester documentées côté tests — toujours gate enabled sur les hooks.
  • PEA-PME : ne pas appeler le quote wallet (plafond money-in incompatible avec les montants PEA min).