Een dating-app op basis van brieven, niet van gezichten!

main

Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.

8.1 kB Ruw bestand
import { useState } from "react";
import { INK, RED, BLUE, PAPER, SKY, MUTE, FONT_MONO, FONT_SERIF } from "../../theme";
import { FAREWELL_MAX, GENERAL_CONTACT } from "../../constants";
import ExportDialog from "./ExportDialog";
import PrivacyDialog from "../privacy/PrivacyDialog";

/*
 * Jouw gegevens (AVG), onderaan Mijn profiel: inzage (onExport() → Promise<export>, getoond in
 * ExportDialog) en vergetelheid (onDelete(password, farewell) → Promise; daarna is de sessie weg
 * en toont App het inlogscherm). Verwijderen vraagt eerst het wachtwoord, inline — geen
 * browser-confirm, want er is echt iets in te vullen.
 *
 * In datzelfde formulier staat de brief aan de beheerder. Dat is de enige plek waar hij thuishoort:
 * wie weggaat heeft meestal een reden, en die reden is precies wat je nooit te horen krijgt als het
 * vertrek één knop is. Hij is niet verplicht, hij houdt niets tegen, en hij blijft staan zonder wie
 * hem schreef — dus antwoorden kan niet, en dat staat er ook bij.
 */
const FAREWELL_START = { text: "", open: false };

