SPB Git

Repository Public

Profil.tsx 8.1 KB 222 lines tsx

1// -----------------------------------------------------------------------------2// Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// pages/Profil.tsx : profil utilisateur — carte de membre Groupe KA (KA-ID),5//   profil Groupe KA (lu du hub, lecture seule), informations du compte,6//   déconnexion7// -----------------------------------------------------------------------------8import { useEffect, useState } from "react";9import { useNavigate } from "react-router-dom";10import { Me, Socials, fetchMe, logout } from "../api";1112const fmtEpoch = (ts: number | null | undefined): string => {13  if (!ts) return "—";14  return new Date(ts * 1000).toLocaleDateString("fr-CA", {15    day: "numeric", month: "long", year: "numeric",16  }).replace(/^1 /, "1ᵉʳ ");17};1819const SOCIAL_BASE: Record<string, string> = {20  instagram: "https://www.instagram.com/",21  facebook: "https://www.facebook.com/",22  x: "https://x.com/",23  linkedin: "https://www.linkedin.com/in/",24  tiktok: "https://www.tiktok.com/@",25  youtube: "https://www.youtube.com/@",26};2728const SOCIAL_FIELDS: { key: keyof Socials; label: string }[] = [29  { key: "instagram", label: "Instagram" },30  { key: "facebook", label: "Facebook" },31  { key: "x", label: "X (Twitter)" },32  { key: "linkedin", label: "LinkedIn" },33  { key: "tiktok", label: "TikTok" },34  { key: "youtube", label: "YouTube" },35];3637/** « @pseudo » ou « pseudo » -> URL complète de la plateforme ; URL laissée telle quelle */38function socialUrl(key: string, value: string): string {39  const v = value.trim();40  if (/^https?:\/\//i.test(v)) return v;41  if (key === "website") return `https://${v}`;42  return (SOCIAL_BASE[key] ?? "https://") + v.replace(/^@/, "");43}4445/** Profil géré au HUB Groupe KA — LECTURE SEULE (l'édition se fait sur46 *  groupe-ka.com/compte, une seule saisie pour toutes les plateformes). */47function HubProfileSection({ me }: { me: Me }) {48  const socialLinks = SOCIAL_FIELDS.filter((s) => (me.socials?.[s.key] ?? "").trim());49  return (50    <section className="hub-profile">51      <h3>Mon profil Groupe KA</h3>52      {me.bio && <p className="hub-bio">{me.bio}</p>}53      <div className="hub-meta">54        {(me.job_title || me.company) && (55          <span className="hub-chip">56            {[me.job_title, me.company].filter(Boolean).join(" · ")}57          </span>58        )}59        {me.city && <span className="hub-chip">{me.city}</span>}60        {me.age != null && <span className="hub-chip">{me.age} ans</span>}61        {me.website && (62          <a className="hub-chip" href={socialUrl("website", me.website)}63             target="_blank" rel="noopener noreferrer">Site web ↗</a>64        )}65        {socialLinks.map((s) => (66          <a key={s.key} className="hub-chip"67             href={socialUrl(s.key, me.socials![s.key]!)}68             target="_blank" rel="noopener noreferrer" title={s.label}>69            {s.label}70          </a>71        ))}72      </div>73      <div>74        <a className="btn" href="https://www.groupe-ka.com/compte"75           target="_blank" rel="noopener noreferrer">76          Modifier mon profil sur groupe-ka.com ↗77        </a>78      </div>79      <p className="hub-hint">80        Votre profil est géré au niveau du groupe : une seule saisie, visible81        sur les huit plateformes du groupe.82      </p>83    </section>84  );85}8687export default function ProfilPage() {88  const [me, setMe] = useState<Me | null | undefined>(undefined); // undefined = chargement89  const [copied, setCopied] = useState(false);90  const nav = useNavigate();9192  useEffect(() => {93    fetchMe().then((r) => setMe(r.user));94  }, []);9596  const copyKaId = async () => {97    if (!me?.ka_id) return;98    try {99      await navigator.clipboard.writeText(me.ka_id);100      setCopied(true);101      setTimeout(() => setCopied(false), 1800);102    } catch { /* presse-papiers indisponible : tant pis */ }103  };104105  if (me === undefined) {106    return <div className="container profil"><div className="notice">Chargement…</div></div>;107  }108109  if (me === null) {110    return (111      <div className="container profil">112        <span className="kicker">Mon compte</span>113        <h1>Connectez-vous pour <em>votre profil</em>.</h1>114        <p className="lede">115          Connectez-vous avec votre <b>KA&nbsp;ID</b> — l'identifiant de membre116          du Groupe&nbsp;KA, le même sur toutes les plateformes du groupe.117        </p>118        <div className="profil-actions">119          <a className="btn login-cta" href="/api/auth/ka/login?next=%2Fprofil">120            Se connecter avec KA ID121          </a>122          <a123            className="btn ghost login-cta"124            href="https://www.groupe-ka.com/connexion"125            target="_blank"126            rel="noopener noreferrer"127          >128            Créer un compte sur groupe-ka.com ↗129          </a>130        </div>131      </div>132    );133  }134135  return (136    <div className="container profil">137      <span className="kicker">Mon compte</span>138      <h1>139        {me.name ? <>Salut, <em>{me.name.split(" ")[0]}</em>.</>140                 : <>Votre <em>profil</em>.</>}141      </h1>142143      {/* ——— Carte de membre Groupe KA ——— */}144      <div className="pc" role="img" aria-label={`Carte de membre ${me.ka_id}`}>145        <div className="pc-watermark" aria-hidden="true">KA</div>146        <div className="pc-head">147          <span className="pc-brand">Groupe <span className="pc-ka">KA</span></span>148          <span className="pc-label">Carte de membre · Groupe KA</span>149        </div>150        <div className="pc-id-block">151          <span className="pc-id-label">KA-ID</span>152          <span className="pc-id">{me.ka_id}</span>153        </div>154        <div className="pc-foot">155          <div className="pc-holder">156            <span className="pc-holder-name">{me.name || me.email}</span>157            {me.role_label && (158              <span className="pc-role-badge">{me.role_label}</span>159            )}160            <span className="pc-holder-since">Membre depuis le {fmtEpoch(me.created)}</span>161          </div>162          {me.picture && (163            <img className="pc-avatar" src={me.picture} alt="" referrerPolicy="no-referrer" />164          )}165        </div>166        <div className="pc-strip" aria-hidden="true">167          {Array.from({ length: 28 }).map((_, i) => <i key={i} />)}168        </div>169      </div>170171      <button className={`btn ghost pc-copy ${copied ? "ok" : ""}`} onClick={copyKaId}>172        {copied ? "✓ Copié" : "Copier mon KA-ID"}173      </button>174175      {/* ——— Informations ——— */}176      <section className="profil-grid">177        <div className="pg-item">178          <span className="pg-label">Nom</span>179          <span className="pg-value">{me.name || "—"}</span>180        </div>181        <div className="pg-item">182          <span className="pg-label">Courriel</span>183          <span className="pg-value">{me.email || "—"}</span>184        </div>185        <div className="pg-item">186          <span className="pg-label">Identifiant membre</span>187          <span className="pg-value mono">{me.ka_id}</span>188        </div>189        <div className="pg-item">190          <span className="pg-label">Connexion</span>191          <span className="pg-value">KA ID (groupe-ka.com)</span>192        </div>193        <div className="pg-item">194          <span className="pg-label">Membre depuis</span>195          <span className="pg-value">{fmtEpoch(me.created)}</span>196        </div>197      </section>198199      {me.profile_source === "groupe-ka" && <HubProfileSection me={me} />}200201      <p className="profil-note">202        Votre <b>KA-ID</b> est votre identifiant unique dans l'écosystème{" "}203        <a href="https://www.groupe-ka.com" target="_blank" rel="noopener noreferrer">204          Groupe KA205        </a>{" "}206        — il vous suit sur toutes les plateformes du groupe. Auto-Ka ne conserve207        que les informations de ce profil ; rien d'autre, et jamais revendues.208      </p>209210      {/* ——— Actions ——— */}211      <div className="profil-actions">212        <button213          className="btn ghost"214          onClick={async () => { await logout(); nav("/"); window.location.reload(); }}215        >216          Se déconnecter217        </button>218      </div>219    </div>220  );221}222
Files main
Menu

Theme