- Accueil façon profil GitHub (bio, homelab, projets épinglés, parcours) avec fond dégradé et panneaux liquid glass - /cv : consultable en ligne (thème sombre), imprimable en PDF propre (thème clair, 1 page) ; numéro de téléphone non scrapable - Pages d'erreur 404 / 500 / global-error animées (GSAP) - Métadonnées : Open Graph, Twitter, JSON-LD Person, sitemap, robots (allowlist Google + bots d'aperçu, reste bloqué) - Images OG et favicon en PNG statiques - Contenu du CV : Saint-Paul, langues, expériences détaillées - Export statique (output: export) + DEPLOY.md - Retrait de l'indicateur de dev Next.js et des fichiers du starter Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3hzMzvei419MwkKFZkvMX
52 lines
1.4 KiB
TypeScript
52 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useSyncExternalStore } from "react";
|
|
|
|
// Numéro inversé : jamais présent tel quel dans le HTML rendu côté serveur,
|
|
// et pas de correspondance grep directe. Il n'est reconstruit et injecté dans
|
|
// le DOM qu'après hydratation (client), pour l'impression du CV et le clic
|
|
// « Afficher ». Les harvesters qui n'exécutent pas de JS ne récupèrent rien.
|
|
const REV = "78 45 06 396 262+";
|
|
const subscribe = () => () => {};
|
|
|
|
export default function RevealPhone() {
|
|
const isClient = useSyncExternalStore(
|
|
subscribe,
|
|
() => true,
|
|
() => false,
|
|
);
|
|
const [shown, setShown] = useState(false);
|
|
|
|
if (!isClient) {
|
|
return <span className="text-muted">Afficher le numéro</span>;
|
|
}
|
|
|
|
const display = REV.split("").reverse().join("");
|
|
const tel = display.replace(/\s/g, "");
|
|
const link = (
|
|
<a href={`tel:${tel}`} className="text-accent hover:underline">
|
|
{display}
|
|
</a>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{/* Visible uniquement à l'impression / export PDF */}
|
|
<span className="hidden print:inline">{link}</span>
|
|
<span className="print:hidden">
|
|
{shown ? (
|
|
link
|
|
) : (
|
|
<button
|
|
type="button"
|
|
onClick={() => setShown(true)}
|
|
className="text-accent hover:underline"
|
|
>
|
|
Afficher le numéro
|
|
</button>
|
|
)}
|
|
</span>
|
|
</>
|
|
);
|
|
}
|