feat(admin): scene-inputs editor in /admin/projects scene list (reuse SceneInputsEditor)

The per-scene inputs (content elements) editor existed only on /admin/templates
(SceneColorEditor). /admin/projects → «صحنه‌ها» used the older ProjectScenes which
had no inputs panel, so admins couldn't see/edit a scene's inputs there. Export
SceneInputsEditor and add an «ورودی‌ها» expander per scene row in ProjectScenes
(GET/POST/PUT/DELETE /v1/scene-elements, 15 element types). Verified c1 → 6 inputs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-06 23:33:06 +03:30
parent 8b716a173c
commit 1aca734343
2 changed files with 29 additions and 12 deletions
+28 -11
View File
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
import { FileUploadField } from "@/components/admin/FileUploadField";
import { ProjectScanImport } from "@/components/admin/ProjectScanImport";
import { SceneInputsEditor } from "@/components/admin/SceneColorEditor";
// ── styles ───────────────────────────────────────────────────────────────────
const inp = "rounded-lg border border-[#262b40] bg-[#0c0e1a] px-2.5 py-1.5 text-sm text-gray-100 outline-none focus:border-indigo-500";
@@ -95,6 +96,7 @@ function ScenesTab({ projectId }: { projectId: string }) {
const [saving, setSaving] = useState(false);
const [err, setErr] = useState<string | null>(null);
const [scanOpen, setScanOpen] = useState(false);
const [inputsFor, setInputsFor] = useState<string | null>(null);
const base = "/api/admin/resource/scenes";
const load = useCallback(async () => {
@@ -163,18 +165,33 @@ function ScenesTab({ projectId }: { projectId: string }) {
) : (
<ul className="space-y-1.5">
{rows.map((s) => (
<li key={s.id} className="flex items-center justify-between rounded-lg border border-[#1e2235] bg-[#0c0e1a] px-3 py-2">
<div className="flex min-w-0 items-center gap-2">
<span className="text-[10px] text-gray-600">#{s.sort}</span>
<span className="truncate text-sm text-gray-200">{s.title}</span>
<code className="truncate rounded bg-[#1e2235] px-1.5 py-0.5 text-[10px] text-indigo-300" dir="ltr">{s.key}</code>
<span className="rounded bg-[#1e2235] px-1.5 py-0.5 text-[10px] text-gray-400">{SCENE_TYPES.find((t) => t.v === s.scene_type)?.l ?? s.scene_type}</span>
{!s.is_active && <span className="rounded bg-gray-500/15 px-1.5 py-0.5 text-[10px] text-gray-400">غیرفعال</span>}
</div>
<div className="flex shrink-0 items-center gap-2">
<button className={ghost} onClick={() => { setEditId(s.id); setDraft(sceneToDraft(s)); }}>ویرایش</button>
<button className={del} onClick={() => remove(s)}>حذف</button>
<li key={s.id} className="rounded-lg border border-[#1e2235] bg-[#0c0e1a]">
<div className="flex items-center justify-between px-3 py-2">
<div className="flex min-w-0 items-center gap-2">
<span className="text-[10px] text-gray-600">#{s.sort}</span>
<span className="truncate text-sm text-gray-200">{s.title}</span>
<code className="truncate rounded bg-[#1e2235] px-1.5 py-0.5 text-[10px] text-indigo-300" dir="ltr">{s.key}</code>
<span className="rounded bg-[#1e2235] px-1.5 py-0.5 text-[10px] text-gray-400">{SCENE_TYPES.find((t) => t.v === s.scene_type)?.l ?? s.scene_type}</span>
{!s.is_active && <span className="rounded bg-gray-500/15 px-1.5 py-0.5 text-[10px] text-gray-400">غیرفعال</span>}
</div>
<div className="flex shrink-0 items-center gap-2">
<button
className={inputsFor === s.id
? "rounded-lg border border-indigo-500 bg-indigo-500/15 px-2.5 py-1 text-xs text-indigo-200"
: "rounded-lg border border-[#262b40] px-2.5 py-1 text-xs text-gray-300 hover:bg-[#161a2e]"}
onClick={() => setInputsFor((cur) => (cur === s.id ? null : s.id))}
>
ورودیها
</button>
<button className={ghost} onClick={() => { setEditId(s.id); setDraft(sceneToDraft(s)); }}>ویرایش</button>
<button className={del} onClick={() => remove(s)}>حذف</button>
</div>
</div>
{inputsFor === s.id && (
<div className="border-t border-[#1e2235] p-3">
<SceneInputsEditor sceneId={s.id} setError={setErr} />
</div>
)}
</li>
))}
</ul>
+1 -1
View File
@@ -737,7 +737,7 @@ const ELEMENT_TYPES = [
"DropDown", "Fill", "Color", "Number", "Date", "Toggle", "Slider", "Counter", "Hidden",
];
function SceneInputsEditor({
export function SceneInputsEditor({
sceneId,
setError,
}: {