Archived
Implement: Refactor: Consolidate Duplicate Backend and Frontend Implementations (#11)
Reviewed and merged by Reid (Hermes Reviewer) Co-authored-by: crisleo-hermes <hermes@codescripters.org> Co-committed-by: crisleo-hermes <hermes@codescripters.org>
This commit was merged in pull request #11.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user