106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { api } from "@/lib/api";
|
|
import { Save } from "lucide-react";
|
|
|
|
const settingFields = [
|
|
{ key: "site_title", label: "Site Title" },
|
|
{ key: "site_tagline", label: "Site Tagline" },
|
|
{ key: "telegram_link", label: "Telegram Link" },
|
|
{ key: "nostr_link", label: "Nostr Link" },
|
|
{ key: "x_link", label: "X Link" },
|
|
{ key: "youtube_link", label: "YouTube Link" },
|
|
{ key: "discord_link", label: "Discord Link" },
|
|
{ key: "linkedin_link", label: "LinkedIn Link" },
|
|
];
|
|
|
|
export default function SettingsPage() {
|
|
const [settings, setSettings] = useState<Record<string, string>>({});
|
|
const [original, setOriginal] = useState<Record<string, string>>({});
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [error, setError] = useState("");
|
|
const [success, setSuccess] = useState("");
|
|
|
|
useEffect(() => {
|
|
async function load() {
|
|
try {
|
|
const data = await api.getSettings();
|
|
setSettings(data);
|
|
setOriginal(data);
|
|
} catch (err: any) {
|
|
setError(err.message);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
load();
|
|
}, []);
|
|
|
|
const handleSave = async () => {
|
|
setSaving(true);
|
|
setError("");
|
|
setSuccess("");
|
|
try {
|
|
const changed = Object.entries(settings).filter(
|
|
([key, value]) => value !== (original[key] || "")
|
|
);
|
|
await Promise.all(
|
|
changed.map(([key, value]) => api.updateSetting(key, value))
|
|
);
|
|
setOriginal({ ...settings });
|
|
setSuccess("Settings saved successfully.");
|
|
} catch (err: any) {
|
|
setError(err.message);
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const hasChanges = Object.entries(settings).some(
|
|
([key, value]) => value !== (original[key] || "")
|
|
);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-[60vh]">
|
|
<div className="text-on-surface/50">Loading settings...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<h1 className="text-2xl font-bold text-on-surface">Site Settings</h1>
|
|
|
|
{error && <p className="text-error text-sm">{error}</p>}
|
|
{success && <p className="text-green-400 text-sm">{success}</p>}
|
|
|
|
<div className="bg-surface-container-low rounded-xl p-6 space-y-4">
|
|
{settingFields.map((field) => (
|
|
<div key={field.key}>
|
|
<label className="block text-on-surface/70 text-sm mb-1">{field.label}</label>
|
|
<input
|
|
value={settings[field.key] || ""}
|
|
onChange={(e) =>
|
|
setSettings({ ...settings, [field.key]: e.target.value })
|
|
}
|
|
className="bg-surface-container-highest text-on-surface rounded-lg px-4 py-3 w-full focus:outline-none focus:ring-1 focus:ring-primary/40"
|
|
/>
|
|
</div>
|
|
))}
|
|
|
|
<button
|
|
onClick={handleSave}
|
|
disabled={saving || !hasChanges}
|
|
className="flex items-center gap-2 px-6 py-3 rounded-lg bg-gradient-to-r from-primary to-primary-container text-on-primary font-semibold text-sm hover:opacity-90 transition-opacity disabled:opacity-50 mt-2"
|
|
>
|
|
<Save size={16} />
|
|
{saving ? "Saving..." : "Save Settings"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|