77d4bd9158
## Summary This PR adds a comprehensive billing usage analytics feature that provides detailed breakdowns of credit consumption across execution types, users, resources, and time periods. The implementation includes a new ClickHouse-backed analytics service, GraphQL API endpoint, and a frontend dashboard component. ## Key Changes ### Backend - **New BillingAnalyticsService**: Queries ClickHouse for usage breakdowns by user, resource, execution type, and time series data - **BillingEventWriterService**: Writes billing events to ClickHouse for analytics while maintaining best-effort semantics (never blocks Stripe billing) - **ClickHouse Schema**: Added `billingEvent` table with 3-year TTL for storing detailed billing event data - **GraphQL Resolver**: New `getBillingAnalytics` query that aggregates usage data for the current billing period, protected by feature flag and billing permissions - **Enhanced BillingUsageEvent**: Added `userWorkspaceId` field to track per-user credit consumption - **AI Billing Integration**: Updated AI billing service to pass `userWorkspaceId` when recording usage events ### Frontend - **SettingsBillingAnalyticsSection**: New component displaying: - Usage breakdown by execution type with progress bars - Daily usage time series chart (28-day view) - Per-user credit consumption breakdown - Per-resource (agent/workflow) credit consumption breakdown - **SettingsUsage Page**: Dedicated page for viewing usage analytics - **GraphQL Query**: `GetBillingAnalytics` query with generated hooks - **Navigation**: Added Usage menu item in settings (feature-flagged) - **Mock Data**: Included screenshot mock data for preview/testing ### Feature Flag - Added `IS_USAGE_ANALYTICS_ENABLED` feature flag to control visibility and access to analytics features ## Implementation Details - Analytics data is queried in parallel for performance - ClickHouse writes are non-blocking to ensure billing operations never fail - Progress bars use dynamic coloring from a predefined palette - Time series visualization normalizes bar heights relative to max value - Empty state handling when no analytics data is available - Responsive UI with proper text truncation for long names https://claude.ai/code/session_01Y1EqrX6PFq3EJxJq89h7DF --------- Co-authored-by: Claude <noreply@anthropic.com>
42 lines
1.1 KiB
TypeScript
42 lines
1.1 KiB
TypeScript
import { useQuery } from '@apollo/client/react';
|
|
import {
|
|
BillingProductKey,
|
|
GetMeteredProductsUsageDocument,
|
|
} from '~/generated-metadata/graphql';
|
|
import { findOrThrow } from 'twenty-shared/utils';
|
|
|
|
export const useGetWorkflowNodeExecutionUsage = () => {
|
|
const { data, loading, refetch } = useQuery(GetMeteredProductsUsageDocument);
|
|
|
|
const refetchMeteredProductsUsage = () => {
|
|
refetch();
|
|
};
|
|
|
|
const isGetMeteredProductsUsageQueryLoaded = () => {
|
|
return data?.getMeteredProductsUsage && !loading;
|
|
};
|
|
|
|
const getMeteredProductUsage = () => {
|
|
if (!data) {
|
|
throw new Error('getMeteredProductUsage was not loaded');
|
|
}
|
|
|
|
return data.getMeteredProductsUsage;
|
|
};
|
|
|
|
const getWorkflowNodeExecutionUsage = () => {
|
|
return findOrThrow(
|
|
getMeteredProductUsage(),
|
|
(productUsage) =>
|
|
productUsage.productKey === BillingProductKey.WORKFLOW_NODE_EXECUTION,
|
|
);
|
|
};
|
|
|
|
return {
|
|
refetchMeteredProductsUsage,
|
|
isGetMeteredProductsUsageQueryLoaded:
|
|
isGetMeteredProductsUsageQueryLoaded(),
|
|
getWorkflowNodeExecutionUsage,
|
|
};
|
|
};
|