TypeScript 50.6%
JavaScript 46.1%
SQL 2.1%
Shell 0.6%
CSS 0.5%
1"use client";2import { useState } from "react";3import Link from "next/link";4import { Notice } from "@/components/ui";5type P = { email: string; displayName: string; publicName: string; phone: string; sellerType: "OWNER" | "AGENT"; bio: string; prefs: { notifications?: Record<string, boolean>; privacy?: Record<string, boolean>; termsAcceptedAt?: string } };6export function ProfileForm({ initial }: { initial: P }) {7 const [f, setF] = useState(initial); const [msg, setMsg] = useState<string | null>(null); const [busy, setBusy] = useState(false);8 const n = { inquiries: true, viewings: true, alerts: true, marketing: false, ...(f.prefs.notifications ?? {}) }; const pv = { showPhone: false, showNameOnListings: true, ...(f.prefs.privacy ?? {}) };9 const save = async (e: React.FormEvent) => { e.preventDefault(); setBusy(true); setMsg(null); const r = await fetch("/api/me", { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ displayName: f.displayName || null, publicName: f.publicName || null, phone: f.phone || null, sellerType: f.sellerType, bio: f.bio || null, prefs: { notifications: n, privacy: pv } }) }); setBusy(false); setMsg(r.ok ? "Profil enregistré." : "Erreur d'enregistrement."); };10 const setN = (k: string, v: boolean) => setF({ ...f, prefs: { ...f.prefs, notifications: { ...n, [k]: v } } }); const setP = (k: string, v: boolean) => setF({ ...f, prefs: { ...f.prefs, privacy: { ...pv, [k]: v } } });11 return (12 <form onSubmit={save} className="space-y-5 max-w-2xl">13 <div><h1 className="text-2xl md:text-3xl font-extrabold tracking-tight">Profil et préférences</h1><p className="text-sm text-ink-2 mt-1">Identifiant : <strong>{f.email}</strong> (vérifié). <Link href="/membre/securite" className="underline">Changer d'adresse ou gérer les sessions</Link>.</p></div>14 <div className="tv-card p-4 grid sm:grid-cols-2 gap-3 text-sm">15 <label className="block"><span className="tv-label">Nom complet (privé)</span><input value={f.displayName} onChange={(e) => setF({ ...f, displayName: e.target.value })} maxLength={80} className="tv-chip w-full !pl-3 mt-1" /></label>16 <label className="block"><span className="tv-label">Nom public (sur vos annonces et messages)</span><input value={f.publicName} onChange={(e) => setF({ ...f, publicName: e.target.value })} maxLength={80} className="tv-chip w-full !pl-3 mt-1" placeholder="ex. Marie T." /></label>17 <label className="block"><span className="tv-label">Téléphone (chiffré, jamais public par défaut)</span><input value={f.phone} onChange={(e) => setF({ ...f, phone: e.target.value })} maxLength={30} inputMode="tel" className="tv-chip w-full !pl-3 mt-1" placeholder="418 555-0199" /></label>18 <label className="block"><span className="tv-label">Je publie habituellement comme</span><select value={f.sellerType} onChange={(e) => setF({ ...f, sellerType: e.target.value as "OWNER" | "AGENT" })} className="tv-chip w-full mt-1 appearance-none"><option value="OWNER">Propriétaire</option><option value="AGENT">Représentant / professionnel</option></select></label>19 <label className="block sm:col-span-2"><span className="tv-label">Présentation (facultatif)</span><textarea value={f.bio} onChange={(e) => setF({ ...f, bio: e.target.value })} rows={3} maxLength={600} className="w-full mt-1 border border-line rounded-sm p-2 text-sm" /></label>20 </div>21 <div className="tv-card p-4 text-sm space-y-2"><p className="font-semibold">Notifications par courriel</p>22 {([["inquiries", "Nouvelles demandes et messages"], ["viewings", "Demandes et confirmations de visite"], ["alerts", "Alertes de recherches sauvegardées"], ["marketing", "Nouveautés Toveli (facultatif, désactivé par défaut)"]] as const).map(([k, l]) => <label key={k} className="flex items-center gap-2"><input type="checkbox" checked={!!n[k]} onChange={(e) => setN(k, e.target.checked)} className="accent-[#e0263c]" /> {l}</label>)}23 </div>24 <div className="tv-card p-4 text-sm space-y-2"><p className="font-semibold">Confidentialité</p>25 <label className="flex items-center gap-2"><input type="checkbox" checked={pv.showNameOnListings} onChange={(e) => setP("showNameOnListings", e.target.checked)} className="accent-[#e0263c]" /> Afficher mon nom public sur mes annonces</label>26 <label className="flex items-center gap-2"><input type="checkbox" checked={pv.showPhone} onChange={(e) => setP("showPhone", e.target.checked)} className="accent-[#e0263c]" /> Montrer mon téléphone aux membres connectés qui m'écrivent</label>27 <Notice>Conformément à la Loi 25, vous pouvez demander l'accès, la rectification ou la suppression de vos données : <Link href="/membre/securite" className="underline">Sécurité → Exporter / supprimer mon compte</Link>. Conditions acceptées le {f.prefs.termsAcceptedAt ? new Date(f.prefs.termsAcceptedAt).toLocaleDateString("fr-CA") : "—"}.</Notice>28 </div>29 <div className="flex items-center gap-3"><button type="submit" disabled={busy} className="tv-btn tv-btn-primary">{busy ? "Enregistrement…" : "Enregistrer"}</button>{msg && <span className="text-sm text-ink-2">{msg}</span>}</div>30 </form>31 );32}33