import React, { useState, useEffect, useRef } from 'react'; import { Download, Upload, ShieldCheck, User, CreditCard, Settings, CheckCircle, Image as ImageIcon, Loader2 } from 'lucide-react'; export default function App() { // --- ÉTATS DES DONNÉES --- const [nom, setNom] = useState('RAOULT'); const [prenom, setPrenom] = useState('Didier'); const [numero, setNumero] = useState('0666'); const [annee, setAnnee] = useState('2026'); const [validite, setValidite] = useState('31/12/2027'); const [statut, setStatut] = useState('MEMBRE SOUTIEN'); const [includePrisme, setIncludePrisme] = useState(false); // --- LOGOS (Format Base64 forcé pour la capture) --- const [logoAstec, setLogoAstec] = useState(''); const [logoPrisme, setLogoPrisme] = useState(''); const [imagesReady, setImagesReady] = useState(false); // --- ÉTATS UI --- const [isGenerating, setIsGenerating] = useState(false); const [showSuccess, setShowSuccess] = useState(false); const [errorMsg, setErrorMsg] = useState(''); const cardRef = useRef(null); // 1. Chargement de html2canvas useEffect(() => { const script = document.createElement('script'); script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"; script.async = true; document.body.appendChild(script); return () => { if (document.body.contains(script)) document.body.removeChild(script); }; }, []); // 2. CONVERSION ANTI-CORS (Double Proxy pour garantir l'affichage au téléchargement) useEffect(() => { let isMounted = true; const fetchAsBase64 = async (url) => { const proxies = [ `https://corsproxy.io/?${encodeURIComponent(url)}`, `https://api.allorigins.win/raw?url=${encodeURIComponent(url)}` ]; for (const proxy of proxies) { try { const res = await fetch(proxy); if (res.ok) { const blob = await res.blob(); return await new Promise((resolve) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.readAsDataURL(blob); }); } } catch (e) { console.warn("Proxy fallback...", proxy); } } return url; // Repli ultime sur l'URL brute si tout échoue }; const initImages = async () => { setImagesReady(false); const baseAstec = await fetchAsBase64('https://eglisenpq.neocities.org/image%20/Image2.png'); const basePrisme = await fetchAsBase64('https://eglisenpq.neocities.org/image%20/Capture%20d%E2%80%99%C3%A9cran%202026-03-05%20085325.jpg'); if (isMounted) { setLogoAstec(baseAstec); setLogoPrisme(basePrisme); setImagesReady(true); } }; initImages(); return () => { isMounted = false; }; }, []); const handleLogoUpload = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => setLogoAstec(reader.result); reader.readAsDataURL(file); } }; const downloadPNG = async () => { if (!window.html2canvas || !cardRef.current) return; setIsGenerating(true); setErrorMsg(''); try { // Petite pause pour s'assurer que le DOM est rafraîchi await new Promise(r => setTimeout(r, 500)); const canvas = await window.html2canvas(cardRef.current, { scale: 4, // Échelle optimisée (4x = 1680x1060 px) pour éviter les crash mémoire du navigateur useCORS: true, allowTaint: true, backgroundColor: "#ffffff", logging: false, width: 420, height: 265 }); const link = document.createElement('a'); link.download = `carte_astec_${nom.toLowerCase().trim()}.png`; link.href = canvas.toDataURL('image/png', 1.0); link.click(); setShowSuccess(true); setTimeout(() => setShowSuccess(false), 3000); } catch (e) { console.error(e); setErrorMsg("Erreur lors de la capture. Essayez d'importer le logo manuellement."); } finally { setIsGenerating(false); } }; return (
{/* Header Dashboard */}

GÉNÉRATEUR ASTEC

Badge Officiel Haute Définition

{/* PANNEAU DE CONTRÔLE */}

Informations Membre

setNom(e.target.value.toUpperCase())} className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl font-black focus:border-[#f89747] outline-none transition-all" placeholder="NOM" /> setPrenom(e.target.value)} className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl font-medium focus:border-[#f89747] outline-none transition-all" placeholder="Prénom" />
setNumero(e.target.value)} className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl font-mono text-sm outline-none" placeholder="N°" /> setAnnee(e.target.value)} className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl font-black text-[#f89747] text-center outline-none" placeholder="Année" />
setStatut(e.target.value.toUpperCase())} className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl font-black text-xs outline-none" placeholder="Statut" /> setValidite(e.target.value)} className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl text-sm outline-none" placeholder="Validité" />

Personnalisation

{errorMsg && (
{errorMsg}
)}
{/* RENDU DE LA CARTE */}

Aperçu du Badge

{showSuccess &&
EXPORTÉ EN HD
}
{/* === LA CARTE ASTEC (BLINDÉE CONTRE LES BUGS HTML2CANVAS) === */}
{/* 1. CADRE BLEU (Couche absolue par-dessus tout, impossible à rater) */}
{/* 2. BARRE ORANGE GAUCHE (Verrouillée) */}
{/* 3. LOGO EN FILIGRANE AVEC DÉGRADÉ STABLE */}
{logoAstec && ASTEC}
{/* 4. FOND RADAR GRAPHIQUE */}
{/* 5. ÉLÉMENTS TEXTUELS (POSITIONS ABSOLUES FIXES z-50) */} {/* En-tête Association */}

Association pour la Science et
la Transmission de l'Esprit Critique

{/* Statut et Année (Haut Droite) */}
{statut} {annee}
{/* Bloc Identité Central (Côté Droit) */}

{nom || 'NOM'}

{prenom || 'Prénom'}

{/* Bloc Données Techniques (Bas Droite) */}

N° ADHÉRENT

{numero || '-'}

VALIDITÉ

{validite}

{/* Logo Prisme Optionnel (Centré au-dessus du nom) */} {includePrisme && (
{logoPrisme && Prisme}
)} {/* Pied de Page (Les 3 Piliers) */}
LAÏCITÉ
HUMANISME
MATÉRIALISME

Système Anti-CORS Actif & Cadre CSS Vectorisé.

); }