Archived
Reviewed and merged by Reid (Hermes Reviewer) Co-authored-by: crisleo-hermes <hermes@codescripters.org> Co-committed-by: crisleo-hermes <hermes@codescripters.org>
80 lines
4.9 KiB
TypeScript
80 lines
4.9 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { booksApi } from "../api/books";
|
|
import type { ReadingSettings } from "../types/book";
|
|
|
|
const BG_COLORS = [
|
|
{ value: "#ffffff", label: "White" },
|
|
{ value: "#f4e4c1", label: "Sepia" },
|
|
{ value: "#1a1a2e", label: "Dark" },
|
|
{ value: "#c7edcc", label: "Green" },
|
|
];
|
|
|
|
export function SettingsPage() {
|
|
const navigate = useNavigate();
|
|
const [settings, setSettings] = useState<ReadingSettings | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [success, setSuccess] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const load = async () => {
|
|
try { const data = await booksApi.getSettings(); setSettings(data); }
|
|
catch (err) { setError(err instanceof Error ? err.message : "Failed to load settings"); }
|
|
finally { setLoading(false); }
|
|
};
|
|
void load();
|
|
}, []);
|
|
|
|
const handleSave = async () => {
|
|
if (!settings) return;
|
|
setSaving(true); setError(null); setSuccess(false);
|
|
try { await booksApi.updateSettings(settings); setSuccess(true); setTimeout(() => setSuccess(false), 2000); }
|
|
catch (err) { setError(err instanceof Error ? err.message : "Failed to save settings"); }
|
|
finally { setSaving(false); }
|
|
};
|
|
|
|
if (loading) return <div style={{ maxWidth: 500, margin: "0 auto", padding: 16, minHeight: "100vh", background: "#f8f9fa" }}><p>Loading settings...</p></div>;
|
|
|
|
return (
|
|
<div style={{ maxWidth: 500, margin: "0 auto", padding: 16, minHeight: "100vh", background: "#f8f9fa" }}>
|
|
<header style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 24, padding: "16px 0", borderBottom: "1px solid #eee" }}>
|
|
<button onClick={() => navigate("/")} style={{ padding: "8px 16px", borderRadius: 6, border: "1px solid #ddd", background: "#fff", cursor: "pointer", fontSize: 14 }}>← Back</button>
|
|
<h1 style={{ fontSize: 24, fontWeight: 700, color: "#1a1a2e", margin: 0 }}>Reading Settings</h1>
|
|
</header>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
|
{error && <div style={{ background: "#fde8e8", padding: 12, borderRadius: 6, color: "#e74c3c", fontSize: 14 }}>{error}</div>}
|
|
{success && <div style={{ background: "#d4edda", padding: 12, borderRadius: 6, color: "#155724", fontSize: 14 }}>Settings saved!</div>}
|
|
|
|
{settings && <>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
<label style={{ fontSize: 14, fontWeight: 600, color: "#555" }}>Font Size ({settings.font_size}px)</label>
|
|
<input type="range" min={12} max={36} value={settings.font_size} onChange={(e) => setSettings({ ...settings, font_size: Number(e.target.value) })} style={{ width: "100%", cursor: "pointer" }} />
|
|
<div style={{ display: "flex", justifyContent: "space-between", fontSize: 12, color: "#999" }}><span>12px</span><span>36px</span></div>
|
|
</div>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
<label style={{ fontSize: 14, fontWeight: 600, color: "#555" }}>Font Style</label>
|
|
<select value={settings.font_style} onChange={(e) => setSettings({ ...settings, font_style: e.target.value as ReadingSettings["font_style"] })} style={{ padding: "10px 12px", borderRadius: 6, border: "1px solid #ddd", fontSize: 16, background: "#fff", outline: "none" }}>
|
|
<option value="sans-serif">Sans Serif</option><option value="serif">Serif</option><option value="monospace">Monospace</option>
|
|
</select>
|
|
</div>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
<label style={{ fontSize: 14, fontWeight: 600, color: "#555" }}>Background Color</label>
|
|
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
|
|
{BG_COLORS.map((bg) => (
|
|
<button key={bg.value} onClick={() => setSettings({ ...settings, background_color: bg.value })}
|
|
style={{ width: 48, height: 48, borderRadius: "50%", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", background: bg.value, border: settings.background_color === bg.value ? "3px solid #1a1a2e" : "3px solid #ddd" }}
|
|
title={bg.label}>
|
|
{settings.background_color === bg.value && <span style={{ fontSize: 20, fontWeight: 700, color: bg.value === "#ffffff" || bg.value === "#f4e4c1" ? "#1a1a2e" : "#fff" }}>✓</span>}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</>}
|
|
|
|
<button onClick={handleSave} disabled={saving} style={{ padding: "12px 24px", borderRadius: 8, border: "none", background: "#1a1a2e", color: "#fff", fontSize: 16, fontWeight: 600, cursor: "pointer", opacity: saving ? 0.6 : 1, marginTop: 8 }}>{saving ? "Saving..." : "Save Settings"}</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |