rubis/apps/landing/src/layouts/LegalLayout.astro
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

205 lines
5.3 KiB
Plaintext

---
/**
* Layout pour pages légales (mentions, confidentialité, CGV).
* Wrappe Layout.astro et fournit un container `prose` cohérent pour
* les textes longs : eyebrow + h1 italique + lede + sections h2 ancrées.
*/
import Layout from "./Layout.astro";
interface Props {
title: string;
description: string;
/** Eyebrow affiché au-dessus du titre. */
eyebrow: string;
/** Titre affiché (peut contenir <em>...</em>). */
h1: string;
/** Paragraphe d'introduction. */
lede: string;
/** Date "Dernière mise à jour" — ISO ou littérale. */
lastUpdated: string;
}
const { title, description, eyebrow, h1, lede, lastUpdated } = Astro.props;
---
<Layout title={title} description={description} solidHeader>
<article class="legal-prose">
<div class="max-w-[760px] mx-auto px-5 sm:px-8 py-16 lg:py-20">
<p
class="text-[11px] uppercase tracking-[0.16em] font-semibold text-rubis flex items-center gap-2"
>
<span aria-hidden class="size-[7px] bg-current rotate-45 inline-block"></span>
{eyebrow}
</p>
<h1
class="mt-5 font-display font-bold text-ink leading-[1.05] tracking-[-0.025em] text-[40px] sm:text-[52px]"
set:html={h1}
/>
<p class="mt-6 text-[18px] text-ink-2 leading-relaxed">{lede}</p>
<p class="mt-3 text-[13px] text-ink-3 italic">Dernière mise à jour : {lastUpdated}</p>
<div class="mt-12 prose-content">
<slot />
</div>
</div>
</article>
<style is:global>
.prose-content {
color: var(--color-ink);
}
.prose-content > * + * {
margin-top: 1.4em;
}
.prose-content h2 {
font-family: var(--font-display);
font-weight: 700;
font-size: 26px;
letter-spacing: -0.02em;
line-height: 1.25;
margin-top: 2.6em;
color: var(--color-ink);
}
.prose-content h3 {
font-family: var(--font-display);
font-weight: 700;
font-size: 19px;
letter-spacing: -0.015em;
line-height: 1.3;
margin-top: 2em;
color: var(--color-ink);
}
.prose-content p,
.prose-content li {
font-size: 16.5px;
line-height: 1.7;
color: var(--color-ink-2);
}
.prose-content strong {
color: var(--color-ink);
font-weight: 600;
}
.prose-content em {
font-style: italic;
color: var(--color-rubis);
}
.prose-content a {
color: var(--color-rubis);
text-decoration: underline;
text-underline-offset: 3px;
}
.prose-content a:hover {
color: var(--color-rubis-deep);
}
.prose-content ul,
.prose-content ol {
padding-left: 1.6em;
}
.prose-content ul {
list-style: disc;
}
.prose-content ol {
list-style: decimal;
}
.prose-content li + li {
margin-top: 0.5em;
}
.prose-content code {
background: var(--color-cream-2);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.92em;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.prose-content .toc {
background: var(--color-cream-2);
border: 1px solid var(--color-line);
border-radius: 14px;
padding: 22px 28px;
margin: 2.5em 0;
}
.prose-content .toc-title {
font-family: var(--font-display);
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-ink);
margin-bottom: 12px;
}
.prose-content .toc ol {
counter-reset: toc;
padding-left: 0;
list-style: none;
margin: 0;
}
.prose-content .toc li {
counter-increment: toc;
padding-left: 32px;
position: relative;
margin-top: 8px;
font-size: 14.5px;
}
.prose-content .toc li::before {
content: counter(toc, decimal-leading-zero);
position: absolute;
left: 0;
top: 1px;
font-family: var(--font-display);
font-weight: 700;
color: var(--color-rubis);
font-size: 12.5px;
letter-spacing: 0.04em;
}
.prose-content .toc a {
text-decoration: none;
color: var(--color-ink-2);
}
.prose-content .toc a:hover {
color: var(--color-rubis);
text-decoration: underline;
}
.prose-content .callout {
background: var(--color-rubis-glow);
border-left: 3px solid var(--color-rubis);
padding: 16px 20px;
border-radius: 0 8px 8px 0;
margin: 1.6em 0;
}
.prose-content .callout p {
margin: 0;
font-size: 15.5px;
}
.prose-content .def-table {
width: 100%;
border-collapse: collapse;
font-size: 14.5px;
margin: 1.4em 0;
}
.prose-content .def-table thead th {
text-align: left;
font-family: var(--font-display);
font-size: 12px;
letter-spacing: 0.08em;
text-transform: uppercase;
font-weight: 700;
color: var(--color-ink);
padding: 12px 14px;
background: var(--color-cream-2);
border-bottom: 2px solid var(--color-line);
}
.prose-content .def-table tbody td {
padding: 12px 14px;
vertical-align: top;
border-bottom: 1px solid var(--color-line);
color: var(--color-ink-2);
}
.prose-content .def-table tbody td:first-child {
width: 38%;
color: var(--color-ink);
}
</style>
</Layout>