{"version":3,"sources":["../../../src/features/tokenusage/contexts/TokenUsageAdminContext.tsx","../../../src/features/tokenusage/components/TokenUsageAdminFilterBar.tsx","../../../src/features/tokenusage/data/TokenUsageAdminService.ts","../../../src/features/tokenusage/components/TokenUsageAdminContainer.tsx","../../../src/features/tokenusage/components/TokenUsageAdminTiles.tsx","../../../src/features/tokenusage/lib/formatters.ts","../../../src/features/tokenusage/lib/metrics.ts","../../../src/features/tokenusage/components/TokenUsageBreakdownTable.tsx","../../../src/features/tokenusage/components/TokenUsageRankedBar.tsx","../../../src/features/tokenusage/lib/operation-label.ts","../../../src/features/tokenusage/lib/palette.ts","../../../src/features/tokenusage/components/TokenUsageTimelineChart.tsx","../../../src/features/tokenusage/data/TokenUsageReportService.ts","../../../src/features/tokenusage/i18n-keys.ts","../../../src/features/tokenusage/contexts/TokenUsageReportContext.tsx","../../../src/features/tokenusage/components/TokenUsageReportFilterBar.tsx","../../../src/features/tokenusage/components/TokenUsageReportContainer.tsx","../../../src/features/tokenusage/components/TokenUsageReportTiles.tsx"],"sourcesContent":["\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport { SharedProvider } from \"../../../contexts\";\nimport { usePageUrlGenerator } from \"../../../hooks\";\nimport { BreadcrumbItemData } from \"../../../interfaces\";\nimport { TokenUsageAdminFilterBar } from \"../components/TokenUsageAdminFilterBar\";\nimport type { TokenUsageAdminBreakdownInterface } from \"../data/tokenusage-admin-breakdown.interface\";\nimport type { TokenUsageAdminSummaryInterface } from \"../data/tokenusage-admin-summary.interface\";\nimport type { TokenUsageAdminTimelineInterface } from \"../data/tokenusage-admin-timeline.interface\";\nimport type { Granularity, Metric, StackBy } from \"../data/tokenusage-admin.types\";\nimport { TokenUsageAdminService } from \"../data/TokenUsageAdminService\";\n\n/**\n * Default route the breadcrumb links back to. A host app that mounts the page\n * elsewhere overrides it with the `pageUrl` prop — the constant stays here\n * because the breadcrumb is built by this provider, not by the app.\n */\nconst TOKEN_USAGE_ADMIN_PAGE_URL = \"/administration/token-usage\";\n\n/** Rows kept per ranked panel before the repository folds the tail into \"other\". */\nconst DEFAULT_TOP_N = 10;\n\nexport type TokenUsageAdminFilterState = {\n  /** ISO 8601 instant. */\n  from: string;\n  /** ISO 8601 instant. */\n  to: string;\n  granularity: Granularity;\n  stackBy: StackBy;\n  companyId?: string;\n  metric: Metric;\n};\n\nexport interface TokenUsageAdminContextType {\n  summary: TokenUsageAdminSummaryInterface[];\n  timeline: TokenUsageAdminTimelineInterface[];\n  byCompany: TokenUsageAdminBreakdownInterface[];\n  byUser: TokenUsageAdminBreakdownInterface[];\n  /** Platform-side spend split by operation. Always empty in single-customer mode. */\n  /** Platform spend by operation — empty in single-customer mode. */\n  byOperation: TokenUsageAdminBreakdownInterface[];\n  /** Customer spend by operation — the customer-side mirror of byOperation. */\n  byCustomerOperation: TokenUsageAdminBreakdownInterface[];\n  companies: { id: string; label: string }[];\n  filters: TokenUsageAdminFilterState;\n  /** Merges a partial patch into the current filters; every key is optional. */\n  setFilters: (next: Partial<TokenUsageAdminFilterState>) => void;\n  /** `true` once a company filter is applied — the platform panels are meaningless then. */\n  singleCustomerMode: boolean;\n  isLoading: boolean;\n  error: string | null;\n}\n\nconst TokenUsageAdminContext = createContext<TokenUsageAdminContextType | undefined>(undefined);\n\n/** Current calendar month to now, which is the window the page opens on. */\nfunction defaultRange(): { from: string; to: string } {\n  const now = new Date();\n  const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);\n  return { from: start.toISOString(), to: now.toISOString() };\n}\n\ntype TokenUsageAdminProviderProps = {\n  children: ReactNode;\n  /** Pre-select a company, which puts the page in single-customer mode from the first render. */\n  initialCompanyId?: string;\n  /** ISO 8601 instant. Defaults to the start of the current month. */\n  initialFrom?: string;\n  /** ISO 8601 instant. Defaults to now. */\n  initialTo?: string;\n  /** Rows per ranked panel. Defaults to 10. */\n  topN?: number;\n  /** Route the breadcrumb links back to. */\n  pageUrl?: string;\n};\n\n/**\n * Owns every filter the administrative token-usage page reads, fetches the five\n * panels behind it, and publishes the filter bar into the page title bar.\n *\n * The filter bar is rendered into `title.functions` here — NOT in the container —\n * because `RoundPageContainer`'s title bar reads `title.functions` from\n * `SharedContext`, and a descendant cannot inject nodes into an ancestor's\n * provider value. That is why the filter state lives at this level.\n */\nexport const TokenUsageAdminProvider = ({\n  children,\n  initialCompanyId,\n  initialFrom,\n  initialTo,\n  topN = DEFAULT_TOP_N,\n  pageUrl = TOKEN_USAGE_ADMIN_PAGE_URL,\n}: TokenUsageAdminProviderProps) => {\n  const t = useTranslations();\n  const generateUrl = usePageUrlGenerator();\n\n  const [filters, setFilterState] = useState<TokenUsageAdminFilterState>(() => {\n    const range = defaultRange();\n    return {\n      from: initialFrom ?? range.from,\n      to: initialTo ?? range.to,\n      granularity: \"day\",\n      stackBy: \"scope\",\n      companyId: initialCompanyId,\n      metric: \"cost\",\n    };\n  });\n\n  const [summary, setSummary] = useState<TokenUsageAdminSummaryInterface[]>([]);\n  const [timeline, setTimeline] = useState<TokenUsageAdminTimelineInterface[]>([]);\n  const [byCompany, setByCompany] = useState<TokenUsageAdminBreakdownInterface[]>([]);\n  const [byUser, setByUser] = useState<TokenUsageAdminBreakdownInterface[]>([]);\n  const [byOperation, setByOperation] = useState<TokenUsageAdminBreakdownInterface[]>([]);\n  const [byCustomerOperation, setByCustomerOperation] = useState<TokenUsageAdminBreakdownInterface[]>([]);\n  const [isLoading, setIsLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n\n  const { from, to, granularity, stackBy, companyId, metric } = filters;\n  const singleCustomerMode = Boolean(companyId);\n\n  useEffect(() => {\n    // `cancelled` is the out-of-order guard: a filter change fires a new request\n    // while the previous one is still in flight, and without this flag the slower\n    // (older) response would land last and overwrite the fresher state.\n    let cancelled = false;\n\n    setIsLoading(true);\n    setError(null);\n\n    const base = { from, to, companyId };\n\n    Promise.all([\n      TokenUsageAdminService.getSummary(base),\n      TokenUsageAdminService.getTimeline({ ...base, granularity, stackBy }),\n      TokenUsageAdminService.getBreakdown({ ...base, dimension: \"company\", scope: \"customer\", limit: topN }),\n      TokenUsageAdminService.getBreakdown({ ...base, dimension: \"user\", scope: \"customer\", limit: topN }),\n      // Platform spend is, by definition, the usage with no owning company, so\n      // filtering it by one is meaningless — the call is skipped entirely rather\n      // than issued and discarded.\n      companyId\n        ? Promise.resolve<TokenUsageAdminBreakdownInterface[]>([])\n        : TokenUsageAdminService.getBreakdown({ ...base, dimension: \"operation\", scope: \"platform\", limit: topN }),\n      // Customer spend by operation — unlike the platform panel this one IS\n      // meaningful under a company filter, so it is always requested.\n      TokenUsageAdminService.getBreakdown({ ...base, dimension: \"operation\", scope: \"customer\", limit: topN }),\n    ])\n      .then(([nextSummary, nextTimeline, nextByCompany, nextByUser, nextByOperation, nextByCustomerOperation]) => {\n        if (cancelled) return;\n        setSummary(nextSummary ?? []);\n        setTimeline(nextTimeline ?? []);\n        setByCompany(nextByCompany ?? []);\n        setByUser(nextByUser ?? []);\n        setByOperation(nextByOperation ?? []);\n        setByCustomerOperation(nextByCustomerOperation ?? []);\n      })\n      .catch((err) => {\n        if (cancelled) return;\n        console.error(\"Failed to load administrative token usage:\", err);\n        setError(err instanceof Error ? err.message : String(err));\n      })\n      .finally(() => {\n        if (cancelled) return;\n        setIsLoading(false);\n      });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [from, to, granularity, stackBy, companyId, topN]);\n\n  const setFilters = useCallback((next: Partial<TokenUsageAdminFilterState>) => {\n    setFilterState((prev) => ({ ...prev, ...next }));\n  }, []);\n\n  /**\n   * Derived from the company breakdown, per the page design. Note the\n   * consequence: once a company filter is applied the breakdown returns that\n   * company alone, so the selector narrows to it — clearing the filter restores\n   * the full list.\n   */\n  const companies = useMemo(\n    () => byCompany.filter((row) => row.id !== \"other\").map((row) => ({ id: row.id, label: row.label })),\n    [byCompany],\n  );\n\n  const breadcrumb = (): BreadcrumbItemData[] => [\n    {\n      name: t(\"token_usage.admin.title\"),\n      href: generateUrl({ page: pageUrl }),\n    },\n  ];\n\n  const title = () => ({\n    type: t(\"token_usage.admin.title\"),\n    functions: (\n      <TokenUsageAdminFilterBar\n        key=\"tokenUsageAdminFilterBar\"\n        from={from}\n        to={to}\n        granularity={granularity}\n        companyId={companyId}\n        metric={metric}\n        companies={companies}\n        onChange={setFilters}\n      />\n    ),\n  });\n\n  const contextValue = useMemo<TokenUsageAdminContextType>(\n    () => ({\n      summary,\n      timeline,\n      byCompany,\n      byUser,\n      byOperation,\n      byCustomerOperation,\n      companies,\n      filters,\n      setFilters,\n      singleCustomerMode,\n      isLoading,\n      error,\n    }),\n    [\n      summary,\n      timeline,\n      byCompany,\n      byUser,\n      byOperation,\n      byCustomerOperation,\n      companies,\n      filters,\n      setFilters,\n      singleCustomerMode,\n      isLoading,\n      error,\n    ],\n  );\n\n  return (\n    <SharedProvider value={{ breadcrumbs: breadcrumb(), title: title() }}>\n      <TokenUsageAdminContext.Provider value={contextValue}>{children}</TokenUsageAdminContext.Provider>\n    </SharedProvider>\n  );\n};\n\nexport const useTokenUsageAdmin = (): TokenUsageAdminContextType => {\n  const ctx = useContext(TokenUsageAdminContext);\n  if (!ctx) {\n    throw new Error(\"useTokenUsageAdmin() called outside <TokenUsageAdminProvider>.\");\n  }\n  return ctx;\n};\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { DateRangeSelector } from \"../../../components/forms/DateRangeSelector\";\nimport { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"../../../shadcnui\";\nimport { cn } from \"../../../utils\";\nimport type { Granularity, Metric } from \"../data/tokenusage-admin.types\";\n\n/** Sentinel for \"no company filter\" — Base UI Select cannot hold `undefined`. */\nconst ALL_COMPANIES = \"all\";\n\ntype FilterState = {\n  from: string;\n  to: string;\n  granularity: Granularity;\n  companyId?: string;\n  metric: Metric;\n};\n\ntype Props = FilterState & {\n  companies: { id: string; label: string }[];\n  /** Receives ONLY the keys that changed. */\n  onChange: (next: Partial<FilterState>) => void;\n};\n\n/**\n * The single control row above the KPI tiles.\n *\n * It is deliberately stateless: every control reports the one key it changed and\n * the owning context re-fetches. Keeping the whole filter state in one place is\n * what lets the page issue a single coordinated batch of requests instead of one\n * per control.\n */\nexport function TokenUsageAdminFilterBar({ granularity, companyId, metric, companies, onChange }: Props) {\n  const t = useTranslations();\n\n  const companyItems: Record<string, string> = {\n    [ALL_COMPANIES]: t(\"token_usage.admin.all_companies\"),\n    ...Object.fromEntries(companies.map((company) => [company.id, company.label])),\n  };\n\n  return (\n    <div className=\"flex flex-wrap items-center gap-2\">\n      <DateRangeSelector\n        onDateChange={(range) => {\n          if (!range?.from || !range?.to) return;\n          onChange({ from: range.from.toISOString(), to: range.to.toISOString() });\n        }}\n      />\n\n      <Segmented\n        ariaLabel={t(\"token_usage.admin.granularity.label\")}\n        value={granularity}\n        options={[\n          { value: \"day\", label: t(\"token_usage.admin.granularity.day\") },\n          { value: \"week\", label: t(\"token_usage.admin.granularity.week\") },\n          { value: \"month\", label: t(\"token_usage.admin.granularity.month\") },\n        ]}\n        onSelect={(value) => onChange({ granularity: value })}\n      />\n\n      <Segmented\n        ariaLabel={t(\"token_usage.admin.metric.label\")}\n        value={metric}\n        options={[\n          { value: \"cost\", label: t(\"token_usage.admin.metric.cost\") },\n          { value: \"credits\", label: t(\"token_usage.admin.metric.credits\") },\n          { value: \"tokens\", label: t(\"token_usage.admin.metric.tokens\") },\n        ]}\n        onSelect={(value) => onChange({ metric: value })}\n      />\n\n      <Select\n        items={companyItems}\n        value={companyId ?? ALL_COMPANIES}\n        onValueChange={(value) =>\n          onChange({ companyId: !value || value === ALL_COMPANIES ? undefined : (value as string) })\n        }\n      >\n        <SelectTrigger className=\"w-56\">\n          <SelectValue placeholder={t(\"token_usage.admin.all_companies\")} />\n        </SelectTrigger>\n        <SelectContent>\n          <SelectItem value={ALL_COMPANIES}>{t(\"token_usage.admin.all_companies\")}</SelectItem>\n          {companies.map((company) => (\n            <SelectItem key={company.id} value={company.id}>\n              {company.label}\n            </SelectItem>\n          ))}\n        </SelectContent>\n      </Select>\n    </div>\n  );\n}\n\n/**\n * A segmented control built from plain Buttons.\n *\n * The design system has no ToggleGroup primitive, and Base UI triggers may never\n * wrap a Button, so the segmented look is composed from Buttons directly — the\n * selected segment takes the solid variant, the rest stay ghosts.\n */\nfunction Segmented<T extends string>({\n  ariaLabel,\n  value,\n  options,\n  onSelect,\n}: {\n  ariaLabel: string;\n  value: T;\n  options: { value: T; label: string }[];\n  onSelect: (value: T) => void;\n}) {\n  return (\n    <div\n      role=\"group\"\n      aria-label={ariaLabel}\n      className=\"border-border inline-flex items-center gap-0.5 rounded-md border p-0.5\"\n    >\n      {options.map((option) => {\n        const selected = option.value === value;\n        return (\n          <Button\n            key={option.value}\n            type=\"button\"\n            size=\"sm\"\n            variant={selected ? \"default\" : \"ghost\"}\n            aria-pressed={selected}\n            className={cn(!selected && \"text-muted-foreground\")}\n            onClick={() => onSelect(option.value)}\n          >\n            {option.label}\n          </Button>\n        );\n      })}\n    </div>\n  );\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { TokenUsageAdminBreakdownInterface } from \"./tokenusage-admin-breakdown.interface\";\nimport { TokenUsageAdminSummaryInterface } from \"./tokenusage-admin-summary.interface\";\nimport { TokenUsageAdminTimelineInterface } from \"./tokenusage-admin-timeline.interface\";\nimport { Dimension, Granularity, Scope, StackBy, TokenUsageAdminFilters } from \"./tokenusage-admin.types\";\n\nfunction withFilters(endpoint: EndpointCreator, filters: TokenUsageAdminFilters): EndpointCreator {\n  endpoint.addAdditionalParam(\"from\", filters.from);\n  endpoint.addAdditionalParam(\"to\", filters.to);\n  if (filters.companyId) endpoint.addAdditionalParam(\"companyId\", filters.companyId);\n  return endpoint;\n}\n\nexport class TokenUsageAdminService extends AbstractService {\n  /** Six rows: {customer, platform, total} × {current, previous}. */\n  static async getSummary(filters: TokenUsageAdminFilters): Promise<TokenUsageAdminSummaryInterface[]> {\n    const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageAdminSummary }), filters);\n\n    return this.callApi<TokenUsageAdminSummaryInterface[]>({\n      type: Modules.TokenUsageAdminSummary,\n      method: HttpMethod.GET,\n      endpoint: endpoint.generate(),\n    });\n  }\n\n  static async getTimeline(\n    params: TokenUsageAdminFilters & { granularity: Granularity; stackBy: StackBy },\n  ): Promise<TokenUsageAdminTimelineInterface[]> {\n    const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageAdminTimeline }), params);\n    endpoint.addAdditionalParam(\"granularity\", params.granularity);\n    endpoint.addAdditionalParam(\"stackBy\", params.stackBy);\n\n    return this.callApi<TokenUsageAdminTimelineInterface[]>({\n      type: Modules.TokenUsageAdminTimeline,\n      method: HttpMethod.GET,\n      endpoint: endpoint.generate(),\n    });\n  }\n\n  static async getBreakdown(\n    params: TokenUsageAdminFilters & { dimension: Dimension; scope: Scope; limit?: number },\n  ): Promise<TokenUsageAdminBreakdownInterface[]> {\n    const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageAdminBreakdown }), params);\n    endpoint.addAdditionalParam(\"dimension\", params.dimension);\n    endpoint.addAdditionalParam(\"scope\", params.scope);\n    if (params.limit !== undefined) endpoint.addAdditionalParam(\"limit\", String(params.limit));\n\n    return this.callApi<TokenUsageAdminBreakdownInterface[]>({\n      type: Modules.TokenUsageAdminBreakdown,\n      method: HttpMethod.GET,\n      endpoint: endpoint.generate(),\n    });\n  }\n}\n","\"use client\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { useTranslations } from \"next-intl\";\nimport { useMemo } from \"react\";\nimport { RoundPageContainer } from \"../../../components\";\nimport {\n  Card,\n  CardAction,\n  CardContent,\n  CardHeader,\n  CardTitle,\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"../../../shadcnui\";\nimport { useTokenUsageAdmin } from \"../contexts/TokenUsageAdminContext\";\nimport type { StackBy } from \"../data/tokenusage-admin.types\";\nimport { TokenUsageAdminTiles } from \"./TokenUsageAdminTiles\";\nimport { TokenUsageBreakdownTable } from \"./TokenUsageBreakdownTable\";\nimport { TokenUsageRankedBar } from \"./TokenUsageRankedBar\";\nimport { TokenUsageTimelineChart } from \"./TokenUsageTimelineChart\";\n\nconst STACK_BY_VALUES: StackBy[] = [\"scope\", \"type\", \"company\"];\n\n/**\n * Page body for the administrative token-usage dashboard.\n *\n * Stateless by design — every value it renders comes from\n * `useTokenUsageAdmin()`. The filter bar is deliberately NOT here: it belongs to\n * the page title bar, which `RoundPageContainer` fills from `SharedContext`, so\n * the provider publishes it (see TokenUsageAdminContext).\n */\nexport function TokenUsageAdminContainer() {\n  const t = useTranslations();\n  const {\n    summary,\n    timeline,\n    byCompany,\n    byUser,\n    byOperation,\n    byCustomerOperation,\n    filters,\n    setFilters,\n    singleCustomerMode,\n    isLoading,\n    error,\n  } = useTokenUsageAdmin();\n\n  // narr8-shaped deployments record essentially no platform spend (usage with no\n  // owning company), which would leave the platform tile and the\n  // platform-by-operation panel as permanent zeros. Treat \"nothing to show\" the\n  // same way a company filter is treated — the customer half then takes full\n  // width. a360ai is unaffected: it has real platform spend.\n  const platformIsEmpty = useMemo(\n    () =>\n      summary\n        .filter((row) => row.scope === \"platform\")\n        .every((row) => row.cost === 0 && row.credits === 0 && row.tokensIn === 0 && row.tokensOut === 0),\n    [summary],\n  );\n\n  const hidePlatform = singleCustomerMode || platformIsEmpty;\n\n  const stackByItems = useMemo(\n    () => ({\n      scope: t(\"token_usage.admin.stack.scope\"),\n      type: t(\"token_usage.admin.stack.type\"),\n      company: t(\"token_usage.admin.stack.company\"),\n    }),\n    [t],\n  );\n\n  if (error) {\n    return (\n      <RoundPageContainer fullWidth forceHeader>\n        <div className=\"p-4\">\n          <Card>\n            <CardContent>\n              <p className=\"text-destructive text-xs/relaxed\">{error}</p>\n            </CardContent>\n          </Card>\n        </div>\n      </RoundPageContainer>\n    );\n  }\n\n  // Loading renders nothing in the body: the title bar (with the filter bar) is\n  // already mounted, so a spinner would only make the controls jump on arrival.\n  if (isLoading) return <RoundPageContainer fullWidth forceHeader />;\n\n  const emptyLabel = t(\"token_usage.admin.no_data\");\n\n  return (\n    <RoundPageContainer fullWidth forceHeader>\n      <div className=\"flex w-full flex-col gap-4 p-4\">\n        <TokenUsageAdminTiles summary={summary} metric={filters.metric} singleCustomerMode={hidePlatform} />\n\n        <Card>\n          <CardHeader>\n            <CardTitle>{t(\"token_usage.admin.usage_over_time\")}</CardTitle>\n            <CardAction>\n              <div className=\"flex items-center gap-2\">\n                <span className=\"text-muted-foreground text-xs\">{t(\"token_usage.admin.stack_by\")}</span>\n                <Select\n                  items={stackByItems}\n                  value={filters.stackBy}\n                  onValueChange={(value) => {\n                    if (value) setFilters({ stackBy: value as StackBy });\n                  }}\n                >\n                  <SelectTrigger size=\"sm\" className=\"w-36\">\n                    <SelectValue />\n                  </SelectTrigger>\n                  <SelectContent>\n                    {STACK_BY_VALUES.map((value) => (\n                      <SelectItem key={value} value={value}>\n                        {stackByItems[value]}\n                      </SelectItem>\n                    ))}\n                  </SelectContent>\n                </Select>\n              </div>\n            </CardAction>\n          </CardHeader>\n          <CardContent>\n            <TokenUsageTimelineChart rows={timeline} metric={filters.metric} stackBy={filters.stackBy} />\n          </CardContent>\n        </Card>\n\n        <div className=\"grid gap-4 md:grid-cols-2\">\n          <Card>\n            <CardHeader>\n              <CardTitle>{t(\"token_usage.admin.by_company\")}</CardTitle>\n            </CardHeader>\n            <CardContent>\n              <TokenUsageRankedBar rows={byCompany} metric={filters.metric} emptyLabel={emptyLabel} />\n            </CardContent>\n          </Card>\n\n          <Card>\n            <CardHeader>\n              <CardTitle>{t(\"token_usage.admin.by_user\")}</CardTitle>\n            </CardHeader>\n            <CardContent>\n              <TokenUsageRankedBar rows={byUser} metric={filters.metric} emptyLabel={emptyLabel} />\n            </CardContent>\n          </Card>\n        </div>\n\n        {/* The two cost centres, broken down the same way so they can be read\n            against each other. Operation types are vocabulary, not data — unlike\n            the company and user panels above, these labels get translated.\n\n            Platform spend has no owning company, so a company filter makes that\n            half meaningless: the provider skips the request and the card is\n            hidden rather than rendered empty, leaving the customer half full\n            width. The same holds when the window records no platform spend at\n            all — see `hidePlatform` above. */}\n        <div className={cn(\"grid gap-4\", !hidePlatform && \"md:grid-cols-2\")}>\n          <Card>\n            <CardHeader>\n              <CardTitle>{t(\"token_usage.admin.customer_by_operation\")}</CardTitle>\n            </CardHeader>\n            <CardContent>\n              <TokenUsageRankedBar\n                rows={byCustomerOperation}\n                metric={filters.metric}\n                emptyLabel={emptyLabel}\n                labelsAreOperationTypes\n              />\n            </CardContent>\n          </Card>\n\n          {!hidePlatform && (\n            <Card>\n              <CardHeader>\n                <CardTitle>{t(\"token_usage.admin.platform_by_operation\")}</CardTitle>\n              </CardHeader>\n              <CardContent>\n                <TokenUsageRankedBar\n                  rows={byOperation}\n                  metric={filters.metric}\n                  emptyLabel={emptyLabel}\n                  labelsAreOperationTypes\n                />\n              </CardContent>\n            </Card>\n          )}\n        </div>\n\n        <Card>\n          <CardHeader>\n            <CardTitle>{t(\"token_usage.admin.detail\")}</CardTitle>\n          </CardHeader>\n          <CardContent>\n            <TokenUsageBreakdownTable rows={byCompany} metric={filters.metric} />\n          </CardContent>\n        </Card>\n      </div>\n    </RoundPageContainer>\n  );\n}\n","\"use client\";\n\nimport { ArrowDownIcon, ArrowUpIcon } from \"lucide-react\";\nimport { useTranslations } from \"next-intl\";\nimport { Card, CardContent } from \"../../../shadcnui\";\nimport { cn } from \"../../../utils\";\nimport type { TokenUsageAdminSummaryInterface } from \"../data/tokenusage-admin-summary.interface\";\nimport type { Metric } from \"../data/tokenusage-admin.types\";\nimport { useUsageFormatters } from \"../lib/formatters\";\nimport { cacheHitPercentage, metricValue, percentageDelta, type TokenUsageMetrics } from \"../lib/metrics\";\n\ntype Props = {\n  /** The six summary rows: {customer, platform, total} × {current, previous}. */\n  summary: TokenUsageAdminSummaryInterface[];\n  metric: Metric;\n  /** True when a company filter is applied — platform spend is then meaningless. */\n  singleCustomerMode: boolean;\n};\n\nconst ZERO: TokenUsageMetrics = { cost: 0, credits: 0, tokensIn: 0, tokensOut: 0, cached: 0, calls: 0 };\n\n/**\n * The KPI header of the administrative token-usage page.\n *\n * Two lead tiles carry the cost centres — customer spend and platform spend —\n * each with its delta against the equal-length preceding window. Three\n * supporting tiles below carry the totals that give those two numbers context.\n *\n * The backend always returns both windows, which is why no tile has to branch on\n * a missing row: an absent scope is simply zero-filled here.\n */\nexport function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }: Props) {\n  const t = useTranslations();\n  const { decimal, metricValue: formatValue, currency, percent } = useUsageFormatters();\n\n  const rowFor = (scope: string, window: string): TokenUsageMetrics =>\n    summary.find((r) => r.scope === scope && r.window === window) ?? ZERO;\n\n  const customerCurrent = rowFor(\"customer\", \"current\");\n  const customerPrevious = rowFor(\"customer\", \"previous\");\n  const platformCurrent = rowFor(\"platform\", \"current\");\n  const platformPrevious = rowFor(\"platform\", \"previous\");\n  const totalCurrent = rowFor(\"total\", \"current\");\n  const totalPrevious = rowFor(\"total\", \"previous\");\n\n  const averagePerCall = totalCurrent.calls ? totalCurrent.cost / totalCurrent.calls : 0;\n  const cacheHit = cacheHitPercentage(totalCurrent.cached, totalCurrent.tokensIn);\n\n  return (\n    <div className=\"grid gap-3\">\n      <div className={cn(\"grid gap-3\", singleCustomerMode ? \"sm:grid-cols-1\" : \"sm:grid-cols-2\")}>\n        <LeadTile\n          testId=\"tile-customer\"\n          label={t(\"token_usage.admin.customer_spend\")}\n          value={formatValue(metricValue(customerCurrent, metric), metric)}\n          delta={percentageDelta(metricValue(customerCurrent, metric), metricValue(customerPrevious, metric))}\n          previousLabel={t(\"token_usage.admin.vs_previous\")}\n          decimal={decimal}\n        />\n        {!singleCustomerMode && (\n          <LeadTile\n            testId=\"tile-platform\"\n            label={t(\"token_usage.admin.platform_spend\")}\n            value={formatValue(metricValue(platformCurrent, metric), metric)}\n            delta={percentageDelta(metricValue(platformCurrent, metric), metricValue(platformPrevious, metric))}\n            previousLabel={t(\"token_usage.admin.vs_previous\")}\n            decimal={decimal}\n          />\n        )}\n      </div>\n\n      <div className=\"grid gap-3 sm:grid-cols-3\">\n        <SupportingTile\n          testId=\"tile-total\"\n          label={t(\"token_usage.admin.total_cost\")}\n          value={formatValue(metricValue(totalCurrent, metric), metric)}\n          delta={percentageDelta(metricValue(totalCurrent, metric), metricValue(totalPrevious, metric))}\n          decimal={decimal}\n        />\n        <SupportingTile\n          testId=\"tile-avg-per-call\"\n          label={t(\"token_usage.admin.avg_per_call\")}\n          value={currency(averagePerCall, 4)}\n          decimal={decimal}\n        />\n        <SupportingTile\n          testId=\"tile-cache-hit\"\n          label={t(\"token_usage.admin.cache_hit\")}\n          value={percent(cacheHit)}\n          decimal={decimal}\n        />\n      </div>\n    </div>\n  );\n}\n\nfunction LeadTile({\n  testId,\n  label,\n  value,\n  delta,\n  previousLabel,\n  decimal,\n}: {\n  testId: string;\n  label: string;\n  value: string;\n  delta: number | undefined;\n  previousLabel: string;\n  /** Passed down because `Delta` is a plain function, not a component: it cannot call the hook itself. */\n  decimal: (value: number, decimals: number) => string;\n}) {\n  return (\n    <Card data-testid={testId}>\n      <CardContent className=\"grid gap-1\">\n        <span className=\"text-muted-foreground text-xs\">{label}</span>\n        <span className=\"text-primary text-xl font-semibold tabular-nums\">{value}</span>\n        <span className=\"flex items-center gap-1\">\n          <Delta testId={`${testId}-delta`} delta={delta} decimal={decimal} />\n          <span className=\"text-muted-foreground text-xs\">{previousLabel}</span>\n        </span>\n      </CardContent>\n    </Card>\n  );\n}\n\nfunction SupportingTile({\n  testId,\n  label,\n  value,\n  delta,\n  decimal,\n}: {\n  testId: string;\n  label: string;\n  value: string;\n  delta?: number | undefined;\n  /** Passed down because `Delta` is a plain function, not a component: it cannot call the hook itself. */\n  decimal: (value: number, decimals: number) => string;\n}) {\n  return (\n    <Card data-testid={testId} size=\"sm\">\n      <CardContent className=\"grid gap-1\">\n        <span className=\"text-muted-foreground text-xs\">{label}</span>\n        <span className=\"flex items-center gap-2\">\n          <span className=\"text-sm font-medium tabular-nums\">{value}</span>\n          {delta !== undefined && <Delta testId={`${testId}-delta`} delta={delta} decimal={decimal} />}\n        </span>\n      </CardContent>\n    </Card>\n  );\n}\n\n/**\n * The delta slot. An undefined delta means the previous window was zero: an\n * em dash says \"not comparable\" where a percentage would say \"infinite growth\".\n */\nfunction Delta({\n  testId,\n  delta,\n  decimal,\n}: {\n  testId: string;\n  delta: number | undefined;\n  decimal: (value: number, decimals: number) => string;\n}) {\n  if (delta === undefined) {\n    return (\n      <span data-testid={testId} className=\"text-muted-foreground text-xs\">\n        —\n      </span>\n    );\n  }\n\n  const increased = delta >= 0;\n  const Icon = increased ? ArrowUpIcon : ArrowDownIcon;\n\n  return (\n    <span\n      data-testid={testId}\n      className={cn(\n        \"inline-flex items-center gap-0.5 text-xs tabular-nums\",\n        increased ? \"text-success\" : \"text-destructive\",\n      )}\n    >\n      <Icon aria-hidden className=\"size-3\" />\n      {decimal(Math.abs(delta), 0)} %\n    </span>\n  );\n}\n","\"use client\";\n\nimport { useLocale } from \"next-intl\";\nimport { useMemo } from \"react\";\nimport type { Metric } from \"../data/tokenusage-admin.types\";\nimport { getTokenUsageCurrency } from \"./config\";\n\nexport type UsageFormatters = {\n  /** Locale-aware fixed-decimal number, e.g. 9.46 → \"9,46\" in it-IT. */\n  decimal(value: number, decimals: number): string;\n  /** Currency for cost, 2 decimals for credits, whole numbers for tokens. */\n  metricValue(value: number, metric: Metric): string;\n  /** Currency with an explicit decimal count — the per-call tile needs 4. */\n  currency(value: number, decimals: number): string;\n  /** Percentage with one decimal by default, e.g. 60 → \"60,0 %\" in it-IT. */\n  percent(value: number, decimals?: number): string;\n  /** Compact notation for axis ticks, e.g. 1500000 → \"1,5 Mln\" in it-IT. */\n  compact(value: number): string;\n  /** A \"YYYY-MM-DD\" bucket key rendered as an axis or tooltip label, in UTC. */\n  bucketDate(iso: string, granularity: \"day\" | \"week\" | \"month\"): string;\n  /** Locale collation for client-side table sorting. */\n  compare(a: string, b: string): number;\n};\n\n/**\n * Builds every formatter the token-usage surfaces need, for one locale and one\n * currency.\n *\n * A pure factory on purpose: it takes no React context, so the arithmetic and\n * the formatting are unit-testable without rendering, and a server component or\n * a test can construct a set for an arbitrary locale.\n *\n * The currency symbol is placed with a space rather than through\n * `style: \"currency\"`, which is what the previous hard-coded implementation\n * rendered (\"€ 9,46\"). Keeping that shape means a consuming app on EUR sees\n * byte-identical output after this refactor.\n *\n * `bucketDate` formats in UTC, always. The wire value is a `type: \"date\"` — a\n * calendar day — parsed to UTC midnight; reading it back with local getters\n * would shift the label a day early west of UTC.\n */\nexport function createUsageFormatters(locale: string, currency: string): UsageFormatters {\n  const symbol =\n    new Intl.NumberFormat(locale, { style: \"currency\", currency, currencyDisplay: \"narrowSymbol\" })\n      .formatToParts(0)\n      .find((part) => part.type === \"currency\")?.value ?? currency;\n\n  const decimal = (value: number, decimals: number): string =>\n    value.toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals });\n\n  const dayFormat = new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\", timeZone: \"UTC\" });\n  const monthFormat = new Intl.DateTimeFormat(locale, { month: \"short\", year: \"numeric\", timeZone: \"UTC\" });\n  const compactFormat = new Intl.NumberFormat(locale, { notation: \"compact\", maximumFractionDigits: 1 });\n  const collator = new Intl.Collator(locale);\n\n  return {\n    decimal,\n\n    metricValue(value, metric) {\n      if (metric === \"cost\") return `${symbol} ${decimal(value, 2)}`;\n      // Credits are stored to 4 decimals (round4(cost / creditCost)), but that\n      // precision is noise to a reader comparing rows: one decimal is enough to\n      // separate two values and keeps the columns narrow enough to scan.\n      if (metric === \"credits\") return decimal(value, 1);\n      return decimal(value, 0);\n    },\n\n    currency(value, decimals) {\n      return `${symbol} ${decimal(value, decimals)}`;\n    },\n\n    percent(value, decimals = 1) {\n      return `${decimal(value, decimals)} %`;\n    },\n\n    compact(value) {\n      return compactFormat.format(value);\n    },\n\n    bucketDate(iso, granularity) {\n      const date = new Date(`${iso}T00:00:00.000Z`);\n      return granularity === \"month\" ? monthFormat.format(date) : dayFormat.format(date);\n    },\n\n    compare(a, b) {\n      return collator.compare(a, b);\n    },\n  };\n}\n\n/**\n * The formatter set for the current request's locale and the app's configured\n * currency. Memoised on both, so the Intl objects are built once per locale\n * rather than on every render.\n */\nexport function useUsageFormatters(): UsageFormatters {\n  const locale = useLocale();\n  const currency = getTokenUsageCurrency();\n  return useMemo(() => createUsageFormatters(locale, currency), [locale, currency]);\n}\n","import type { Metric } from \"../data/tokenusage-admin.types\";\n\n/**\n * Locale-free arithmetic for the token-usage surfaces.\n *\n * Everything that turns a number into a STRING lives in `./formatters` instead,\n * because it depends on the request's locale and the app's configured currency.\n * This file stays pure arithmetic so it needs neither.\n */\n\n/**\n * The metric field set every admin token-usage resource carries. Declared\n * structurally so it accepts the summary, timeline and breakdown interfaces\n * alike — all three expose exactly these getters.\n */\nexport type TokenUsageMetrics = {\n  cost: number;\n  credits: number;\n  tokensIn: number;\n  tokensOut: number;\n  cached: number;\n  calls: number;\n};\n\n/** Reads the single number a row contributes for the currently selected metric. */\nexport function metricValue(row: TokenUsageMetrics, metric: Metric): number {\n  if (metric === \"cost\") return row.cost;\n  if (metric === \"credits\") return row.credits;\n  return row.tokensIn + row.tokensOut;\n}\n\n/**\n * Share of cached input tokens. Returns 0 rather than NaN when the window holds\n * no input tokens at all, so the tile renders \"0,0 %\" instead of \"NaN %\".\n */\nexport function cacheHitPercentage(cached: number, tokensIn: number): number {\n  if (!tokensIn) return 0;\n  return (cached / tokensIn) * 100;\n}\n\n/**\n * Percentage change of `current` against `previous`, rounded to a whole number.\n *\n * Returns `undefined` when the previous window is zero — there is no meaningful\n * percentage change from nothing, and rendering Infinity would be worse than\n * rendering nothing. Callers show an em dash in that slot.\n */\nexport function percentageDelta(current: number, previous: number): number | undefined {\n  if (!previous) return undefined;\n  return Math.round(((current - previous) / previous) * 100);\n}\n","\"use client\";\n\nimport { ArrowDownIcon, ArrowUpIcon } from \"lucide-react\";\nimport { useTranslations } from \"next-intl\";\nimport { useMemo, useState } from \"react\";\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from \"../../../shadcnui\";\nimport { cn } from \"../../../utils\";\nimport type { TokenUsageAdminBreakdownInterface } from \"../data/tokenusage-admin-breakdown.interface\";\nimport type { Metric } from \"../data/tokenusage-admin.types\";\nimport { useUsageFormatters } from \"../lib/formatters\";\nimport { cacheHitPercentage, metricValue } from \"../lib/metrics\";\n\ntype Props = {\n  rows: TokenUsageAdminBreakdownInterface[];\n  metric: Metric;\n};\n\ntype SortKey =\n  \"label\" | \"sublabel\" | \"activeUsers\" | \"calls\" | \"tokensIn\" | \"tokensOut\" | \"cacheHit\" | \"cost\" | \"credits\" | \"share\";\n\ntype SortState = { key: SortKey; direction: \"asc\" | \"desc\" };\n\n/**\n * The numeric detail behind the ranked bars: every row, every metric column.\n *\n * The bars answer \"who is biggest\"; this answers \"why\". Sorting is client-side\n * because the whole ranked set — top-N plus the \"other\" rollup — is already in\n * memory, so a round trip would buy nothing.\n */\nexport function TokenUsageBreakdownTable({ rows, metric }: Props) {\n  const t = useTranslations();\n  const { decimal, metricValue: formatValue, percent, compare } = useUsageFormatters();\n  const [sort, setSort] = useState<SortState | undefined>(undefined);\n\n  // The company dimension is the only one that reports active users. Rather than\n  // render an empty column on user/operation breakdowns, the column disappears.\n  const showActiveUsers = rows.some((row) => row.activeUsers !== undefined);\n\n  const total = useMemo(() => rows.reduce((sum, row) => sum + metricValue(row, metric), 0), [rows, metric]);\n\n  const sortValue = (row: TokenUsageAdminBreakdownInterface, key: SortKey): number | string => {\n    switch (key) {\n      case \"label\":\n        return row.label ?? \"\";\n      case \"sublabel\":\n        return row.sublabel ?? \"\";\n      case \"activeUsers\":\n        return row.activeUsers ?? 0;\n      case \"cacheHit\":\n        return cacheHitPercentage(row.cached, row.tokensIn);\n      case \"share\":\n        return metricValue(row, metric);\n      default:\n        return row[key];\n    }\n  };\n\n  const sortedRows = useMemo(() => {\n    if (!sort) return rows;\n\n    const factor = sort.direction === \"desc\" ? -1 : 1;\n    return [...rows].sort((a, b) => {\n      const left = sortValue(a, sort.key);\n      const right = sortValue(b, sort.key);\n      if (typeof left === \"string\" || typeof right === \"string\") {\n        return compare(String(left), String(right)) * factor;\n      }\n      return (left - right) * factor;\n    });\n  }, [rows, sort, metric, compare]);\n\n  // First click on a column sorts descending: on a spend table the interesting\n  // end of every numeric column is the top one.\n  const toggleSort = (key: SortKey) =>\n    setSort((current) =>\n      current?.key === key\n        ? { key, direction: current.direction === \"desc\" ? \"asc\" : \"desc\" }\n        : { key, direction: \"desc\" },\n    );\n\n  const columns: { key: SortKey; label: string; numeric: boolean }[] = [\n    { key: \"label\", label: t(\"token_usage.admin.columns.label\"), numeric: false },\n    { key: \"sublabel\", label: t(\"token_usage.admin.columns.sublabel\"), numeric: false },\n    ...(showActiveUsers\n      ? [{ key: \"activeUsers\" as const, label: t(\"token_usage.admin.columns.active_users\"), numeric: true }]\n      : []),\n    { key: \"calls\", label: t(\"token_usage.admin.columns.calls\"), numeric: true },\n    { key: \"tokensIn\", label: t(\"token_usage.admin.columns.tokens_in\"), numeric: true },\n    { key: \"tokensOut\", label: t(\"token_usage.admin.columns.tokens_out\"), numeric: true },\n    // Reuses the tile's key rather than minting a column-specific one — same\n    // metric, same words.\n    { key: \"cacheHit\", label: t(\"token_usage.admin.cache_hit\"), numeric: true },\n    { key: \"cost\", label: t(\"token_usage.admin.columns.cost\"), numeric: true },\n    { key: \"credits\", label: t(\"token_usage.admin.columns.credits\"), numeric: true },\n    { key: \"share\", label: t(\"token_usage.admin.columns.share\"), numeric: true },\n  ];\n\n  return (\n    // The package's <Table> wraps itself in an `overflow-x-clip` container, which\n    // would swallow the overflow before this scroller ever saw it — hence the\n    // child override. Wide metric tables must scroll, never widen the page.\n    <div className=\"overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible\">\n      <Table>\n        <TableHeader>\n          <TableRow>\n            {columns.map((column) => (\n              <TableHead key={column.key} className={cn(column.numeric && \"text-right\")}>\n                <button\n                  type=\"button\"\n                  onClick={() => toggleSort(column.key)}\n                  className={cn(\n                    \"hover:text-primary inline-flex items-center gap-1 text-xs font-medium\",\n                    column.numeric && \"flex-row-reverse\",\n                  )}\n                >\n                  {column.label}\n                  {sort?.key === column.key &&\n                    (sort.direction === \"desc\" ? (\n                      <ArrowDownIcon aria-hidden className=\"size-3\" />\n                    ) : (\n                      <ArrowUpIcon aria-hidden className=\"size-3\" />\n                    ))}\n                </button>\n              </TableHead>\n            ))}\n          </TableRow>\n        </TableHeader>\n        <TableBody>\n          {sortedRows.map((row) => {\n            const value = metricValue(row, metric);\n            const share = total > 0 ? (value / total) * 100 : 0;\n\n            return (\n              <TableRow key={row.id} data-testid={`breakdown-row-${row.id}`}>\n                <TableCell className=\"text-xs\">\n                  {row.id === \"other\" ? t(\"token_usage.admin.other\") : row.label}\n                </TableCell>\n                <TableCell className=\"text-muted-foreground text-xs\">{row.sublabel ?? \"\"}</TableCell>\n                {showActiveUsers && (\n                  <TableCell className=\"text-right text-xs tabular-nums\">\n                    {row.activeUsers === undefined ? \"\" : decimal(row.activeUsers, 0)}\n                  </TableCell>\n                )}\n                <TableCell className=\"text-right text-xs tabular-nums\">{decimal(row.calls, 0)}</TableCell>\n                <TableCell className=\"text-right text-xs tabular-nums\">{decimal(row.tokensIn, 0)}</TableCell>\n                <TableCell className=\"text-right text-xs tabular-nums\">{decimal(row.tokensOut, 0)}</TableCell>\n                <TableCell className=\"text-right text-xs tabular-nums\">\n                  {percent(cacheHitPercentage(row.cached, row.tokensIn))}\n                </TableCell>\n                <TableCell className=\"text-right text-xs tabular-nums\">{formatValue(row.cost, \"cost\")}</TableCell>\n                <TableCell className=\"text-right text-xs tabular-nums\">{formatValue(row.credits, \"credits\")}</TableCell>\n                <TableCell className=\"text-right text-xs tabular-nums\">{percent(share)}</TableCell>\n              </TableRow>\n            );\n          })}\n        </TableBody>\n      </Table>\n    </div>\n  );\n}\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport type { Metric } from \"../data/tokenusage-admin.types\";\nimport { useUsageFormatters } from \"../lib/formatters\";\nimport { metricValue, type TokenUsageMetrics } from \"../lib/metrics\";\nimport { operationLabel } from \"../lib/operation-label\";\nimport { SEQUENTIAL_RAMP } from \"../lib/palette\";\n\n/**\n * Everything this component actually reads from a row.\n *\n * Declared STRUCTURALLY rather than as one of the breakdown interfaces on\n * purpose: the same list ranks administrative rows (which additionally carry\n * activeUsers / monthlyCredits / availableMonthlyCredits) and self-service\n * report rows (which do not). Narrowing this to the administrative interface\n * would force every self-service caller into a cast, and duplicating the\n * component would fork the ramp and the share arithmetic.\n */\nexport type TokenUsageRankedRow = TokenUsageMetrics & {\n  /** Row identity; the literal \"other\" marks the folded tail. */\n  id: string;\n  label: string;\n};\n\ntype Props = {\n  /** Ranked rows, already ordered descending by the backend, \"other\" last. */\n  rows: TokenUsageRankedRow[];\n  metric: Metric;\n  /** Copy shown when there is nothing to rank, already translated by the caller. */\n  emptyLabel: string;\n  /**\n   * Whether `row.label` is an operation TYPE (vocabulary the consuming app\n   * translates) rather than an entity NAME (data, rendered verbatim).\n   *\n   * Company and user rows carry names — translating them would be nonsense — so\n   * this defaults to false and only the platform-by-operation panel opts in.\n   */\n  labelsAreOperationTypes?: boolean;\n};\n\n/**\n * A ranked horizontal bar list — \"who spent the most\".\n *\n * This does a MAGNITUDE job, not a categorical one, so colour comes from a\n * single sequential ramp indexed by RANK POSITION, never from a categorical\n * palette keyed by entity. That is deliberate: a filter change reorders the\n * rows, and rank-indexed colour simply re-shades them instead of repainting a\n * company into another company's identity hue.\n *\n * There is exactly one series, so there is no legend: every bar is\n * direct-labelled with its value and its share of the total.\n */\nexport function TokenUsageRankedBar({ rows, metric, emptyLabel, labelsAreOperationTypes = false }: Props) {\n  const t = useTranslations();\n  const { metricValue: formatValue, percent } = useUsageFormatters();\n\n  const renderLabel = (row: TokenUsageRankedRow): string => {\n    if (row.id === \"other\") return t(\"token_usage.admin.other\");\n    if (!labelsAreOperationTypes) return row.label;\n    return operationLabel(\n      row.label,\n      (key) => t(key),\n      (key) => t.has(key),\n    );\n  };\n\n  if (rows.length === 0) {\n    return <p className=\"text-muted-foreground py-6 text-center text-sm\">{emptyLabel}</p>;\n  }\n\n  const values = rows.map((row) => metricValue(row, metric));\n  const total = values.reduce((sum, value) => sum + value, 0);\n  const max = Math.max(...values, 0);\n\n  return (\n    <div className=\"grid gap-0.5\">\n      {rows.map((row, index) => {\n        const value = values[index];\n        // The backend folds everything past the limit into one \"other\" row; it\n        // gets the most recessive ramp step so it never out-shouts a real entity.\n        const isOther = row.id === \"other\";\n        const step = isOther ? SEQUENTIAL_RAMP.length - 1 : Math.min(index, SEQUENTIAL_RAMP.length - 1);\n        const width = max > 0 ? (value / max) * 100 : 0;\n        // Shares are taken against the supplied rows, which already include\n        // \"other\" — so the column sums to 100 %.\n        const share = total > 0 ? (value / total) * 100 : 0;\n\n        return (\n          <div key={row.id} className=\"grid grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-x-3 gap-y-0.5 py-0.5\">\n            <div className=\"grid min-w-0 gap-0.5\">\n              <span className=\"text-muted-foreground truncate text-xs\">{renderLabel(row)}</span>\n              <div className=\"bg-muted h-2 w-full overflow-hidden rounded-r-[4px]\">\n                <div\n                  data-testid={`ranked-fill-${row.id}`}\n                  data-ramp-step={String(step)}\n                  className=\"h-full rounded-r-[4px]\"\n                  style={{ width: `${width}%`, backgroundColor: SEQUENTIAL_RAMP[step] }}\n                />\n              </div>\n            </div>\n            <span data-testid={`ranked-value-${row.id}`} className=\"text-right text-xs tabular-nums\">\n              {formatValue(value, metric)}\n            </span>\n            <span\n              data-testid={`ranked-share-${row.id}`}\n              className=\"text-muted-foreground w-16 text-right text-xs tabular-nums\"\n            >\n              {percent(share)}\n            </span>\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n","/**\n * The i18n namespace under which a consuming app supplies its own vocabulary for\n * token-usage operation types (`summariser`, `massima_extraction`, …).\n *\n * The set of operations is application-specific — the package cannot know that\n * a360ai calls `massima_extraction` \"Estrazione Massime\" — so the package looks\n * the copy up here and falls back to the raw key when the app has no entry.\n */\nexport const OPERATION_LABEL_NAMESPACE = \"token_usage.types\";\n\n/**\n * `snake_case` / `kebab-case` → `camelCase`, the shape the i18n keys use.\n *\n * Shared by every surface that renders an operation type — the timeline chart's\n * series labels and the ranked bar's operation rows — so the two can never\n * disagree about which key a given type maps to.\n */\nexport const toCamelCase = (value: string): string => {\n  const parts = value.split(/[^a-zA-Z0-9]+/).filter(Boolean);\n  if (parts.length === 0) return value;\n  return parts\n    .map((part, index) =>\n      index === 0 ? part.charAt(0).toLowerCase() + part.slice(1) : part.charAt(0).toUpperCase() + part.slice(1),\n    )\n    .join(\"\");\n};\n\n/**\n * Resolves an operation type to its Italian label, falling back to the raw key.\n *\n * `has` is passed rather than the whole translator so this stays a pure function\n * and both callers can share it regardless of how they obtained `t`.\n */\nexport const operationLabel = (\n  type: string,\n  translate: (key: string) => string,\n  has: (key: string) => boolean,\n): string => {\n  const key = `${OPERATION_LABEL_NAMESPACE}.${toCamelCase(type)}`;\n  return has(key) ? translate(key) : type;\n};\n","/**\n * Chart palette for the administrative token-usage dashboard.\n *\n * Every value here is a documented step of the dataviz skill's reference ramps —\n * nothing was eyeballed, nothing was hand-mixed. The palette was run through the\n * skill's validator against THIS application's real chart surfaces in both modes.\n * The verbatim output is below.\n *\n * The surface is `--card`, NOT `--background`. Every chart that uses this palette\n * is painted inside a `<Card>`, so the card is the surface the marks actually sit\n * on — and in dark mode it is the *lighter* of the two (`oklch(0.205 0 0)` vs the\n * background's `oklch(0.145 0 0)`), which is the harder case for contrast. Taken\n * from `apps/web/src/app/globals.css` (narr8 declares two `:root`/`.dark` pairs;\n * the LATER pair wins, and it is the one read here) — light `oklch(1 0 0)`, dark\n * `oklch(0.205 0 0)`. a360ai declares the same two values.\n *\n * oklch→hex conversion: the algebraic inverse of the Björn Ottosson OKLab\n * matrices — the same matrices the validator applies in the forward direction —\n * giving light `#ffffff` and dark `#171717`. Cross-checked by feeding it the\n * previously recorded background value, which it reproduces exactly:\n * `oklch(0.145 0 0)` → `#0a0a0a`. ✓\n *\n * Light and dark are two SELECTED sets of steps, chosen for their own surface —\n * never one set with its lightness flipped at runtime. Consumers pick a set from\n * the resolved theme (`seriesColor(i, mode)` / `sequentialColor(i, mode)`).\n *\n * ---------------------------------------------------------------------------\n * VALIDATOR OUTPUT — dataviz `scripts/validate_palette.js`, run 2026-08-15\n * ---------------------------------------------------------------------------\n *\n * $ node scripts/validate_palette.js \"#2a78d6,#eb6834,#1baf7a,#eda100,#e87ba4,#008300,#4a3aa7\" --mode light --surface \"#ffffff\"\n *\n * Palette (light, surface #ffffff, categorical): 7 slots\n *   [PASS] Lightness band         all 7 inside L 0.43–0.77\n *   [PASS] Chroma floor           all 7 >= 0.1\n *   [PASS] CVD separation         worst adjacent #eda100↔#1baf7a ΔE 9.1 (protan) · tritan 5.8\n *   [PASS] Normal-vision floor    worst adjacent #e87ba4↔#eda100 ΔE 19.6 (normal)\n *   [WARN] Contrast vs surface    below 3:1 — relief required (visible labels or table view): [[\"#1baf7a\",2.82],[\"#eda100\",2.17],[\"#e87ba4\",2.69]]\n *\n *   → ALL CHECKS PASS  (CVD in the 6–8 floor band is legal ONLY with secondary encoding: direct labels, gaps, or texture)\n *   scope: categorical palettes only. For a lone status/text color check WCAG text contrast; for a sequential ramp, lightness monotonicity.\n *\n * $ node scripts/validate_palette.js \"#3987e5,#d95926,#199e70,#c98500,#d55181,#008300,#9085e9\" --mode dark --surface \"#171717\"\n *\n * Palette (dark, surface #171717, categorical): 7 slots\n *   [PASS] Lightness band         all 7 inside L 0.48–0.67\n *   [PASS] Chroma floor           all 7 >= 0.1\n *   [PASS] CVD separation         worst adjacent #c98500↔#199e70 ΔE 8.4 (protan) · tritan 8.7\n *   [PASS] Normal-vision floor    worst adjacent #d55181↔#c98500 ΔE 19.3 (normal)\n *   [PASS] Contrast vs surface    all 7 >= 3:1\n *\n *   → ALL CHECKS PASS  (CVD in the 6–8 floor band is legal ONLY with secondary encoding: direct labels, gaps, or texture)\n *   scope: categorical palettes only. For a lone status/text color check WCAG text contrast; for a sequential ramp, lightness monotonicity.\n *\n * $ node scripts/validate_palette.js \"#104281,#1c5cab,#2a78d6,#5598e7,#86b6ef\" --mode light --surface \"#ffffff\" --ordinal\n *\n * Palette (light, surface #ffffff, ordinal ramp): 5 slots\n *   [PASS] Lightness monotone     steps read light→dark\n *   [PASS] Adjacent ΔL            all gaps >= 0.06\n *   [PASS] Light-end contrast     #86b6ef at 2.11:1 vs surface\n *   [PASS] Single hue             hue spread 3°\n *\n *   → ALL CHECKS PASS  (ordinal: one hue, monotone L, visible step gaps, light end clears surface)\n *\n * $ node scripts/validate_palette.js \"#9ec5f4,#6da7ec,#3987e5,#256abf,#184f95\" --mode dark --surface \"#171717\" --ordinal\n *\n * Palette (dark, surface #171717, ordinal ramp): 5 slots\n *   [PASS] Lightness monotone     steps read light→dark\n *   [PASS] Adjacent ΔL            all gaps >= 0.06\n *   [PASS] Light-end contrast     #184f95 at 2.21:1 vs surface\n *   [PASS] Single hue             hue spread 3°\n *\n *   → ALL CHECKS PASS  (ordinal: one hue, monotone L, visible step gaps, light end clears surface)\n *\n * OTHER_COLOR is achromatic, so the categorical checks (which gate hue identity)\n * do not apply to it; it was gated on contrast alone with the validator's own\n * `contrast()` export: 3.59:1 on `#ffffff`, 4.99:1 on `#171717` — both clear 3:1.\n *\n * ---------------------------------------------------------------------------\n * WHAT THE RESULTS OBLIGE US TO DO\n * ---------------------------------------------------------------------------\n *\n * - NO COLOUR VALUE CHANGED in the move from `--background` to `--card`. Every\n *   gate that passed on the background still passes on the card, so re-stepping\n *   a hue would have been churn, not a fix. Only the recorded surface, the\n *   recorded numbers and this prose changed.\n * - The dark set still clears 3:1 on the LIGHTER card surface: the worst slot is\n *   green `#008300` at 3.63:1, and the ordinal ramp's surface-nearest step lands\n *   at 2.21:1 (down from 2.44:1 on the background) — still above the 2:1 gate.\n *   That headroom is thin, so a future card-surface lightening MUST re-run these\n *   four commands rather than assume the set still holds.\n * - The light-mode contrast WARN is NOT dismissable. Three slots (aqua, yellow,\n *   magenta) sit below 3:1 on white, so every surface that paints with this\n *   palette MUST ship the relief channel: a visible legend, direct labels and a\n *   tooltip carrying the value in text. The timeline chart and the breakdown\n *   table both do. The light card is `#ffffff`, identical to the light\n *   background, so this WARN is unchanged rather than newly incurred.\n * - CVD separation is measured on ADJACENT pairs, which is the correct pairlist\n *   for stacked bars, grouped bars and lines — the only forms this palette paints.\n *   A scatter / bubble / small-multiples chart would need `--pairs all`, which\n *   caps the usable slot count at three; do not reuse this array there without\n *   re-running the validator with that flag.\n */\n\n/** Which surface the colours are being painted on. */\nexport type ChartMode = \"light\" | \"dark\";\n\n/**\n * The chart surfaces the palette was validated against (globals.css `--card`).\n *\n * `--card`, never `--background`: the charts are painted inside a `<Card>`.\n */\nexport const CHART_SURFACE: Readonly<Record<ChartMode, string>> = {\n  light: \"#ffffff\",\n  dark: \"#171717\",\n};\n\n/**\n * Categorical slots, in FIXED order. Slot n is series n — the order is the\n * CVD-safety mechanism, not decoration, so it is never re-ordered and never\n * cycled. The two-series default (customer / platform) takes slots 0 and 1.\n *\n * Hues, in order: blue, orange, aqua, yellow, magenta, green, violet.\n */\nconst CATEGORICAL_LIGHT: readonly string[] = [\n  \"#2a78d6\",\n  \"#eb6834\",\n  \"#1baf7a\",\n  \"#eda100\",\n  \"#e87ba4\",\n  \"#008300\",\n  \"#4a3aa7\",\n];\n\n/** The same seven hues, re-stepped for the dark surface. Not a lightness flip. */\nconst CATEGORICAL_DARK: readonly string[] = [\n  \"#3987e5\",\n  \"#d95926\",\n  \"#199e70\",\n  \"#c98500\",\n  \"#d55181\",\n  \"#008300\",\n  \"#9085e9\",\n];\n\n/**\n * The default (light-surface) categorical set.\n *\n * Prefer `seriesColor(index, mode)` — this array exists for consumers that only\n * need the documented default order.\n */\nexport const CATEGORICAL: readonly string[] = CATEGORICAL_LIGHT;\n\n/**\n * How many series may carry their own identity hue. An 8th series is NEVER a\n * generated hue: it folds into `other`.\n */\nexport const CATEGORICAL_CEILING = CATEGORICAL_LIGHT.length;\n\n/**\n * The \"other\" rollup colour: deliberately achromatic so it reads as \"not an\n * identity\" beside the seven hues. Same step in both modes — it clears 3:1 on\n * both card surfaces (3.59:1 on `#ffffff`, 4.99:1 on `#171717`).\n */\nexport const OTHER_COLOR = \"#898781\";\n\n/**\n * Sequential (single-hue, blue) ramp for MAGNITUDE, indexed by rank position:\n * index 0 is rank 1 — the largest value — so the strongest step leads.\n *\n * On the light surface \"strongest\" is the darkest step; on the dark surface it\n * is the lightest. The anchor flips with the surface, which is why there are two\n * selected sets rather than one array reversed at runtime.\n *\n * Five steps, not eight: with the adjacent-ΔL ≥ 0.06 gate and the ≥ 2:1\n * surface-contrast gate on the step nearest the surface, the blue ramp fits\n * exactly five distinguishable steps. Consumers clamp past the end.\n */\nconst SEQUENTIAL_RAMP_LIGHT: readonly string[] = [\"#104281\", \"#1c5cab\", \"#2a78d6\", \"#5598e7\", \"#86b6ef\"];\n\nconst SEQUENTIAL_RAMP_DARK: readonly string[] = [\"#9ec5f4\", \"#6da7ec\", \"#3987e5\", \"#256abf\", \"#184f95\"];\n\n/**\n * The default (light-surface) sequential ramp.\n *\n * Prefer `sequentialColor(index, mode)` — this array exists for consumers that\n * only need the documented default.\n */\nexport const SEQUENTIAL_RAMP: readonly string[] = SEQUENTIAL_RAMP_LIGHT;\n\nconst categoricalSet = (mode: ChartMode): readonly string[] => (mode === \"dark\" ? CATEGORICAL_DARK : CATEGORICAL_LIGHT);\n\nconst sequentialSet = (mode: ChartMode): readonly string[] =>\n  mode === \"dark\" ? SEQUENTIAL_RAMP_DARK : SEQUENTIAL_RAMP_LIGHT;\n\n/**\n * The identity colour for series `index`.\n *\n * Never generates a hue and never cycles: anything at or past the ceiling gets\n * `OTHER_COLOR`, because a series past the ceiling should already have been\n * folded into the \"other\" bucket by the caller.\n */\nexport function seriesColor(index: number, mode: ChartMode = \"light\"): string {\n  if (index < 0 || index >= CATEGORICAL_CEILING) return OTHER_COLOR;\n  return categoricalSet(mode)[index];\n}\n\n/**\n * Both mode steps for series `index`, shaped for a shadcn `ChartConfig` entry's\n * `theme` field.\n */\nexport function seriesTheme(index: number): { light: string; dark: string } {\n  return { light: seriesColor(index, \"light\"), dark: seriesColor(index, \"dark\") };\n}\n\n/**\n * The magnitude colour for rank position `index`, clamped to the last step so a\n * long list keeps its weakest step rather than falling off the ramp.\n */\nexport function sequentialColor(index: number, mode: ChartMode = \"light\"): string {\n  const ramp = sequentialSet(mode);\n  const clamped = Math.min(Math.max(index, 0), ramp.length - 1);\n  return ramp[clamped];\n}\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { useTheme } from \"next-themes\";\nimport { useMemo } from \"react\";\nimport { Bar, BarChart, CartesianGrid, XAxis, YAxis } from \"recharts\";\nimport {\n  ChartConfig,\n  ChartContainer,\n  ChartLegend,\n  ChartLegendContent,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"../../../shadcnui\";\nimport { TokenUsageAdminTimelineInterface } from \"../data/tokenusage-admin-timeline.interface\";\nimport { Metric, StackBy } from \"../data/tokenusage-admin.types\";\nimport { useUsageFormatters } from \"../lib/formatters\";\nimport { operationLabel } from \"../lib/operation-label\";\nimport { CATEGORICAL_CEILING, ChartMode, OTHER_COLOR, seriesColor } from \"../lib/palette\";\n\n/** The rollup series every series past the colour ceiling folds into. */\nconst OTHER_SERIES = \"other\";\n\ntype TokenUsageTimelineChartProps = {\n  rows: TokenUsageAdminTimelineInterface[];\n  metric: Metric;\n  stackBy: StackBy;\n  className?: string;\n};\n\ntype TimelineBucket = { bucket: string } & Record<string, number | string>;\n\n/**\n * The bucket key, derived with UTC getters.\n *\n * The backend field is `type: \"date\"` — a calendar day with no time — and the\n * model parses the `YYYY-MM-DD` wire value with `new Date(...)`, which lands on\n * UTC midnight. Reading it back with local getters would shift the bucket a day\n * early west of UTC, so the key (and every label built from it) stays in UTC.\n */\nconst bucketKey = (date: Date): string => {\n  const y = date.getUTCFullYear();\n  const m = `${date.getUTCMonth() + 1}`.padStart(2, \"0\");\n  const d = `${date.getUTCDate()}`.padStart(2, \"0\");\n  return `${y}-${m}-${d}`;\n};\n\nconst metricValue = (row: TokenUsageAdminTimelineInterface, metric: Metric): number => {\n  if (metric === \"cost\") return row.cost;\n  if (metric === \"credits\") return row.credits;\n  return row.tokensIn + row.tokensOut;\n};\n\n/**\n * The granularity the data was bucketed at, inferred from the spacing between\n * consecutive buckets. The backend chooses the granularity and the rows carry\n * the consequence, so the axis reads it off the data rather than taking a prop\n * that could disagree with what was actually fetched.\n */\nconst inferGranularity = (buckets: string[]): \"day\" | \"week\" | \"month\" => {\n  if (buckets.length < 2) return \"day\";\n\n  const dayMs = 24 * 60 * 60 * 1000;\n  let smallestGap = Number.POSITIVE_INFINITY;\n  for (let i = 1; i < buckets.length; i += 1) {\n    const gap = (Date.parse(buckets[i]) - Date.parse(buckets[i - 1])) / dayMs;\n    if (gap > 0 && gap < smallestGap) smallestGap = gap;\n  }\n\n  if (smallestGap >= 28) return \"month\";\n  if (smallestGap >= 7) return \"week\";\n  return \"day\";\n};\n\n/**\n * Usage over time as a stacked bar chart.\n *\n * Colour does an IDENTITY job here — each stacked segment is a series, not a\n * magnitude — so it draws the fixed categorical order from `../lib/palette` and\n * never generates or cycles a hue: series past the ceiling are summed into a\n * single \"other\" segment painted in the palette's neutral.\n *\n * The palette's light-mode validator run carries a sub-3:1 contrast WARN on\n * three slots, which obliges a relief channel. That is why the legend and the\n * value-carrying tooltip below are not optional decoration: they are what makes\n * the low-contrast fills readable.\n */\nexport function TokenUsageTimelineChart({ rows, metric, stackBy, className }: TokenUsageTimelineChartProps) {\n  const t = useTranslations();\n  const { compact, bucketDate, metricValue: formatValue } = useUsageFormatters();\n  const { resolvedTheme } = useTheme();\n  const mode: ChartMode = resolvedTheme === \"dark\" ? \"dark\" : \"light\";\n\n  const { chartData, seriesKeys } = useMemo(() => {\n    if (rows.length === 0) return { chartData: [] as TimelineBucket[], seriesKeys: [] as string[] };\n\n    // Totals per raw series decide who keeps an identity colour: the biggest\n    // seven do, everything else is summed into \"other\". Ranking by total (not by\n    // first appearance) keeps the assignment stable while a filter changes which\n    // series are present.\n    const totals = new Map<string, number>();\n    for (const row of rows) totals.set(row.series, (totals.get(row.series) ?? 0) + metricValue(row, metric));\n\n    const ranked = [...totals.entries()].sort((a, b) => b[1] - a[1]).map(([series]) => series);\n    const named = ranked.slice(0, CATEGORICAL_CEILING);\n    const folded = new Set(ranked.slice(CATEGORICAL_CEILING));\n\n    const keys = folded.size > 0 ? [...named, OTHER_SERIES] : named;\n\n    const byBucket = new Map<string, TimelineBucket>();\n    for (const row of rows) {\n      const key = bucketKey(row.bucket);\n      let entry = byBucket.get(key);\n      if (!entry) {\n        entry = { bucket: key };\n        for (const series of keys) entry[series] = 0;\n        byBucket.set(key, entry);\n      }\n      const series = folded.has(row.series) ? OTHER_SERIES : row.series;\n      entry[series] = (entry[series] as number) + metricValue(row, metric);\n    }\n\n    return {\n      chartData: [...byBucket.values()].sort((a, b) => a.bucket.localeCompare(b.bucket)),\n      seriesKeys: keys,\n    };\n  }, [rows, metric]);\n\n  const seriesLabel = (series: string): string => {\n    if (series === OTHER_SERIES) {\n      const key = \"token_usage.series.other\";\n      return t.has(key) ? t(key) : \"Other\";\n    }\n    // Stacking by company makes the series a company NAME, which is data, not\n    // vocabulary — it is never looked up. Scope and type keys are vocabulary the\n    // consuming app supplies, with the raw key as the fallback.\n    if (stackBy === \"company\") return series;\n    return operationLabel(\n      series,\n      (key) => t(key),\n      (key) => t.has(key),\n    );\n  };\n\n  const chartConfig = useMemo(\n    () => Object.fromEntries(seriesKeys.map((series) => [series, { label: seriesLabel(series) }])) as ChartConfig,\n    [seriesKeys, stackBy],\n  );\n\n  if (chartData.length === 0) {\n    const key = \"token_usage.timeline.empty\";\n    return (\n      <p className={className ? `text-muted-foreground text-sm ${className}` : \"text-muted-foreground text-sm\"}>\n        {t.has(key) ? t(key) : \"No data in the selected period\"}\n      </p>\n    );\n  }\n\n  const granularity = inferGranularity(chartData.map((entry) => entry.bucket));\n  const seriesColorFor = (series: string, index: number) =>\n    series === OTHER_SERIES ? OTHER_COLOR : seriesColor(index, mode);\n\n  return (\n    <div className={className}>\n      {/* The pivot, exposed for assertions and for screen readers that would\n          otherwise get nothing from the SVG. */}\n      <span className=\"sr-only\" data-testid=\"timeline-data\">\n        {JSON.stringify(chartData)}\n      </span>\n      <span className=\"sr-only\" data-testid=\"timeline-series\">\n        {JSON.stringify(seriesKeys)}\n      </span>\n\n      <ChartContainer config={chartConfig} className=\"aspect-auto h-72 w-full\">\n        <BarChart accessibilityLayer data={chartData} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>\n          <CartesianGrid vertical={false} />\n          <XAxis\n            dataKey=\"bucket\"\n            tickLine={false}\n            axisLine={false}\n            tickMargin={8}\n            minTickGap={16}\n            tickFormatter={(value: string) => bucketDate(value, granularity)}\n          />\n          <YAxis\n            tickLine={false}\n            axisLine={false}\n            tickMargin={8}\n            width={48}\n            tickFormatter={(value: number) => compact(value)}\n          />\n          <ChartTooltip\n            content={\n              <ChartTooltipContent\n                labelFormatter={(value) => bucketDate(String(value), granularity)}\n                // Without this the tooltip falls back to toLocaleString() and\n                // prints the raw stored precision (credits carry 4 decimals),\n                // which disagrees with every other number on the page.\n                valueFormatter={(value) => formatValue(Number(value), metric)}\n                // Every series appears in every bucket's payload, so a day with\n                // one active operation would otherwise list the whole vocabulary\n                // as zeros and bury the one number that matters.\n                hideZeroValues\n              />\n            }\n          />\n          {seriesKeys.length >= 2 ? <ChartLegend content={<ChartLegendContent />} /> : null}\n          {seriesKeys.map((series, index) => (\n            <Bar\n              key={series}\n              dataKey={series}\n              stackId=\"usage\"\n              maxBarSize={28}\n              fill={seriesColorFor(series, index)}\n              // A 2px stroke in the surface colour is the gap between stacked\n              // segments and between neighbouring bars — the palette's own\n              // secondary-encoding channel, not a border.\n              stroke=\"var(--background)\"\n              strokeWidth={2}\n              radius={index === seriesKeys.length - 1 ? [4, 4, 0, 0] : 0}\n            />\n          ))}\n        </BarChart>\n      </ChartContainer>\n    </div>\n  );\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { TokenUsageReportBreakdownInterface } from \"./tokenusage-report-breakdown.interface\";\nimport { TokenUsageReportSummaryInterface } from \"./tokenusage-report-summary.interface\";\nimport { TokenUsageReportTimelineInterface } from \"./tokenusage-report-timeline.interface\";\nimport { ReportDimension, ReportMetric, TokenUsageReportFilters } from \"./tokenusage-report.types\";\n\nfunction withFilters(endpoint: EndpointCreator, filters: TokenUsageReportFilters): EndpointCreator {\n  endpoint.addAdditionalParam(\"from\", filters.from);\n  endpoint.addAdditionalParam(\"to\", filters.to);\n  return endpoint;\n}\n\n/**\n * The self-service token-usage endpoints.\n *\n * No companyId parameter anywhere: the backend scopes every query to the\n * caller's own company through the CLS preamble, so there is nothing for the\n * client to name. No `metric: \"cost\"` either — the controller rejects it.\n */\nexport class TokenUsageReportService extends AbstractService {\n  /** Two rows: window \"current\" and \"previous\". */\n  static async getSummary(filters: TokenUsageReportFilters): Promise<TokenUsageReportSummaryInterface[]> {\n    const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageReportSummary }), filters);\n\n    return this.callApi<TokenUsageReportSummaryInterface[]>({\n      type: Modules.TokenUsageReportSummary,\n      method: HttpMethod.GET,\n      endpoint: endpoint.generate(),\n    });\n  }\n\n  static async getTimeline(\n    params: TokenUsageReportFilters & { granularity: \"day\" },\n  ): Promise<TokenUsageReportTimelineInterface[]> {\n    const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageReportTimeline }), params);\n    endpoint.addAdditionalParam(\"granularity\", params.granularity);\n\n    return this.callApi<TokenUsageReportTimelineInterface[]>({\n      type: Modules.TokenUsageReportTimeline,\n      method: HttpMethod.GET,\n      endpoint: endpoint.generate(),\n    });\n  }\n\n  static async getBreakdown(\n    params: TokenUsageReportFilters & {\n      dimension: ReportDimension;\n      targetLabel?: string;\n      metric: ReportMetric;\n      limit?: number;\n    },\n  ): Promise<TokenUsageReportBreakdownInterface[]> {\n    const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageReportBreakdown }), params);\n    endpoint.addAdditionalParam(\"dimension\", params.dimension);\n    if (params.targetLabel) endpoint.addAdditionalParam(\"targetLabel\", params.targetLabel);\n    endpoint.addAdditionalParam(\"metric\", params.metric);\n    if (params.limit !== undefined) endpoint.addAdditionalParam(\"limit\", String(params.limit));\n\n    return this.callApi<TokenUsageReportBreakdownInterface[]>({\n      type: Modules.TokenUsageReportBreakdown,\n      method: HttpMethod.GET,\n      endpoint: endpoint.generate(),\n    });\n  }\n}\n","/**\n * The fixed namespace of i18n keys the administrative token-usage feature reads\n * via useTranslations/getTranslations. Consuming apps must define each entry in\n * their messages/<locale>.json — this list is the contract between the package\n * and the app.\n *\n * One key is NOT listed because it is resolved from data rather than from a\n * fixed string: the timeline chart labels each series through\n * `token_usage.types.<camelCase>` and falls back to the raw series key when the\n * app has no entry, so that namespace stays the app's own vocabulary.\n */\nexport const TOKEN_USAGE_ADMIN_I18N_KEYS = [\n  // Page + KPI tiles\n  \"token_usage.admin.title\",\n  \"token_usage.admin.customer_spend\",\n  \"token_usage.admin.platform_spend\",\n  \"token_usage.admin.total_cost\",\n  \"token_usage.admin.avg_per_call\",\n  \"token_usage.admin.cache_hit\",\n  \"token_usage.admin.vs_previous\",\n\n  // Panel titles\n  \"token_usage.admin.usage_over_time\",\n  \"token_usage.admin.by_company\",\n  \"token_usage.admin.by_user\",\n  \"token_usage.admin.customer_by_operation\",\n  \"token_usage.admin.platform_by_operation\",\n  \"token_usage.admin.detail\",\n\n  // Shared states\n  \"token_usage.admin.other\",\n  \"token_usage.admin.no_data\",\n\n  // Filter bar\n  \"token_usage.admin.all_companies\",\n  \"token_usage.admin.granularity.label\",\n  \"token_usage.admin.granularity.day\",\n  \"token_usage.admin.granularity.week\",\n  \"token_usage.admin.granularity.month\",\n  \"token_usage.admin.metric.label\",\n  \"token_usage.admin.metric.cost\",\n  \"token_usage.admin.metric.credits\",\n  \"token_usage.admin.metric.tokens\",\n\n  // Timeline stacking control\n  \"token_usage.admin.stack_by\",\n  \"token_usage.admin.stack.scope\",\n  \"token_usage.admin.stack.type\",\n  \"token_usage.admin.stack.company\",\n\n  // Breakdown table columns\n  \"token_usage.admin.columns.label\",\n  \"token_usage.admin.columns.sublabel\",\n  \"token_usage.admin.columns.calls\",\n  \"token_usage.admin.columns.tokens_in\",\n  \"token_usage.admin.columns.tokens_out\",\n  \"token_usage.admin.columns.cost\",\n  \"token_usage.admin.columns.credits\",\n  \"token_usage.admin.columns.share\",\n  \"token_usage.admin.columns.active_users\",\n\n  // Timeline chart\n  \"token_usage.series.other\",\n  \"token_usage.timeline.empty\",\n] as const;\n\n/**\n * The fixed namespace of i18n keys the self-service token-usage feature reads.\n * Consuming apps must define each entry in their messages/<locale>.json — this\n * list is the contract between the package and the app.\n *\n * The target panel's title is NOT listed: its key is supplied per app through\n * the provider's `targetPanelTitleKey`, because what usage is attributed to is\n * application-specific. Operation labels resolve through\n * `token_usage.types.<camelCase>` with the raw key as fallback, so that\n * namespace stays the app's own vocabulary.\n */\nexport const TOKEN_USAGE_REPORT_I18N_KEYS = [\n  // Page + KPI tiles\n  \"token_usage.report.title\",\n  \"token_usage.report.used_in_period\",\n  \"token_usage.report.vs_previous\",\n  \"token_usage.report.monthly_left\",\n  \"token_usage.report.extra_credits\",\n  \"token_usage.report.calls\",\n\n  // Panel titles\n  \"token_usage.report.usage_over_time\",\n  \"token_usage.report.by_operation\",\n\n  // Shared states\n  \"token_usage.report.no_data\",\n] as const;\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport { SharedProvider } from \"../../../contexts\";\nimport { usePageUrlGenerator } from \"../../../hooks\";\nimport { BreadcrumbItemData } from \"../../../interfaces\";\nimport { TokenUsageReportFilterBar } from \"../components/TokenUsageReportFilterBar\";\nimport type { TokenUsageReportBreakdownInterface } from \"../data/tokenusage-report-breakdown.interface\";\nimport type { TokenUsageReportSummaryInterface } from \"../data/tokenusage-report-summary.interface\";\nimport type { TokenUsageReportTimelineInterface } from \"../data/tokenusage-report-timeline.interface\";\nimport type { ReportMetric } from \"../data/tokenusage-report.types\";\nimport { TokenUsageReportService } from \"../data/TokenUsageReportService\";\n\n/** Default page the breadcrumb links back to; overridable per host app. */\nconst TOKEN_USAGE_REPORT_PAGE_URL = \"/tokenusage\";\n\n/** Rows kept per ranked panel before the backend folds the tail into \"other\". */\nconst DEFAULT_TOP_N = 10;\n\n/**\n * The unit this surface reports in, always. A game master is billed in credits,\n * so credits are the only number that means anything to them: cost would leak\n * platform margin and a raw token count is an implementation detail nobody is\n * charged for. Pinned rather than exposed as a filter — there is no metric\n * selector on the page.\n */\nconst REPORT_METRIC: ReportMetric = \"credits\";\n\nexport type TokenUsageReportFilterState = {\n  /** ISO 8601 instant. */\n  from: string;\n  /** ISO 8601 instant. */\n  to: string;\n};\n\nexport interface TokenUsageReportContextType {\n  summary: TokenUsageReportSummaryInterface[];\n  timeline: TokenUsageReportTimelineInterface[];\n  byOperation: TokenUsageReportBreakdownInterface[];\n  /** Empty unless the host app declared a targetLabel. */\n  byTarget: TokenUsageReportBreakdownInterface[];\n  /** i18n key for the target panel's title; undefined when the host set no targetLabel. */\n  targetPanelTitleKey?: string;\n  filters: TokenUsageReportFilterState;\n  /** Merges a partial patch into the current filters; every key is optional. */\n  setFilters: (next: Partial<TokenUsageReportFilterState>) => void;\n  isLoading: boolean;\n  error: string | null;\n}\n\nconst TokenUsageReportContext = createContext<TokenUsageReportContextType | undefined>(undefined);\n\n/** Current calendar month to now, which is the window the page opens on. */\nfunction defaultRange(): { from: string; to: string } {\n  const now = new Date();\n  const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);\n  return { from: start.toISOString(), to: now.toISOString() };\n}\n\ntype TokenUsageReportProviderProps = {\n  children: ReactNode;\n  /**\n   * The Neo4j label the \"by target\" panel groups by, e.g. \"Campaign\". The set of\n   * things usage can be attributed to is application-specific, so the package\n   * cannot pick one — omit it and the panel is skipped rather than rendered\n   * empty, and no request is issued.\n   */\n  targetLabel?: string;\n  /** i18n key for the target panel's title. Required when targetLabel is set. */\n  targetPanelTitleKey?: string;\n  /** ISO 8601 instant. Defaults to the start of the current month. */\n  initialFrom?: string;\n  /** ISO 8601 instant. Defaults to now. */\n  initialTo?: string;\n  /** Rows per ranked panel. Defaults to 10. */\n  topN?: number;\n  /** Route the breadcrumb links back to. */\n  pageUrl?: string;\n};\n\n/**\n * Owns the date range of the self-service token-usage page, fetches the three\n * panels behind it, and publishes the filter bar into the page title bar.\n *\n * The filter bar is rendered into `title.functions` here — NOT in the container —\n * because `RoundPageContainer`'s title bar reads `title.functions` from\n * `SharedContext`, and a descendant cannot inject nodes into an ancestor's\n * provider value. That is why the filter state lives at this level.\n */\nexport const TokenUsageReportProvider = ({\n  children,\n  targetLabel,\n  targetPanelTitleKey,\n  initialFrom,\n  initialTo,\n  topN = DEFAULT_TOP_N,\n  pageUrl = TOKEN_USAGE_REPORT_PAGE_URL,\n}: TokenUsageReportProviderProps) => {\n  const t = useTranslations();\n  const generateUrl = usePageUrlGenerator();\n\n  const [filters, setFilterState] = useState<TokenUsageReportFilterState>(() => {\n    const range = defaultRange();\n    return { from: initialFrom ?? range.from, to: initialTo ?? range.to };\n  });\n\n  const [summary, setSummary] = useState<TokenUsageReportSummaryInterface[]>([]);\n  const [timeline, setTimeline] = useState<TokenUsageReportTimelineInterface[]>([]);\n  const [byOperation, setByOperation] = useState<TokenUsageReportBreakdownInterface[]>([]);\n  const [byTarget, setByTarget] = useState<TokenUsageReportBreakdownInterface[]>([]);\n  const [isLoading, setIsLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n\n  const { from, to } = filters;\n\n  useEffect(() => {\n    // `cancelled` is the out-of-order guard: a filter change fires a new request\n    // while the previous one is still in flight, and without this flag the slower\n    // (older) response would land last and overwrite the fresher state.\n    let cancelled = false;\n\n    setIsLoading(true);\n    setError(null);\n\n    const base = { from, to };\n\n    Promise.all([\n      TokenUsageReportService.getSummary(base),\n      // granularity is pinned to \"day\": week/month bucketing buys nothing on the\n      // ranges a single tenant browses.\n      TokenUsageReportService.getTimeline({ ...base, granularity: \"day\" }),\n      TokenUsageReportService.getBreakdown({ ...base, dimension: \"operation\", metric: REPORT_METRIC, limit: topN }),\n      // No targetLabel means the host app never opted in, so the request is\n      // skipped entirely rather than issued and discarded.\n      targetLabel\n        ? TokenUsageReportService.getBreakdown({\n            ...base,\n            dimension: \"target\",\n            targetLabel,\n            metric: REPORT_METRIC,\n            limit: topN,\n          })\n        : Promise.resolve<TokenUsageReportBreakdownInterface[]>([]),\n    ])\n      .then(([nextSummary, nextTimeline, nextByOperation, nextByTarget]) => {\n        if (cancelled) return;\n        setSummary(nextSummary ?? []);\n        setTimeline(nextTimeline ?? []);\n        setByOperation(nextByOperation ?? []);\n        setByTarget(nextByTarget ?? []);\n      })\n      .catch((err) => {\n        if (cancelled) return;\n        console.error(\"Failed to load token usage:\", err);\n        setError(err instanceof Error ? err.message : String(err));\n      })\n      .finally(() => {\n        if (cancelled) return;\n        setIsLoading(false);\n      });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [from, to, targetLabel, topN]);\n\n  const setFilters = useCallback((next: Partial<TokenUsageReportFilterState>) => {\n    setFilterState((prev) => ({ ...prev, ...next }));\n  }, []);\n\n  const breadcrumb = (): BreadcrumbItemData[] => [\n    { name: t(\"token_usage.report.title\"), href: generateUrl({ page: pageUrl }) },\n  ];\n\n  const title = () => ({\n    type: t(\"token_usage.report.title\"),\n    functions: <TokenUsageReportFilterBar key=\"tokenUsageReportFilterBar\" onChange={setFilters} />,\n  });\n\n  const contextValue = useMemo<TokenUsageReportContextType>(\n    () => ({ summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error }),\n    [summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error],\n  );\n\n  return (\n    <SharedProvider value={{ breadcrumbs: breadcrumb(), title: title() }}>\n      <TokenUsageReportContext.Provider value={contextValue}>{children}</TokenUsageReportContext.Provider>\n    </SharedProvider>\n  );\n};\n\nexport const useTokenUsageReport = (): TokenUsageReportContextType => {\n  const ctx = useContext(TokenUsageReportContext);\n  if (!ctx) {\n    throw new Error(\"useTokenUsageReport() called outside <TokenUsageReportProvider>.\");\n  }\n  return ctx;\n};\n","\"use client\";\n\nimport { DateRangeSelector } from \"../../../components/forms/DateRangeSelector\";\n\ntype Props = {\n  /** Receives ONLY the keys that changed. */\n  onChange: (next: { from?: string; to?: string }) => void;\n};\n\n/**\n * The single control row above the KPI tiles.\n *\n * Deliberately stateless: the control reports the keys it changed and the owning\n * context re-fetches, which is what lets the page issue a single coordinated\n * batch of requests instead of one per control.\n *\n * There is NO metric selector. A game master is billed in credits, so credits\n * are the only unit this surface speaks: cost would leak platform margin (the\n * controller rejects metric=cost outright) and a raw token count is an\n * implementation detail nobody is charged for. There is no company selector\n * either — a self-service caller has exactly one company.\n */\nexport function TokenUsageReportFilterBar({ onChange }: Props) {\n  return (\n    <div className=\"flex flex-wrap items-center gap-2\">\n      <DateRangeSelector\n        onDateChange={(range) => {\n          if (!range?.from || !range?.to) return;\n          onChange({ from: range.from.toISOString(), to: range.to.toISOString() });\n        }}\n      />\n    </div>\n  );\n}\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { RoundPageContainer } from \"../../../components\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../../../shadcnui\";\nimport { cn } from \"../../../utils\";\nimport { useTokenUsageReport } from \"../contexts/TokenUsageReportContext\";\nimport { TokenUsageRankedBar } from \"./TokenUsageRankedBar\";\nimport { TokenUsageReportTiles, type TokenUsageBalances } from \"./TokenUsageReportTiles\";\nimport { TokenUsageTimelineChart } from \"./TokenUsageTimelineChart\";\n\ntype Props = {\n  /**\n   * The caller's own credit balances. Supplied by the host app, which reads them\n   * from its CurrentUserContext — the package has no access to that context.\n   */\n  balances?: TokenUsageBalances | null;\n};\n\n/**\n * Page body for the self-service token-usage dashboard.\n *\n * Stateless by design — every value comes from useTokenUsageReport(). The filter\n * bar is deliberately NOT here: it belongs to the page title bar, which\n * RoundPageContainer fills from SharedContext, so the provider publishes it.\n *\n * The timeline and the ranked bars are the PACKAGE'S EXISTING components, used\n * verbatim. They take the same six metric getters the report interfaces were\n * given, which is what makes that reuse possible.\n */\nexport function TokenUsageReportContainer({ balances = null }: Props) {\n  const t = useTranslations();\n  const { summary, timeline, byOperation, byTarget, isLoading, error, targetPanelTitleKey } = useTokenUsageReport();\n\n  if (error) {\n    return (\n      <RoundPageContainer fullWidth forceHeader>\n        <div className=\"p-4\">\n          <Card>\n            <CardContent>\n              <p className=\"text-destructive text-xs/relaxed\">{error}</p>\n            </CardContent>\n          </Card>\n        </div>\n      </RoundPageContainer>\n    );\n  }\n\n  // Loading renders nothing in the body: the title bar (with the filter bar) is\n  // already mounted, so a spinner would only make the controls jump on arrival.\n  if (isLoading) return <RoundPageContainer fullWidth forceHeader />;\n\n  const emptyLabel = t(\"token_usage.report.no_data\");\n  // The panel needs both a title the host app owns and rows to put under it.\n  const targetTitle = targetPanelTitleKey && byTarget.length > 0 ? t(targetPanelTitleKey) : undefined;\n\n  return (\n    <RoundPageContainer fullWidth forceHeader>\n      <div className=\"flex w-full flex-col gap-4 p-4\">\n        <TokenUsageReportTiles summary={summary} metric=\"credits\" balances={balances} />\n\n        <Card>\n          <CardHeader>\n            <CardTitle>{t(\"token_usage.report.usage_over_time\")}</CardTitle>\n          </CardHeader>\n          <CardContent>\n            <TokenUsageTimelineChart rows={timeline} metric=\"credits\" stackBy=\"type\" />\n          </CardContent>\n        </Card>\n\n        <div className={cn(\"grid gap-4\", targetTitle && \"md:grid-cols-2\")}>\n          <Card>\n            <CardHeader>\n              <CardTitle>{t(\"token_usage.report.by_operation\")}</CardTitle>\n            </CardHeader>\n            <CardContent>\n              {/* Operation types are vocabulary the host app translates, unlike\n                  the target panel's rows, which carry entity NAMES. */}\n              <TokenUsageRankedBar\n                rows={byOperation}\n                metric=\"credits\"\n                emptyLabel={emptyLabel}\n                labelsAreOperationTypes\n              />\n            </CardContent>\n          </Card>\n\n          {targetTitle && (\n            <Card>\n              <CardHeader>\n                <CardTitle>{targetTitle}</CardTitle>\n              </CardHeader>\n              <CardContent>\n                <TokenUsageRankedBar rows={byTarget} metric=\"credits\" emptyLabel={emptyLabel} />\n              </CardContent>\n            </Card>\n          )}\n        </div>\n      </div>\n    </RoundPageContainer>\n  );\n}\n","\"use client\";\n\nimport { ArrowDownIcon, ArrowUpIcon } from \"lucide-react\";\nimport { useTranslations } from \"next-intl\";\nimport { Card, CardContent } from \"../../../shadcnui\";\nimport { cn } from \"../../../utils\";\nimport type { TokenUsageReportSummaryInterface } from \"../data/tokenusage-report-summary.interface\";\nimport type { ReportMetric } from \"../data/tokenusage-report.types\";\nimport { useUsageFormatters } from \"../lib/formatters\";\nimport { metricValue, percentageDelta, type TokenUsageMetrics } from \"../lib/metrics\";\n\n/**\n * The caller's own credit position, as the host app reads it from its\n * CurrentUserContext. The package has no access to that context, so the numbers\n * arrive as a prop.\n */\nexport type TokenUsageBalances = {\n  monthlyCredits: number;\n  availableMonthlyCredits: number;\n  availableExtraCredits: number;\n};\n\ntype Props = {\n  /** Two rows: window \"current\" and \"previous\". */\n  summary: TokenUsageReportSummaryInterface[];\n  metric: ReportMetric;\n  /** The caller's own balances, read from CurrentUserContext by the container. */\n  balances: TokenUsageBalances | null;\n};\n\nconst ZERO: TokenUsageMetrics = { cost: 0, credits: 0, tokensIn: 0, tokensOut: 0, cached: 0, calls: 0 };\n\n/**\n * The KPI header of the self-service token-usage page.\n *\n * One lead tile carries the number the page exists to answer — how much was\n * spent in the period — with its delta against the equal-length preceding\n * window. Three supporting tiles give it context: what is left this month, what\n * extra sits behind that, and how many calls produced the spend.\n *\n * Credits are fractional. Customer-facing BALANCES are floored to whole credits\n * (Math.max(0, Math.floor(v))) so nobody is told they have 715.4 of something\n * indivisible; the percentage arithmetic behind the colour keeps the raw floats.\n * Spend is NOT floored — it is a measurement, not a wallet.\n */\nexport function TokenUsageReportTiles({ summary, metric, balances }: Props) {\n  const t = useTranslations();\n  const { decimal, metricValue: formatValue } = useUsageFormatters();\n\n  const rowFor = (window: string): TokenUsageMetrics => summary.find((row) => row.window === window) ?? ZERO;\n\n  const current = rowFor(\"current\");\n  const previous = rowFor(\"previous\");\n\n  const monthlyPercentage =\n    balances && balances.monthlyCredits > 0 ? (balances.availableMonthlyCredits / balances.monthlyCredits) * 100 : 0;\n\n  // Three bands, not four: the previous implementation had a dead branch where\n  // >= 25 and >= 5 both returned the same class.\n  const monthlyColor =\n    monthlyPercentage > 75 ? \"text-success\" : monthlyPercentage >= 5 ? \"text-warning\" : \"text-destructive\";\n\n  const whole = (value: number) => decimal(Math.max(0, Math.floor(value)), 0);\n\n  return (\n    <div className=\"grid gap-3\">\n      <Card data-testid=\"tile-used\">\n        <CardContent className=\"grid gap-1\">\n          <span className=\"text-muted-foreground text-xs\">{t(\"token_usage.report.used_in_period\")}</span>\n          <span className=\"text-primary text-xl font-semibold tabular-nums\">\n            {formatValue(metricValue(current, metric), metric)}\n          </span>\n          <span className=\"flex items-center gap-1\">\n            <Delta\n              testId=\"tile-used-delta\"\n              delta={percentageDelta(metricValue(current, metric), metricValue(previous, metric))}\n              decimal={decimal}\n            />\n            <span className=\"text-muted-foreground text-xs\">{t(\"token_usage.report.vs_previous\")}</span>\n          </span>\n        </CardContent>\n      </Card>\n\n      <div className=\"grid gap-3 sm:grid-cols-3\">\n        {balances && (\n          <Card data-testid=\"tile-monthly\" size=\"sm\">\n            <CardContent className=\"grid gap-1\">\n              <span className=\"text-muted-foreground text-xs\">{t(\"token_usage.report.monthly_left\")}</span>\n              <span className=\"flex items-baseline gap-1\">\n                <span data-testid=\"tile-monthly-value\" className={cn(\"text-sm font-medium tabular-nums\", monthlyColor)}>\n                  {whole(balances.availableMonthlyCredits)}\n                </span>\n                <span className=\"text-muted-foreground text-xs tabular-nums\">/ {whole(balances.monthlyCredits)}</span>\n              </span>\n            </CardContent>\n          </Card>\n        )}\n\n        {balances && (\n          <Card data-testid=\"tile-extra\" size=\"sm\">\n            <CardContent className=\"grid gap-1\">\n              <span className=\"text-muted-foreground text-xs\">{t(\"token_usage.report.extra_credits\")}</span>\n              <span className=\"text-sm font-medium tabular-nums\">{whole(balances.availableExtraCredits)}</span>\n            </CardContent>\n          </Card>\n        )}\n\n        <Card data-testid=\"tile-calls\" size=\"sm\">\n          <CardContent className=\"grid gap-1\">\n            <span className=\"text-muted-foreground text-xs\">{t(\"token_usage.report.calls\")}</span>\n            <span className=\"text-sm font-medium tabular-nums\">{decimal(current.calls, 0)}</span>\n          </CardContent>\n        </Card>\n      </div>\n    </div>\n  );\n}\n\n/**\n * The delta slot. An undefined delta means the previous window was zero: an em\n * dash says \"not comparable\" where a percentage would say \"infinite growth\".\n *\n * `decimal` arrives as a prop rather than from the hook because this is a\n * module-level function, not a component — calling a hook here would break the\n * rules of hooks.\n */\nfunction Delta({\n  testId,\n  delta,\n  decimal,\n}: {\n  testId: string;\n  delta: number | undefined;\n  decimal: (value: number, decimals: number) => string;\n}) {\n  if (delta === undefined) {\n    return (\n      <span data-testid={testId} className=\"text-muted-foreground text-xs\">\n        —\n      </span>\n    );\n  }\n\n  const increased = delta >= 0;\n  const Icon = increased ? ArrowUpIcon : ArrowDownIcon;\n\n  // On a SPEND page more is not better, so the semantics are inverted relative\n  // to the administrative tiles: rising spend reads as a warning, not a success.\n  return (\n    <span\n      data-testid={testId}\n      className={cn(\n        \"inline-flex items-center gap-0.5 text-xs tabular-nums\",\n        increased ? \"text-warning\" : \"text-success\",\n      )}\n    >\n      <Icon aria-hidden className=\"size-3\" />\n      {decimal(Math.abs(delta), 0)} %\n    </span>\n  );\n}\n"],"mappings":"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","names":["useTranslations","jsx","useTranslations","useTranslations","useMemo","useTranslations","useMemo","useMemo","jsx","jsxs","useTranslations","ArrowDownIcon","ArrowUpIcon","useTranslations","useMemo","useState","jsx","jsxs","useTranslations","useState","useMemo","ArrowDownIcon","ArrowUpIcon","useTranslations","jsx","jsxs","useTranslations","useTranslations","useMemo","jsx","jsxs","metricValue","useTranslations","useMemo","series","jsx","jsxs","useTranslations","useMemo","withFilters","useTranslations","createContext","useCallback","useContext","useEffect","useMemo","useState","jsx","jsx","DEFAULT_TOP_N","createContext","defaultRange","useTranslations","useState","useEffect","useCallback","useMemo","useContext","useTranslations","ArrowDownIcon","ArrowUpIcon","useTranslations","jsx","jsxs","ZERO","useTranslations","Delta","ArrowUpIcon","ArrowDownIcon","jsx","jsxs","useTranslations"]}