feat(admin-panel) - add billing/usage section (#21672)
Add billing/usage section <img width="740" height="763" alt="Screenshot 2026-06-16 at 14 39 51" src="https://github.com/user-attachments/assets/42db4fe4-3158-4ab8-aee5-28121ea530cd" /> <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21672?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -117,6 +117,7 @@ export type AdminPanelWorkspaceBilling = {
|
||||
creditBalance?: Maybe<Scalars['Float']['output']>;
|
||||
stripeCustomerId?: Maybe<Scalars['String']['output']>;
|
||||
subscription?: Maybe<AdminPanelWorkspaceSubscription>;
|
||||
usage?: Maybe<AdminPanelWorkspaceUsage>;
|
||||
};
|
||||
|
||||
export type AdminPanelWorkspaceSubscription = {
|
||||
@@ -146,6 +147,17 @@ export type AdminPanelWorkspaceSubscriptionItem = {
|
||||
unitAmount?: Maybe<Scalars['Float']['output']>;
|
||||
};
|
||||
|
||||
export type AdminPanelWorkspaceUsage = {
|
||||
__typename?: 'AdminPanelWorkspaceUsage';
|
||||
grantedCredits: Scalars['Float']['output'];
|
||||
periodEnd: Scalars['DateTime']['output'];
|
||||
periodStart: Scalars['DateTime']['output'];
|
||||
remainingCredits: Scalars['Float']['output'];
|
||||
rolloverCredits: Scalars['Float']['output'];
|
||||
totalGrantedCredits: Scalars['Float']['output'];
|
||||
usedCredits: Scalars['Float']['output'];
|
||||
};
|
||||
|
||||
export type AdminWorkspaceChatThread = {
|
||||
__typename?: 'AdminWorkspaceChatThread';
|
||||
conversationSize: Scalars['Int']['output'];
|
||||
@@ -1096,7 +1108,7 @@ export type WorkspaceBillingAdminPanelQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type WorkspaceBillingAdminPanelQuery = { __typename?: 'Query', workspaceBillingAdminPanel?: { __typename?: 'AdminPanelWorkspaceBilling', stripeCustomerId?: string | null, creditBalance?: number | null, subscription?: { __typename?: 'AdminPanelWorkspaceSubscription', stripeSubscriptionId: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, currency: string, planKey?: string | null, currentPeriodStart: string, currentPeriodEnd: string, trialStart?: string | null, trialEnd?: string | null, cancelAt?: string | null, canceledAt?: string | null, cancelAtPeriodEnd: boolean, items: Array<{ __typename?: 'AdminPanelWorkspaceSubscriptionItem', productName: string, productKey?: string | null, stripePriceId: string, quantity?: number | null, unitAmount?: number | null, includedCredits?: number | null }> } | null } | null };
|
||||
export type WorkspaceBillingAdminPanelQuery = { __typename?: 'Query', workspaceBillingAdminPanel?: { __typename?: 'AdminPanelWorkspaceBilling', stripeCustomerId?: string | null, creditBalance?: number | null, usage?: { __typename?: 'AdminPanelWorkspaceUsage', periodStart: string, periodEnd: string, usedCredits: number, grantedCredits: number, rolloverCredits: number, totalGrantedCredits: number, remainingCredits: number } | null, subscription?: { __typename?: 'AdminPanelWorkspaceSubscription', stripeSubscriptionId: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, currency: string, planKey?: string | null, currentPeriodStart: string, currentPeriodEnd: string, trialStart?: string | null, trialEnd?: string | null, cancelAt?: string | null, canceledAt?: string | null, cancelAtPeriodEnd: boolean, items: Array<{ __typename?: 'AdminPanelWorkspaceSubscriptionItem', productName: string, productKey?: string | null, stripePriceId: string, quantity?: number | null, unitAmount?: number | null, includedCredits?: number | null }> } | null } | null };
|
||||
|
||||
export type UserLookupAdminPanelQueryVariables = Exact<{
|
||||
userIdentifier: Scalars['String']['input'];
|
||||
@@ -1235,7 +1247,7 @@ export const GetAdminWorkspaceChatThreadsDocument = {"kind":"Document","definiti
|
||||
export const GetServerAdminsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServerAdmins"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getServerAdmins"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"canAccessFullAdminPanel"}},{"kind":"Field","name":{"kind":"Name","value":"canImpersonate"}}]}}]}}]} as unknown as DocumentNode<GetServerAdminsQuery, GetServerAdminsQueryVariables>;
|
||||
export const GetUpgradeStatusDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUpgradeStatus"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getUpgradeStatus"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"inferredVersion"}},{"kind":"Field","name":{"kind":"Name","value":"health"}},{"kind":"Field","name":{"kind":"Name","value":"latestCommand"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"executedByVersion"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode<GetUpgradeStatusQuery, GetUpgradeStatusQueryVariables>;
|
||||
export const GetVersionInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetVersionInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}}]}}]}}]} as unknown as DocumentNode<GetVersionInfoQuery, GetVersionInfoQueryVariables>;
|
||||
export const WorkspaceBillingAdminPanelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceBillingAdminPanel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBillingAdminPanel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeCustomerId"}},{"kind":"Field","name":{"kind":"Name","value":"creditBalance"}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeSubscriptionId"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"interval"}},{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"planKey"}},{"kind":"Field","name":{"kind":"Name","value":"currentPeriodStart"}},{"kind":"Field","name":{"kind":"Name","value":"currentPeriodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"trialStart"}},{"kind":"Field","name":{"kind":"Name","value":"trialEnd"}},{"kind":"Field","name":{"kind":"Name","value":"cancelAt"}},{"kind":"Field","name":{"kind":"Name","value":"canceledAt"}},{"kind":"Field","name":{"kind":"Name","value":"cancelAtPeriodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"productKey"}},{"kind":"Field","name":{"kind":"Name","value":"stripePriceId"}},{"kind":"Field","name":{"kind":"Name","value":"quantity"}},{"kind":"Field","name":{"kind":"Name","value":"unitAmount"}},{"kind":"Field","name":{"kind":"Name","value":"includedCredits"}}]}}]}}]}}]}}]} as unknown as DocumentNode<WorkspaceBillingAdminPanelQuery, WorkspaceBillingAdminPanelQueryVariables>;
|
||||
export const WorkspaceBillingAdminPanelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceBillingAdminPanel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBillingAdminPanel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeCustomerId"}},{"kind":"Field","name":{"kind":"Name","value":"creditBalance"}},{"kind":"Field","name":{"kind":"Name","value":"usage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"periodStart"}},{"kind":"Field","name":{"kind":"Name","value":"periodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"usedCredits"}},{"kind":"Field","name":{"kind":"Name","value":"grantedCredits"}},{"kind":"Field","name":{"kind":"Name","value":"rolloverCredits"}},{"kind":"Field","name":{"kind":"Name","value":"totalGrantedCredits"}},{"kind":"Field","name":{"kind":"Name","value":"remainingCredits"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeSubscriptionId"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"interval"}},{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"planKey"}},{"kind":"Field","name":{"kind":"Name","value":"currentPeriodStart"}},{"kind":"Field","name":{"kind":"Name","value":"currentPeriodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"trialStart"}},{"kind":"Field","name":{"kind":"Name","value":"trialEnd"}},{"kind":"Field","name":{"kind":"Name","value":"cancelAt"}},{"kind":"Field","name":{"kind":"Name","value":"canceledAt"}},{"kind":"Field","name":{"kind":"Name","value":"cancelAtPeriodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"productKey"}},{"kind":"Field","name":{"kind":"Name","value":"stripePriceId"}},{"kind":"Field","name":{"kind":"Name","value":"quantity"}},{"kind":"Field","name":{"kind":"Name","value":"unitAmount"}},{"kind":"Field","name":{"kind":"Name","value":"includedCredits"}}]}}]}}]}}]}}]} as unknown as DocumentNode<WorkspaceBillingAdminPanelQuery, WorkspaceBillingAdminPanelQueryVariables>;
|
||||
export const UserLookupAdminPanelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserLookupAdminPanel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userLookupAdminPanel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"userIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserInfoFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"totalUsers"}},{"kind":"Field","name":{"kind":"Name","value":"activationStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"allowImpersonation"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceUrls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customUrl"}},{"kind":"Field","name":{"kind":"Name","value":"subdomainUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"users"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featureFlags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserInfoFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"UserInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<UserLookupAdminPanelQuery, UserLookupAdminPanelQueryVariables>;
|
||||
export const WorkspaceLookupAdminPanelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceLookupAdminPanel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceLookupAdminPanel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserInfoFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"allowImpersonation"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"totalUsers"}},{"kind":"Field","name":{"kind":"Name","value":"activationStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceUrls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customUrl"}},{"kind":"Field","name":{"kind":"Name","value":"subdomainUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"users"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featureFlags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserInfoFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"UserInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<WorkspaceLookupAdminPanelQuery, WorkspaceLookupAdminPanelQueryVariables>;
|
||||
export const DeleteJobsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteJobs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"queueName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"jobIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteJobs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"queueName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"queueName"}}},{"kind":"Argument","name":{"kind":"Name","value":"jobIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"jobIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deletedCount"}},{"kind":"Field","name":{"kind":"Name","value":"results"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"jobId"}},{"kind":"Field","name":{"kind":"Name","value":"success"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]}}]} as unknown as DocumentNode<DeleteJobsMutation, DeleteJobsMutationVariables>;
|
||||
|
||||
+66
-2
@@ -1,6 +1,7 @@
|
||||
import { useQuery } from '@apollo/client/react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Tag } from 'twenty-ui-deprecated/components';
|
||||
import {
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
IconBox,
|
||||
IconCalendarEvent,
|
||||
IconCalendarRepeat,
|
||||
IconChartBar,
|
||||
IconCircleX,
|
||||
IconCoins,
|
||||
IconCreditCard,
|
||||
@@ -19,7 +21,10 @@ import {
|
||||
} from 'twenty-ui-deprecated/display';
|
||||
import { Section } from 'twenty-ui-deprecated/layout';
|
||||
import { type ThemeColor } from 'twenty-ui-deprecated/theme';
|
||||
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
|
||||
import {
|
||||
ThemeContext,
|
||||
themeCssVariables,
|
||||
} from 'twenty-ui-deprecated/theme-constants';
|
||||
|
||||
import { useApolloAdminClient } from '@/settings/admin-panel/apollo/hooks/useApolloAdminClient';
|
||||
import { GET_WORKSPACE_BILLING_ADMIN_PANEL } from '@/settings/admin-panel/graphql/queries/getWorkspaceBillingAdminPanel';
|
||||
@@ -73,6 +78,10 @@ const StyledItemValue = styled.div`
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledProgressBarContainer = styled.div`
|
||||
margin-bottom: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const STATUS_COLORS: Record<SubscriptionStatus, ThemeColor> = {
|
||||
[SubscriptionStatus.Active]: 'green',
|
||||
[SubscriptionStatus.Trialing]: 'blue',
|
||||
@@ -137,6 +146,7 @@ export const SettingsAdminWorkspaceBillingContent = ({
|
||||
}: SettingsAdminWorkspaceBillingContentProps) => {
|
||||
const { t } = useLingui();
|
||||
const { formatNumber } = useNumberFormat();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const apolloAdminClient = useApolloAdminClient();
|
||||
|
||||
const { data, loading } = useQuery<WorkspaceBillingAdminPanelQuery>(
|
||||
@@ -171,7 +181,10 @@ export const SettingsAdminWorkspaceBillingContent = ({
|
||||
);
|
||||
}
|
||||
|
||||
const { stripeCustomerId, creditBalance, subscription } = billing;
|
||||
const { stripeCustomerId, creditBalance, subscription, usage } = billing;
|
||||
|
||||
const formatCredits = (credits: number): string =>
|
||||
formatNumber(credits, { abbreviate: true, decimals: 2 });
|
||||
|
||||
const customerItems = [
|
||||
{
|
||||
@@ -207,6 +220,39 @@ export const SettingsAdminWorkspaceBillingContent = ({
|
||||
: null;
|
||||
const isTrialing = subscription?.status === SubscriptionStatus.Trialing;
|
||||
|
||||
const usageItems = isDefined(usage)
|
||||
? [
|
||||
{
|
||||
Icon: IconChartBar,
|
||||
label: t`Credits used`,
|
||||
value: `${formatCredits(usage.usedCredits)} / ${formatCredits(usage.totalGrantedCredits)}`,
|
||||
},
|
||||
...(!isTrialing
|
||||
? [
|
||||
{
|
||||
Icon: IconCoins,
|
||||
label: t`Base credits`,
|
||||
value: formatCredits(usage.grantedCredits),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(usage.rolloverCredits > 0
|
||||
? [
|
||||
{
|
||||
Icon: IconCoins,
|
||||
label: t`Rollover credits`,
|
||||
value: formatCredits(usage.rolloverCredits),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
Icon: IconCalendarRepeat,
|
||||
label: t`Usage period`,
|
||||
value: formatPeriod(usage.periodStart, usage.periodEnd),
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const formatItemValue = (
|
||||
item: NonNullable<typeof subscription>['items'][number],
|
||||
): string => {
|
||||
@@ -349,6 +395,24 @@ export const SettingsAdminWorkspaceBillingContent = ({
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Usage`}
|
||||
description={
|
||||
isDefined(usage)
|
||||
? t`Credit consumption for the current period`
|
||||
: t`No usage data is available for this workspace.`
|
||||
}
|
||||
/>
|
||||
{isDefined(usage) && (
|
||||
<SettingsTableCard
|
||||
rounded
|
||||
items={usageItems}
|
||||
gridAutoColumns="3fr 8fr"
|
||||
/>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Subscription`}
|
||||
|
||||
+9
@@ -5,6 +5,15 @@ export const GET_WORKSPACE_BILLING_ADMIN_PANEL = gql`
|
||||
workspaceBillingAdminPanel(workspaceId: $workspaceId) {
|
||||
stripeCustomerId
|
||||
creditBalance
|
||||
usage {
|
||||
periodStart
|
||||
periodEnd
|
||||
usedCredits
|
||||
grantedCredits
|
||||
rolloverCredits
|
||||
totalGrantedCredits
|
||||
remainingCredits
|
||||
}
|
||||
subscription {
|
||||
stripeSubscriptionId
|
||||
status
|
||||
|
||||
+4
@@ -1,6 +1,7 @@
|
||||
import { Field, Float, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { AdminPanelWorkspaceSubscriptionDTO } from 'src/engine/core-modules/admin-panel/dtos/admin-panel-workspace-subscription.dto';
|
||||
import { AdminPanelWorkspaceUsageDTO } from 'src/engine/core-modules/admin-panel/dtos/admin-panel-workspace-usage.dto';
|
||||
|
||||
@ObjectType('AdminPanelWorkspaceBilling')
|
||||
export class AdminPanelWorkspaceBillingDTO {
|
||||
@@ -12,4 +13,7 @@ export class AdminPanelWorkspaceBillingDTO {
|
||||
|
||||
@Field(() => AdminPanelWorkspaceSubscriptionDTO, { nullable: true })
|
||||
subscription: AdminPanelWorkspaceSubscriptionDTO | null;
|
||||
|
||||
@Field(() => AdminPanelWorkspaceUsageDTO, { nullable: true })
|
||||
usage: AdminPanelWorkspaceUsageDTO | null;
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { Field, Float, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('AdminPanelWorkspaceUsage')
|
||||
export class AdminPanelWorkspaceUsageDTO {
|
||||
@Field(() => Date)
|
||||
periodStart: Date;
|
||||
|
||||
@Field(() => Date)
|
||||
periodEnd: Date;
|
||||
|
||||
@Field(() => Float)
|
||||
usedCredits: number;
|
||||
|
||||
@Field(() => Float)
|
||||
grantedCredits: number;
|
||||
|
||||
@Field(() => Float)
|
||||
rolloverCredits: number;
|
||||
|
||||
@Field(() => Float)
|
||||
totalGrantedCredits: number;
|
||||
|
||||
@Field(() => Float)
|
||||
remainingCredits: number;
|
||||
}
|
||||
+58
-1
@@ -1,14 +1,18 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { In, type Repository } from 'typeorm';
|
||||
|
||||
import { AdminPanelWorkspaceBillingDTO } from 'src/engine/core-modules/admin-panel/dtos/admin-panel-workspace-billing.dto';
|
||||
import { type AdminPanelWorkspaceUsageDTO } from 'src/engine/core-modules/admin-panel/dtos/admin-panel-workspace-usage.dto';
|
||||
import { BillingCustomerEntity } from 'src/engine/core-modules/billing/entities/billing-customer.entity';
|
||||
import { BillingPriceEntity } from 'src/engine/core-modules/billing/entities/billing-price.entity';
|
||||
import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum';
|
||||
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
|
||||
import { BillingUsageService } from 'src/engine/core-modules/billing/services/billing-usage.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { toDisplayCredits } from 'src/engine/core-modules/usage/utils/to-display-credits.util';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator';
|
||||
import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository';
|
||||
const CREDIT_BALANCE_MICRO_UNIT = 1_000_000;
|
||||
@@ -19,12 +23,17 @@ const KNOWN_PLAN_KEYS: ReadonlySet<string> = new Set(
|
||||
|
||||
@Injectable()
|
||||
export class AdminPanelBillingService {
|
||||
private readonly logger = new Logger(AdminPanelBillingService.name);
|
||||
|
||||
constructor(
|
||||
@InjectWorkspaceScopedRepository(BillingCustomerEntity)
|
||||
private readonly billingCustomerRepository: WorkspaceScopedRepository<BillingCustomerEntity>,
|
||||
@InjectRepository(BillingPriceEntity)
|
||||
private readonly billingPriceRepository: Repository<BillingPriceEntity>,
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
private readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
private readonly billingSubscriptionService: BillingSubscriptionService,
|
||||
private readonly billingUsageService: BillingUsageService,
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
) {}
|
||||
|
||||
@@ -57,9 +66,12 @@ export class AdminPanelBillingService {
|
||||
stripeCustomerId,
|
||||
creditBalance,
|
||||
subscription: null,
|
||||
usage: null,
|
||||
};
|
||||
}
|
||||
|
||||
const usage = await this.getWorkspaceUsage(workspaceId);
|
||||
|
||||
const items = subscription.billingSubscriptionItems ?? [];
|
||||
const priceIds = items.map((item) => item.stripePriceId);
|
||||
const prices = priceIds.length
|
||||
@@ -80,6 +92,7 @@ export class AdminPanelBillingService {
|
||||
return {
|
||||
stripeCustomerId,
|
||||
creditBalance,
|
||||
usage,
|
||||
subscription: {
|
||||
stripeSubscriptionId: subscription.stripeSubscriptionId,
|
||||
status: subscription.status,
|
||||
@@ -112,4 +125,48 @@ export class AdminPanelBillingService {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async getWorkspaceUsage(
|
||||
workspaceId: string,
|
||||
): Promise<AdminPanelWorkspaceUsageDTO | null> {
|
||||
const workspace = await this.workspaceRepository.findOne({
|
||||
where: { id: workspaceId },
|
||||
});
|
||||
|
||||
if (!workspace) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const [usage] =
|
||||
await this.billingUsageService.getResourceCreditProductUsage(workspace);
|
||||
|
||||
if (!usage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const usedCredits = toDisplayCredits(usage.usedCredits);
|
||||
const grantedCredits = toDisplayCredits(usage.grantedCredits);
|
||||
const rolloverCredits = toDisplayCredits(usage.rolloverCredits);
|
||||
const totalGrantedCredits = toDisplayCredits(usage.totalGrantedCredits);
|
||||
|
||||
return {
|
||||
periodStart: usage.periodStart,
|
||||
periodEnd: usage.periodEnd,
|
||||
usedCredits,
|
||||
grantedCredits,
|
||||
rolloverCredits,
|
||||
totalGrantedCredits,
|
||||
remainingCredits: totalGrantedCredits - usedCredits,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
`Failed to compute credit usage for workspace ${workspaceId}: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user