rubis/apps/landing/src/pages/index.astro
ordinarthur 5f88a6411e
All checks were successful
Build & Deploy Landing / build-and-deploy (push) Successful in 1m0s
feat(landing): instrumentation PostHog (Astro client)
Setup PostHog côté landing — loader inline dans Layout.astro + tracking
de 5 events business côté browser :
- blog_article_viewed / blog_cta_clicked (funnel blog → app)
- pricing_pro_cta_clicked / pricing_plan_selected (intent upgrade)
- signup_cta_clicked (CTA hero/header/finalCTA, location-aware)

Vars PUBLIC_POSTHOG_* inlinées au build via build-arg CI
(POSTHOG_PROJECT_TOKEN, partagé avec apps/web). Token public phc_*,
safe à bake dans le bundle.

Au passage : supprime posthog-server.ts laissé par le wizard
(dead code, importait posthog-node qui n'est pas dans les deps).

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

109 lines
4.1 KiB
Plaintext

---
/**
* / — landing publique de rubis.pro.
*
* Statique au build (`prerender = true`) : HTML figé, performances LCP/CLS
* optimales. Toute mise à jour de copy passe par un re-déploiement (acceptable
* vu la fréquence de modif d'une landing). Les sections internes sont des
* composants React .tsx — ce fichier .astro n'est qu'un wrapper de page.
*/
export const prerender = true;
import Layout from "../layouts/Layout.astro";
import { Hero } from "../components/sections/Hero";
import { Stats } from "../components/sections/Stats";
import { Promise as PromiseSection } from "../components/sections/Promise";
import { HowItWorks } from "../components/sections/HowItWorks";
import { Gamification } from "../components/sections/Gamification";
import { AutoBanking } from "../components/sections/AutoBanking";
import { Legal } from "../components/sections/Legal";
import { Pricing } from "../components/sections/Pricing";
import { FAQ } from "../components/sections/FAQ";
import { FinalCTA } from "../components/sections/FinalCTA";
import { Footnotes } from "../components/sections/Footnotes";
const title = "Vos factures relancées toutes seules pendant que vous travaillez";
const description =
"Le SaaS de relance de factures impayées pour TPE-PME françaises. Drag-and-drop, OCR, plans de relance automatiques. 30 jours gratuits, sans carte bancaire.";
const jsonLd = {
"@context": "https://schema.org",
"@type": "SoftwareApplication",
name: "Rubis sur l'ongle",
description,
url: "https://rubis.pro",
applicationCategory: "BusinessApplication",
operatingSystem: "Web",
offers: [
{ "@type": "Offer", name: "Free", price: "0", priceCurrency: "EUR" },
{ "@type": "Offer", name: "Pro", price: "19", priceCurrency: "EUR" },
{ "@type": "Offer", name: "Business", price: "49", priceCurrency: "EUR" },
],
inLanguage: "fr-FR",
publisher: { "@type": "Organization", name: "Rubis sur l'ongle", url: "https://rubis.pro" },
};
---
<Layout title={title} description={description} jsonLd={jsonLd}>
<Hero />
<Stats />
<PromiseSection />
<HowItWorks />
<Gamification />
<AutoBanking />
<Legal />
<Pricing />
<FAQ />
<FinalCTA />
<Footnotes />
</Layout>
<script is:inline>
document.addEventListener('DOMContentLoaded', function () {
// Track CTA clicks linking to the app (Hero, Header, FinalCTA)
document.querySelectorAll('a[href^="https://app.rubis.pro"]').forEach(function (link) {
link.addEventListener('click', function () {
var section = link.closest('section');
var sectionId = section ? (section.id || 'unknown') : 'header';
// Distinguish the Pro plan CTA inside #pricing from generic CTAs
if (section && section.id === 'pricing' && link.textContent.trim().startsWith('Commencer')) {
window.posthog?.capture('pricing_pro_cta_clicked', {
label: link.textContent.trim(),
});
} else if (section && section.id === 'pricing') {
// Free / Business aside cards
var planName = link.querySelector('[class*="font-display"][class*="font-bold"]')?.textContent?.trim() || 'unknown';
window.posthog?.capture('pricing_plan_selected', {
plan: planName,
});
} else {
window.posthog?.capture('signup_cta_clicked', {
location: sectionId || 'header',
label: link.textContent.trim(),
});
}
});
});
// Track header CTA separately (lives outside <section>)
document.querySelectorAll('header a[href^="https://app.rubis.pro"]').forEach(function (link) {
link.addEventListener('click', function () {
window.posthog?.capture('signup_cta_clicked', {
location: 'header',
label: link.textContent.trim(),
});
});
});
// Track FAQ accordion opens
document.querySelectorAll('#faq details').forEach(function (details) {
details.addEventListener('toggle', function () {
if (details.open) {
var question = details.querySelector('summary')?.textContent?.trim() || '';
window.posthog?.capture('faq_item_opened', { question: question });
}
});
});
});
</script>