first commit
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { JsonForm, type JsonValue } from './JsonForm';
|
||||
|
||||
type Status = 'idle' | 'saving' | 'saved' | 'error';
|
||||
|
||||
export function SectionEditor({
|
||||
sectionKey,
|
||||
title,
|
||||
initial,
|
||||
isOverridden,
|
||||
}: {
|
||||
sectionKey: string;
|
||||
title: string;
|
||||
initial: { fa: JsonValue; en: JsonValue };
|
||||
isOverridden: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [data, setData] = useState<{ fa: JsonValue; en: JsonValue }>(initial);
|
||||
const [tab, setTab] = useState<'fa' | 'en'>('fa');
|
||||
const [status, setStatus] = useState<Status>('idle');
|
||||
const [overridden, setOverridden] = useState(isOverridden);
|
||||
|
||||
async function save() {
|
||||
setStatus('saving');
|
||||
try {
|
||||
const res = await fetch('/api/admin/section', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ key: sectionKey, data }),
|
||||
});
|
||||
if (!res.ok) throw new Error(String(res.status));
|
||||
setStatus('saved');
|
||||
setOverridden(true);
|
||||
router.refresh();
|
||||
setTimeout(() => setStatus('idle'), 2500);
|
||||
} catch {
|
||||
setStatus('error');
|
||||
}
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
if (!confirm('Revert this section to its built-in default? Your edits will be removed.')) return;
|
||||
setStatus('saving');
|
||||
try {
|
||||
const res = await fetch(`/api/admin/section?key=${encodeURIComponent(sectionKey)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
if (!res.ok) throw new Error(String(res.status));
|
||||
router.refresh();
|
||||
window.location.reload();
|
||||
} catch {
|
||||
setStatus('error');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Toolbar */}
|
||||
<div className="sticky top-0 z-10 -mx-6 flex flex-wrap items-center justify-between gap-3 border-b border-white/8 bg-base-900/80 px-6 py-3 backdrop-blur sm:-mx-8 sm:px-8">
|
||||
<div className="flex items-center gap-1 rounded-full border border-white/10 bg-white/[0.02] p-1">
|
||||
<TabBtn active={tab === 'fa'} onClick={() => setTab('fa')}>FA · فارسی</TabBtn>
|
||||
<TabBtn active={tab === 'en'} onClick={() => setTab('en')}>EN · English</TabBtn>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{status === 'saved' && <span className="text-sm text-emerald">Saved ✓</span>}
|
||||
{status === 'error' && <span className="text-sm text-magenta">Save failed</span>}
|
||||
{overridden && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={reset}
|
||||
className="rounded-lg border border-white/10 px-3 py-2 text-sm text-slate-300 transition-colors hover:bg-white/[0.05]"
|
||||
>
|
||||
Reset to default
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={save}
|
||||
disabled={status === 'saving'}
|
||||
className="rounded-lg bg-electric px-4 py-2 text-sm font-semibold text-base-900 transition-opacity hover:opacity-90 disabled:opacity-50"
|
||||
>
|
||||
{status === 'saving' ? 'Saving…' : 'Save changes'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* The form for the active locale. FA renders RTL. */}
|
||||
<div dir={tab === 'fa' ? 'rtl' : 'ltr'} className="pb-24">
|
||||
<JsonForm
|
||||
key={tab}
|
||||
value={data[tab]}
|
||||
onChange={(nv) => setData((d) => ({ ...d, [tab]: nv }))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TabBtn({
|
||||
active,
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
active: boolean;
|
||||
onClick: () => void;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className={
|
||||
'rounded-full px-4 py-1.5 text-sm font-medium transition-colors ' +
|
||||
(active ? 'bg-electric text-base-900' : 'text-slate-300 hover:text-white')
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user