From d2c91e7b652f72f62723e98d1f16e3d3cb8bf92c Mon Sep 17 00:00:00 2001 From: twotalesanimation <80506065+twotalesanimation@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:07:59 +0200 Subject: [PATCH] Shot status grouping --- .../shot-status/ShotStatusClient.tsx | 118 +++++++++++++++--- app/(dashboard)/shot-status/page.tsx | 1 + 2 files changed, 100 insertions(+), 19 deletions(-) diff --git a/app/(dashboard)/shot-status/ShotStatusClient.tsx b/app/(dashboard)/shot-status/ShotStatusClient.tsx index b42836a..a98b1d4 100644 --- a/app/(dashboard)/shot-status/ShotStatusClient.tsx +++ b/app/(dashboard)/shot-status/ShotStatusClient.tsx @@ -55,6 +55,7 @@ type ShotRow = { description: string | null; isKeyShot: boolean; artist: { id: string; name: string | null; image: string | null; email: string } | null; + shotGroup: { id: string; name: string } | null; }; interface Project { @@ -491,7 +492,8 @@ export function ShotStatusClient({ const [expandedEpisodes, setExpandedEpisodes] = useState>(new Set()); const [expandedScenes, setExpandedScenes] = useState>(new Set()); - const [groupByScene, setGroupByScene] = useState(false); + const [expandedGroups, setExpandedGroups] = useState>(new Set()); + const [groupMode, setGroupMode] = useState<"flat" | "scene" | "group">("flat"); const [selectedIds, setSelectedIds] = useState>(new Set()); // Load saved episode expand state when project changes @@ -504,10 +506,10 @@ export function ShotStatusClient({ setExpandedEpisodes(new Set()); } try { - const savedGroup = localStorage.getItem(`shotStatus:${selectedProjectId}:groupByScene`); - setGroupByScene(savedGroup === "1"); + const savedGroup = localStorage.getItem(`shotStatus:${selectedProjectId}:groupMode`); + setGroupMode((savedGroup as "flat" | "scene" | "group") ?? "flat"); } catch { - setGroupByScene(false); + setGroupMode("flat"); } }, [selectedProjectId]); const [dueDateDialogOpen, setDueDateDialogOpen] = useState(false); @@ -531,11 +533,19 @@ export function ShotStatusClient({ return next; }); - const handleGroupByScene = (checked: boolean) => { - setGroupByScene(checked); - if (checked) setExpandedScenes(new Set(shots.map((s) => s.scene))); + const toggleGroup = (g: string) => + setExpandedGroups((prev) => { + const next = new Set(prev); + next.has(g) ? next.delete(g) : next.add(g); + return next; + }); + + const handleGroupMode = (mode: "flat" | "scene" | "group") => { + setGroupMode(mode); + if (mode === "scene") setExpandedScenes(new Set(shots.map((s) => s.scene))); + if (mode === "group") setExpandedGroups(new Set(shots.map((s) => s.shotGroup?.id ?? ""))); if (selectedProjectId) { - try { localStorage.setItem(`shotStatus:${selectedProjectId}:groupByScene`, checked ? "1" : "0"); } catch {} + try { localStorage.setItem(`shotStatus:${selectedProjectId}:groupMode`, mode); } catch {} } }; @@ -590,7 +600,7 @@ export function ShotStatusClient({ })() : []; - const sceneGroups: [string, ShotRow[]][] = !isEpisodic && groupByScene + const sceneGroups: [string, ShotRow[]][] = !isEpisodic && groupMode === "scene" ? (() => { const map = new Map(); for (const shot of shots) { @@ -602,6 +612,19 @@ export function ShotStatusClient({ })() : []; + const shotGroupGroups: [string, string, ShotRow[]][] = !isEpisodic && groupMode === "group" + ? (() => { + const map = new Map(); + for (const shot of shots) { + const id = shot.shotGroup?.id ?? ""; + const name = shot.shotGroup?.name ?? "(No Group)"; + if (!map.has(id)) map.set(id, [name, []]); + map.get(id)![1].push(shot); + } + return Array.from(map.entries()).map(([id, [name, s]]) => [id, name, s] as [string, string, ShotRow[]]); + })() + : []; + const episodeDueDateMap = new Map( episodeDueDates.map((e) => [e.episode, new Date(e.dueDate)]) ); @@ -641,15 +664,23 @@ export function ShotStatusClient({ {selectedProject && !isEpisodic && ( - + <> + Group by: + {(["flat", "scene", "group"] as const).map((mode) => ( + + ))} + )} @@ -773,7 +804,7 @@ export function ShotStatusClient({ ); })} - ) : groupByScene ? ( + ) : groupMode === "scene" ? (
{sceneGroups.map(([scene, sceneShots]) => { const collapsed = !expandedScenes.has(scene); @@ -822,6 +853,55 @@ export function ShotStatusClient({ ); })}
+ ) : groupMode === "group" ? ( +
+ {shotGroupGroups.map(([groupId, groupName, groupShots]) => { + const collapsed = !expandedGroups.has(groupId); + const groupIds = groupShots.map((s) => s.id); + const allGrSelected = groupIds.every((id) => selectedIds.has(id)); + const someGrSelected = groupIds.some((id) => selectedIds.has(id)); + return ( +
+
+ {canManage && ( + toggleMany(groupIds, e.target.checked)} + className="shrink-0" + /> + )} + +
+ {!collapsed && ( +
+ +
+ )} +
+ ); + })} +
) : (