// Usage in always-loaded chrome: the account-menu row and the composer's
// near/at-limit notice. Eager and dependency-light (the SDK's `/usage/me` read
// plus the presentational React views); budget pages stay in their lazy chunk.
import { UsageLimitNoticeView, UsageMeterView, summarizeUsage } from "../../../usage";
import { getMyUsage, type WorkspaceUsageResponse } from "@opengeni/sdk/usage-allowances";
import { useNavigate } from "../../lib/navigation";
import { GaugeIcon } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { DropdownMenuItem, DropdownMenuSeparator } from "../../../ui/dropdown-menu";
import { useAppContext } from "../../app-context";
import { CONSOLE_ALLOWANCE_LABELS } from "../../lib/allowance-labels";
import { isPersonalWorkspace } from "../../lib/managed-self-context";
/** The signed-in person's `/usage/me`; nothing for Personal workspaces or on error. */
function useOwnUsage(workspaceId: string, refreshKey?: unknown) {
const context = useAppContext();
const workspace = context.workspaces.find((candidate) => candidate.id === workspaceId) ?? null;
// Budgets exist only in shared workspaces; a Personal workspace never asks.
const enabled = Boolean(workspace) && !isPersonalWorkspace(workspace, context.managedSelfContext);
const client = context.client;
const [usage, setUsage] = useState<{ workspaceId: string; value: WorkspaceUsageResponse } | null>(
null,
);
useEffect(() => {
if (!enabled) return;
const abort = new AbortController();
getMyUsage(client, workspaceId, {}, { signal: abort.signal })
.then((value) => {
if (!abort.signal.aborted) setUsage({ workspaceId, value });
})
// Usage is a hint here: a failed or refused read just shows nothing.
.catch(() => undefined);
return () => abort.abort();
}, [client, enabled, workspaceId, refreshKey]);
const value = enabled && usage?.workspaceId === workspaceId ? usage.value : null;
return useMemo(() => (value ? summarizeUsage(value) : null), [value]);
}
/**
* "Usage ยท 38% left" in the account menu while the current workspace has a
* limit for you. Opens the workspace's Usage page. Nothing when unlimited.
*/
export function AccountUsageMenuItem({ workspaceId }: { workspaceId: string }) {
const navigate = useNavigate();
const summary = useOwnUsage(workspaceId);
if (!summary || summary.state === "unlimited") return null;
return (
<>
void navigate({
to: "/workspaces/$workspaceId/settings",
params: { workspaceId },
search: { section: "usage" },
})
}
className="gap-2"
>
Usage
>
);
}
/**
* The composer's near/at-limit line: what happened, who can raise it, when it
* resets. A warning can be dismissed for this tab; reaching a limit can't.
*/
export function ComposerUsageNotice({
workspaceId,
refreshKey,
}: {
workspaceId: string;
refreshKey?: unknown | undefined;
}) {
const summary = useOwnUsage(workspaceId, refreshKey);
return (
);
}