feat(#42): FIX projects can't add scenes (studio + admin)
Build backend images / build content-svc (push) Failing after 57s
Build backend images / build file-svc (push) Failing after 56s
Build backend images / build gateway (push) Failing after 54s
Build backend images / build identity-svc (push) Failing after 1m0s
Build backend images / build notification-svc (push) Failing after 47s
Build backend images / build render-svc (push) Failing after 53s
Build backend images / build studio-svc (push) Failing after 57s
Build backend images / build content-svc (push) Failing after 57s
Build backend images / build file-svc (push) Failing after 56s
Build backend images / build gateway (push) Failing after 54s
Build backend images / build identity-svc (push) Failing after 1m0s
Build backend images / build notification-svc (push) Failing after 47s
Build backend images / build render-svc (push) Failing after 53s
Build backend images / build studio-svc (push) Failing after 57s
Template copy now carries choose_mode from the content project → studio store gets chooseMode; AddSceneMenu returns null for FIX/MusicVisualizer. Admin ProjectScenes hides '+ صحنهٔ جدید' (shows an 'scenes defined in AE' note) for fixed modes. Verified choose_mode=FIX flows end-to-end. (Visible admin nav link added earlier.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -193,6 +193,14 @@ public class StudioService(StudioDbContext db)
|
||||
FROM content.shared_colors sc
|
||||
WHERE sc.project_id = {1};",
|
||||
savedProjectId, originalProjectId);
|
||||
|
||||
// 5. carry the template's render mode (FIX/FLEXIBLE/…) so the studio knows whether
|
||||
// adding scenes is allowed, plus a few useful project defaults.
|
||||
await db.Database.ExecuteSqlRawAsync(@"
|
||||
UPDATE studio.saved_projects sp
|
||||
SET choose_mode = COALESCE((SELECT p.choose_mode::text FROM content.projects p WHERE p.id = {1}), sp.choose_mode)
|
||||
WHERE sp.id = {0};",
|
||||
savedProjectId, originalProjectId);
|
||||
}
|
||||
|
||||
public async Task<SavedProjectFullResponse> UpdateProjectAsync(
|
||||
|
||||
@@ -67,7 +67,10 @@ function sceneToDraft(s: Scene): SceneDraft {
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
export function ProjectScenes({ projectId }: { projectId: string }) {
|
||||
// FIX / MusicVisualizer projects have a fixed scene set from After Effects layer names.
|
||||
const FIXED_SCENE_MODES = new Set(["fix", "musicvisualizer"]);
|
||||
|
||||
export function ProjectScenes({ projectId, mode }: { projectId: string; mode?: string }) {
|
||||
const [tab, setTab] = useState<"scenes" | "colors" | "presets">("scenes");
|
||||
return (
|
||||
<div dir="rtl">
|
||||
@@ -80,7 +83,9 @@ export function ProjectScenes({ projectId }: { projectId: string }) {
|
||||
>{l}</button>
|
||||
))}
|
||||
</div>
|
||||
{tab === "scenes" && <ScenesTab projectId={projectId} />}
|
||||
{tab === "scenes" && (
|
||||
<ScenesTab projectId={projectId} fixedScenes={FIXED_SCENE_MODES.has((mode ?? "").toLowerCase())} />
|
||||
)}
|
||||
{tab === "colors" && <ColorsTab projectId={projectId} />}
|
||||
{tab === "presets" && <PresetsTab projectId={projectId} />}
|
||||
</div>
|
||||
@@ -88,7 +93,7 @@ export function ProjectScenes({ projectId }: { projectId: string }) {
|
||||
}
|
||||
|
||||
// ── Scenes ────────────────────────────────────────────────────────────────────
|
||||
function ScenesTab({ projectId }: { projectId: string }) {
|
||||
function ScenesTab({ projectId, fixedScenes }: { projectId: string; fixedScenes?: boolean }) {
|
||||
const [rows, setRows] = useState<Scene[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [draft, setDraft] = useState<SceneDraft | null>(null);
|
||||
@@ -152,7 +157,11 @@ function ScenesTab({ projectId }: { projectId: string }) {
|
||||
<p className="text-xs text-gray-500">صحنهها بلوکهای قابلویرایش این قالب هستند. کلید هر صحنه باید با نام کامپوزیشن افترافکت یکی باشد.</p>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<button className="rounded-lg border border-indigo-500/40 px-3 py-2 text-sm text-indigo-300 hover:bg-indigo-600/10" onClick={() => setScanOpen(true)}>اسکن از افترافکت</button>
|
||||
<button className={btn} onClick={() => { setEditId(null); setDraft(emptyDraft(rows.length)); }}>+ صحنهٔ جدید</button>
|
||||
{fixedScenes ? (
|
||||
<span className="rounded-lg border border-[#262b40] px-3 py-2 text-xs text-gray-500">صحنهها از روی پروژهٔ افترافکت تعریف میشوند (پروژهٔ Fix)</span>
|
||||
) : (
|
||||
<button className={btn} onClick={() => { setEditId(null); setDraft(emptyDraft(rows.length)); }}>+ صحنهٔ جدید</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{scanOpen && (
|
||||
|
||||
@@ -12,6 +12,7 @@ interface Proj {
|
||||
id: string; container_id: string; container_name: string; container_slug: string;
|
||||
name: string; image?: string | null; aspect?: string | null; resolution: string;
|
||||
aep_file_url?: string | null; render_aep_comp: string; is_published: boolean; sort: number;
|
||||
choose_mode?: string | null;
|
||||
}
|
||||
|
||||
const card = "rounded-xl border border-[#1e2235] bg-[#0f1120]";
|
||||
@@ -315,7 +316,7 @@ export function ProjectsAdmin() {
|
||||
<button className="rounded-lg px-2 py-1 text-gray-400 hover:bg-[#161a2e] hover:text-white" onClick={() => setOpenScenes(null)}>✕</button>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-5">
|
||||
<ProjectScenes projectId={openScenes.id} />
|
||||
<ProjectScenes projectId={openScenes.id} mode={openScenes.choose_mode ?? undefined} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,16 +10,26 @@ import {
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { useStudioStore } from "@/lib/studio-store";
|
||||
|
||||
interface AddSceneMenuProps {
|
||||
onAddBlank: () => void;
|
||||
variant?: "header" | "footer";
|
||||
}
|
||||
|
||||
// FIX / MusicVisualizer projects have a fixed scene set defined in After Effects
|
||||
// (scenes come from layer names), so adding scenes is not allowed.
|
||||
const FIXED_SCENE_MODES = new Set(["fix", "musicvisualizer"]);
|
||||
|
||||
export function AddSceneMenu({ onAddBlank, variant = "footer" }: AddSceneMenuProps) {
|
||||
const t = useTranslations("auto.componentsStudioAddSceneMenu");
|
||||
const [open, setOpen] = useState(false);
|
||||
const isHeader = variant === "header";
|
||||
const chooseMode = useStudioStore((s) => s.chooseMode);
|
||||
|
||||
if (FIXED_SCENE_MODES.has(chooseMode.toLowerCase())) {
|
||||
return null; // fixed-structure project — no add-scene
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
|
||||
@@ -164,6 +164,9 @@ export interface VideoPersistedSceneData {
|
||||
audioVolume?: number;
|
||||
sceneBackgroundColor?: string;
|
||||
sceneAccentColor?: string;
|
||||
/** Project render mode (FIX / FLEXIBLE / MusicVisualizer / …). FIX/MusicVisualizer
|
||||
* scenes come from AE layer names, so adding scenes is disabled. */
|
||||
chooseMode?: string;
|
||||
}
|
||||
|
||||
export function buildVideoSceneDataPayload(
|
||||
@@ -214,5 +217,11 @@ export function parseVideoSceneData(
|
||||
typeof sceneData.sceneAccentColor === "string"
|
||||
? sceneData.sceneAccentColor
|
||||
: DEFAULT_SCENE_ACCENT_COLOR,
|
||||
chooseMode:
|
||||
typeof sceneData.chooseMode === "string"
|
||||
? sceneData.chooseMode
|
||||
: typeof sceneData.primaryMode === "string"
|
||||
? sceneData.primaryMode
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -159,6 +159,8 @@ export interface StudioState {
|
||||
audioVolume: number;
|
||||
sceneBackgroundColor: string;
|
||||
sceneAccentColor: string;
|
||||
/** Project render mode (FIX / FLEXIBLE / MusicVisualizer / …). Empty until hydrated. */
|
||||
chooseMode: string;
|
||||
past: StudioHistorySnapshot[];
|
||||
future: StudioHistorySnapshot[];
|
||||
layerClipboard: Layer | null;
|
||||
@@ -249,6 +251,7 @@ export const useStudioStore = create<StudioStore>((set, get) => {
|
||||
audioVolume: 100,
|
||||
sceneBackgroundColor: DEFAULT_SCENE_BACKGROUND_COLOR,
|
||||
sceneAccentColor: DEFAULT_SCENE_ACCENT_COLOR,
|
||||
chooseMode: "",
|
||||
past: [],
|
||||
future: [],
|
||||
layerClipboard: null,
|
||||
@@ -757,6 +760,7 @@ export const useStudioStore = create<StudioStore>((set, get) => {
|
||||
parsed.sceneBackgroundColor ?? DEFAULT_SCENE_BACKGROUND_COLOR,
|
||||
sceneAccentColor:
|
||||
parsed.sceneAccentColor ?? DEFAULT_SCENE_ACCENT_COLOR,
|
||||
chooseMode: parsed.chooseMode ?? "",
|
||||
past: [],
|
||||
future: [],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user