ordinarthur e5530930b3
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
feat: refactor frontend en stack React unifiée (Astro + packages/ui)
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

56 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Eyebrow } from "@rubis/ui";
const STATS = [
{
num: "44 j",
desc: "Le retard moyen de paiement pour une facture émise par une TPE en France.",
source: "Source : Altares · Observatoire des délais de paiement, 2024",
},
{
num: "15 Md€",
desc: "De trésorerie bloquée chez les PME françaises à cause des retards de paiement.",
source: "Source : Banque de France · Rapport annuel 2024",
},
{
num: "26 %",
desc: "De chances d'être payé si vous attendez plus de 30 jours pour relancer.",
source: "Source : AFDCC · Étude crédit management",
},
];
export function Stats() {
return (
<section className="bg-cream-2 border-y border-line">
<div className="max-w-[1180px] mx-auto px-5 sm:px-8 py-20 lg:py-24">
<div className="text-center max-w-[640px] mx-auto mb-12">
<Eyebrow>L'état des paiements en France</Eyebrow>
<h2 className="mt-4 font-display font-bold text-ink leading-[1.1] tracking-[-0.025em] text-[34px] sm:text-[44px]">
Trois chiffres qui devraient vous fâcher.
</h2>
<p className="mt-4 text-[17px] text-ink-2 leading-relaxed">
Si vous lisez ça, vous avez probablement une facture impayée à l'heure on parle.
Vous n'êtes pas un cas isolé.
</p>
</div>
<div className="grid sm:grid-cols-3 gap-5 lg:gap-7">
{STATS.map((s) => (
<div
key={s.num}
className="bg-white border border-line rounded-card p-7 lg:p-8 shadow-soft"
>
<div className="font-display font-extrabold text-rubis text-[44px] sm:text-[52px] tracking-[-0.03em] leading-none">
{s.num}
</div>
<p className="mt-4 text-[15.5px] text-ink leading-relaxed">{s.desc}</p>
<p className="mt-4 pt-4 border-t border-line text-[12px] text-ink-3 italic">
{s.source}
</p>
</div>
))}
</div>
</div>
</section>
);
}