8 Commits

Author SHA1 Message Date
ordinarthur
cecbddc496 refactor(landing): mono-langue FR + quick wins optimisations conversion
All checks were successful
Build & Deploy Landing / build-and-deploy (push) Successful in 1m1s
- Retire le système i18n EN (apps/landing/src/i18n/, pages /en/*) ajouté
  en 4f3417f. Source unique de copy dans src/copy.ts (FR uniquement).
  Switcher de langue retiré du header, sitemap nettoyé des hreflang.
- Header : micro-baseline « Logiciel de relance de factures impayées »
  sous le wordmark pour lever l'ambiguïté du nom (§1).
- CTA principal : « Lancer Rubis » → « Démarrer mon essai 14 jours »
  avec sous-texte sur Hero / FinalCTA / Pricing (§5).
- Essai 30 j → 14 j sur landing + CGV §6.2 (§3).
- Blog promu en nav primaire avec label « Ressources » (§6).

Doc d'arbitrage : docs/tech/landing-optimisations.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 09:46:42 +02:00
ordinarthur
4f3417fcef feat(landing): support i18n EN avec routing /en/* (Astro i18n natif)
Active Astro i18n avec `defaultLocale: fr` et `prefixDefaultLocale: false`
— les URLs FR restent canoniques à la racine, l'EN vit sous `/en/*` pour
ne pas casser le SEO existant.

Architecture :
- `src/i18n/{types,fr,en,index}.ts` — dico FR fait foi (Dict inféré),
  EN doit matcher la shape ; helpers `getTranslations(locale)` et
  `getAlternateUrl()` pour le language switcher.
- `Layout.astro` lit `Astro.currentLocale`, propage `locale` aux
  composants React, set `<html lang>`, og:locale + alt, hreflang.
- `SiteHeader` expose un lien switcher FR↔EN qui préserve la page.
- Toutes les sections (Hero, Stats, Promise, HowItWorks, Gamification,
  AutoBanking, Legal, Pricing, FAQ, FinalCTA, Footnotes, SiteFooter)
  acceptent une prop `locale` et tirent leurs chaînes du dico.

Pages EN créées :
- `/en/` — home complète
- `/en/blog`, `/en/changelog` — chrome traduit, contenu reste dans la
  langue de rédaction (les .md changelog + posts API sont FR)
- `/en/cgv`, `/en/mentions-legales`, `/en/confidentialite` — résumés
  courts ; la version juridiquement contraignante reste la FR (droit
  français, conformité GDPR/LCEN/LME).

Sitemap mis à jour avec entrées FR/EN + `xhtml:link rel="alternate"`.

Pas de détection auto via Accept-Language pour l'instant — le switcher
header suffit en V1.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 13:29:57 +02:00
ordinarthur
e9e0ac6abb update landing
All checks were successful
Build & Deploy Landing / build-and-deploy (push) Successful in 57s
2026-05-14 15:14:07 +02:00
ordinarthur
1200c549a0 feat(landing): section "Mode automatique bientôt" + FAQ + pricing
All checks were successful
Build & Deploy Landing / build-and-deploy (push) Successful in 1m2s
Annonce la connexion bancaire auto sur la landing pendant la fenêtre
KYC Powens. Trois touches cohérentes pour préparer le marché aux
plans Pro / Business :

- Nouvelle section AutoBanking entre Gamification et Legal : badge
  "Bientôt disponible", h2 « Plus jamais besoin de répondre "C'est
  payé" », 4 bénéfices clés (détection temps réel via Powens AISP,
  toutes banques françaises, mode validation ou auto-pilote, lecture
  seule), badge "Inclus sur Pro et Business", mention DSP2 / AISP
  ACPR pour la conformité. Mock visuel d'email "Garage Lemoine a
  payé F2026-0013 · 4 189,40 €" avec halo glow rubis, récap
  client/facture/montant + check-list "facture payée · relances
  annulées · remerciement envoyé · +1 rubis".
- FAQ : enrichit la 1re question (paiement hors plateforme) avec
  une teaser vers la section AutoBanking, et ajoute une nouvelle
  question dédiée « La connexion bancaire, c'est sécurisé ? Vous
  pouvez bouger mon argent ? » avec réponse explicite sur le statut
  AISP (lecture seule, DSP2, révocation 1 clic).
- Pricing : ajoute une feature dans la liste des inclus Pro avec
  mention "à venir" pour la détection bancaire automatique.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:21:14 +02:00
ordinarthur
3052a7e909 feat(landing): brand "Rubis.pro" + corrections copy
- Brand : suffixe `sur l'ongle` → `.pro` (attaché, muted, non italique).
  Propagé partout via `<Brand withSuffix>` — header/footer landing,
  topbar/sidebar SPA, login/signup/onboarding. Renforce l'identification
  brand requise par Google OAuth (gem seul jugé trop générique).

- Hero : dots groupés avec leur label (inline-flex) — ne se baladent plus
  en début de ligne au flex wrap. Ajout d'un topbar mock "Rubis.pro ·
  Tableau de bord" en haut de la carte hero pour identifier le mock comme
  un vrai dashboard. `DSO` → `DSO*`.

- Stats : "Trois chiffres qui devraient vous fâcher" → "Trois chiffres
  exorbitants". Sous-titre remplacé par "Et vous faites sûrement partie
  intégrante de ces enquêtes." (plus direct, moins gratuit).

- Promise : "Votre temps vaut plus que ça" → "Votre temps est plus
  précieux". Réécriture de l'amorce ("votre boîte / lundis soirs" →
  "votre entreprise / journées"). Ajout d'une ligne italique "Parfois
  moins, si votre plan par défaut est bien réglé".

- HowItWorks : Step 01 — suppression de "à la caisse" et de ", RIB"
  (l'OCR ne lit pas le RIB en V1). Step 03 — "La machine fait le reste"
  → "L'algorithme fait le reste".

- Gamification : suppression de "Pas un PDF abscons" (jargon inutile) et
  de "Et oui, on garde un classement amical" (le classement n'est pas en
  V1). `DSO` → `DSO*`.

- Footnotes : ajout de la définition DSO (Days Sales Outstanding) sous
  celle d'OCR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 14:48:17 +02:00
ordinarthur
06a3aaf468 feat(landing): step 04 — remerciement automatique au client
All checks were successful
Build & Deploy Landing / build-and-deploy (push) Successful in 58s
Ajoute une 4e étape dans la section « Comment ça marche » qui
matérialise la fin heureuse du cycle : le client paye, Rubis envoie
automatiquement un mot court de remerciement (« Merci, paiement bien
reçu »).

Pourquoi c'est important côté pitch :
- Aligne le produit avec le principe brand « respectueux du client
  final » (cf. CLAUDE.md). On n'est pas qu'un outil de pression — on
  est aussi celui qui sait dire merci.
- Crée une attente positive de fin de cycle, qui s'enchaîne mieux
  vers le compteur de rubis (déplacé du step 03 vers 04 pour servir
  de récompense narrative à la boucle complète).

Modifs :
- En-tête : « Trois étapes → Quatre étapes » + ajustement du
  sous-titre.
- Step 03 : retitré « Vous validez. La machine fait le reste. »
  (le punchline « Et puis c'est tout » migre implicitement sur 04).
- Step 04 (nouveau) : email de remerciement + encart rubis.
- Nouveau ThankYouWidget : email card stylé Apple Mail (sender,
  sujet, corps, badge « Envoyé automatiquement ») en tokens rubis
  uniquement (pas de vert — interdit par brand).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 18:52:15 +02:00
ordinarthur
6dcae6956c feat(blog): admin CRUD + image upload + sidebar link
All checks were successful
Build & Deploy Web / build-and-deploy (push) Successful in 1m32s
Build & Deploy API / build-and-deploy (push) Successful in 2m20s
Build & Deploy Landing / build-and-deploy (push) Successful in 1m20s
L'éditeur du blog (jusqu'ici limité au seeder) a maintenant une vraie
interface au-dessus de l'API.

Backend (apps/api) :
* Migration users.is_admin (boolean default false).
* Middleware admin (404 si user.is_admin=false après auth).
* Commande ace promote:admin --email=… [--revoke].
* AdminPostsController CRUD complet : list/show/store/update/publish/
  unpublish/destroy + suggest-slug. Au save, contentHtml + wordCount +
  readingTime sont re-calculés via blog_renderer. Au publish, durcit la
  validation SEO (titre ≤60, excerpt 120-160, hero+alt requis, ≥600 mots),
  flippe status='published' + publishedAt, ping Google+Bing pour le sitemap.
* BlogUploadsController :
  - POST /api/v1/admin/uploads (multipart, JPEG/PNG/WebP, max 4MB)
    → MinIO clé uploads/blog/{uuid}.{ext}
    → renvoie URL relative /api/v1/uploads/blog/{filename}
  - GET /api/v1/uploads/blog/:filename (public, cache immutable 1 an)
    → stream depuis MinIO, regex anti-traversal sur le nom.
* UserTransformer expose isAdmin (cf. shared/types/user).
* k3s/app/landing.yml : NodePort 30111 explicite (pour Traefik repo proxmox).

Frontend (apps/web) :
* Lib typée admin-blog (calls API, queryKeys, helpers URL).
* Route /admin/blog : liste filtrable avec status badge, ouverture
  publique, dépublier, supprimer, "+ Nouveau brouillon".
* Route /admin/blog/:id : éditeur 2-colonnes
  - Gauche : @uiw/react-md-editor (lazy import) avec preview live.
  - Droite : hero image (drag&drop + alt), excerpt avec compteur
    120-160, tags, aperçu Google snippet, validations bloquantes.
  - Autosave debounce 2s + bouton Publier qui sauve d'abord.
  - Hero image upload via MinIO (HeroImageUpload component).
* Sidebar : lien "Blog (admin)" si user.isAdmin.
* Gate côté client (beforeLoad redirect si non admin) + côté serveur
  (middleware admin) — defense in depth.

Note : les requirements de publish miroir backend ↔ frontend (cf.
PUBLISH_REQUIREMENTS dans validators/post.ts et VALIDATION_RULES dans
admin.blog_.\$id.tsx). À synchroniser si un seuil bouge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 17:25:34 +02:00
ordinarthur
e5530930b3 feat: refactor frontend en stack React unifiée (Astro + packages/ui)
Some checks failed
Build & Deploy API / build-and-deploy (push) Failing after 17s
Build & Deploy Web / build-and-deploy (push) Successful in 1m15s
Build & Deploy Landing / build-and-deploy (push) Failing after 3m43s
Trois surfaces partagent désormais le même design system, Tailwind v4
et React 19 — au lieu d'avoir landing en HTML vanilla, app en React, et
blog en Adonis SSR :

* packages/ui — design system partagé (tokens Tailwind v4 + composants
  TSX) extrait depuis apps/web : Brand, Gem, Button, Card, Chip, Eyebrow,
  EmptyState. apps/web migre 41 imports vers @rubis/ui.

* apps/landing — nouvelle app Astro 6 SSR (rubis.pro), remplace l'ancienne
  landing nginx vanilla. Embarque :
  - Landing complète portée en sections React (Hero, Stats, Promise,
    HowItWorks, Gamification, Legal, Pricing, FAQ, FinalCTA, Footnotes)
  - Pages légales (mentions, confidentialité, CGV) via LegalLayout.astro
  - Blog SSR (/blog, /blog/:slug) qui consomme /api/v1/posts
  - sitemap.xml, blog/rss.xml, robots.txt en endpoints Astro
  - SEO complet (canonical, hreflang, OG, Twitter Card, JSON-LD
    Article/BreadcrumbList/Blog/SoftwareApplication)

* apps/api — BlogController réduit à 2 endpoints JSON (GET /api/v1/posts
  + GET /api/v1/posts/:slug). Suppression des templates SSR Adonis
  (apps/api/app/blog/), de l'alias #blog/*, des deps react-dom et
  @types/react-dom. PostTransformer + PostSummaryTransformer ajoutés.
  Le service blog_renderer + le seeder + les 3 articles fondateurs
  restent intacts (réutilisés par futurs admin + cron IA).

* Infra :
  - Dockerfile.landing (multi-stage Node 22 + tini, Astro standalone)
  - k3s/app/landing.yml (Deployment + Service rubis-landing:4321 +
    ConfigMap avec API_URL=http://rubis-api.rubis.svc.cluster.local:3333)
  - .gitea/workflows/deploy.yml mis à jour pour build rubis-landing
  - .gitea/workflows/deploy-web.yml + Dockerfile.web : prennent en
    compte packages/ui/ comme dépendance
  - Suppression du Dockerfile nginx legacy + k3s/{deployment,service}.yml
  - Suppression de landing/ (assets favicons migrés vers
    apps/landing/public/)

* Docs : architecture.md (vue d'ensemble + §4bis apps/landing complet,
  §3 endpoints JSON blog, layout monorepo), CLAUDE.md (stack technique,
  documents associés, déploiement).

Note infra : l'ancien Deployment "rubis" (nginx) et son Service ne sont
PAS supprimés par la CI — à nettoyer manuellement après validation que
Traefik a été repointé sur rubis-landing:4321 dans le repo proxmox.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 15:09:13 +02:00