diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 046c8a13fc..2b63721f22 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -249,14 +249,12 @@ export type BillingEndTrialPeriodOutput = { export type BillingMeteredProductUsageOutput = { __typename?: 'BillingMeteredProductUsageOutput'; - freeTierQuantity: Scalars['Float']; - freeTrialQuantity: Scalars['Float']; + grantedCredits: Scalars['Float']; periodEnd: Scalars['DateTime']; periodStart: Scalars['DateTime']; productKey: BillingProductKey; - totalCostCents: Scalars['Float']; unitPriceCents: Scalars['Float']; - usageQuantity: Scalars['Float']; + usedCredits: Scalars['Float']; }; /** The different billing plans available */ @@ -290,6 +288,14 @@ export type BillingPriceMeteredDto = { tiersMode?: Maybe; }; +export type BillingPriceOutput = { + __typename?: 'BillingPriceOutput'; + amount: Scalars['Float']; + nickname: Scalars['String']; + recurringInterval: SubscriptionInterval; + stripePriceId: Scalars['String']; +}; + export type BillingPriceTierDto = { __typename?: 'BillingPriceTierDTO'; flatAmount?: Maybe; @@ -335,6 +341,7 @@ export type BillingSessionOutput = { export type BillingSubscription = { __typename?: 'BillingSubscription'; billingSubscriptionItems?: Maybe>; + currentPeriodEnd?: Maybe; id: Scalars['UUID']; interval?: Maybe; metadata: Scalars['JSON']; @@ -347,6 +354,7 @@ export type BillingSubscriptionItem = { hasReachedCurrentPeriodCap: Scalars['Boolean']; id: Scalars['UUID']; quantity?: Maybe; + stripePriceId?: Maybe; }; export type BillingTrialPeriodDto = { @@ -1450,6 +1458,7 @@ export type Mutation = { updateOneRole: Role; updateOneServerlessFunction: ServerlessFunction; updatePasswordViaResetToken: InvalidatePassword; + updateSubscriptionItemPrice: BillingUpdateOutput; updateWebhook?: Maybe; updateWorkflowRunStep: WorkflowAction; updateWorkflowVersionPositions: Scalars['Boolean']; @@ -2064,6 +2073,11 @@ export type MutationUpdatePasswordViaResetTokenArgs = { }; +export type MutationUpdateSubscriptionItemPriceArgs = { + priceId: Scalars['String']; +}; + + export type MutationUpdateWebhookArgs = { input: UpdateWebhookDto; }; @@ -2452,6 +2466,7 @@ export type Query = { getTimelineThreadsFromPersonId: TimelineThreadsWithTotal; index: Index; indexMetadatas: IndexConnection; + listAvailableMeteredBillingPrices: Array; object: Object; objects: ObjectConnection; plans: Array; @@ -4112,6 +4127,13 @@ export type SwitchSubscriptionToYearlyIntervalMutationVariables = Exact<{ [key: export type SwitchSubscriptionToYearlyIntervalMutation = { __typename?: 'Mutation', switchToYearlyInterval: { __typename?: 'BillingUpdateOutput', success: boolean } }; +export type UpdateSubscriptionItemPriceMutationVariables = Exact<{ + priceId: Scalars['String']; +}>; + + +export type UpdateSubscriptionItemPriceMutation = { __typename?: 'Mutation', updateSubscriptionItemPrice: { __typename?: 'BillingUpdateOutput', success: boolean } }; + export type BillingBaseProductPricesQueryVariables = Exact<{ [key: string]: never; }>; @@ -4127,7 +4149,12 @@ export type BillingPortalSessionQuery = { __typename?: 'Query', billingPortalSes export type GetMeteredProductsUsageQueryVariables = Exact<{ [key: string]: never; }>; -export type GetMeteredProductsUsageQuery = { __typename?: 'Query', getMeteredProductsUsage: Array<{ __typename?: 'BillingMeteredProductUsageOutput', productKey: BillingProductKey, usageQuantity: number, freeTierQuantity: number, freeTrialQuantity: number, unitPriceCents: number, totalCostCents: number }> }; +export type GetMeteredProductsUsageQuery = { __typename?: 'Query', getMeteredProductsUsage: Array<{ __typename?: 'BillingMeteredProductUsageOutput', productKey: BillingProductKey, usedCredits: number, grantedCredits: number, unitPriceCents: number }> }; + +export type ListAvailableMeteredBillingPricesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type ListAvailableMeteredBillingPricesQuery = { __typename?: 'Query', listAvailableMeteredBillingPrices: Array<{ __typename?: 'BillingPriceOutput', nickname: string, amount: number, stripePriceId: string, recurringInterval: SubscriptionInterval }> }; export type RemoteServerFieldsFragment = { __typename?: 'RemoteServer', id: string, createdAt: string, foreignDataWrapperId: string, foreignDataWrapperOptions?: any | null, foreignDataWrapperType: string, updatedAt: string, schema?: string | null, label: string, userMappingOptions?: { __typename?: 'UserMappingOptionsUser', user?: string | null } | null }; @@ -4639,7 +4666,7 @@ export type VerifyTwoFactorAuthenticationMethodForAuthenticatedUserMutationVaria export type VerifyTwoFactorAuthenticationMethodForAuthenticatedUserMutation = { __typename?: 'Mutation', verifyTwoFactorAuthenticationMethodForAuthenticatedUser: { __typename?: 'VerifyTwoFactorAuthenticationMethodOutput', success: boolean } }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectRecordsPermissions?: Array | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null, views?: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string }> }> | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectRecordsPermissions?: Array | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId?: string | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null, views?: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string }> }> | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } }; export type WorkspaceUrlsFragmentFragment = { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }; @@ -4658,7 +4685,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>; -export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectRecordsPermissions?: Array | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null, views?: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string }> }> | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectRecordsPermissions?: Array | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId?: string | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null, views?: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string }> }> | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } }; export type ViewFieldFragmentFragment = { __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null }; @@ -5444,10 +5471,12 @@ export const UserQueryFragmentFragmentDoc = gql` status interval metadata + currentPeriodEnd billingSubscriptionItems { id hasReachedCurrentPeriodCap quantity + stripePriceId billingProduct { name description @@ -7196,6 +7225,39 @@ export function useSwitchSubscriptionToYearlyIntervalMutation(baseOptions?: Apol export type SwitchSubscriptionToYearlyIntervalMutationHookResult = ReturnType; export type SwitchSubscriptionToYearlyIntervalMutationResult = Apollo.MutationResult; export type SwitchSubscriptionToYearlyIntervalMutationOptions = Apollo.BaseMutationOptions; +export const UpdateSubscriptionItemPriceDocument = gql` + mutation UpdateSubscriptionItemPrice($priceId: String!) { + updateSubscriptionItemPrice(priceId: $priceId) { + success + } +} + `; +export type UpdateSubscriptionItemPriceMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateSubscriptionItemPriceMutation__ + * + * To run a mutation, you first call `useUpdateSubscriptionItemPriceMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateSubscriptionItemPriceMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateSubscriptionItemPriceMutation, { data, loading, error }] = useUpdateSubscriptionItemPriceMutation({ + * variables: { + * priceId: // value for 'priceId' + * }, + * }); + */ +export function useUpdateSubscriptionItemPriceMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateSubscriptionItemPriceDocument, options); + } +export type UpdateSubscriptionItemPriceMutationHookResult = ReturnType; +export type UpdateSubscriptionItemPriceMutationResult = Apollo.MutationResult; +export type UpdateSubscriptionItemPriceMutationOptions = Apollo.BaseMutationOptions; export const BillingBaseProductPricesDocument = gql` query billingBaseProductPrices { plans { @@ -7279,11 +7341,9 @@ export const GetMeteredProductsUsageDocument = gql` query GetMeteredProductsUsage { getMeteredProductsUsage { productKey - usageQuantity - freeTierQuantity - freeTrialQuantity + usedCredits + grantedCredits unitPriceCents - totalCostCents } } `; @@ -7314,6 +7374,43 @@ export function useGetMeteredProductsUsageLazyQuery(baseOptions?: Apollo.LazyQue export type GetMeteredProductsUsageQueryHookResult = ReturnType; export type GetMeteredProductsUsageLazyQueryHookResult = ReturnType; export type GetMeteredProductsUsageQueryResult = Apollo.QueryResult; +export const ListAvailableMeteredBillingPricesDocument = gql` + query listAvailableMeteredBillingPrices { + listAvailableMeteredBillingPrices { + nickname + amount + stripePriceId + recurringInterval + } +} + `; + +/** + * __useListAvailableMeteredBillingPricesQuery__ + * + * To run a query within a React component, call `useListAvailableMeteredBillingPricesQuery` and pass it any options that fit your needs. + * When your component renders, `useListAvailableMeteredBillingPricesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useListAvailableMeteredBillingPricesQuery({ + * variables: { + * }, + * }); + */ +export function useListAvailableMeteredBillingPricesQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(ListAvailableMeteredBillingPricesDocument, options); + } +export function useListAvailableMeteredBillingPricesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(ListAvailableMeteredBillingPricesDocument, options); + } +export type ListAvailableMeteredBillingPricesQueryHookResult = ReturnType; +export type ListAvailableMeteredBillingPricesLazyQueryHookResult = ReturnType; +export type ListAvailableMeteredBillingPricesQueryResult = Apollo.QueryResult; export const CreateServerDocument = gql` mutation createServer($input: CreateRemoteServerInput!) { createOneRemoteServer(input: $input) { diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 8853fd75c0..4a6daafd53 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -249,14 +249,12 @@ export type BillingEndTrialPeriodOutput = { export type BillingMeteredProductUsageOutput = { __typename?: 'BillingMeteredProductUsageOutput'; - freeTierQuantity: Scalars['Float']; - freeTrialQuantity: Scalars['Float']; + grantedCredits: Scalars['Float']; periodEnd: Scalars['DateTime']; periodStart: Scalars['DateTime']; productKey: BillingProductKey; - totalCostCents: Scalars['Float']; unitPriceCents: Scalars['Float']; - usageQuantity: Scalars['Float']; + usedCredits: Scalars['Float']; }; /** The different billing plans available */ @@ -290,6 +288,14 @@ export type BillingPriceMeteredDto = { tiersMode?: Maybe; }; +export type BillingPriceOutput = { + __typename?: 'BillingPriceOutput'; + amount: Scalars['Float']; + nickname: Scalars['String']; + recurringInterval: SubscriptionInterval; + stripePriceId: Scalars['String']; +}; + export type BillingPriceTierDto = { __typename?: 'BillingPriceTierDTO'; flatAmount?: Maybe; @@ -335,6 +341,7 @@ export type BillingSessionOutput = { export type BillingSubscription = { __typename?: 'BillingSubscription'; billingSubscriptionItems?: Maybe>; + currentPeriodEnd?: Maybe; id: Scalars['UUID']; interval?: Maybe; metadata: Scalars['JSON']; @@ -347,6 +354,7 @@ export type BillingSubscriptionItem = { hasReachedCurrentPeriodCap: Scalars['Boolean']; id: Scalars['UUID']; quantity?: Maybe; + stripePriceId?: Maybe; }; export type BillingTrialPeriodDto = { @@ -1401,6 +1409,7 @@ export type Mutation = { updateOneRole: Role; updateOneServerlessFunction: ServerlessFunction; updatePasswordViaResetToken: InvalidatePassword; + updateSubscriptionItemPrice: BillingUpdateOutput; updateWebhook?: Maybe; updateWorkflowRunStep: WorkflowAction; updateWorkflowVersionPositions: Scalars['Boolean']; @@ -1975,6 +1984,11 @@ export type MutationUpdatePasswordViaResetTokenArgs = { }; +export type MutationUpdateSubscriptionItemPriceArgs = { + priceId: Scalars['String']; +}; + + export type MutationUpdateWebhookArgs = { input: UpdateWebhookDto; }; @@ -2360,6 +2374,7 @@ export type Query = { getTimelineThreadsFromPersonId: TimelineThreadsWithTotal; index: Index; indexMetadatas: IndexConnection; + listAvailableMeteredBillingPrices: Array; object: Object; objects: ObjectConnection; plans: Array; diff --git a/packages/twenty-front/src/modules/billing/components/SettingsBillingCreditsSection.tsx b/packages/twenty-front/src/modules/billing/components/SettingsBillingCreditsSection.tsx new file mode 100644 index 0000000000..ea6da5d2f4 --- /dev/null +++ b/packages/twenty-front/src/modules/billing/components/SettingsBillingCreditsSection.tsx @@ -0,0 +1,102 @@ +import { SettingsBillingLabelValueItem } from '@/billing/components/SettingsBillingLabelValueItem'; +import { useGetWorkflowNodeExecutionUsage } from '@/billing/hooks/useGetWorkflowNodeExecutionUsage'; +import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; +import styled from '@emotion/styled'; +import { t } from '@lingui/core/macro'; +import { H2Title } from 'twenty-ui/display'; +import { ProgressBar } from 'twenty-ui/feedback'; +import { Section } from 'twenty-ui/layout'; +import { BACKGROUND_LIGHT, COLOR } from 'twenty-ui/theme'; +import { SubscriptionStatus } from '~/generated/graphql'; +import { formatAmount } from '~/utils/format/formatAmount'; +import { formatNumber } from '~/utils/format/number'; +import { SubscriptionInfoContainer } from '@/billing/components/SubscriptionInfoContainer'; +import { useListAvailableMeteredBillingPricesQuery } from '~/generated-metadata/graphql'; +import { MeteredPriceSelector } from '@/billing/components/internal/MeteredPriceSelector'; +import { type CurrentWorkspace } from '@/auth/states/currentWorkspaceState'; +import { + getIntervalLabel, + isMonthlyPlan, +} from '@/billing/utils/subscriptionFlags'; + +const StyledLineSeparator = styled.div` + width: 100%; + height: 1px; + background-color: ${({ theme }) => theme.background.tertiary}; +`; + +export const SettingsBillingCreditsSection = ({ + currentWorkspace, +}: { + currentWorkspace: CurrentWorkspace; +}) => { + const subscriptionStatus = useSubscriptionStatus(); + + const isTrialing = subscriptionStatus === SubscriptionStatus.Trialing; + + const { usedCredits, grantedCredits, unitPriceCents } = + useGetWorkflowNodeExecutionUsage(); + + const { data: meteredBillingPrices } = + useListAvailableMeteredBillingPricesQuery(); + + const progressBarValue = (usedCredits / grantedCredits) * 100; + + const intervalLabel = getIntervalLabel(isMonthlyPlan(currentWorkspace)); + + return ( + <> +
+ + + + 100 ? COLOR.red40 : COLOR.blue} + backgroundColor={BACKGROUND_LIGHT.tertiary} + withBorderRadius={true} + /> + + + {!isTrialing && ( + + )} + + {!isTrialing && ( + + )} + +
+
+ {meteredBillingPrices?.listAvailableMeteredBillingPrices && ( + + )} +
+ + ); +}; diff --git a/packages/twenty-front/src/modules/billing/components/SettingsBillingMonthlyCreditsSection.tsx b/packages/twenty-front/src/modules/billing/components/SettingsBillingMonthlyCreditsSection.tsx deleted file mode 100644 index 182dbfd809..0000000000 --- a/packages/twenty-front/src/modules/billing/components/SettingsBillingMonthlyCreditsSection.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { SettingsBillingLabelValueItem } from '@/billing/components/SettingsBillingLabelValueItem'; -import { useGetWorkflowNodeExecutionUsage } from '@/billing/hooks/useGetWorkflowNodeExecutionUsage'; -import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; -import styled from '@emotion/styled'; -import { t } from '@lingui/core/macro'; -import { H2Title } from 'twenty-ui/display'; -import { ProgressBar } from 'twenty-ui/feedback'; -import { Section } from 'twenty-ui/layout'; -import { BACKGROUND_LIGHT, COLOR } from 'twenty-ui/theme'; -import { SubscriptionStatus } from '~/generated/graphql'; -import { formatAmount } from '~/utils/format/formatAmount'; -import { formatNumber } from '~/utils/format/number'; -import { SubscriptionInfoContainer } from '@/billing/components/SubscriptionInfoContainer'; - -const StyledLineSeparator = styled.div` - width: 100%; - height: 1px; - background-color: ${({ theme }) => theme.background.tertiary}; -`; - -export const SettingsBillingMonthlyCreditsSection = () => { - const subscriptionStatus = useSubscriptionStatus(); - - const isTrialing = subscriptionStatus === SubscriptionStatus.Trialing; - - const { - freeUsageQuantity, - includedFreeQuantity, - paidUsageQuantity, - unitPriceCents, - totalCostCents, - } = useGetWorkflowNodeExecutionUsage(); - - const isFreeCreditProgressBarCompleted = - freeUsageQuantity === includedFreeQuantity; - - const progressBarValue = (freeUsageQuantity / includedFreeQuantity) * 100; - - const formattedFreeUsageQuantity = isFreeCreditProgressBarCompleted - ? formatAmount(freeUsageQuantity) - : formatNumber(freeUsageQuantity); - - return ( -
- - - - - - - {!isTrialing && ( - - )} - - {!isTrialing && ( - - )} - -
- ); -}; diff --git a/packages/twenty-front/src/modules/billing/components/SettingsBillingSubscriptionInfo.tsx b/packages/twenty-front/src/modules/billing/components/SettingsBillingSubscriptionInfo.tsx index 18702d6885..7afa13911a 100644 --- a/packages/twenty-front/src/modules/billing/components/SettingsBillingSubscriptionInfo.tsx +++ b/packages/twenty-front/src/modules/billing/components/SettingsBillingSubscriptionInfo.tsx @@ -3,6 +3,13 @@ import { SubscriptionInfoRowContainer } from '@/billing/components/SubscriptionI import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { formatMonthlyPrices } from '@/billing/utils/formatMonthlyPrices'; +import { + isMonthlyPlan as isMonthlyPlanFn, + isYearlyPlan as isYearlyPlanFn, + isProPlan as isProPlanFn, + isEnterprisePlan as isEnterprisePlanFn, + getIntervalLabel, +} from '@/billing/utils/subscriptionFlags'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; @@ -10,14 +17,16 @@ import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useRecoilState } from 'recoil'; -import { isDefined } from 'twenty-shared/utils'; +import { capitalize, isDefined } from 'twenty-shared/utils'; import { Tag } from 'twenty-ui/components'; import { H2Title, IconArrowUp, IconCalendarEvent, + IconCircleX, IconTag, IconUsers, + IconCalendarRepeat, } from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; import { Section } from 'twenty-ui/layout'; @@ -25,17 +34,23 @@ import { BillingPlanKey, type BillingPlanOutput, BillingProductKey, + PermissionFlagType, SubscriptionInterval, SubscriptionStatus, useBillingBaseProductPricesQuery, useSwitchSubscriptionToEnterprisePlanMutation, useSwitchSubscriptionToYearlyIntervalMutation, } from '~/generated-metadata/graphql'; +import { beautifyExactDate } from '~/utils/date-utils'; +import { useEndSubscriptionTrialPeriod } from '@/billing/hooks/useEndSubscriptionTrialPeriod'; +import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap'; const SWITCH_BILLING_INTERVAL_MODAL_ID = 'switch-billing-interval-modal'; const SWITCH_BILLING_PLAN_MODAL_ID = 'switch-billing-plan-modal'; +const END_TRIAL_PERIOD_MODAL_ID = 'end-trial-period-modal'; + const StyledSwitchButtonContainer = styled.div` align-items: center; display: flex; @@ -64,36 +79,38 @@ export const SettingsBillingSubscriptionInfo = () => { currentWorkspaceState, ); - const isMonthlyPlan = - currentWorkspace?.currentBillingSubscription?.interval === - SubscriptionInterval.Month; + const isMonthlyPlan = isMonthlyPlanFn(currentWorkspace); - const isYearlyPlan = - currentWorkspace?.currentBillingSubscription?.interval === - SubscriptionInterval.Year; + const isYearlyPlan = isYearlyPlanFn(currentWorkspace); - const isProPlan = - currentWorkspace?.currentBillingSubscription?.metadata['plan'] === - BillingPlanKey.PRO; + const isProPlan = isProPlanFn(currentWorkspace); - const isEnterprisePlan = - currentWorkspace?.currentBillingSubscription?.metadata['plan'] === - BillingPlanKey.ENTERPRISE; + const isEnterprisePlan = isEnterprisePlanFn(currentWorkspace); + + const isTrialPeriod = subscriptionStatus === SubscriptionStatus.Trialing; const canSwitchSubscription = subscriptionStatus !== SubscriptionStatus.PastDue; - const planTag = isProPlan ? ( - - ) : isEnterprisePlan ? ( - + const { endTrialPeriod, isLoading: isEndTrialPeriodLoading } = + useEndSubscriptionTrialPeriod(); + + const planDescriptor = isProPlan + ? { color: 'sky' as const, label: t`Pro` } + : isEnterprisePlan + ? { color: 'purple' as const, label: t`Organization` } + : undefined; + + const planTag = planDescriptor ? ( + <> + + {isTrialPeriod && } + ) : undefined; - const intervalLabel = isMonthlyPlan - ? t`Monthly` - : isYearlyPlan - ? t`Yearly` - : undefined; + const intervalLabel = capitalize(getIntervalLabel(isMonthlyPlan, true)); + const { [PermissionFlagType.WORKSPACE]: hasPermissionToEndTrialPeriod } = + usePermissionFlagMap(); const seats = currentWorkspace?.currentBillingSubscription?.billingSubscriptionItems?.find( @@ -106,6 +123,9 @@ export const SettingsBillingSubscriptionInfo = () => { const formattedPrices = formatMonthlyPrices(baseProductPrices); + const renewDate = + currentWorkspace?.currentBillingSubscription?.currentPeriodEnd; + const yearlyPrice = formattedPrices?.[ currentWorkspace?.currentBillingSubscription?.metadata[ @@ -183,6 +203,13 @@ export const SettingsBillingSubscriptionInfo = () => { Icon={IconCalendarEvent} value={intervalLabel} /> + {renewDate && ( + + )} { disabled={!canSwitchSubscription} /> )} + {isTrialPeriod && hasPermissionToEndTrialPeriod && ( +