Files
VibeFinance/frontend/src/components/InstellingenModal.jsx
T
2026-09-15 14:02:17 +02:00

154 lines
5.8 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useApp } from "../context/AppContext.jsx";
import { THEMAS } from "../constants/themas.js";
const BACKUP_ICON = (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none">
<path d="M12 15V5M8 11l4-4 4 4" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M4 17v2a1 1 0 001 1h14a1 1 0 001-1v-2" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/>
</svg>
);
const RESTORE_ICON = (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none">
<path d="M4 8a8 8 0 1 1 0 8" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/>
<polyline points="4,4 4,8 8,8" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
);
function ActionRow({ icon, title, description, onClick }) {
return (
<div
onClick={onClick}
style={{
display: "flex", alignItems: "center", gap: 14,
padding: "12px 0", cursor: "pointer",
}}
>
<div style={{ color: "#94a3b8", flexShrink: 0 }}>{icon}</div>
<div>
<div style={{ fontWeight: 600, fontSize: 14, color: "inherit", marginBottom: 2 }}>{title}</div>
<div style={{ fontSize: 12, color: "#64748b" }}>{description}</div>
</div>
</div>
);
}
export default function InstellingenModal({ onClose }) {
const { T, darkMode, setDarkMode, thema, setThema, doBackup, doRestore } = useApp();
return (
<div
onClick={onClose}
style={{
position: "fixed", inset: 0, zIndex: 400,
background: "rgba(0,0,0,0.55)", backdropFilter: "blur(4px)",
display: "flex", alignItems: "center", justifyContent: "center",
}}
>
<div
onClick={(e) => e.stopPropagation()}
style={{
background: T.popupBg, border: `1px solid ${T.border}`,
borderRadius: 16, padding: "22px 22px 8px",
width: 360, boxShadow: "0 16px 48px rgba(0,0,0,0.4)",
color: T.text,
}}
>
{/* Header */}
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 20 }}>
<span style={{ fontWeight: 700, fontSize: 16 }}>Instellingen</span>
<button onClick={onClose} style={{
background: "transparent", border: "none", cursor: "pointer",
color: T.muted, fontSize: 20, lineHeight: 1, padding: "0 4px",
}}>×</button>
</div>
{/* Dark mode rij */}
<div style={{
display: "flex", alignItems: "center", justifyContent: "space-between",
padding: "4px 0 16px",
}}>
<span style={{ fontWeight: 600, fontSize: 14 }}>Dark mode</span>
<button
type="button"
role="switch"
aria-checked={darkMode}
aria-label="Donkere modus"
onClick={() => setDarkMode((d) => !d)}
style={{
width: 44, height: 24, borderRadius: 12, cursor: "pointer",
background: darkMode ? T.accent : "#334155",
border: "none", padding: 0,
position: "relative", transition: "background 0.2s", flexShrink: 0,
}}
>
<span style={{
position: "absolute", top: 3, borderRadius: "50%",
width: 18, height: 18, background: "#fff",
left: darkMode ? 23 : 3,
transition: "left 0.2s",
boxShadow: "0 1px 4px rgba(0,0,0,0.3)",
}} />
</button>
</div>
{/* Kleurthema */}
<div style={{ padding: "0 0 18px" }}>
<div style={{
display: "flex", alignItems: "baseline", justifyContent: "space-between",
gap: 10, marginBottom: 10,
}}>
<span style={{ fontWeight: 600, fontSize: 14 }}>Kleurthema</span>
<span style={{ fontSize: 12, color: T.muted }}>{T.themaNaam}</span>
</div>
{/* Drie kolommen: negen thema's vullen zo precies drie rijen, zonder
een halve rij onderaan. */}
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8 }}>
{THEMAS.map((t) => {
const gekozen = t.id === thema;
return (
<button
key={t.id}
type="button"
onClick={() => setThema(t.id)}
title={t.naam}
aria-label={`Thema ${t.naam}`}
aria-pressed={gekozen}
style={{
height: 30, borderRadius: 9, cursor: "pointer", padding: 0,
background: `linear-gradient(135deg, ${t.hoofd}, ${t.licht})`,
// De gekozen kleur krijgt een ring in de kaartkleur plus een
// rand, zodat hij ook opvalt als het thema zelf licht is.
border: `2px solid ${gekozen ? T.text : "transparent"}`,
boxShadow: gekozen ? `0 0 0 2px ${T.accentLicht}` : "none",
transition: "box-shadow 0.15s, border-color 0.15s",
}}
/>
);
})}
</div>
</div>
<div style={{ height: 1, background: T.border, margin: "0 -22px" }} />
{/* Acties */}
<div style={{ padding: "4px 0 10px" }}>
<ActionRow
icon={BACKUP_ICON}
title="Backup maken"
description="Download een volledige backup als ZIP"
onClick={() => { doBackup(); onClose(); }}
/>
<div style={{ height: 1, background: T.border }} />
<ActionRow
icon={RESTORE_ICON}
title="Backup herstellen"
description="Herstel data vanuit een backup-bestand"
onClick={() => { doRestore(); onClose(); }}
/>
</div>
</div>
</div>
);
}