Files
OpenHands/frontend/src/hooks/use-settings-nav-items.ts
openhands 93fe1613d7 feat: Add Usage dashboard for organization admins and owners
This PR adds a Usage page to the Settings menu that displays organization
usage statistics including:
- Total number of conversations
- Number of merged PRs
- Average cost per conversation
- A chart showing conversation counts over the last 90 days

The page is only visible to users with admin or owner roles in team organizations.

Backend changes:
- Add /api/organizations/{org_id}/usage endpoint in enterprise/server/routes/usage.py
- Register usage_router in enterprise/saas_server.py

Frontend changes:
- Add usage-service API client and types
- Add useUsageStats React Query hook
- Add Usage nav item to settings navigation (admin/owner only, team orgs only)
- Create usage-settings.tsx page with stat cards and conversation chart
- Add i18n translations for Usage page (all supported languages)
- Add route configuration for /settings/usage

Co-authored-by: openhands <openhands@all-hands.dev>
2026-03-19 16:32:20 +00:00

66 lines
2.2 KiB
TypeScript

import { useConfig } from "#/hooks/query/use-config";
import {
SAAS_NAV_ITEMS,
OSS_NAV_ITEMS,
SettingsNavItem,
} from "#/constants/settings-nav";
import { OrganizationUserRole } from "#/types/org";
import { isBillingHidden } from "#/utils/org/billing-visibility";
import { isSettingsPageHidden } from "#/utils/settings-utils";
import { useMe } from "./query/use-me";
import { usePermission } from "./organizations/use-permissions";
import { useOrgTypeAndAccess } from "./use-org-type-and-access";
/**
* Build Settings navigation items based on:
* - app mode (saas / oss)
* - feature flags
* - active user's role
* - org type (personal vs team)
* @returns Settings Nav Items []
*/
export function useSettingsNavItems(): SettingsNavItem[] {
const { data: config } = useConfig();
const { data: user } = useMe();
const userRole: OrganizationUserRole = user?.role ?? "member";
const { hasPermission } = usePermission(userRole);
const { isPersonalOrg, isTeamOrg, organizationId } = useOrgTypeAndAccess();
const shouldHideBilling = isBillingHidden(
config,
hasPermission("view_billing"),
);
const isSaasMode = config?.app_mode === "saas";
const featureFlags = config?.feature_flags;
let items = isSaasMode ? SAAS_NAV_ITEMS : OSS_NAV_ITEMS;
// First apply feature flag-based hiding
items = items.filter((item) => !isSettingsPageHidden(item.to, featureFlags));
// Hide billing when billing is not accessible OR when in team org
if (shouldHideBilling || isTeamOrg) {
items = items.filter((item) => item.to !== "/settings/billing");
}
// Hide org routes for personal orgs, missing permissions, or no org selected
if (!hasPermission("view_billing") || !organizationId || isPersonalOrg) {
items = items.filter((item) => item.to !== "/settings/org");
}
if (
!hasPermission("invite_user_to_organization") ||
!organizationId ||
isPersonalOrg
) {
items = items.filter((item) => item.to !== "/settings/org-members");
}
// Hide usage page for users without view_billing permission or personal orgs
if (!hasPermission("view_billing") || !organizationId || isPersonalOrg) {
items = items.filter((item) => item.to !== "/settings/usage");
}
return items;
}