diff --git a/app/(dashboard)/shot-status/ShotStatusClient.tsx b/app/(dashboard)/shot-status/ShotStatusClient.tsx index 0dec056..b42836a 100644 --- a/app/(dashboard)/shot-status/ShotStatusClient.tsx +++ b/app/(dashboard)/shot-status/ShotStatusClient.tsx @@ -490,6 +490,8 @@ export function ShotStatusClient({ }, []); const [expandedEpisodes, setExpandedEpisodes] = useState>(new Set()); + const [expandedScenes, setExpandedScenes] = useState>(new Set()); + const [groupByScene, setGroupByScene] = useState(false); const [selectedIds, setSelectedIds] = useState>(new Set()); // Load saved episode expand state when project changes @@ -501,6 +503,12 @@ export function ShotStatusClient({ } catch { setExpandedEpisodes(new Set()); } + try { + const savedGroup = localStorage.getItem(`shotStatus:${selectedProjectId}:groupByScene`); + setGroupByScene(savedGroup === "1"); + } catch { + setGroupByScene(false); + } }, [selectedProjectId]); const [dueDateDialogOpen, setDueDateDialogOpen] = useState(false); @@ -516,6 +524,21 @@ export function ShotStatusClient({ return next; }); + const toggleScene = (sc: string) => + setExpandedScenes((prev) => { + const next = new Set(prev); + next.has(sc) ? next.delete(sc) : next.add(sc); + return next; + }); + + const handleGroupByScene = (checked: boolean) => { + setGroupByScene(checked); + if (checked) setExpandedScenes(new Set(shots.map((s) => s.scene))); + if (selectedProjectId) { + try { localStorage.setItem(`shotStatus:${selectedProjectId}:groupByScene`, checked ? "1" : "0"); } catch {} + } + }; + const handleProjectChange = (id: string) => { localStorage.setItem("shotStatus:lastProjectId", id); setSelectedIds(new Set()); @@ -567,6 +590,18 @@ export function ShotStatusClient({ })() : []; + const sceneGroups: [string, ShotRow[]][] = !isEpisodic && groupByScene + ? (() => { + const map = new Map(); + for (const shot of shots) { + const key = shot.scene || "(No Scene)"; + if (!map.has(key)) map.set(key, []); + map.get(key)!.push(shot); + } + return Array.from(map.entries()); + })() + : []; + const episodeDueDateMap = new Map( episodeDueDates.map((e) => [e.episode, new Date(e.dueDate)]) ); @@ -605,6 +640,17 @@ export function ShotStatusClient({ ))} + {selectedProject && !isEpisodic && ( + + )} {/* No project selected */} @@ -727,6 +773,55 @@ export function ShotStatusClient({ ); })} + ) : groupByScene ? ( +
+ {sceneGroups.map(([scene, sceneShots]) => { + const collapsed = !expandedScenes.has(scene); + const sceneIds = sceneShots.map((s) => s.id); + const allScSelected = sceneIds.every((id) => selectedIds.has(id)); + const someScSelected = sceneIds.some((id) => selectedIds.has(id)); + return ( +
+
+ {canManage && ( + toggleMany(sceneIds, e.target.checked)} + className="shrink-0" + /> + )} + +
+ {!collapsed && ( +
+ +
+ )} +
+ ); + })} +
) : (