From dd15a1a29dd017b312b6781555c33128e0dde746 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 20 Oct 2026 19:43:01 -0700 Subject: [PATCH 1/2] improvement(insights): clarify unassigned usage and refresh usage charts --- .../[id]/usage/calendar/route.ts | 29 +++ .../components/activity-summary.tsx | 9 +- .../components/usage-calendar.tsx | 36 ++++ .../components/usage-consumers.tsx | 69 ++++++- .../components/usage-heatmap.tsx | 182 ++++++++++++++++++ .../components/usage-monitoring.tsx | 7 + apps/sim/ee/organization-usage/constants.ts | 16 +- apps/sim/hooks/queries/organization-usage.ts | 20 ++ .../queries/utils/organization-usage-keys.ts | 4 + .../lib/api/contracts/organization-usage.ts | 17 ++ .../get-organization-usage-breakdown.ts | 10 +- .../get-organization-usage-calendar.ts | 43 +++++ .../get-organization-usage-overview.test.ts | 17 ++ .../organization-usage/operations.ts | 7 + .../lib/billing/core/usage-analytics.test.ts | 20 ++ apps/sim/lib/billing/core/usage-analytics.ts | 23 ++- .../emcn/src/components/charts/bar-chart.tsx | 146 ++++---------- .../src/components/charts/geometry.test.ts | 27 ++- .../emcn/src/components/charts/geometry.ts | 12 ++ 19 files changed, 549 insertions(+), 145 deletions(-) create mode 100644 apps/sim/app/api/organizations/[id]/usage/calendar/route.ts create mode 100644 apps/sim/ee/organization-usage/components/usage-calendar.tsx create mode 100644 apps/sim/ee/organization-usage/components/usage-heatmap.tsx create mode 100644 apps/sim/lib/billing/application/organization-usage/get-organization-usage-calendar.ts diff --git a/apps/sim/app/api/organizations/[id]/usage/calendar/route.ts b/apps/sim/app/api/organizations/[id]/usage/calendar/route.ts new file mode 100644 index 00000000000..ecb915e03b5 --- /dev/null +++ b/apps/sim/app/api/organizations/[id]/usage/calendar/route.ts @@ -0,0 +1,29 @@ +import { getOrganizationUsageCalendarContract } from '@/lib/api/contracts/organization-usage' +import { + defineInternalJsonRoute, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { getOrganizationUsageCalendar } from '@/lib/billing/application/organization-usage/get-organization-usage-calendar' +import { organizationUsageOperations } from '@/lib/billing/application/organization-usage/operations' +import { organizationUsageErrorPolicy } from '@/app/api/organizations/[id]/usage/error-policy' + +export const dynamic = 'force-dynamic' + +export const GET = defineInternalJsonRoute({ + contract: getOrganizationUsageCalendarContract, + auth: internalSessionAuth, + operation: organizationUsageOperations.readCalendar, + rateLimit: internalRateLimits.none({ + reason: + 'Authenticated org-admin settings read, gated on enterprise entitlement and billing authority', + }), + errorPolicy: organizationUsageErrorPolicy, + mapInput: ({ params, query }) => ({ + organizationId: params.id, + workspaceId: query.workspaceId, + timezone: query.timezone, + }), + useCase: getOrganizationUsageCalendar, + present: (result) => result, +}) diff --git a/apps/sim/ee/organization-usage/components/activity-summary.tsx b/apps/sim/ee/organization-usage/components/activity-summary.tsx index 75376f8e62d..0d590c02a40 100644 --- a/apps/sim/ee/organization-usage/components/activity-summary.tsx +++ b/apps/sim/ee/organization-usage/components/activity-summary.tsx @@ -25,13 +25,12 @@ import type { OrganizationUsageWindowKey } from '@/hooks/queries/utils/organizat const CHART_HEIGHT = 180 /** - * Outcome layers, bottom-up. Failed is the status red and sits on the stack where a - * spike reads at a glance; Other (cancelled, paused, unfinished) stays neutral, in a - * gray whose lightness keeps it apart from the red for color-vision deficiency. + * Outcome layers, bottom-up. Failed stays red; cancelled, paused and unfinished + * runs share the neutral remainder. */ const OUTCOMES = [ - { id: 'completed', label: 'Completed', color: 'var(--brand-blue)' }, - { id: 'failed', label: 'Failed', color: 'var(--text-error)' }, + { id: 'completed', label: 'Completed', color: 'var(--usage-workflow)' }, + { id: 'failed', label: 'Failed', color: 'var(--usage-failed)' }, { id: 'other', label: 'Other', color: USAGE_OTHER_COLOR }, ] as const diff --git a/apps/sim/ee/organization-usage/components/usage-calendar.tsx b/apps/sim/ee/organization-usage/components/usage-calendar.tsx new file mode 100644 index 00000000000..6171d2093ad --- /dev/null +++ b/apps/sim/ee/organization-usage/components/usage-calendar.tsx @@ -0,0 +1,36 @@ +'use client' + +import { ChartFrame } from '@sim/emcn' +import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' +import { UsageHeatmap } from '@/ee/organization-usage/components/usage-heatmap' +import { useOrganizationUsageCalendar } from '@/hooks/queries/organization-usage' + +interface UsageCalendarProps { + organizationId: string + timezone: string + workspaceId?: string +} + +/** Loads the year separately so its history cannot delay the selected period's chart. */ +export function UsageCalendar({ organizationId, timezone, workspaceId }: UsageCalendarProps) { + const calendar = useOrganizationUsageCalendar(organizationId, timezone, workspaceId) + return ( + Last 365 days} + > + {calendar.isError || !calendar.data ? ( + void calendar.refetch()} + > + {null} + + ) : ( + + )} + + ) +} diff --git a/apps/sim/ee/organization-usage/components/usage-consumers.tsx b/apps/sim/ee/organization-usage/components/usage-consumers.tsx index ac8087b0888..7ee9a3f5eff 100644 --- a/apps/sim/ee/organization-usage/components/usage-consumers.tsx +++ b/apps/sim/ee/organization-usage/components/usage-consumers.tsx @@ -1,8 +1,20 @@ 'use client' -import type { ComponentType } from 'react' -import { Avatar, cn, disclosureChevronClass, formatChartCompactNumber } from '@sim/emcn' -import { ArrowRight, ChevronDown } from '@sim/emcn/icons' +import { type ComponentType, useId, useState } from 'react' +import { + Avatar, + Button, + cn, + disclosureChevronClass, + FloatingTooltip, + formatChartCompactNumber, + OverflowText, + Popover, + PopoverContent, + PopoverTrigger, + useFloatingTooltip, +} from '@sim/emcn' +import { ArrowRight, ChevronDown, CircleInfo } from '@sim/emcn/icons' import { AnthropicIcon, AzureIcon, @@ -91,6 +103,7 @@ const PROVIDER_ICONS: Readonly !open, { revalidateKey: open }) + + return ( + + + + + + {info} + + {!open && ( + + )} + + ) +} + /** * A tabular row, not `SettingsResourceRow` — tabular columns are the sanctioned * exception in `sim-settings-pages.md`, alongside billing invoices and credit usage. */ function UsageConsumerRow({ row, + info, isMember, showTokensOnly, onSelect, @@ -158,7 +205,14 @@ function UsageConsumerRow({ ) : ( ProviderIcon && )} - {row.label} +
+ + {info && } +