Compare commits
3 Commits
06a3aaf468
...
fb248553a8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb248553a8 | ||
|
|
9eaac0c7ef | ||
|
|
801168fc74 |
20
CLAUDE.md
20
CLAUDE.md
@ -36,12 +36,12 @@ TPE-PME françaises, 5 à 50 salariés, qui émettent 10 à 200 factures par moi
|
||||
| **Couleur primaire** | `#9F1239` — rubis profond légèrement violacé. *Anti-Coca-Cola.* |
|
||||
| **Couleur secondaires** | `#771328` (deep), `#C9415C` (light), `#FBE4EA` (glow) |
|
||||
| **Neutres** | Crème `#FAF7F2`, encre chaude `#1A1410`. Jamais de blanc pur, jamais de noir pur. |
|
||||
| **Typo display** | Bricolage Grotesque (500–800), Google Fonts |
|
||||
| **Typo body** | Inter (400–700), Google Fonts |
|
||||
| **Typo display** | Bricolage Grotesque (500–800), self-hosted via `@fontsource-variable/bricolage-grotesque` |
|
||||
| **Typo body** | Inter (400–700), self-hosted via `@fontsource-variable/inter` |
|
||||
| **Icônes** | Lucide (regular weight) |
|
||||
| **Pas de** | or, bleu, vert, violet, emojis joaillerie 💎💰, mot "recouvrement" en com publique |
|
||||
|
||||
Voir `/docs/marque.md` pour la référence complète et `/brand-identity.html` pour la présentation visuelle (note : la mention de l'or accent dans ce fichier est obsolète, à ignorer).
|
||||
Voir `/docs/marque.md` pour la référence complète et `/docs/brand-identity.html` pour la présentation visuelle.
|
||||
|
||||
## Voix
|
||||
|
||||
@ -55,7 +55,7 @@ Direct, concret, chaleureux, précis, empathique. *On parle comme un bon associ
|
||||
- **Rubis** : unité de gamification. **1 rubis = 10 minutes libérées** = 1 relance qu'on n'a pas eu à faire à la main.
|
||||
- **Plan de relance** : cadence d'emails automatisés (ex. J+3, J+10, J+20). Chaque facture est associée à un plan.
|
||||
- **Étape** : un email programmé dans un plan (ex. "J+10 — relance ferme").
|
||||
- **Check-in** : email envoyé **à l'utilisateur** (pas au client) pour confirmer si une facture a été payée avant l'envoi de la prochaine relance. Remplace l'intégration banking en V1.
|
||||
- **Confirmation** *(anciennement « check-in »)* : email envoyé **à l'utilisateur** (pas au client) pour confirmer si une facture a été payée avant l'envoi de la prochaine relance. Remplace l'intégration banking en V1.
|
||||
- **Mise en demeure** : étape ferme du plan. **Toujours sous validation manuelle** via modale de confirmation, jamais auto.
|
||||
- **DSO** : Days Sales Outstanding. Métrique secondaire dans l'app, jamais dans la com publique.
|
||||
- **LME** : loi de modernisation de l'économie (2008). Plafonne les délais de paiement à 60 jours (ou 45 jours fin de mois). Sanctions DGCCRF jusqu'à 2 M€.
|
||||
@ -64,18 +64,20 @@ Direct, concret, chaleureux, précis, empathique. *On parle comme un bon associ
|
||||
|
||||
### IN
|
||||
|
||||
- Auth email/password + Google SSO
|
||||
- Auth email/password + Google SSO + Microsoft SSO
|
||||
- Onboarding 3 étapes (compte, entreprise, signature email)
|
||||
- Upload drag-and-drop + OCR factures (PDF, PNG, JPG)
|
||||
- Saisie manuelle (fallback)
|
||||
- Bibliothèque de plans (4 plans fournis par défaut)
|
||||
- Bibliothèque de plans (4 plans fournis par défaut : *Standard B2B*, *Rapide*, *Patient*, *Ferme*)
|
||||
- Éditeur de plan (cadence + templates email avec variables)
|
||||
- Check-in email à l'utilisateur (cadence configurable) → confirme si payé → relance ou stop
|
||||
- Confirmation par email à l'utilisateur (cadence configurable) → confirme si payé → relance ou stop. *Anciennement « check-in ».*
|
||||
- Dashboard avec compteur rubis + KPIs (à relancer, encaissé, DSO)
|
||||
- Liste filtrable des factures
|
||||
- Détail facture avec timeline des relances
|
||||
- Stripe billing (checkout, portal, webhook) + période de grâce post-signup
|
||||
- Mode démo (sandbox in-app sans engagement)
|
||||
- App mobile (web responsive)
|
||||
- **Blog `rubis.pro/blog`** — SSR par `apps/landing` (Astro 6), contenu en DB (`posts`) servi par `apps/api` via `/api/v1/posts/*`, admin de validation côté `app.rubis.pro/admin/blog` (à venir PR3), génération hebdomadaire IA via cron (Sonnet 4.6) avec review humaine obligatoire. Détails dans `/docs/tech/architecture.md`.
|
||||
- **Blog `rubis.pro/blog`** — SSR par `apps/landing` (Astro 6), contenu en DB (`posts`) servi par `apps/api` via `/api/v1/posts/*`, admin de validation côté `app.rubis.pro/admin/blog`, génération hebdomadaire IA via cron (Sonnet 4.6) avec review humaine obligatoire. Détails dans `/docs/tech/architecture.md`.
|
||||
|
||||
### OUT (V2 ou plus tard)
|
||||
|
||||
@ -180,4 +182,4 @@ Détails dans `/docs/tech/backend.md` §12.5.
|
||||
|
||||
---
|
||||
|
||||
*Dernière mise à jour : 2026-05-07 · Maintenu par Arthur + Claude.*
|
||||
*Dernière mise à jour : 2026-05-09 · Maintenu par Arthur + Claude.*
|
||||
|
||||
@ -680,7 +680,7 @@
|
||||
<div class="section-head">
|
||||
<span class="num">03</span>
|
||||
<h2>Palette</h2>
|
||||
<p>Une seule couleur dominante (le rubis), des neutres chauds, un accent or à doser. Pas de bleu — on n'est pas une fintech.</p>
|
||||
<p>Une seule couleur dominante (le rubis), des neutres chauds. Pas d'or, pas de bleu, pas de vert — on n'est pas une fintech, et le rubis suffit à signer la marque.</p>
|
||||
</div>
|
||||
|
||||
<h4>Couleur de marque</h4>
|
||||
@ -717,14 +717,9 @@
|
||||
<div class="role">Backgrounds doux, panneaux mis en avant, succès</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swatch">
|
||||
<div class="chip" style="background: var(--gold);"></div>
|
||||
<div class="meta">
|
||||
<div class="name">Or discret</div>
|
||||
<div class="hex">#B89F6B</div>
|
||||
<div class="role">Accent rare · badges premium · jamais > 5 % de l'écran</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- L'accent or (#B89F6B) a été retiré de la palette officielle.
|
||||
La marque s'en tient désormais à un système 100% rubis + neutres
|
||||
crème/encre. Voir docs/marque.md (référence écrite). -->
|
||||
</div>
|
||||
|
||||
<h4>Neutres chauds</h4>
|
||||
@ -1119,7 +1114,7 @@
|
||||
<li><b>Direction de logo :</b> recommandation A+C, mais la D est défendable si tu veux un positionnement plus polarisant côté éditorial.</li>
|
||||
<li><b>Bricolage Grotesque vs General Sans :</b> les deux sont gratuits. Bricolage a plus de caractère "français contemporain", General Sans est plus neutre/scalable. À tester côte à côte sur un vrai bloc de copy.</li>
|
||||
<li><b>Couleur primaire :</b> #9F1239 est mon best guess (rubis profond légèrement violacé). À tester sur écran imprimé/affiche — peut paraître trop sombre pour certains contextes outdoor. Variante plus vive : #B91C3C.</li>
|
||||
<li><b>Or accent :</b> à utiliser <i>très</i> peu — éventuellement pour un badge "premium" ou un état spécial. Si on l'enlève complètement, ça simplifie sans appauvrir. À voir.</li>
|
||||
<li><b>Or accent :</b> ❌ <i>retiré de la palette</i> — la décision finale (cf. <code>docs/marque.md</code>) est de tenir sur 100 % rubis + neutres. Plus simple, plus signature.</li>
|
||||
<li><b>Animation du gem :</b> potentiel logo animé sur la landing (rotation lente, scintillement) — à scoper en tant que micro-interaction plus tard.</li>
|
||||
<li><b>Iconographie :</b> on n'a pas tranché le set d'icônes UI (Lucide, Phosphor, custom ?). Pour le MVP, je recommande Lucide en regular weight — neutre et libre.</li>
|
||||
</ul>
|
||||
|
||||
19
docs/flow.md
19
docs/flow.md
@ -265,15 +265,18 @@ Toutes appellent in fine la même logique métier (mêmes mutations DB, mêmes e
|
||||
```
|
||||
Plan
|
||||
├── name: "Standard B2B"
|
||||
├── slug: "standard-b2b"
|
||||
├── description: "Plan équilibré pour la majorité des factures"
|
||||
├── slug: "standard-30j"
|
||||
├── description: "Cadence sobre, ton qui monte progressivement."
|
||||
└── steps[]
|
||||
├── PlanStep { offsetDays: 3, tone: "amical", subject, body, requiresManualValidation: false }
|
||||
├── PlanStep { offsetDays: 10, tone: "ferme", subject, body, requiresManualValidation: false }
|
||||
└── PlanStep { offsetDays: 25, tone: "stricte", subject, body, requiresManualValidation: true }
|
||||
↑
|
||||
mise en demeure → bouton manuel,
|
||||
jamais d'envoi auto
|
||||
├── PlanStep { offsetDays: 3, tone: "amical", subject, body, requiresManualValidation: false }
|
||||
├── PlanStep { offsetDays: 10, tone: "courtois", subject, body, requiresManualValidation: false }
|
||||
└── PlanStep { offsetDays: 25, tone: "ferme", subject, body, requiresManualValidation: true }
|
||||
↑
|
||||
mise en demeure → bouton manuel,
|
||||
jamais d'envoi auto
|
||||
|
||||
Tons disponibles : `amical | courtois | ferme | mise_en_demeure`
|
||||
(cf. `apps/api/app/services/default_plans.ts:18`).
|
||||
```
|
||||
|
||||
### 6.2 Plans pré-fournis
|
||||
|
||||
371
docs/marketing/launch-kit.md
Normal file
371
docs/marketing/launch-kit.md
Normal file
@ -0,0 +1,371 @@
|
||||
# Launch kit — J1 prêt à dégainer
|
||||
|
||||
> Contenu copy-paste pour activer ton acquisition tomorrow morning.
|
||||
> Référence stratégique : `/docs/marketing/playbook.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Profil LinkedIn fondateur (10 min de setup)
|
||||
|
||||
### Bannière (1584 × 396 px)
|
||||
|
||||
À générer rapidement (Canva / Figma). Composition simple :
|
||||
- Fond `#FAF7F2` (cream)
|
||||
- Le ◆ rubis (#9F1239) à gauche, taille moyenne
|
||||
- Tagline en Bricolage Grotesque 700 à droite : *"Vos factures relancées toutes seules pendant que vous travaillez."*
|
||||
- Sous-tagline en Inter 500, encre #4F4640 : *"L'app de relance pour TPE-PME françaises."*
|
||||
- Coin en bas droite : `rubis.pro` en petit, italique
|
||||
|
||||
### Headline (220 caractères max)
|
||||
|
||||
**Option A — sobre :**
|
||||
> Fondateur Rubis Sur l'Ongle · L'app qui automatise la relance de factures impayées pour les TPE-PME françaises
|
||||
|
||||
**Option B — plus accrocheuse (recommandée) :**
|
||||
> J'aide les TPE et freelances à arrêter de courir après leurs factures impayées · Fondateur Rubis Sur l'Ongle (rubis.pro)
|
||||
|
||||
### About (3000 caractères max — colle ce qui suit)
|
||||
|
||||
```
|
||||
Il y a deux ans, je facturais en freelance. Sur 12 factures émises ce mois-là, 5 ont été payées en retard. Une de 45 jours.
|
||||
|
||||
Le pire ? Le temps que j'ai passé à les relancer. Écrire des emails polis, vérifier qui avait répondu, attendre, relancer encore. Plusieurs heures par semaine. Pour de l'argent qu'on me devait déjà.
|
||||
|
||||
Je me suis dit que ça n'avait pas de sens.
|
||||
|
||||
Quelques chiffres qui m'ont confirmé que je n'étais pas seul :
|
||||
• 44 jours — le retard moyen pour une facture de TPE en France (Altares)
|
||||
• 15 milliards d'euros — la trésorerie bloquée chez les PME françaises
|
||||
• −26 % — le taux de recouvrement quand on attend 30 jours pour relancer (AFDCC)
|
||||
|
||||
J'ai construit Rubis Sur l'Ongle pour que ce cercle vicieux s'arrête.
|
||||
|
||||
Tu glisses ta facture (PDF, photo, scan).
|
||||
Tu choisis un plan de relance (J+3, J+10, J+20).
|
||||
Et c'est tout.
|
||||
|
||||
Rubis envoie les emails au bon moment, sous ton nom, suit les ouvertures, et te demande discrètement avant chaque relance si la facture a été payée entre-temps.
|
||||
|
||||
Tu travailles. Rubis relance.
|
||||
|
||||
Bench moyen chez les utilisateurs d'outils similaires : 5 heures par semaine récupérées.
|
||||
|
||||
Si tu es freelance, dirigeant de TPE ou PME, et que les retards de paiement te bouffent du temps — viens jeter un œil : rubis.pro
|
||||
|
||||
DM ouvertes pour tes retours, tes objections, ou si tu veux juste discuter du sujet.
|
||||
|
||||
Arthur
|
||||
```
|
||||
|
||||
### Lien du profil
|
||||
|
||||
Ajoute `rubis.pro` dans le champ "Site web personnel" du profil (Preferences → Profile → About → Add website).
|
||||
|
||||
### Photo de profil
|
||||
|
||||
- Pas de costume-cravate. Ton métier c'est de servir des entrepreneurs détendus, pas une banque.
|
||||
- Lumière naturelle, sourire moyen, fond simple.
|
||||
- Si tu n'as pas le temps de faire mieux : la photo que tu as déjà fera l'affaire. Évite juste les photos de soirée ou floues.
|
||||
|
||||
### Calendly publique
|
||||
|
||||
`cal.com` (gratuit). Crée un événement *"Démo Rubis (30 min)"*, dispos en semaine 9h-18h (Marseille), avec le formulaire qui demande : prénom, email, taille de boîte, nombre de factures/mois.
|
||||
|
||||
URL à mettre dans :
|
||||
- Headline LinkedIn (en lien si possible)
|
||||
- Signature email
|
||||
- Bio Twitter / X si tu en as une
|
||||
- About LinkedIn (à la fin, après "DM ouvertes")
|
||||
|
||||
---
|
||||
|
||||
## 2. Premier post LinkedIn — 3 variantes au choix
|
||||
|
||||
**Recommandation : Variante A** (storytelling, conversion la plus haute pour un lancement). Mais teste laquelle te ressemble le plus.
|
||||
|
||||
### Variante A — storytelling personnel (♥ recommandée)
|
||||
|
||||
```
|
||||
Il y a 2 ans, je facturais en freelance.
|
||||
|
||||
5 factures sur 12 ont été payées en retard ce mois-là. Une de 45 jours.
|
||||
|
||||
Le pire ? Le temps que j'ai passé à les relancer.
|
||||
À écrire des emails polis, à vérifier qui avait répondu, à attendre, à relancer encore.
|
||||
Plusieurs heures par semaine. Pour de l'argent qu'on me devait déjà.
|
||||
|
||||
Je n'étais pas seul.
|
||||
|
||||
→ 44 jours : le retard moyen pour une facture de TPE en France (Altares, 2024)
|
||||
→ 15 milliards d'euros bloqués chez les PME françaises
|
||||
→ -26 % de chances d'être payé si tu attends 30 jours pour relancer (AFDCC)
|
||||
|
||||
J'ai construit Rubis Sur l'Ongle pour que ça s'arrête.
|
||||
|
||||
Tu glisses ta facture.
|
||||
Tu choisis un plan de relance.
|
||||
Tu travailles.
|
||||
|
||||
Rubis envoie les emails au moment prévu, sous ton nom, et te demande discrètement avant chaque relance si la facture a été payée entre-temps.
|
||||
|
||||
Bench moyen : 5 heures par semaine récupérées.
|
||||
|
||||
On est en ligne : rubis.pro
|
||||
|
||||
Si tu es freelance, dirigeant de TPE/PME, ou si tu connais quelqu'un qui galère sur ce sujet — DM ouvertes.
|
||||
|
||||
#freelance #TPE #PME #facturation #impayés
|
||||
```
|
||||
|
||||
### Variante B — data-first
|
||||
|
||||
```
|
||||
44 jours.
|
||||
|
||||
C'est le retard moyen pour une facture de TPE en France en 2024 (Altares).
|
||||
|
||||
15 milliards d'euros bloqués chez les PME.
|
||||
+25 % de risque de faillite quand tu subis des retards.
|
||||
-26 % de chances d'être payé si tu laisses traîner 30 jours.
|
||||
|
||||
Et pendant ce temps, les dirigeants de TPE passent en moyenne 5 à 8 heures par semaine à rédiger des emails de relance qu'ils détestent.
|
||||
|
||||
J'en ai eu marre.
|
||||
|
||||
J'ai construit Rubis Sur l'Ongle. Tu glisses tes factures, tu choisis un plan de relance (J+3, J+10, J+20), et c'est tout. L'app envoie, suit, relance. Tu récupères tes heures.
|
||||
|
||||
On est en ligne aujourd'hui : rubis.pro
|
||||
|
||||
Si tu factures du B2B et que les retards de paiement te bouffent ta semaine, viens tester. DM si questions.
|
||||
|
||||
#TPE #PME #facturation #impayés #frenchtech
|
||||
```
|
||||
|
||||
### Variante C — directe, engagement bait
|
||||
|
||||
```
|
||||
Question aux freelances et dirigeants de TPE/PME qui me lisent :
|
||||
|
||||
→ Combien de temps tu passes par semaine à relancer tes factures impayées ?
|
||||
|
||||
(Vraiment, je suis curieux. Réponds en commentaire ou en DM.)
|
||||
|
||||
Je pose la question parce que j'ai construit un truc autour de ça : Rubis Sur l'Ongle (rubis.pro).
|
||||
|
||||
Tu glisses tes PDF, tu choisis un plan de relance, et l'app envoie les emails à ta place — sous ton nom, au bon moment, en te tenant au courant.
|
||||
|
||||
Le bench que je vois chez les early users : ~5h/semaine récupérées.
|
||||
|
||||
Mais avant de te dire que c'est génial, j'aimerais d'abord t'écouter. C'est combien chez toi ? Et qu'est-ce qui te gonfle le plus dans ce processus ?
|
||||
```
|
||||
|
||||
### Tips de post
|
||||
|
||||
- Publie le **mardi ou mercredi 8h-10h** ou **jeudi 17h-19h** (engagement LinkedIn FR le plus fort)
|
||||
- **Réponds à TOUS les commentaires dans la 1ère heure** (l'algo LinkedIn boost les posts avec engagement précoce)
|
||||
- **Tag personne** dans le post initial (ça paraît artificiel) — laisse les gens venir à toi
|
||||
- **Re-partage le post 7 jours plus tard** avec un nouveau hook ("Une semaine après, voici ce qui s'est passé...")
|
||||
|
||||
---
|
||||
|
||||
## 3. Messages perso — 30 contacts à activer (J2)
|
||||
|
||||
Liste 30 contacts pro dans ton réseau warm (anciens collègues, école, amis entrepreneurs, famille avec activité pro). Adapte le message à chaque relation. **Ne JAMAIS copy-paste le même texte sur les 30** — ça se voit, ça brûle.
|
||||
|
||||
### Template 1 — Ancien collègue / dev / tech
|
||||
|
||||
```
|
||||
Hey [prénom],
|
||||
|
||||
J'espère que [contexte boîte/projet — ex. la migration K8s chez Acme, le launch de votre nouvelle plateforme] roule bien.
|
||||
|
||||
De mon côté, je viens de lancer Rubis Sur l'Ongle (rubis.pro) — une app qui automatise la relance de factures impayées pour les TPE-PME et freelances B2B. Ça fait gagner ~5h/sem de relances à la main.
|
||||
|
||||
Je cherche 5 testeurs early adopters qui ont au moins 10 factures/mois. Tu connais quelqu'un dans ton réseau qui pourrait être intéressé ? Ou toi-même si tu factures en parallèle ?
|
||||
|
||||
Si oui, je te file un accès gratuit pendant 3 mois et je m'occupe perso de l'onboarding.
|
||||
|
||||
Pas de pression, dis-moi !
|
||||
|
||||
Arthur
|
||||
```
|
||||
|
||||
### Template 2 — Ami entrepreneur / freelance
|
||||
|
||||
```
|
||||
Salut [prénom],
|
||||
|
||||
T'as 30 secondes ? Je viens de lancer un truc et je pense direct à toi.
|
||||
|
||||
Rubis Sur l'Ongle (rubis.pro) — automatise la relance de factures impayées pour les indépendants et TPE B2B. Drag & drop des PDF, choix d'un plan de relance, c'est l'app qui envoie au moment prévu.
|
||||
|
||||
Vu que tu factures du B2B [détail spécifique de leur activité], ça pourrait te gagner ~5h/sem.
|
||||
|
||||
J'offre 3 mois Pro gratuits aux 5 premiers testeurs warm — tu prends ?
|
||||
|
||||
Si pas pour toi, tu connais quelqu'un dans ta sphère qui galère avec ce sujet ?
|
||||
```
|
||||
|
||||
### Template 3 — Connaissance pro / famille avec activité
|
||||
|
||||
```
|
||||
Hello [prénom],
|
||||
|
||||
J'espère que tu vas bien. J'ai pensé à toi parce que [contexte — ex. tu m'avais raconté que tes clients payaient avec X jours de retard / parce que ton fils m'avait dit que tu lances ta boîte].
|
||||
|
||||
Je viens de lancer Rubis Sur l'Ongle (rubis.pro), une app qui automatise les relances de factures impayées. C'est pensé pour les TPE et freelances qui en ont marre de courir après l'argent qu'on leur doit.
|
||||
|
||||
Si ça parle à ton activité (ou si tu connais quelqu'un autour de toi à qui ça parlerait), j'offre 3 mois Pro gratuits aux 5 premiers testeurs.
|
||||
|
||||
Sinon dis-moi juste ce que tu en penses, ton retour m'intéresse.
|
||||
|
||||
À bientôt,
|
||||
Arthur
|
||||
```
|
||||
|
||||
### Règles d'or pour ces 30 messages
|
||||
|
||||
1. **Personnalisation obligatoire** — le détail [contexte] doit être réel, pas générique.
|
||||
2. **Demande claire** — soit "tu testes" soit "tu me présentes quelqu'un". Pas les deux dans la même phrase.
|
||||
3. **Pas de gros pitch** — 4 lignes max, le but c'est démarrer une conversation.
|
||||
4. **Pas le dimanche ni le lundi matin** — préfère mardi-jeudi en milieu de journée.
|
||||
|
||||
### Ratio attendu
|
||||
|
||||
Sur 30 messages :
|
||||
- ~10 répondent (bon ou mauvais)
|
||||
- ~5 introduisent un contact warm
|
||||
- ~2-3 testent eux-mêmes
|
||||
- = **3 à 5 testeurs/clients en 7-10 jours**
|
||||
|
||||
---
|
||||
|
||||
## 4. Dream 100 — template prêt à copier
|
||||
|
||||
Crée un Notion ou Google Sheet avec ces colonnes :
|
||||
|
||||
| # | Prénom Nom | Entreprise | Avatar | Volume estimé | Lieu | LinkedIn | Email pro | Source | Statut | Dernier contact | Prochain follow-up | Notes |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
|
||||
**Définition des colonnes** :
|
||||
|
||||
- **Avatar** : `1` (consultant freelance B2B premium), `2` (TPE service B2B 5-15 sal), `3` (entrepreneur récent 12-24 mois). Cf. playbook section 2.
|
||||
- **Volume estimé** : nombre de factures/mois (estimer à partir de leur activité).
|
||||
- **Source** : `Sales Nav`, `LinkedIn organique`, `Recommandation de [nom]`, `Forum X`, etc.
|
||||
- **Statut** : `À contacter` → `Connect envoyé` → `Accepté` → `DM envoyé` → `Démo bookée` → `Démo faite` → `Client` / `Out`
|
||||
- **Notes** : verbatim de leur profil (ce qu'ils ont posté, leur pain point visible). C'est ce qui te permet de personnaliser ton message à 99 %.
|
||||
|
||||
### Filtres Sales Navigator pour Avatar 1 (consultant freelance B2B)
|
||||
|
||||
Connecte-toi à [linkedin.com/sales](https://linkedin.com/sales) (essai gratuit 1 mois si pas encore abonné), puis :
|
||||
|
||||
- **Géographie** : France
|
||||
- **Taille de l'entreprise** : 1-10 employés
|
||||
- **Fonction** : Founder, Owner, Self-employed, Independent Consultant
|
||||
- **Secteur** : Services aux entreprises, Conseil, Marketing & publicité, IT services
|
||||
- **Mots-clés dans le poste actuel** : "freelance" OR "consultant" OR "indépendant" OR "fondatrice" OR "fondateur"
|
||||
|
||||
→ Tu obtiens ~2000-5000 résultats. **Ne fais pas tout** — prends les 30 premiers qui sont actifs sur LinkedIn (dernier post < 30 jours = signal qu'ils utilisent vraiment la plateforme et risquent de répondre).
|
||||
|
||||
### Filtres Sales Navigator pour Avatar 2 (TPE service B2B 5-15 sal)
|
||||
|
||||
- **Géographie** : France (cibler Paris, Lyon, Marseille, Toulouse, Bordeaux, Nantes en priorité)
|
||||
- **Taille** : 11-50 employés
|
||||
- **Fonction** : CEO, Founder, Managing Director, Directeur Général, Gérant
|
||||
- **Secteur** : Marketing, Conseil, Services IT, RH, Comptabilité
|
||||
|
||||
### Sources gratuites (sans Sales Navigator)
|
||||
|
||||
- **Maltverse / Malt** — annuaires de freelances, profils publics
|
||||
- **Twitter recherche** : `"facture impayée" lang:fr min_replies:2`
|
||||
- **Communautés Slack freelances FR** — observer qui parle de facturation/cash flow
|
||||
- **Annuaire sociétés.com** : taille + secteur + géo, payant mais utilisable en V2
|
||||
|
||||
### 5 exemples concrets de prospects à mettre dans la liste
|
||||
|
||||
| # | Type | Comment trouver | Pourquoi parfait |
|
||||
|---|---|---|---|
|
||||
| 1 | Une consultante RH freelance qui a tweeté son ras-le-bol des retards | Twitter `"retard de paiement" lang:fr` | Pain point exprimé = warm de fait |
|
||||
| 2 | Le gérant d'une agence digitale 8 personnes à Lyon | LinkedIn taille 5-10 + Lyon + agence | Volume pertinent + LinkedIn-actif |
|
||||
| 3 | Un développeur freelance qui poste sur sa facturation | LinkedIn recherche "freelance dev" actif | Public engageant + aligné avec toi |
|
||||
| 4 | Un cabinet d'avocats indépendant à Marseille | Annuaire CCI + LinkedIn vérification | Pain point classique + ta zone géo |
|
||||
| 5 | Un prestataire SEO indépendant qui poste sur le cash flow | LinkedIn hashtag #freelance #cashflow | Triple match : profil + thématique + actif |
|
||||
|
||||
---
|
||||
|
||||
## 5. Plan de bataille — 7 jours, heure par heure
|
||||
|
||||
### J1 — Lundi (2h30)
|
||||
|
||||
- 09:00 → 10:00 : LinkedIn profile (bannière, headline, about, photo)
|
||||
- 10:00 → 10:15 : Setup Calendly + insertion lien dans LinkedIn
|
||||
- 10:15 → 11:30 : Construire la Dream 100 spreadsheet avec les **30 premiers** prospects (cf. filtres section 4)
|
||||
|
||||
### J2 — Mardi (1h30)
|
||||
|
||||
- 09:00 → 09:30 : Publier le post LinkedIn (variante A par défaut)
|
||||
- 09:30 → 11:00 : Envoyer 30 messages perso (templates section 3) à ton réseau warm
|
||||
- Toute la journée : **répondre à chaque commentaire / DM dans l'heure** sur le post
|
||||
|
||||
### J3 — Mercredi (1h)
|
||||
|
||||
- 14:00 → 15:00 : Connect requests LinkedIn aux 30 premiers Dream 100 (note personnalisée pour chacun, cf. playbook section 8.1)
|
||||
|
||||
### J4 — Jeudi (1h30)
|
||||
|
||||
- 09:00 → 09:30 : Suivi des connect acceptés depuis hier → DM 1ᵉʳ touche
|
||||
- 09:30 → 10:30 : Préparer 5 cold emails ultra-personnalisés (5 min de recherche par prospect)
|
||||
- 10:30 → 11:00 : Envoyer les 5 cold emails
|
||||
|
||||
### J5 — Vendredi (1h30 — variable selon les bookings)
|
||||
|
||||
- Honorer les **interviews découverte** bookées via Calendly
|
||||
- Notes denses dans Notion (vocabulaire prospect, pain points cités, demande de référence)
|
||||
|
||||
### Week-end
|
||||
|
||||
Repos. Tu as travaillé.
|
||||
|
||||
### J7 — Dimanche soir (30 min)
|
||||
|
||||
- Compter : connects envoyés, acceptés, DMs envoyés, emails envoyés, démos bookées
|
||||
- Identifier ce qui a marché vs raté (taux d'acceptation < 30 % → revoir la note de connect)
|
||||
- Préparer le batch de la semaine 2
|
||||
|
||||
---
|
||||
|
||||
## 6. Stack outils gratuits/cheap pour démarrer
|
||||
|
||||
| Besoin | Outil | Coût | Pourquoi |
|
||||
|---|---|---|---|
|
||||
| CRM Dream 100 | Notion (gratuit) ou Folk (gratuit) | 0 € | Suffit largement pour 100 prospects |
|
||||
| Booker démos | Cal.com | 0 € | Open-source, dispo Calendly-like |
|
||||
| Trouver emails | Hunter (essai 25 emails/mois) | 0 € | Pour les cold emails, suffisant J1-J7 |
|
||||
| Outbound LinkedIn | LinkedIn manuel | 0 € | Sales Navigator (~80 €/mois) seulement si tu accélères en S2 |
|
||||
| Tracking landing | Plausible | déjà branché | Tu vois en temps réel les visites depuis ton post |
|
||||
|
||||
**Total budget acquisition semaine 1** : 0 €.
|
||||
|
||||
---
|
||||
|
||||
## 7. Ce qu'il NE faut PAS faire en S1
|
||||
|
||||
- ❌ Lancer un Product Hunt — pas le moment, prépare-le pour S6 quand tu as 10 témoignages
|
||||
- ❌ Activer du paid (Google/LinkedIn Ads) — pas de baseline pour étalonner
|
||||
- ❌ Spammer LinkedIn (200 connects/jour) — ton compte se fait limiter, ton domaine mail aussi
|
||||
- ❌ Construire de nouvelles features parce que "ça serait cool" — tu DOIS résister, c'est le moment d'écouter, pas de coder
|
||||
- ❌ Te décourager si tu fais 0 démo bookée la première semaine — c'est normal, ça démarre vraiment en S2
|
||||
|
||||
---
|
||||
|
||||
## 8. Mantras à se répéter
|
||||
|
||||
> *"Le seul KPI qui compte cette semaine c'est : nombre de conversations qualifiées."*
|
||||
|
||||
> *"Demande une référence à chaque conversation : 'Tu connais quelqu'un d'autre qui galère sur ce sujet ?'"*
|
||||
|
||||
> *"5 conversations / semaine. Toutes les semaines. Pas de magie ailleurs."*
|
||||
|
||||
---
|
||||
|
||||
*Maintenu par Arthur + Claude. Si tu plafonnes après 2 semaines, on revient en discussion pour ajuster le plan plutôt que d'envoyer plus de messages dans le vide.*
|
||||
@ -459,7 +459,9 @@ Tu peux démarrer demain. Voici le calendrier minute par minute :
|
||||
>
|
||||
> *On lance les premiers comptes au printemps 2026. Si tu es freelance ou TPE et que tu en as marre de courir après ton argent, viens sur la waitlist : [lien]*
|
||||
>
|
||||
> *#freelance #facturation #TPE #PME #recouvrement*
|
||||
> *#freelance #facturation #TPE #PME #impayés*
|
||||
|
||||
⚠️ **Pas de `#recouvrement`** en com publique (cf. `marque.md:151`). Le mot signale un produit "DAF" auquel on ne veut pas être assimilés.
|
||||
|
||||
À adapter avec ta voix mais garde la structure : storytelling personnel → stat-choc → produit → bénéfice chiffré → CTA waitlist.
|
||||
|
||||
@ -483,7 +485,7 @@ Tu peux démarrer demain. Voici le calendrier minute par minute :
|
||||
- `/docs/marque.md` — voix et ton à reprendre dans tous les messages
|
||||
- `/docs/munitions-marketing.md` — chiffres (44j, 15 Md€, −26 %) à citer dans la copy outreach
|
||||
- `/docs/decisions.md` — décisions produit pour répondre aux objections lors des démos
|
||||
- `/landing/index.html` — la page que les prospects voient en premier
|
||||
- `apps/landing/src/pages/index.astro` — la page que les prospects voient en premier (Astro 6 SSR sur `https://rubis.pro`)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
# Référence de marque — Rubis Sur l'Ongle
|
||||
|
||||
> Version : 0.1 · Dernière maj : 2026-05-05
|
||||
> Version : 0.2 · Dernière maj : 2026-05-09
|
||||
|
||||
Ce document est la source de vérité écrite pour la marque. Pour la présentation visuelle, voir `/brand-identity.html`. Quand les deux divergent, ce fichier gagne.
|
||||
Ce document est la source de vérité écrite pour la marque. Pour la présentation visuelle, voir [`/docs/brand-identity.html`](./brand-identity.html). Quand les deux divergent, ce fichier gagne.
|
||||
|
||||
---
|
||||
|
||||
@ -86,8 +86,8 @@ Un rubis géométrique vu de dessus, kite/diamond shape avec facettes suggérée
|
||||
|
||||
| Usage | Typo | Source | Poids utilisés |
|
||||
|---|---|---|---|
|
||||
| Display (titres) | **Bricolage Grotesque** | Google Fonts | 500, 600, 700, 800 |
|
||||
| Body (texte courant, UI) | **Inter** | Google Fonts | 400, 500, 600, 700 |
|
||||
| Display (titres) | **Bricolage Grotesque** | self-hosted via `@fontsource-variable/bricolage-grotesque` | 500, 600, 700, 800 |
|
||||
| Body (texte courant, UI) | **Inter** | self-hosted via `@fontsource-variable/inter` | 400, 500, 600, 700 |
|
||||
|
||||
### Échelle typographique
|
||||
|
||||
|
||||
@ -202,7 +202,7 @@ Pas de chiffres figés tant qu'on n'a pas testé, mais les bornes du marché don
|
||||
|
||||
- **Free** : jusqu'à 5 factures actives en relance (attire les freelances, montre la valeur).
|
||||
- **Pro** : 19 €/mois (10–50 factures, OCR illimité, 4 plans, 1 utilisateur) → **prix d'entrée agressif** vs Sellsy/Axonaut, défensable car on est mono-produit.
|
||||
- **Business** : 49 €/mois (illimité, multi-utilisateurs, branding email, intégration banking).
|
||||
- **Business** : 49 €/mois (illimité, multi-utilisateurs V2, branding email, SMS V2). *L'intégration banking est explicitement V2+ — on ne la promet pas en V1, la confirmation paiement passe par les emails de check-in à l'utilisateur.*
|
||||
|
||||
> Le sweet spot du discours : *« moins cher qu'une heure de votre temps mensuel »*. À 50 €/h facturés, 19 €/mois est trivial à vendre.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Spécification produit — Rubis Sur l'Ongle
|
||||
|
||||
> Version : 0.1 (MVP) · Dernière maj : 2026-05-05
|
||||
> Version : 0.2 (MVP — blog + Stripe livrés) · Dernière maj : 2026-05-09
|
||||
|
||||
Ce document est la source de vérité produit. Il décrit ce qu'on fait, pour qui, comment, et **ce qu'on ne fait pas**. Quand un wireframe et ce document divergent, ce document gagne.
|
||||
|
||||
@ -44,7 +44,7 @@ Rubis Sur l'Ongle libère le temps des dirigeants de TPE-PME en automatisant la
|
||||
### 4.1 Authentification & onboarding
|
||||
|
||||
- Inscription email/password (2 champs minimum)
|
||||
- Google SSO en option
|
||||
- Google SSO + Microsoft SSO en option (via Adonis Ally)
|
||||
- Onboarding 3 étapes au max :
|
||||
1. Compte (email, mot de passe)
|
||||
2. Entreprise (nom, SIRET facultatif, secteur, volume mensuel via chips)
|
||||
@ -61,13 +61,14 @@ Rubis Sur l'Ongle libère le temps des dirigeants de TPE-PME en automatisant la
|
||||
|
||||
### 4.3 Plans de relance
|
||||
|
||||
- **Bibliothèque** : 4 plans pré-fournis par défaut
|
||||
- *Standard B2B* : J+3, J+10, J+20 (mise en demeure brouillon)
|
||||
- *Premium clients VIP* : J+7, J+15, J+30 — ton doux
|
||||
- *Court cash flow tendu* : J+1, J+5, J+10 — ton ferme
|
||||
- *Personnalisable* : à créer par l'utilisateur
|
||||
- **Éditeur** : cadence à gauche, contenu email à droite. Variables sous forme de chips drag-to-insert (`{{prenom_client}}`, `{{numero}}`, `{{montant}}`, `{{date_echeance}}`, `{{signature}}`)
|
||||
- **Tonalité** affichée comme tag visible (Doux / Standard / Ferme)
|
||||
- **Bibliothèque** : 4 plans pré-fournis par défaut (cf. `apps/api/app/services/default_plans.ts`)
|
||||
- *Standard B2B* (`standard-30j`) : J+3, J+10, J+25 — *amical → courtois → mise en demeure*
|
||||
- *Rapide* (`rapide-15j`) : J+1, J+7, J+15 — cadence resserrée pour récurrents / délais courts
|
||||
- *Patient* (`patient-60j`) : J+15, J+30 — clients de longue date, on laisse respirer
|
||||
- *Ferme* (`ferme-7j`) : cadence stricte pour clients à risque
|
||||
- **Personnalisation** : l'utilisateur crée ses propres plans via `/plans/nouveau` (variantes / sur-mesure)
|
||||
- **Éditeur** : cadence à gauche, contenu email à droite. Variables sous forme de chips drag-to-insert (`{{client.name}}`, `{{numero}}`, `{{amount}}`, `{{dueDate}}`, `{{signature}}`)
|
||||
- **Tonalité** affichée comme tag visible — 4 valeurs : `amical`, `courtois`, `ferme`, `mise_en_demeure` (la dernière déclenche `requiresManualValidation: true`)
|
||||
|
||||
### 4.4 Check-in email (réconciliation V1)
|
||||
|
||||
@ -171,14 +172,14 @@ Rubis Sur l'Ongle libère le temps des dirigeants de TPE-PME en automatisant la
|
||||
|
||||
**Stratégie** : prix d'entrée agressif vs Sellsy/Axonaut (~30 €) défendable parce qu'on est mono-produit. Argument : *"moins cher qu'une heure de votre temps mensuel"*.
|
||||
|
||||
**Trial** : 14 jours offerts sans CB, puis fallback automatique sur Free.
|
||||
**Trial commercial** : 30 jours d'essai sans carte bancaire (CTA landing : *« Commencer l'essai 30 jours »*). Le code utilise actuellement une *« période de grâce 3 mois »* post-signup côté `apps/api/app/services/billing.ts` — incohérence à aligner sur 30 jours dans un PR séparé (cf. `docs/decisions.md`).
|
||||
|
||||
## 8. Notes architecture (à formaliser avec stack)
|
||||
|
||||
Quelques contraintes produit qui doivent guider l'architecture :
|
||||
|
||||
- **Statuts de facture pivotables** : `pending`, `awaiting_user_confirmation`, `paid`, `relanced`, `litigation`, `cancelled` — pas hardcodés au flow email
|
||||
- **Modèle d'événement abstrait** pour le check-in : un `payment_status_check` peut être déclenché par email-réponse en V1, par webhook banking en V2 — l'app ne doit pas savoir d'où vient le signal
|
||||
- **Statuts de facture pivotables** : `pending`, `awaiting_user_confirmation`, `paid`, `in_relance`, `litigation`, `cancelled` — pas hardcodés au flow email (cf. `packages/shared/src/constants/index.ts`)
|
||||
- **Modèle d'événement abstrait** pour la confirmation : un `payment_status_check` peut être déclenché par email-réponse en V1, par webhook banking en V2 — l'app ne doit pas savoir d'où vient le signal
|
||||
- **Multi-tenant dès le jour 1** : même si V1 est mono-user par compte, la structure DB doit supporter `organization` → `users` → `invoices` pour ne pas refactorer en V2
|
||||
- **OCR** : provider abstrait derrière une interface — on doit pouvoir switcher Mindee → Document AI → Tesseract sans toucher au reste
|
||||
- **Email outbound** : provider abstrait (Resend / Postmark / SendGrid) avec retry et tracking
|
||||
|
||||
@ -44,14 +44,16 @@ Ce document est la source de vérité technique. Quand le code et ce fichier div
|
||||
| Composant | Rôle | Hosting | Status |
|
||||
|---|---|---|---|
|
||||
| `apps/landing` | Landing + blog publics (Astro 6 SSR) | Pod Node K3s (`rubis-landing:4321`) | ✅ Déployé |
|
||||
| `apps/web` | SPA React (SaaS) | nginx pod K3s (build statique) | À écrire |
|
||||
| `apps/api` | API REST AdonisJS — logique métier, jobs, email | Pod Node K3s (`rubis-api:3333`) | À écrire |
|
||||
| `packages/shared` | Types TS, schemas Zod, constantes communes | workspace local | À écrire |
|
||||
| `packages/ui` | Design system partagé (tokens Tailwind + composants TSX) | workspace local | ✅ |
|
||||
| PostgreSQL | Base de données métier | LXC Proxmox existant | ✅ En place |
|
||||
| MinIO | Stockage PDF + pièces jointes (S3-compat) | LXC Proxmox existant | ✅ En place |
|
||||
| Provider OCR | Extraction texte des factures | Externe (HTTPS) | ADR-020 à venir |
|
||||
| Provider Email | Envoi outbound (relances + check-in) | Externe (HTTPS) | ADR-021 à venir |
|
||||
| `apps/web` | SPA React + TanStack Router/Query (SaaS) | nginx pod K3s (build statique + proxy `/api/*` → rubis-api) | ✅ Déployé |
|
||||
| `apps/api` | API REST AdonisJS v7 — logique métier, jobs BullMQ inline, email, billing Stripe | Pod Node K3s (`rubis-api:3333`) | ✅ Déployé |
|
||||
| `packages/shared` | Types TS, schemas Zod, constantes (statuts, tons, plans, etc.) | workspace local | ✅ |
|
||||
| `packages/ui` | Design system partagé (tokens Tailwind v4 + composants TSX) | workspace local | ✅ |
|
||||
| PostgreSQL | Base de données métier | LXC Proxmox existant (`10.10.10.3`) | ✅ En place |
|
||||
| MinIO | Stockage PDF + uploads blog (S3-compat, bucket `rubis-prod-invoices`) | namespace K3s `minio` | ✅ En place |
|
||||
| Redis | Backend BullMQ (queues: relances, checkins, payment-thanks) | Deployment K3s `rubis-redis` + PVC | ✅ Déployé |
|
||||
| Provider OCR | Extraction texte des factures | Mistral (`OCR_PROVIDER=mistral`) | ✅ Choisi |
|
||||
| Provider Email outbound | Resend (`relances@rubis.pro`, sub-domaine `send.rubis.pro`) | Externe (HTTPS) | ✅ En place |
|
||||
| Provider Email inbound | OVH MX (`contact@rubis.pro`, `dev@rubis.pro`) | Externe (HTTPS/IMAP) | ✅ En place |
|
||||
|
||||
---
|
||||
|
||||
@ -487,21 +489,25 @@ SPA retry l'appel original avec nouveau token
|
||||
### Pods K3s
|
||||
|
||||
```yaml
|
||||
# Namespace: rubis
|
||||
- Deployment: rubis-api # AdonisJS Node, port 3333
|
||||
- Deployment: rubis-web # nginx, sert le bundle Vite, port 80
|
||||
- Deployment: rubis-landing # déjà existant
|
||||
- Service: rubis-api-svc # ClusterIP
|
||||
- Service: rubis-web-svc # ClusterIP
|
||||
- Service: postgres-external # ExternalName → IP du LXC postgres
|
||||
- Service: minio-external # ExternalName → IP du LXC minio
|
||||
- Secret: rubis-config # DB credentials, MinIO credentials, OCR API key, mail API key
|
||||
- IngressRoute (Traefik) :
|
||||
api.rubis.pro → rubis-api-svc:3333
|
||||
app.rubis.pro → rubis-web-svc:80
|
||||
rubis.pro → rubis-landing-svc:80
|
||||
# Namespace: rubis (cf. k3s/app/*.yml)
|
||||
- Deployment: rubis-api # AdonisJS Node, port 3333 (workers BullMQ inline, pas de pod séparé)
|
||||
- Deployment: rubis-web # nginx, sert le bundle Vite + proxy /api → rubis-api:3333, port 80
|
||||
- Deployment: rubis-landing # Astro 6 SSR Node, port 4321
|
||||
- Deployment: rubis-redis # Redis 7 + PVC pour BullMQ
|
||||
- Service: rubis-api # NodePort 30100 → 3333
|
||||
- Service: rubis-web # NodePort 30110 → 80
|
||||
- Service: rubis-landing # NodePort 30111 → 4321
|
||||
- Service: rubis-redis # ClusterIP 6379
|
||||
- ConfigMap: rubis-api-config + rubis-landing-config + rubis-web-config
|
||||
- Secret: rubis-api-secrets # DB credentials, S3 credentials, OCR/Mistral API key, Resend key, Stripe keys
|
||||
- Routing Traefik (config dynamique sur la VM gateway, repo proxmox) :
|
||||
rubis.pro → 10.10.10.5:30111 (rubis-landing)
|
||||
app.rubis.pro/* → 10.10.10.5:30110 (rubis-web — qui proxie /api/* vers rubis-api en interne K3s)
|
||||
# Note : pas de hostname `api.rubis.pro` exposé. L'API est servie via app.rubis.pro/api/*.
|
||||
```
|
||||
|
||||
**Ressources externes (LXC Proxmox, hors K3s)** : PostgreSQL (`PG_HOST=10.10.10.3` en clair dans `rubis-api-config`), MinIO (DNS `minio.minio.svc.cluster.local:9000` via le namespace MinIO du même cluster, bucket prod = `rubis-prod-invoices`). Pas de `Service ExternalName` créé, les URLs sont posées directement dans la ConfigMap.
|
||||
|
||||
### Pipeline CI Gitea
|
||||
|
||||
```
|
||||
@ -554,13 +560,14 @@ healthchecks readinessProbe → service public
|
||||
|
||||
À trancher avant fin V1, par ordre de priorité :
|
||||
|
||||
| # | Sujet | Échéance suggérée |
|
||||
| # | Sujet | Statut |
|
||||
|---|---|---|
|
||||
| 019 | **Domain model** (entités, relations, index) | Avant la 1ère migration |
|
||||
| 020 | **Provider OCR** (Mindee, Document AI, Textract, Tesseract) | Avant l'implémentation du job ProcessOcr |
|
||||
| 021 | **Provider email** (Resend, Postmark, SendGrid, AWS SES) | Avant l'implémentation des relances |
|
||||
| 022 | **Pricing exact** (Free 5 factures ? Pro 19 €/mois ?) | Avant le payment flow |
|
||||
| 023 | **Endpoint waitlist** (Resend / Formspree / API Adonis) | Au push de la landing en prod |
|
||||
| 019 | **Domain model** (entités, relations, index) | ✅ tranché — modèles dans `apps/api/app/models/` |
|
||||
| 020 | **Provider OCR** | ✅ tranché — Mistral (`OCR_PROVIDER=mistral`, cf. `.env.example` + `k3s/app/api.yml`). ADR à formaliser |
|
||||
| 021 | **Provider email** | ✅ tranché — Resend pour le sortant, OVH MX pour l'entrant (cf. `docs/tech/backend.md` §12.5). ADR à formaliser |
|
||||
| 022 | **Pricing exact** | ✅ tranché — Free 5 factures / Pro 19 € / Business 49 € (cf. `apps/api/app/services/billing.ts:34`) |
|
||||
| 023 | **Endpoint waitlist** | ❌ obsolète — la landing pousse directement vers signup, pas de waitlist en prod |
|
||||
| 024 | **Sentry pour error tracking** | ✅ tranché — voir `docs/decisions.md` ADR-024 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
# Guide d'implémentation — Backend
|
||||
|
||||
> Version : 0.1 · Dernière maj : 2026-05-06
|
||||
> Décisions de référence : ADR-014 (stack), ADR-015 (monorepo), ADR-016 (PG), ADR-017 (auth), ADR-018 (storage).
|
||||
> Version : 0.2 · Dernière maj : 2026-05-09
|
||||
> Décisions de référence : ADR-014 (stack), ADR-015 (monorepo), ADR-016 (PG), ADR-017 (auth), ADR-018 (storage), ADR-024 (Sentry).
|
||||
|
||||
Ce document est le **guide pratique d'implémentation de l'API**. Il complète `architecture.md` (qui décrit le **quoi**) en expliquant le **comment** : commandes, snippets, conventions, et — surtout — le **contrat exact** que le SPA attend déjà côté front (les mocks MSW de `apps/web/src/mocks/handlers/` sont la source de vérité du contrat actuel).
|
||||
> ⚠️ **Note de cohérence (audit 2026-05-09)** : ce guide a été rédigé en phase de planification. Plusieurs claims ont divergé du code livré. Pour la **liste exhaustive des routes**, lire **`apps/api/start/routes.ts`** comme source de vérité (~80 routes : auth, factures, clients, plans, billing Stripe, blog admin/public, demo, AI, uploads, KPIs). Les modèles vivants sont dans `apps/api/app/models/`, les services dans `apps/api/app/services/`. Les sections ci-dessous sont valides **dans leur esprit** mais détaillent parfois des choix pré-livraison qui ont évolué.
|
||||
|
||||
Ce document est le **guide pratique d'implémentation de l'API**. Il complète `architecture.md` (qui décrit le **quoi**) en expliquant le **comment** : commandes, snippets, conventions.
|
||||
|
||||
**À lire avant** :
|
||||
|
||||
- `/CLAUDE.md` — contexte top-level
|
||||
- `/docs/produit.md` — flows utilisateur, IN/OUT V1
|
||||
- `/docs/tech/architecture.md` — vue d'ensemble du système
|
||||
- `/docs/tech/frontend.md` — guide d'implémentation du SPA (utile pour comprendre ce que le back doit servir)
|
||||
- `/packages/shared/src/` — types et schemas Zod déjà partagés
|
||||
- `/apps/web/src/mocks/handlers/` — **le contrat API tel qu'il est consommé**
|
||||
- `/docs/tech/frontend.md` — guide d'implémentation du SPA
|
||||
- `/packages/shared/src/` — types et schemas Zod partagés (statuts, tons, plans, billing)
|
||||
- `/apps/api/start/routes.ts` — **contrat API actuel (source de vérité)**
|
||||
- `/apps/api/app/services/` — logique métier (billing, posts, blog_uploads, default_plans, import_batch, etc.)
|
||||
|
||||
---
|
||||
|
||||
@ -22,12 +25,15 @@ L'API (`apps/api/`) est un **AdonisJS v7** en TypeScript, qui sert :
|
||||
|
||||
- **JSON-only** sur `/api/v1/*` (pas de Inertia, pas de Hypermedia — le SPA est un consommateur séparé)
|
||||
- **Auth Bearer** stateless via `@adonisjs/auth` access_tokens (cf. ADR-017), avec un refresh token cookie httpOnly géré custom par-dessus
|
||||
- **Tuyau** pour générer le client TS typé consommé par le SPA → contrat API ↔ web verrouillé par le compilateur
|
||||
- **PostgreSQL** comme base relationnelle (cf. ADR-016, instance LXC Proxmox existante)
|
||||
- **MinIO** pour les pièces jointes (cf. ADR-018)
|
||||
- **Provider OCR** externe (à benchmarker, ADR-020)
|
||||
- **Provider Email** externe (à benchmarker, ADR-021)
|
||||
- **Background jobs** pour l'OCR différé, les relances programmées, les check-ins
|
||||
- **Type-sharing** via `packages/shared` (Zod schemas + types TS), consommé en workspace symlink par `apps/web` et `apps/landing`. *Note historique : la doc évoquait Tuyau pour générer un client TS typé — `@tuyau/core` est dans les deps mais n'est pas utilisé en pratique côté SPA, qui consomme l'API via un client `fetch()` minimaliste dans `apps/web/src/lib/api.ts`.*
|
||||
- **PostgreSQL** comme base relationnelle (cf. ADR-016, instance LXC Proxmox existante, IP `10.10.10.3`)
|
||||
- **MinIO** pour les pièces jointes (cf. ADR-018, namespace K3s `minio`, bucket `rubis-prod-invoices`)
|
||||
- **OCR** : Mistral (`OCR_PROVIDER=mistral`)
|
||||
- **Email outbound** : Resend (sub-domaine `send.rubis.pro`)
|
||||
- **Email inbound** : OVH MX (`contact@rubis.pro`)
|
||||
- **Background jobs** : BullMQ (Redis), workers inline dans le pod API (pas de pod worker séparé)
|
||||
- **Billing** : Stripe (checkout, customer portal, webhook `/billing/webhook`)
|
||||
- **Error tracking** : Sentry (cf. ADR-024)
|
||||
|
||||
Le scaffold initial a été créé via `pnpm create adonisjs@latest -- apps/api --kit=api --pkg=pnpm`, kit `api`. Ça nous a déjà donné :
|
||||
|
||||
@ -50,31 +56,33 @@ Tout le reste (org, clients, factures, plans, jobs, OCR, email) est à construir
|
||||
| Authz | `@adonisjs/bouncer` | Policies pour les permissions (V1 mono-user, prêt V2 multi-user) |
|
||||
| Validation | `@vinejs/vine` | Validateurs typés natifs Adonis, mappés sur les schemas Zod de `packages/shared` |
|
||||
| Mail | `@adonisjs/mail` | Templates + provider switchable (Resend / Postmark / SES) |
|
||||
| Queue | `@rlanz/bull-queue` (BullMQ) | Jobs différés (OCR, envoi email, check-ins) |
|
||||
| Queue | BullMQ direct + ioredis | Jobs différés (envoi relance, envoi confirmation, envoi remerciement paiement). Pas de wrapper Adonis. Code dans `app/services/queue.ts` |
|
||||
| Cache / queue backend | Redis | Backend de BullMQ + cache des KPIs dashboard |
|
||||
| Rate-limit | `@adonisjs/limiter` | 5 req/min sur `/auth/*`, 10/h sur upload |
|
||||
| Tests | `@japa/runner` + `@japa/api-client` | Tests d'intégration HTTP |
|
||||
| Type-sharing front | `@tuyau/core` | Génère `.adonisjs/api.ts` consommé par le SPA |
|
||||
| Type-sharing front | `packages/shared` (workspace) | Zod schemas + types TS (statuts, tons, plans, billing) — consommé par `apps/web` et `apps/landing` |
|
||||
| HTTP client | `@adonisjs/limiter` + `ky` (côté SPA) | — |
|
||||
| Storage | `@adonisjs/drive` (S3 driver MinIO) | Abstraction stockage PDFs |
|
||||
|
||||
### Dépendances déjà installées par le starter API
|
||||
### Dépendances installées (cf. `apps/api/package.json`)
|
||||
|
||||
Voir `apps/api/package.json`. À ajouter pour V1 :
|
||||
Stack actuelle (V1 livrée) :
|
||||
|
||||
```bash
|
||||
cd apps/api
|
||||
pnpm add @adonisjs/bouncer @adonisjs/mail @adonisjs/limiter @adonisjs/drive
|
||||
pnpm add @rlanz/bull-queue bullmq ioredis
|
||||
pnpm add @aws-sdk/client-s3 # pour MinIO via le driver S3 d'@adonisjs/drive
|
||||
pnpm add resend # ou postmark / @aws-sdk/client-ses selon ADR-021
|
||||
node ace add @adonisjs/bouncer
|
||||
node ace add @adonisjs/mail --providers=resend
|
||||
node ace add @adonisjs/limiter
|
||||
node ace add @adonisjs/drive --services=s3
|
||||
node ace add @rlanz/bull-queue
|
||||
```jsonc
|
||||
// apps/api/package.json (extrait)
|
||||
"@adonisjs/auth", "@adonisjs/bouncer", "@adonisjs/mail",
|
||||
"@adonisjs/limiter", "@adonisjs/drive", "@adonisjs/lucid",
|
||||
"bullmq", "ioredis", // queues directes, pas de wrapper Adonis
|
||||
"@aws-sdk/client-s3", // MinIO via driver S3
|
||||
"resend", // email outbound
|
||||
"@mistralai/mistralai", // OCR
|
||||
"stripe", // billing
|
||||
"@anthropic-ai/sdk", // génération IA blog
|
||||
"@sentry/node", // ADR-024
|
||||
```
|
||||
|
||||
Pas de BullMQ direct (sans wrapper Adonis), pas de `@tuyau/server` (le `@tuyau/core` historique n'est plus utilisé côté SPA).
|
||||
|
||||
---
|
||||
|
||||
## 3. Repo layout (apps/api)
|
||||
@ -185,9 +193,11 @@ apps/api/
|
||||
│ │ ├── XXXX_create_import_drafts_table.ts
|
||||
│ │ └── XXXX_create_activity_events_table.ts
|
||||
│ ├── seeders/
|
||||
│ │ ├── default_plans_seeder.ts # 4 plans pré-fournis (cf. seed.ts MSW)
|
||||
│ │ └── demo_data_seeder.ts # comptes démo (dev seulement)
|
||||
│ └── factories/ # Factories Lucid pour les tests
|
||||
│ │ └── (pas de seeders Adonis : les 4 plans pré-fournis vivent dans
|
||||
│ │ app/services/default_plans.ts et sont insérés à la création de
|
||||
│ │ chaque organisation. Le mode démo est servi par
|
||||
│ │ app/services/demo_simulator.ts, pas par un seeder.)
|
||||
│ └── factories.ts # Factories Lucid (mono-fichier, pas de dossier — V1)
|
||||
├── tests/
|
||||
│ ├── functional/ # Tests HTTP via @japa/api-client
|
||||
│ │ ├── auth.spec.ts
|
||||
@ -693,12 +703,12 @@ const driveConfig = defineConfig({
|
||||
services: {
|
||||
s3: services.s3({
|
||||
credentials: {
|
||||
accessKeyId: env.get('MINIO_ACCESS_KEY'),
|
||||
secretAccessKey: env.get('MINIO_SECRET_KEY'),
|
||||
accessKeyId: env.get('S3_ACCESS_KEY'),
|
||||
secretAccessKey: env.get('S3_SECRET_KEY'),
|
||||
},
|
||||
endpoint: env.get('MINIO_ENDPOINT'), // http://lxc-minio:9000
|
||||
region: 'fr-par',
|
||||
bucket: 'rubis-invoices',
|
||||
endpoint: env.get('S3_ENDPOINT'), // http://minio.minio.svc.cluster.local:9000
|
||||
region: env.get('S3_REGION'),
|
||||
bucket: env.get('S3_BUCKET'), // rubis-prod-invoices en prod
|
||||
forcePathStyle: true, // requis pour MinIO
|
||||
visibility: 'private',
|
||||
}),
|
||||
@ -912,7 +922,7 @@ Inbound : les deux veulent le MX `@`. Plan probable : on garde OVH pour
|
||||
|
||||
### 13.1 Stack
|
||||
|
||||
`@rlanz/bull-queue` (Adonis 7 wrapper de BullMQ) + Redis comme backend.
|
||||
BullMQ direct (sans wrapper Adonis) (Adonis 7 wrapper de BullMQ) + Redis comme backend.
|
||||
|
||||
```ts
|
||||
// config/queue.ts
|
||||
@ -931,12 +941,17 @@ const queueConfig = defineConfig({
|
||||
|
||||
### 13.2 Jobs
|
||||
|
||||
| Job | Trigger | Idempotent | Retry |
|
||||
Réalité V1 (cf. `apps/api/app/jobs/`) :
|
||||
|
||||
| Job (fichier) | Trigger | Idempotent | Retry |
|
||||
|---|---|---|---|
|
||||
| `ProcessOcrJob` | POST /invoices/upload | oui (status=validated/skipped → no-op) | 3× exponential backoff |
|
||||
| `SendRelanceJob` | RelanceTask.sendAt | oui (status=sent → no-op) | 5× |
|
||||
| `SendCheckinJob` | CheckinTask.sendAt | oui | 3× |
|
||||
| `RecomputeKpisJob` | nightly cron + post-mutation | oui | 1× |
|
||||
| `send_relance_job.ts` | RelanceTask.sendAt | oui (status=sent → no-op) | 5× |
|
||||
| `send_checkin_job.ts` | CheckinTask.sendAt | oui | 3× |
|
||||
| `send_payment_thanks_job.ts` | confirmation paiement (step 04 du flow) | oui | 3× |
|
||||
|
||||
**OCR** : pas de job dédié — l'extraction est synchrone via `app/services/import_batch.ts` (appel Mistral bloquant pendant l'upload). Si le volume monte, basculer en job différé.
|
||||
|
||||
**KPIs** : calculés on-the-fly à chaque GET dashboard/timeseries (cf. commentaire dans `start/routes.ts`). Pas de `RecomputeKpisJob` — pas de cache Redis pour les KPIs en V1.
|
||||
|
||||
### 13.3 Programmation des relances
|
||||
|
||||
@ -1111,11 +1126,11 @@ REDIS_HOST=lxc-redis.proxmox.local
|
||||
REDIS_PORT=6379
|
||||
|
||||
# MinIO
|
||||
MINIO_ENDPOINT=http://lxc-minio.proxmox.local:9000
|
||||
MINIO_ACCESS_KEY=<secret>
|
||||
MINIO_SECRET_KEY=<secret>
|
||||
MINIO_INVOICES_BUCKET=rubis-invoices
|
||||
MINIO_BACKUPS_BUCKET=rubis-backups
|
||||
S3_ENDPOINT=http://minio.minio.svc.cluster.local:9000
|
||||
S3_ACCESS_KEY=<secret>
|
||||
S3_SECRET_KEY=<secret>
|
||||
S3_REGION=us-east-1
|
||||
S3_BUCKET=rubis-prod-invoices # bucket réel en prod (cf. k3s/app/api.yml)
|
||||
|
||||
# Mail (Resend)
|
||||
MAIL_FROM_ADDRESS=relances@rubis.pro
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
> Version : 0.1 · 2026-05-06
|
||||
|
||||
Tout ce qu'il faut pour faire tourner Rubis en local : services backing (Postgres, Redis, MinIO, Mailhog) + l'API + le SPA.
|
||||
Tout ce qu'il faut pour faire tourner Rubis en local : services backing (Postgres, Redis, MinIO, Mailpit) + l'API + le SPA.
|
||||
|
||||
## Prérequis
|
||||
|
||||
@ -68,7 +68,7 @@ Si Docker n'est pas dispo, on peut basculer sur SQLite :
|
||||
DB_CONNECTION=sqlite
|
||||
```
|
||||
|
||||
Les migrations tournent sur les deux. Pour les jobs/storage/mail, il faut quand même Redis/MinIO/Mailhog — donc Docker reste recommandé.
|
||||
Les migrations tournent sur les deux. Pour les jobs/storage/mail, il faut quand même Redis/MinIO/Mailpit — donc Docker reste recommandé.
|
||||
|
||||
## 3. Web (apps/web)
|
||||
|
||||
@ -86,7 +86,7 @@ Voir `apps/api/.env.example` — c'est la source de vérité. Récap :
|
||||
- **PG_*** : connexion Postgres (default ports décalés docker-compose)
|
||||
- **REDIS_*** : BullMQ
|
||||
- **S3_*** : MinIO (driver S3 d'@adonisjs/drive)
|
||||
- **MAIL_*** + **SMTP_*** : Mailhog en dev, Resend en prod (`MAIL_DRIVER=resend` + `RESEND_API_KEY`)
|
||||
- **MAIL_*** + **SMTP_*** : Mailpit en dev, Resend en prod (`MAIL_DRIVER=resend` + `RESEND_API_KEY`)
|
||||
- **OCR_PROVIDER** : `mock` en dev, `mistral` quand l'API key est en place
|
||||
- **ACCESS_TOKEN_TTL_MINUTES** / **REFRESH_TOKEN_TTL_DAYS** : durées d'auth
|
||||
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
# Guide d'implémentation — Frontend
|
||||
|
||||
> Version : 0.1 · Dernière maj : 2026-05-05
|
||||
> Décisions de référence : ADR-014 (stack), ADR-015 (monorepo), ADR-017 (auth).
|
||||
> Version : 0.2 · Dernière maj : 2026-05-09
|
||||
> Décisions de référence : ADR-014 (stack), ADR-015 (monorepo), ADR-017 (auth), ADR-024 (Sentry).
|
||||
|
||||
> ⚠️ **Note de cohérence (audit 2026-05-09)** : ce guide a été rédigé en phase de planification. Plusieurs choix ont évolué :
|
||||
> - **Tuyau N'EST PAS utilisé** côté SPA. Le client API vit dans `apps/web/src/lib/api.ts` (fetch + ApiError custom). Le partage de types entre API et SPA passe par `packages/shared` (Zod + types TS), pas par un client typed-RPC. Les sections §6 et §7 ci-dessous, qui décrivent l'install et l'usage de Tuyau, sont **historiques** — gardées pour mémoire mais non applicables au code livré.
|
||||
> - Les **tokens** de design vivent dans `packages/ui/src/styles/{tokens,base}.css`, pas inline dans `apps/web/src/styles/app.css` comme la doc l'écrit.
|
||||
> - Les **polices** sont self-hosted via `@fontsource-variable/{bricolage-grotesque,inter}`, pas via `<link>` Google Fonts.
|
||||
|
||||
Ce document est le **guide pratique d'implémentation du SPA**. Il complète `architecture.md` (qui décrit le **quoi**) en expliquant le **comment** : commandes exactes, snippets de config, conventions de dossier.
|
||||
|
||||
@ -9,16 +14,16 @@ Ce document est le **guide pratique d'implémentation du SPA**. Il complète `ar
|
||||
- `/CLAUDE.md` — contexte top-level
|
||||
- `/docs/produit.md` — flows utilisateur, IN/OUT V1
|
||||
- `/docs/marque.md` — palette, typo, voix, do/don't
|
||||
- `/docs/wireframes-mvp.html` — les 13 écrans MVP avec annotations
|
||||
- `/docs/wireframes-mvp.html` — wireframes low-fi initiaux (13 écrans, à jour partiellement)
|
||||
- `/docs/tech/architecture.md` — vue d'ensemble du système
|
||||
|
||||
---
|
||||
|
||||
## 1. Vue d'ensemble
|
||||
|
||||
L'app web (`apps/web/`) est un SPA React 19 buildé par Vite, qui consomme l'API AdonisJS `apps/api/` via un client HTTP type-safe (Tuyau). Le routing client est géré par **TanStack Router** (file-based, type-safe), le state serveur par **TanStack Query**, le styling par **Tailwind CSS v4** avec les tokens de marque issus de `marque.md`.
|
||||
L'app web (`apps/web/`) est un SPA React 19 buildé par Vite, qui consomme l'API AdonisJS `apps/api/` via un client `fetch()` minimaliste dans `apps/web/src/lib/api.ts`. Le routing client est géré par **TanStack Router** (file-based, type-safe), le state serveur par **TanStack Query**, le styling par **Tailwind CSS v4** avec les tokens de marque issus de `packages/ui` (consommé en workspace).
|
||||
|
||||
**Périmètre V1** : 13 écrans listés dans `wireframes-mvp.html`. Auth Bearer (cf. ADR-017) avec refresh token httpOnly cookie. Mobile responsive, pas d'app native.
|
||||
**Périmètre V1 livré** : ~15 routes `_app/` (dashboard, factures, clients, plans, paramètres, abonnement, insights, admin/blog) + onboarding 3 étapes + auth (login, signup, reset-password, callbacks SSO Google/Microsoft). Auth Bearer (cf. ADR-017) avec refresh token httpOnly cookie. Mobile responsive, pas d'app native.
|
||||
|
||||
**Hors scope V1** : SSR (pas nécessaire pour un SaaS B2B authentifié), i18n (FR uniquement), PWA offline (nice-to-have V2).
|
||||
|
||||
@ -163,16 +168,22 @@ body {
|
||||
|
||||
Importé dans `main.tsx` : `import './styles/app.css'`.
|
||||
|
||||
### Polices Google Fonts
|
||||
### Polices self-hosted via @fontsource-variable
|
||||
|
||||
`index.html` :
|
||||
Les fonts sont bundlées au build (pas de fetch Google Fonts au runtime → pas de FOUT, pas de fuite RGPD vers `fonts.googleapis.com`).
|
||||
|
||||
```html
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,500;12..96,600;12..96,700;12..96,800&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
```bash
|
||||
pnpm add @fontsource-variable/bricolage-grotesque @fontsource-variable/inter
|
||||
```
|
||||
|
||||
```css
|
||||
/* apps/web/src/styles/app.css (ou packages/ui/src/styles/base.css selon où tu importes) */
|
||||
@import "@fontsource-variable/bricolage-grotesque";
|
||||
@import "@fontsource-variable/inter";
|
||||
```
|
||||
|
||||
**Bonus prod** : sur la landing Astro, on preload les woff2 latin critiques dans le `<head>` pour casser la chaîne HTML→CSS→fonts du critical path (cf. `apps/landing/src/layouts/Layout.astro`).
|
||||
|
||||
### Usage typique
|
||||
|
||||
```tsx
|
||||
@ -207,27 +218,31 @@ Référence : les 13 écrans dans `wireframes-mvp.html`.
|
||||
|
||||
```
|
||||
apps/web/src/routes/
|
||||
├── __root.tsx # Layout global, providers, AuthGate
|
||||
├── login.tsx # 1.2 Connexion
|
||||
├── signup.tsx # 1.1 Inscription
|
||||
├── _onboarding/ # Layout onboarding (sans sidebar)
|
||||
│ ├── _onboarding.tsx
|
||||
│ ├── compte.tsx # 1.3 step 1
|
||||
│ ├── entreprise.tsx # 1.3 step 2
|
||||
│ └── signature.tsx # 1.3 step 3
|
||||
└── _app/ # Layout app authentifiée
|
||||
├── _app.tsx # Layout : sidebar + topbar + tab bar mobile
|
||||
├── index.tsx # 4.1 Dashboard
|
||||
├── factures.tsx # 2.4 Liste filtrable
|
||||
├── factures.$id.tsx # 4.2 Détail facture (timeline)
|
||||
├── factures.import.$batchId.tsx # 2.2 Vérification OCR
|
||||
├── plans.tsx # 3.1 Bibliothèque
|
||||
├── plans.$slug.tsx # 3.2 Éditeur (cadence + templates)
|
||||
├── clients.tsx # liste clients
|
||||
└── parametres.tsx # paramètres compte
|
||||
├── __root.tsx # Layout global, providers, AuthGate
|
||||
├── login.tsx, signup.tsx, reset-password.tsx, accept-invite.tsx
|
||||
├── auth/ # callbacks SSO Google + Microsoft
|
||||
├── onboarding.tsx # layout onboarding (segment URL /onboarding)
|
||||
├── onboarding/ # 3 étapes inscription post-signup
|
||||
│ ├── compte.tsx
|
||||
│ ├── entreprise.tsx
|
||||
│ ├── signature.tsx
|
||||
│ └── index.tsx
|
||||
└── _app/ # Layout app authentifiée (group route, pas de segment)
|
||||
├── _app.tsx # sidebar + topbar + tab bar mobile
|
||||
├── index.tsx # Dashboard (compteur rubis + KPIs)
|
||||
├── factures.tsx # Liste filtrable
|
||||
├── factures_.$id.tsx # Détail facture + timeline relances
|
||||
├── factures_.import.tsx # Drag-and-drop OCR (étape 1)
|
||||
├── factures_.import_.$batchId.tsx # Vérification OCR (étape 2)
|
||||
├── clients.tsx, clients_.$id.tsx
|
||||
├── plans.tsx, plans_.$slug.tsx, plans_.nouveau.tsx
|
||||
├── parametres.tsx, parametres_.abonnement.tsx
|
||||
├── insights.tsx # KPIs avancés (timeseries)
|
||||
├── admin.blog.tsx # liste posts (admin only)
|
||||
└── admin.blog_.$id.tsx # éditeur post + publish workflow
|
||||
```
|
||||
|
||||
Les routes commençant par `_` sont des **layout routes** (n'ajoutent pas de segment URL).
|
||||
Les routes en `_app/*` sont sous **layout group route** (pas de segment URL ajouté). Le suffixe `_` dans `factures_.$id` empêche l'imbrication visuelle dans `factures.tsx` (TanStack Router file-based convention).
|
||||
|
||||
### Configuration root
|
||||
|
||||
@ -405,7 +420,9 @@ const markPaidMutation = useMutation({
|
||||
|
||||
---
|
||||
|
||||
## 6. Tuyau — client HTTP typé pour AdonisJS
|
||||
## 6. Tuyau — *historique, non utilisé en V1*
|
||||
|
||||
> ⚠️ Cette section décrit un choix initial qui n'a pas été retenu côté SPA livré. Le client HTTP réel est un `fetch()` minimaliste dans `apps/web/src/lib/api.ts`, et le partage de types entre l'API et le SPA passe par `packages/shared` (Zod schemas + types TS exportés en workspace). Section gardée pour mémoire et pour un éventuel pivot futur si la surface API grossit suffisamment.
|
||||
|
||||
[Tuyau](https://github.com/Julien-R44/tuyau) est l'équivalent tRPC pour AdonisJS. Il génère un client TS qui connaît toutes les routes API, leurs payloads, et leurs réponses — depuis le code Adonis lui-même.
|
||||
|
||||
@ -676,14 +693,17 @@ VITE_API_URL=http://localhost:3333
|
||||
VITE_PUBLIC_LANDING_URL=https://rubis.pro
|
||||
```
|
||||
|
||||
Production via secret K3s injecté dans le build Vite :
|
||||
Production : le SPA appelle son API via le **même origin** (`/api/v1/*`), proxifié par le nginx du pod `rubis-web` vers `rubis-api:3333` côté K3s. Pas de hostname `api.rubis.pro`. Le `VITE_API_URL` peut donc être laissé vide ou `'/api/v1'` en prod.
|
||||
|
||||
```bash
|
||||
VITE_API_URL=https://api.rubis.pro
|
||||
VITE_API_URL= # vide → fetch en relatif
|
||||
VITE_PUBLIC_LANDING_URL=https://rubis.pro
|
||||
VITE_USE_MOCKS=false
|
||||
VITE_SENTRY_DSN_WEB=<dsn> # ADR-024
|
||||
VITE_APP_VERSION=$GIT_SHA # tag image / sha commit
|
||||
```
|
||||
|
||||
Toutes les vars accessibles côté SPA **doivent être préfixées `VITE_`** (sinon Vite ne les expose pas au bundle).
|
||||
Toutes les vars accessibles côté SPA **doivent être préfixées `VITE_`** (sinon Vite ne les expose pas au bundle). Liste complète : voir `apps/web/src/lib/env.ts`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -415,7 +415,7 @@
|
||||
|
||||
<header class="page-header">
|
||||
<h1>Rubis Sur l'Ongle — Wireframes MVP <span style="color:var(--ink-3); font-weight: 400;">v0.1</span></h1>
|
||||
<div class="meta">Low-fidelity · niveaux de gris · 13 écrans · centré sur la promesse "3 clics maximum"</div>
|
||||
<div class="meta">Low-fidelity · niveaux de gris · 13 écrans low-fi initiaux (la SPA livrée en compte ~15 + onboarding multi-étapes — cf. <code>apps/web/src/routes/_app/</code>) · centré sur la promesse "3 clics maximum"</div>
|
||||
<nav class="toc">
|
||||
<a href="#onboarding">1. Onboarding & Auth</a>
|
||||
<a href="#upload">2. Upload & OCR</a>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user