export default function DataSection({ email, onExport, onDelete }) {
  const [exp, setExp] = useState({ busy: false, data: null, error: null });
  const [del, setDel] = useState({ open: false, password: "", busy: false, error: null });
  const [farewell, setFarewell] = useState(FAREWELL_START);
  const [privacyOpen, setPrivacyOpen] = useState(false);

  const exportData = async () => {
    setExp({ busy: true, data: null, error: null });
    try {
      setExp({ busy: false, data: await onExport(), error: null });
    } catch (e) {
      setExp({ busy: false, data: null, error: e.message });
    }
  };

  const deleteAccount = async (e) => {
    e.preventDefault();
    if (!del.password) return;
    setDel((d) => ({ ...d, busy: true, error: null }));
    try {
      await onDelete(del.password, farewell.text);
      // gelukt: de sessie is gewist, App wisselt naar het inlogscherm — hier valt niets meer te tonen
    } catch (err) {
      setDel((d) => ({ ...d, busy: false, error: err.message }));
    }
  };

  const closeDelete = () => {
    setDel({ open: false, password: "", busy: false, error: null });
    setFarewell(FAREWELL_START);
  };

  const outline = (color) => ({ background: "#fff", color, border: `2px solid ${color}`, padding: "8px 14px", fontSize: 13, fontWeight: 600 });

  return (
    <div style={{ marginTop: 36, paddingTop: 16, borderTop: `1px solid ${SKY}` }}>
      <h3 style={{ margin: "0 0 4px", fontSize: 16 }}>Jouw gegevens</h3>
      <p style={{ fontSize: 13, color: MUTE, margin: "0 0 12px", lineHeight: 1.5 }}>
        Je kunt altijd opvragen wat we van je bewaren (je profiel, brieven, favorieten, blokkades en sessies) en
        je account met alles erin laten wissen — zoals de AVG voorschrijft. Wat we bewaren en waarom staat in de{" "}
        <button onClick={() => setPrivacyOpen(true)} style={{ background: "none", border: "none", color: BLUE, fontWeight: 700, fontSize: 13, padding: 0, textDecoration: "underline" }}>
          privacyverklaring
        </button>.
      </p>
      {privacyOpen && <PrivacyDialog onClose={() => setPrivacyOpen(false)} />}
      <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
        <button onClick={exportData} disabled={exp.busy} style={{ ...outline(INK), opacity: exp.busy ? 0.6 : 1 }}>
          {exp.busy ? "Ophalen…" : "⤓ Mijn gegevens opvragen"}
        </button>
        {!del.open && (
          <button onClick={() => setDel({ open: true, password: "", busy: false, error: null })} style={outline(RED)}>
            Account verwijderen
          </button>
        )}
      </div>
      {exp.error && (
        <p role="alert" style={{ fontFamily: FONT_MONO, fontSize: 12, color: RED, margin: "8px 0 0" }}>fout: {exp.error}</p>
      )}
      {exp.data && <ExportDialog data={exp.data} onClose={() => setExp((s) => ({ ...s, data: null }))} />}

      {del.open && (
        <form onSubmit={deleteAccount} style={{ marginTop: 14, padding: "12px 14px", border: `2px solid ${RED}`, background: "#fff" }}>
          <p style={{ fontSize: 14, fontWeight: 700, margin: "0 0 4px", color: RED }}>Je account definitief verwijderen?</p>
          <p style={{ fontSize: 13, color: MUTE, margin: "0 0 10px", lineHeight: 1.5 }}>
            Dit wist je account, je profiel en álle brieven — ook wat je aan anderen schreef; bij hen verdwijnt de
            correspondentie eveneens. Dit kan niet ongedaan worden gemaakt. Bevestig met je wachtwoord.
          </p>
          {/*
            * De brief aan de beheerder. Ingeklapt, want hij mag het vertrek niet in de weg zitten:
            * wie weg wil moet weg kunnen zonder eerst een tekstvak te zien dat om uitleg vraagt.
            */}
          {!farewell.open ? (
            <button
              type="button"
              onClick={() => setFarewell({ text: "", open: true })}
              style={{ background: "none", border: "none", color: BLUE, fontWeight: 700, fontSize: 13, padding: 0, textDecoration: "underline", marginBottom: 12 }}
            >
              een brief aan de beheerder achterlaten
            </button>
          ) : (
            <div style={{ marginBottom: 14 }}>
              <label htmlFor="afscheidsbrief" style={{ display: "block", fontSize: 13, fontWeight: 600, marginBottom: 4 }}>
                Brief aan de beheerder <span style={{ fontWeight: 400, color: MUTE }}>(niet verplicht)</span>
              </label>
              <textarea
                id="afscheidsbrief"
                value={farewell.text}
                onChange={(e) => setFarewell({ open: true, text: e.target.value.slice(0, FAREWELL_MAX) })}
                rows={6}
                autoFocus
                placeholder="Wat je hier gevonden hebt, of wat er mis was. Allebei mag."
                style={{
                  width: "100%", padding: 12, fontSize: 14.5, fontFamily: FONT_SERIF, lineHeight: 1.6,
                  border: `2px solid ${INK}`, background: PAPER, resize: "vertical",
                }}
              />
              <p style={{ fontFamily: FONT_MONO, fontSize: 11, color: MUTE, margin: "6px 0 0" }}>
                nog {FAREWELL_MAX - farewell.text.length} tekens
              </p>
              <p style={{ fontSize: 13, color: MUTE, margin: "8px 0 0", lineHeight: 1.5 }}>
                Deze brief blijft bewaard als je account weg is — maar zonder jou erbij: geen naam, geen e-mailadres,
                geen verwijzing naar je account. Alleen de tekst en de dag. Er komt dus geen antwoord; wil je dat wel,
                mail dan {GENERAL_CONTACT}.
              </p>
            </div>
          )}

          <label style={{ display: "block", fontSize: 13, fontWeight: 600 }}>
            Wachtwoord van {email}
            <input
              type="password"
              value={del.password}
              onChange={(e) => setDel((d) => ({ ...d, password: e.target.value }))}
              autoComplete="current-password"
              autoFocus
              style={{ display: "block", width: "100%", marginTop: 4, padding: 10, border: `2px solid ${INK}`, fontSize: 15 }}
            />
          </label>
          {del.error && (
            <p role="alert" style={{ fontFamily: FONT_MONO, fontSize: 12, color: RED, margin: "8px 0 0" }}>fout: {del.error}</p>
          )}
          <div style={{ display: "flex", gap: 10, alignItems: "center", marginTop: 12 }}>
            <button
              type="submit"
              disabled={!del.password || del.busy}
              style={{
                background: RED, color: "#fff", border: "none", padding: "10px 16px", fontSize: 14, fontWeight: 700,
                opacity: !del.password || del.busy ? 0.5 : 1,
              }}
            >
              {del.busy ? "Verwijderen…" : "Ja, wis mijn account"}
            </button>
            <button
              type="button"
              onClick={closeDelete}
              disabled={del.busy}
              style={{ background: "none", border: "none", color: MUTE, fontSize: 13, fontWeight: 600, textDecoration: "underline" }}
            >
              annuleer
            </button>
          </div>
        </form>
      )}
    </div>
  );
}