fix(allocations): expand grouped rows by default
This commit is contained in:
@@ -22,6 +22,13 @@ import { useViewPrefs } from "~/hooks/useViewPrefs.js";
|
|||||||
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
|
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
|
||||||
import { ALLOCATION_STATUS_BADGE as STATUS_BADGE } from "~/lib/status-styles.js";
|
import { ALLOCATION_STATUS_BADGE as STATUS_BADGE } from "~/lib/status-styles.js";
|
||||||
import { SuccessToast } from "~/components/ui/SuccessToast.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 */
|
/** Left-border color by allocation status for instant visual scanning */
|
||||||
const STATUS_LEFT_BORDER: Record<string, string> = {
|
const STATUS_LEFT_BORDER: Record<string, string> = {
|
||||||
@@ -190,7 +197,9 @@ export function AllocationsClient() {
|
|||||||
if (typeof window === "undefined") return "grouped";
|
if (typeof window === "undefined") return "grouped";
|
||||||
return (localStorage.getItem("capakraken:allocations:viewMode") as "grouped" | "flat") ?? "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"
|
// Track expanded project sub-groups: key = "resourceId::projectId"
|
||||||
const [expandedSubGroups, setExpandedSubGroups] = useState<Set<string>>(new Set());
|
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 groupIds = useMemo(() => groups.map((g) => g.resourceId), [groups]);
|
||||||
|
|
||||||
const toggleGroup = useCallback((resourceId: string) => {
|
const toggleGroup = useCallback((resourceId: string) => {
|
||||||
setCollapsedGroups((prev) => {
|
setCollapsedGroups((prev) => toggleCollapsedAllocationGroup(prev, groupIds, resourceId));
|
||||||
// "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;
|
|
||||||
});
|
|
||||||
}, [groupIds]);
|
}, [groupIds]);
|
||||||
|
|
||||||
const collapseAll = useCallback(() => {
|
const collapseAll = useCallback(() => {
|
||||||
setCollapsedGroups("all");
|
setCollapsedGroups(collapseAllAllocationGroups());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const expandAll = useCallback(() => {
|
const expandAll = useCallback(() => {
|
||||||
setCollapsedGroups(new Set());
|
setCollapsedGroups(expandAllAllocationGroups());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleSubGroup = useCallback((resourceId: string, projectId: string) => {
|
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>();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user