fix(allocations): expand grouped rows by default

This commit is contained in:
2026-04-01 15:13:24 +02:00
parent 4b14db9dc6
commit 7df751d5eb
3 changed files with 84 additions and 15 deletions
@@ -22,6 +22,13 @@ import { useViewPrefs } from "~/hooks/useViewPrefs.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { ALLOCATION_STATUS_BADGE as STATUS_BADGE } from "~/lib/status-styles.js";
import { SuccessToast } from "~/components/ui/SuccessToast.js";
import {
collapseAllAllocationGroups,
createInitialCollapsedAllocationGroups,
expandAllAllocationGroups,
toggleCollapsedAllocationGroup,
type CollapsedAllocationGroups,
} from "./allocationGroupState.js";
/** Left-border color by allocation status for instant visual scanning */
const STATUS_LEFT_BORDER: Record<string, string> = {
@@ -190,7 +197,9 @@ export function AllocationsClient() {
if (typeof window === "undefined") return "grouped";
return (localStorage.getItem("capakraken:allocations:viewMode") as "grouped" | "flat") ?? "grouped";
});
const [collapsedGroups, setCollapsedGroups] = useState<Set<string> | "all">("all");
const [collapsedGroups, setCollapsedGroups] = useState<CollapsedAllocationGroups>(
() => createInitialCollapsedAllocationGroups(),
);
// Track expanded project sub-groups: key = "resourceId::projectId"
const [expandedSubGroups, setExpandedSubGroups] = useState<Set<string>>(new Set());
@@ -293,26 +302,15 @@ export function AllocationsClient() {
const groupIds = useMemo(() => groups.map((g) => g.resourceId), [groups]);
const toggleGroup = useCallback((resourceId: string) => {
setCollapsedGroups((prev) => {
// "all" → expand just this one (materialize all IDs minus clicked)
if (prev === "all") {
const next = new Set(groupIds);
next.delete(resourceId);
return next;
}
const next = new Set(prev);
if (next.has(resourceId)) next.delete(resourceId);
else next.add(resourceId);
return next;
});
setCollapsedGroups((prev) => toggleCollapsedAllocationGroup(prev, groupIds, resourceId));
}, [groupIds]);
const collapseAll = useCallback(() => {
setCollapsedGroups("all");
setCollapsedGroups(collapseAllAllocationGroups());
}, []);
const expandAll = useCallback(() => {
setCollapsedGroups(new Set());
setCollapsedGroups(expandAllAllocationGroups());
}, []);
const toggleSubGroup = useCallback((resourceId: string, projectId: string) => {
@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import {
collapseAllAllocationGroups,
createInitialCollapsedAllocationGroups,
expandAllAllocationGroups,
toggleCollapsedAllocationGroup,
} from "./allocationGroupState.js";
describe("allocationGroupState", () => {
it("starts grouped allocations expanded so rows are visible on first load", () => {
const initial = createInitialCollapsedAllocationGroups();
expect(initial).toBeInstanceOf(Set);
expect([...initial]).toEqual([]);
});
it("collapses and re-expands a single group from the expanded default", () => {
const collapsed = toggleCollapsedAllocationGroup(createInitialCollapsedAllocationGroups(), ["r1", "r2"], "r1");
expect(collapsed).toBeInstanceOf(Set);
expect([...(collapsed as Set<string>)]).toEqual(["r1"]);
const expandedAgain = toggleCollapsedAllocationGroup(collapsed, ["r1", "r2"], "r1");
expect(expandedAgain).toBeInstanceOf(Set);
expect([...(expandedAgain as Set<string>)]).toEqual([]);
});
it("keeps collapse-all behavior available for bulk controls", () => {
expect(collapseAllAllocationGroups()).toBe("all");
const expandedOnlyR2 = toggleCollapsedAllocationGroup("all", ["r1", "r2"], "r2");
expect(expandedOnlyR2).toBeInstanceOf(Set);
expect([...(expandedOnlyR2 as Set<string>)].sort()).toEqual(["r1"]);
const expandedAll = expandAllAllocationGroups();
expect(expandedAll).toBeInstanceOf(Set);
expect([...(expandedAll as Set<string>)]).toEqual([]);
});
});
@@ -0,0 +1,33 @@
export type CollapsedAllocationGroups = Set<string> | "all";
export function createInitialCollapsedAllocationGroups(): CollapsedAllocationGroups {
return new Set<string>();
}
export function toggleCollapsedAllocationGroup(
previous: CollapsedAllocationGroups,
groupIds: string[],
resourceId: string,
): CollapsedAllocationGroups {
if (previous === "all") {
const next = new Set(groupIds);
next.delete(resourceId);
return next;
}
const next = new Set(previous);
if (next.has(resourceId)) {
next.delete(resourceId);
} else {
next.add(resourceId);
}
return next;
}
export function collapseAllAllocationGroups(): CollapsedAllocationGroups {
return "all";
}
export function expandAllAllocationGroups(): CollapsedAllocationGroups {
return new Set<string>();
}