FN-5929: track hidden usage windows by instance

Store hidden usage windows with per-instance identities while keeping legacy labels restorable.

- persist hidden usage windows with an index-qualified identity instead of label-only keys
- treat legacy label-only entries as matches for restore/show-hidden behavior
- guard hidden-window counts when provider windows are empty or undefined
- add regression coverage for duplicate labels, multiple hidden windows, and legacy persistence

Files changed:
 packages/dashboard/app/components/UsageIndicator.tsx    |  71 +++++++++----
 packages/dashboard/app/components/__tests__/UsageIndicator.test.tsx   | 113 ++++++++++++++++++++-
 2 files changed, 159 insertions(+), 25 deletions(-)

Fusion-Task-Id: FN-5929

Fusion-Task-Lineage: eb48988e-80b8-4d6c-90b9-0d324564f1aa
This commit is contained in:
gsxdsm
2026-06-02 22:46:12 -07:00
parent fab2377fd1
commit 669a6ecfdc
2 changed files with 159 additions and 25 deletions

View File

@@ -162,39 +162,62 @@ function setProviderOrder(names: string[], projectId: string | undefined): void
setScopedItem(PROVIDER_ORDER_KEY, JSON.stringify(names), projectId); setScopedItem(PROVIDER_ORDER_KEY, JSON.stringify(names), projectId);
} }
const WINDOW_IDENTITY_DELIMITER = "::";
function getWindowIdentity(windowLabel: string, windowIndex: number): string {
return `${windowIndex}${WINDOW_IDENTITY_DELIMITER}${windowLabel}`;
}
function matchesHiddenWindowEntry(
persistedEntry: string,
windowLabel: string,
windowIndex: number
): boolean {
return (
persistedEntry === getWindowIdentity(windowLabel, windowIndex) ||
persistedEntry === windowLabel
);
}
function isWindowHidden( function isWindowHidden(
providerName: string, providerName: string,
windowLabel: string, windowLabel: string,
windowIndex: number,
hidden: Record<string, string[]> hidden: Record<string, string[]>
): boolean { ): boolean {
return hidden[providerName]?.includes(windowLabel) ?? false; return (hidden[providerName] ?? []).some((persistedEntry) =>
matchesHiddenWindowEntry(persistedEntry, windowLabel, windowIndex)
);
} }
function getRenderedHiddenWindowCount( function getRenderedHiddenWindowCount(
providerName: string, providerName: string,
windows: UsageWindow[], windows: UsageWindow[] | undefined,
hidden: Record<string, string[]> hidden: Record<string, string[]>
): number { ): number {
return windows.reduce((count, window) => { return (windows ?? []).reduce((count, window, windowIndex) => {
return count + (isWindowHidden(providerName, window.label, hidden) ? 1 : 0); return count + (isWindowHidden(providerName, window.label, windowIndex, hidden) ? 1 : 0);
}, 0); }, 0);
} }
function getRestorableHiddenWindowCount( function getRestorableHiddenWindowCount(
providerName: string, providerName: string,
windows: UsageWindow[], windows: UsageWindow[] | undefined,
hidden: Record<string, string[]> hidden: Record<string, string[]>
): number { ): number {
const renderedHiddenCount = getRenderedHiddenWindowCount(providerName, windows, hidden); const liveWindows = windows ?? [];
const persistedHiddenLabels = hidden[providerName] ?? []; const renderedHiddenCount = getRenderedHiddenWindowCount(providerName, liveWindows, hidden);
const persistedHiddenEntries = hidden[providerName] ?? [];
if (persistedHiddenLabels.length === 0) { if (persistedHiddenEntries.length === 0) {
return renderedHiddenCount; return renderedHiddenCount;
} }
const liveWindowLabels = new Set(windows.map((window) => window.label)); const orphanedHiddenCount = persistedHiddenEntries.reduce((count, persistedEntry) => {
const orphanedHiddenCount = persistedHiddenLabels.reduce((count, label) => { const matchesLiveWindow = liveWindows.some((window, windowIndex) =>
return count + (liveWindowLabels.has(label) ? 0 : 1); matchesHiddenWindowEntry(persistedEntry, window.label, windowIndex)
);
return count + (matchesLiveWindow ? 0 : 1);
}, 0); }, 0);
return renderedHiddenCount + orphanedHiddenCount; return renderedHiddenCount + orphanedHiddenCount;
@@ -349,7 +372,7 @@ interface ProviderCardProps {
provider: ProviderUsage; provider: ProviderUsage;
viewMode: 'used' | 'remaining'; viewMode: 'used' | 'remaining';
hiddenWindows: Record<string, string[]>; hiddenWindows: Record<string, string[]>;
onToggleWindow: (providerName: string, windowLabel: string) => void; onToggleWindow: (providerName: string, windowLabel: string, windowIndex: number) => void;
onShowAllHidden: (providerName: string) => void; onShowAllHidden: (providerName: string) => void;
isDragging: boolean; isDragging: boolean;
isDragOver: boolean; isDragOver: boolean;
@@ -434,7 +457,8 @@ function ProviderCard({
onMoveUp, onMoveUp,
onMoveDown, onMoveDown,
}: ProviderCardProps) { }: ProviderCardProps) {
const hiddenCount = getRestorableHiddenWindowCount(provider.name, provider.windows, hiddenWindows); const providerWindows = provider.windows ?? [];
const hiddenCount = getRestorableHiddenWindowCount(provider.name, providerWindows, hiddenWindows);
const getStatusBadge = () => { const getStatusBadge = () => {
switch (provider.status) { switch (provider.status) {
case "ok": case "ok":
@@ -526,10 +550,10 @@ function ProviderCard({
</div> </div>
)} )}
{provider.windows.length > 0 ? ( {providerWindows.length > 0 ? (
<div className="usage-provider-windows"> <div className="usage-provider-windows">
{provider.windows.map((window, index) => { {providerWindows.map((window, index) => {
const hidden = isWindowHidden(provider.name, window.label, hiddenWindows); const hidden = isWindowHidden(provider.name, window.label, index, hiddenWindows);
return ( return (
<UsageWindowRow <UsageWindowRow
@@ -537,7 +561,7 @@ function ProviderCard({
window={window} window={window}
viewMode={viewMode} viewMode={viewMode}
isHidden={hidden} isHidden={hidden}
onToggleHidden={() => onToggleWindow(provider.name, window.label)} onToggleHidden={() => onToggleWindow(provider.name, window.label, index)}
/> />
); );
})} })}
@@ -694,10 +718,15 @@ export function UsageIndicator({ isOpen, onClose, projectId, anchorRect }: Usage
setHiddenWindows(hiddenWindows, projectId); setHiddenWindows(hiddenWindows, projectId);
}, [hiddenWindows, projectId]); }, [hiddenWindows, projectId]);
const handleToggleWindow = useCallback((providerName: string, windowLabel: string) => { const handleToggleWindow = useCallback((providerName: string, windowLabel: string, windowIndex: number) => {
setHiddenWindowsState((previous) => { setHiddenWindowsState((previous) => {
if (isWindowHidden(providerName, windowLabel, previous)) { const windowIdentity = getWindowIdentity(windowLabel, windowIndex);
const remaining = (previous[providerName] ?? []).filter((label) => label !== windowLabel); const providerHiddenWindows = previous[providerName] ?? [];
if (isWindowHidden(providerName, windowLabel, windowIndex, previous)) {
const remaining = providerHiddenWindows.filter(
(persistedEntry) => !matchesHiddenWindowEntry(persistedEntry, windowLabel, windowIndex)
);
if (remaining.length === 0) { if (remaining.length === 0) {
const { [providerName]: _removed, ...rest } = previous; const { [providerName]: _removed, ...rest } = previous;
return rest; return rest;
@@ -711,7 +740,7 @@ export function UsageIndicator({ isOpen, onClose, projectId, anchorRect }: Usage
return { return {
...previous, ...previous,
[providerName]: [...(previous[providerName] ?? []), windowLabel], [providerName]: [...providerHiddenWindows, windowIdentity],
}; };
}); });
}, []); }, []);

View File

@@ -45,6 +45,10 @@ const USAGE_VIEW_MODE_KEY = scopedKey("kb-usage-view-mode", TEST_PROJECT_ID);
const USAGE_HIDDEN_WINDOWS_KEY = scopedKey("kb-usage-hidden-windows", TEST_PROJECT_ID); const USAGE_HIDDEN_WINDOWS_KEY = scopedKey("kb-usage-hidden-windows", TEST_PROJECT_ID);
const USAGE_PROVIDER_ORDER_KEY = scopedKey("kb-usage-provider-order", TEST_PROJECT_ID); const USAGE_PROVIDER_ORDER_KEY = scopedKey("kb-usage-provider-order", TEST_PROJECT_ID);
function getWindowIdentity(label: string, index: number): string {
return `${index}::${label}`;
}
describe("UsageIndicator", () => { describe("UsageIndicator", () => {
const mockOnClose = vi.fn(); const mockOnClose = vi.fn();
const mockRefresh = vi.fn(); const mockRefresh = vi.fn();
@@ -913,11 +917,11 @@ describe("UsageIndicator", () => {
fireEvent.click(screen.getByRole("button", { name: "Hide Session (5h)" })); fireEvent.click(screen.getByRole("button", { name: "Hide Session (5h)" }));
expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe( expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe(
JSON.stringify({ Anthropic: ["Session (5h)"] }) JSON.stringify({ Anthropic: [getWindowIdentity("Session (5h)", 0)] })
); );
}); });
it("restores hidden windows from localStorage on mount", () => { it("restores hidden windows from legacy label-only localStorage on mount", () => {
localStorage.setItem( localStorage.setItem(
USAGE_HIDDEN_WINDOWS_KEY, USAGE_HIDDEN_WINDOWS_KEY,
JSON.stringify({ Anthropic: ["Session (5h)"] }) JSON.stringify({ Anthropic: ["Session (5h)"] })
@@ -1028,7 +1032,56 @@ describe("UsageIndicator", () => {
expect(screen.getByTestId("usage-show-hidden-btn")).toHaveTextContent("Show hidden (2)"); expect(screen.getByTestId("usage-show-hidden-btn")).toHaveTextContent("Show hidden (2)");
}); });
it("counts currently rendered hidden rows when persisted labels match duplicate live windows", () => { it("hiding one duplicate-labeled MiniMax window only hides that instance", () => {
mockUseUsageData.mockReturnValue(createUsageDataState({
providers: [
{
name: "MiniMax",
icon: "🧠",
status: "ok",
windows: [
{
label: "Daily",
percentUsed: 20,
percentLeft: 80,
resetText: "resets in 1h",
resetMs: 3600000,
},
{
label: "Daily",
percentUsed: 60,
percentLeft: 40,
resetText: "resets in 4h",
resetMs: 14400000,
},
],
},
],
loading: false,
error: null,
lastUpdated: new Date(),
refresh: mockRefresh,
}));
render(<UsageIndicator isOpen={true} onClose={mockOnClose} projectId={TEST_PROJECT_ID} />);
fireEvent.click(screen.getAllByRole("button", { name: "Hide Daily" })[1]);
const hiddenRows = Array.from(document.querySelectorAll(".usage-window--hidden"));
expect(hiddenRows).toHaveLength(1);
expect(hiddenRows[0]).toHaveTextContent("Daily");
expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe(
JSON.stringify({ MiniMax: [getWindowIdentity("Daily", 1)] })
);
expect(screen.getByTestId("usage-show-hidden-btn")).toHaveTextContent("Show hidden (1)");
fireEvent.click(screen.getByTestId("usage-show-hidden-btn"));
expect(document.querySelectorAll(".usage-window--hidden")).toHaveLength(0);
expect(screen.queryByTestId("usage-show-hidden-btn")).not.toBeInTheDocument();
});
it("counts currently rendered hidden rows when legacy persisted labels match duplicate live windows", () => {
localStorage.setItem( localStorage.setItem(
USAGE_HIDDEN_WINDOWS_KEY, USAGE_HIDDEN_WINDOWS_KEY,
JSON.stringify({ Anthropic: ["Daily"] }) JSON.stringify({ Anthropic: ["Daily"] })
@@ -1132,7 +1185,7 @@ describe("UsageIndicator", () => {
fireEvent.click(screen.getByRole("button", { name: "Hide Session (5h)" })); fireEvent.click(screen.getByRole("button", { name: "Hide Session (5h)" }));
expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe( expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe(
JSON.stringify({ Anthropic: ["Session (5h)"] }) JSON.stringify({ Anthropic: [getWindowIdentity("Session (5h)", 0)] })
); );
fireEvent.click(screen.getByTestId("usage-show-hidden-btn")); fireEvent.click(screen.getByTestId("usage-show-hidden-btn"));
@@ -1151,6 +1204,32 @@ describe("UsageIndicator", () => {
expect(screen.getByText("Session (5h)").closest(".usage-window")).not.toHaveClass("usage-window--hidden"); expect(screen.getByText("Session (5h)").closest(".usage-window")).not.toHaveClass("usage-window--hidden");
}); });
it("tracks multiple distinct hidden Anthropic windows per instance", () => {
mockUseUsageData.mockReturnValue(createUsageDataState({
providers: mockProviders,
loading: false,
error: null,
lastUpdated: new Date(),
refresh: mockRefresh,
}));
render(<UsageIndicator isOpen={true} onClose={mockOnClose} projectId={TEST_PROJECT_ID} />);
fireEvent.click(screen.getByRole("button", { name: "Hide Session (5h)" }));
fireEvent.click(screen.getByRole("button", { name: "Hide Weekly" }));
expect(document.querySelectorAll(".usage-window--hidden")).toHaveLength(2);
expect(screen.getByTestId("usage-show-hidden-btn")).toHaveTextContent("Show hidden (2)");
expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe(
JSON.stringify({
Anthropic: [
getWindowIdentity("Session (5h)", 0),
getWindowIdentity("Weekly", 1),
],
})
);
});
it("does not show provider-level show hidden button when no windows are hidden", () => { it("does not show provider-level show hidden button when no windows are hidden", () => {
mockUseUsageData.mockReturnValue(createUsageDataState({ mockUseUsageData.mockReturnValue(createUsageDataState({
providers: mockProviders, providers: mockProviders,
@@ -1165,6 +1244,32 @@ describe("UsageIndicator", () => {
expect(screen.queryByTestId("usage-show-hidden-btn")).not.toBeInTheDocument(); expect(screen.queryByTestId("usage-show-hidden-btn")).not.toBeInTheDocument();
}); });
it("does not crash or show hidden controls when provider windows are empty or undefined", () => {
mockUseUsageData.mockReturnValue(createUsageDataState({
providers: [
{ name: "Anthropic", icon: "🅰️", status: "ok", windows: [] },
{ name: "MiniMax", icon: "🧠", status: "ok", windows: undefined as unknown as ProviderUsage["windows"] },
],
loading: false,
error: null,
lastUpdated: new Date(),
refresh: mockRefresh,
}));
render(
<UsageIndicator
isOpen={true}
onClose={mockOnClose}
projectId={TEST_PROJECT_ID}
anchorRect={createAnchorRect()}
/>
);
expect(screen.getByText("Anthropic")).toBeInTheDocument();
expect(screen.getByText("MiniMax")).toBeInTheDocument();
expect(screen.queryByTestId("usage-show-hidden-btn")).not.toBeInTheDocument();
});
// ProviderIcon integration tests // ProviderIcon integration tests
it("renders SVG provider icons instead of emoji", () => { it("renders SVG provider icons instead of emoji", () => {