import { useState } from "react"; import { INK, RED, BLUE, PAPER, SKY, MUTE, FONT_MONO, FONT_SERIF } from "../../theme"; import { GENDER_LABEL, RELATIONSHIP_LABEL, genderLabel, AGE_MIN, AGE_MAX, BIO_MAX } from "../../constants"; import { fmtDate, agesToBirthRange } from "../../lib/dates"; import { currentLocation } from "../../lib/location"; import { locatieFout } from "../../lib/plugin-fouten"; import DataSection from "./DataSection"; import AccountSection from "./AccountSection"; import PostSection from "./PostSection"; /* * Profiel bewerken: leeftijden voor, geboortedata achter. onSave(profile) → Promise. * `onboarding`: stap 2 na registratie (andere kop en knoptekst, geen uitloggen/AVG-sectie). * `blocked`: [{ user, since }] — wie ik geblokkeerd heb; onUnblock(userId) → Promise. Alleen getoond als er iemand in staat. * `onUpdateAccount(changes)` → Promise: naam/e-mail/geboortedatum/gender/wachtwoord (zie AccountSection). * `onLogoutOthers(currentPassword)` → Promise<{ loggedOut }>: overal elders uitloggen, dit toestel blijft. * `onExport()` → Promise en `onDelete(password, farewell)` → Promise: de AVG-knoppen onderaan (zie DataSection). * `token` en `lastLetterId`: voor de meldingsinstelling (PostSection); in de browser doet die niets. */ export default function ProfileEditor({ me, onboarding = false, onSave, onLogout, blocked = [], onUnblock, onUpdateAccount, onLogoutOthers, onExport, onDelete, token, lastLetterId = "0" }) { const [unblocking, setUnblocking] = useState({ id: null, error: null }); const unblock = async (userId) => { setUnblocking({ id: userId, error: null }); try { await onUnblock(userId); setUnblocking({ id: null, error: null }); } catch (e) { setUnblocking({ id: null, error: e.message }); } }; const [text, setText] = useState(me.text); const [hidden, setHidden] = useState(me.hidden ?? false); const [relationship, setRelationship] = useState(me.relationship); const [seekRelatie, setSeekRelatie] = useState(me.filter.seekRelatie); const [seekTrans, setSeekTrans] = useState(me.filter.seekTrans); const [aMin, setAMin] = useState(me.filter.ageMin); const [aMax, setAMax] = useState(me.filter.ageMax); const [genders, setGenders] = useState(me.filter.genders); const [lat, setLat] = useState(me.location?.lat ?? ""); const [lon, setLon] = useState(me.location?.lon ?? ""); const [useRadius, setUseRadius] = useState(me.filter.radiusKm != null); const [radiusKm, setRadiusKm] = useState(me.filter.radiusKm ?? 25); const [geoStatus, setGeoStatus] = useState(null); // null | "bezig" | de reden waarom het niet lukte const [saveState, setSaveState] = useState({ busy: false, saved: false, error: null }); /* Een profiel zonder tekst verschijnt niet in Zoeken (zie MUTUAL_MATCH); dat hoort de schrijver te weten */ const leeg = text.trim() === ""; const range = agesToBirthRange(Math.min(aMin, aMax), Math.max(aMin, aMax)); const toggle = (g) => setGenders((gs) => (gs.includes(g) ? gs.filter((x) => x !== g) : [...gs, g])); const hasLocation = lat !== "" && lon !== "" && Number.isFinite(+lat) && Number.isFinite(+lon); const location = hasLocation ? { lat: +lat, lon: +lon } : null; const radiusActive = useRadius && hasLocation && radiusKm > 0; const locate = async () => { setGeoStatus("bezig"); try { const pos = await currentLocation(); setLat(pos.lat); setLon(pos.lon); setGeoStatus(null); } catch (e) { setGeoStatus(locatieFout(e)); } }; const save = async () => { setSaveState({ busy: true, saved: false, error: null }); try { await onSave({ text, hidden, relationship, genders, seekRelatie, seekTrans, ageMin: Math.min(aMin, aMax), ageMax: Math.max(aMin, aMax), location, radiusKm: radiusActive ? Math.round(radiusKm) : null, }); setSaveState({ busy: false, saved: true, error: null }); setTimeout(() => setSaveState((s) => ({ ...s, saved: false })), 2000); } catch (e) { setSaveState({ busy: false, saved: false, error: e.message }); } }; const { busy, saved, error } = saveState; const numberInput = { width: 64, padding: 8, border: `2px solid ${INK}`, fontSize: 15 }; const coordInput = { ...numberInput, width: 96 }; /* * Een getalveld is nooit leeg: haal je het cijfer weg, dan staat er 0 (of de oude waarde) en moet * je die er eerst uit peuteren voor je kunt typen. Bij focus alles selecteren maakt van "wijzigen" * weer "intypen" — de eerste toets vervangt wat er stond. */ const selectAll = (e) => e.target.select(); const smallButton = { background: "#fff", color: INK, border: `2px solid ${INK}`, padding: "6px 10px", fontSize: 13, fontWeight: 600 }; const chip = (active) => ({ padding: "8px 14px", border: `2px solid ${INK}`, fontWeight: 600, fontSize: 14, background: active ? INK : "#fff", color: active ? "#fff" : INK, }); return (

{onboarding ? "Welkom, " + me.name : "Mijn profiel"}

{onboarding ? "stap 2 van 2 · " : ""}alleen tekst — je foto's verstuur je per brief, één per brief

{onboarding && (

Schrijf in een paar zinnen wie je bent en wat je zoekt, en stel in wie jij wilt zien. Op deze woorden word je gevonden — je profiel is tekst, foto's horen in brieven.

)} {/* ---------- Over mij: identiteit, relatiestatus, tekst, locatie ---------- */}

Over mij

{/* buiten onboarding staat deze regel bij Account (verderop), waar hij ook te wijzigen is */} {onboarding && (

{me.name} · {genderLabel(me)} · {me.age} jaar · {me.email}

)}
{Object.entries(RELATIONSHIP_LABEL).map(([r, label]) => ( ))}

{relationship === "relatie" ? "Je bent alleen zichtbaar voor mensen die ook mensen in een relatie willen zien." : "Geef het aan als je al een relatie hebt — anderen kiezen zelf of ze je dan willen zien."}