This repository has been archived on 2026-07-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
cloud-reader/frontend/src/pages/Settings.tsx
T
markoandreid f84a593c5d 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>
2026-05-26 03:54:25 +00:00

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>
);
}