diff --git a/README.md b/README.md
index 191012dc4..258f6f410 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,13 @@ We felt the need for a CRM platform that empowers rather than constrains. We bel
# Demo
+
+
Go to demo.twenty.com and login with the following credentials:
```
diff --git a/packages/twenty-front/package.json b/packages/twenty-front/package.json
index 76ae9c72f..3e91a2eea 100644
--- a/packages/twenty-front/package.json
+++ b/packages/twenty-front/package.json
@@ -48,6 +48,7 @@
"buffer": "^6.0.3",
"docx": "^9.1.0",
"file-saver": "^2.0.5",
+ "recoil-sync": "^0.2.0",
"transliteration": "^2.3.5"
},
"devDependencies": {
diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx
index 6290fbfcd..d06c6140b 100644
--- a/packages/twenty-front/src/generated/graphql.tsx
+++ b/packages/twenty-front/src/generated/graphql.tsx
@@ -115,6 +115,13 @@ export type Billing = {
isBillingEnabled: Scalars['Boolean'];
};
+/** The different billing plans available */
+export enum BillingPlanKey {
+ Base = 'BASE',
+ Enterprise = 'ENTERPRISE',
+ Pro = 'PRO'
+}
+
export type BillingSubscription = {
__typename?: 'BillingSubscription';
id: Scalars['UUID'];
@@ -182,6 +189,30 @@ export type ComputeStepOutputSchemaInput = {
step: Scalars['JSON'];
};
+export type CreateFieldInput = {
+ defaultValue?: InputMaybe;
+ description?: InputMaybe;
+ icon?: InputMaybe;
+ isActive?: InputMaybe;
+ isCustom?: InputMaybe;
+ isLabelSyncedWithName?: InputMaybe;
+ isNullable?: InputMaybe;
+ isRemoteCreation?: InputMaybe;
+ isSystem?: InputMaybe;
+ isUnique?: InputMaybe;
+ label: Scalars['String'];
+ name: Scalars['String'];
+ objectMetadataId: Scalars['String'];
+ options?: InputMaybe;
+ settings?: InputMaybe;
+ type: FieldMetadataType;
+};
+
+export type CreateOneFieldMetadataInput = {
+ /** The record to create */
+ field: CreateFieldInput;
+};
+
export type CreateServerlessFunctionInput = {
description?: InputMaybe;
name: Scalars['String'];
@@ -205,6 +236,11 @@ export type CursorPaging = {
last?: InputMaybe;
};
+export type DeleteOneFieldInput = {
+ /** The id of the field to delete. */
+ id: Scalars['UUID'];
+};
+
export type DeleteOneObjectInput = {
/** The id of the record to delete. */
id: Scalars['UUID'];
@@ -447,12 +483,14 @@ export type Mutation = {
computeStepOutputSchema: Scalars['JSON'];
createOIDCIdentityProvider: SetupSsoOutput;
createOneAppToken: AppToken;
+ createOneField: Field;
createOneObject: Object;
createOneServerlessFunction: ServerlessFunction;
createSAMLIdentityProvider: SetupSsoOutput;
createWorkflowVersionStep: WorkflowAction;
deactivateWorkflowVersion: Scalars['Boolean'];
deleteCurrentWorkspace: Workspace;
+ deleteOneField: Field;
deleteOneObject: Object;
deleteOneServerlessFunction: ServerlessFunction;
deleteSSOIdentityProvider: DeleteSsoOutput;
@@ -478,6 +516,7 @@ export type Mutation = {
switchWorkspace: PublicWorkspaceDataOutput;
track: Analytics;
updateBillingSubscription: UpdateBillingEntity;
+ updateOneField: Field;
updateOneObject: Object;
updateOneServerlessFunction: ServerlessFunction;
updatePasswordViaResetToken: InvalidatePassword;
@@ -528,7 +567,9 @@ export type MutationChallengeArgs = {
export type MutationCheckoutSessionArgs = {
+ plan?: BillingPlanKey;
recurringInterval: SubscriptionInterval;
+ requirePaymentMethod?: Scalars['Boolean'];
successUrlPath?: InputMaybe;
};
@@ -543,6 +584,11 @@ export type MutationCreateOidcIdentityProviderArgs = {
};
+export type MutationCreateOneFieldArgs = {
+ input: CreateOneFieldMetadataInput;
+};
+
+
export type MutationCreateOneServerlessFunctionArgs = {
input: CreateServerlessFunctionInput;
};
@@ -563,6 +609,11 @@ export type MutationDeactivateWorkflowVersionArgs = {
};
+export type MutationDeleteOneFieldArgs = {
+ input: DeleteOneFieldInput;
+};
+
+
export type MutationDeleteOneObjectArgs = {
input: DeleteOneObjectInput;
};
@@ -665,6 +716,11 @@ export type MutationTrackArgs = {
};
+export type MutationUpdateOneFieldArgs = {
+ input: UpdateOneFieldMetadataInput;
+};
+
+
export type MutationUpdateOneObjectArgs = {
input: UpdateOneObjectInput;
};
@@ -825,6 +881,8 @@ export type Query = {
clientConfig: ClientConfig;
currentUser: User;
currentWorkspace: Workspace;
+ field: Field;
+ fields: FieldConnection;
findAvailableWorkspacesByEmail: Array;
findManyServerlessFunctions: Array;
findOneServerlessFunction: ServerlessFunction;
@@ -1241,6 +1299,22 @@ export type UpdateBillingEntity = {
success: Scalars['Boolean'];
};
+export type UpdateFieldInput = {
+ defaultValue?: InputMaybe;
+ description?: InputMaybe;
+ icon?: InputMaybe;
+ isActive?: InputMaybe;
+ isCustom?: InputMaybe;
+ isLabelSyncedWithName?: InputMaybe;
+ isNullable?: InputMaybe;
+ isSystem?: InputMaybe;
+ isUnique?: InputMaybe;
+ label?: InputMaybe;
+ name?: InputMaybe;
+ options?: InputMaybe;
+ settings?: InputMaybe;
+};
+
export type UpdateObjectPayload = {
description?: InputMaybe;
icon?: InputMaybe;
@@ -1255,6 +1329,13 @@ export type UpdateObjectPayload = {
shortcut?: InputMaybe;
};
+export type UpdateOneFieldMetadataInput = {
+ /** The id of the record to update */
+ id: Scalars['UUID'];
+ /** The record to update */
+ update: UpdateFieldInput;
+};
+
export type UpdateOneObjectInput = {
/** The id of the object to update */
id: Scalars['UUID'];
@@ -1944,6 +2025,8 @@ export type BillingPortalSessionQuery = { __typename?: 'Query', billingPortalSes
export type CheckoutSessionMutationVariables = Exact<{
recurringInterval: SubscriptionInterval;
successUrlPath?: InputMaybe;
+ plan: BillingPlanKey;
+ requirePaymentMethod: Scalars['Boolean'];
}>;
@@ -3234,10 +3317,12 @@ export type BillingPortalSessionQueryHookResult = ReturnType;
export type BillingPortalSessionQueryResult = Apollo.QueryResult;
export const CheckoutSessionDocument = gql`
- mutation CheckoutSession($recurringInterval: SubscriptionInterval!, $successUrlPath: String) {
+ mutation CheckoutSession($recurringInterval: SubscriptionInterval!, $successUrlPath: String, $plan: BillingPlanKey!, $requirePaymentMethod: Boolean!) {
checkoutSession(
recurringInterval: $recurringInterval
successUrlPath: $successUrlPath
+ plan: $plan
+ requirePaymentMethod: $requirePaymentMethod
) {
url
}
@@ -3260,6 +3345,8 @@ export type CheckoutSessionMutationFn = Apollo.MutationFunction({
key: 'canCreateActivityState',
diff --git a/packages/twenty-front/src/modules/activities/states/isActivityInCreateModeState.ts b/packages/twenty-front/src/modules/activities/states/isActivityInCreateModeState.ts
index 61aaf33d3..4ae973044 100644
--- a/packages/twenty-front/src/modules/activities/states/isActivityInCreateModeState.ts
+++ b/packages/twenty-front/src/modules/activities/states/isActivityInCreateModeState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isActivityInCreateModeState = createState({
key: 'isActivityInCreateModeState',
diff --git a/packages/twenty-front/src/modules/activities/states/isCreatingActivityInDBState.ts b/packages/twenty-front/src/modules/activities/states/isCreatingActivityInDBState.ts
index 278b2430c..30e111a66 100644
--- a/packages/twenty-front/src/modules/activities/states/isCreatingActivityInDBState.ts
+++ b/packages/twenty-front/src/modules/activities/states/isCreatingActivityInDBState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isUpsertingActivityInDBState = createState({
key: 'isUpsertingActivityInDBState',
diff --git a/packages/twenty-front/src/modules/app/components/App.tsx b/packages/twenty-front/src/modules/app/components/App.tsx
index f760ee9f6..af4c1eec0 100644
--- a/packages/twenty-front/src/modules/app/components/App.tsx
+++ b/packages/twenty-front/src/modules/app/components/App.tsx
@@ -7,26 +7,29 @@ import { ExceptionHandlerProvider } from '@/error-handler/components/ExceptionHa
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { HelmetProvider } from 'react-helmet-async';
import { RecoilRoot } from 'recoil';
+import { RecoilURLSyncJSON } from 'recoil-sync';
import { IconsProvider } from 'twenty-ui';
export const App = () => {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
index 659cab893..d28d49d59 100644
--- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
+++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
@@ -43,16 +43,17 @@ import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTi
import { currentUserState } from '../states/currentUserState';
import { tokenPairState } from '../states/tokenPairState';
+import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspaceSubdomain';
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
+import { useRedirect } from '@/domain-manager/hooks/useRedirect';
+import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
-import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
import { AppPath } from '@/types/AppPath';
-import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
-import { useRedirect } from '@/domain-manager/hooks/useRedirect';
+import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
export const useAuth = () => {
const setTokenPair = useSetRecoilState(tokenPairState);
@@ -382,6 +383,7 @@ export const useAuth = () => {
params: {
workspacePersonalInviteToken?: string;
workspaceInviteHash?: string;
+ billingCheckoutSession?: BillingCheckoutSession;
},
) => {
const url = new URL(`${REACT_APP_SERVER_BASE_URL}${path}`);
@@ -394,6 +396,12 @@ export const useAuth = () => {
params.workspacePersonalInviteToken,
);
}
+ if (isDefined(params.billingCheckoutSession)) {
+ url.searchParams.set(
+ 'billingCheckoutSessionState',
+ JSON.stringify(params.billingCheckoutSession),
+ );
+ }
if (isDefined(workspaceSubdomain)) {
url.searchParams.set('workspaceSubdomain', workspaceSubdomain);
@@ -408,6 +416,7 @@ export const useAuth = () => {
(params: {
workspacePersonalInviteToken?: string;
workspaceInviteHash?: string;
+ billingCheckoutSession?: BillingCheckoutSession;
}) => {
redirect(buildRedirectUrl('/auth/google', params));
},
@@ -418,6 +427,7 @@ export const useAuth = () => {
(params: {
workspacePersonalInviteToken?: string;
workspaceInviteHash?: string;
+ billingCheckoutSession?: BillingCheckoutSession;
}) => {
redirect(buildRedirectUrl('/auth/microsoft', params));
},
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts
index 6cb9f84ed..5a7072cea 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts
@@ -1,7 +1,9 @@
-import { renderHook } from '@testing-library/react';
-import { useParams, useSearchParams } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth';
import { useSignInWithGoogle } from '@/auth/sign-in-up/hooks/useSignInWithGoogle';
+import { renderHook } from '@testing-library/react';
+import { useParams, useSearchParams } from 'react-router-dom';
+import { BillingPlanKey, SubscriptionInterval } from '~/generated/graphql';
+import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
jest.mock('react-router-dom', () => ({
useParams: jest.fn(),
@@ -13,10 +15,24 @@ jest.mock('@/auth/hooks/useAuth', () => ({
}));
describe('useSignInWithGoogle', () => {
+ const mockBillingCheckoutSession = {
+ plan: BillingPlanKey.Pro,
+ interval: SubscriptionInterval.Month,
+ requirePaymentMethod: true,
+ skipPlanPage: false,
+ };
+
+ const Wrapper = getJestMetadataAndApolloMocksWrapper({
+ apolloMocks: [],
+ });
+
it('should call signInWithGoogle with correct params', () => {
const signInWithGoogleMock = jest.fn();
const mockUseParams = { workspaceInviteHash: 'testHash' };
- const mockSearchParams = new URLSearchParams('inviteToken=testToken');
+
+ const mockSearchParams = new URLSearchParams(
+ 'inviteToken=testToken&billingCheckoutSessionState={"plan":"Pro","interval":"Month","requirePaymentMethod":true,"skipPlanPage":false}',
+ );
(useParams as jest.Mock).mockReturnValue(mockUseParams);
(useSearchParams as jest.Mock).mockReturnValue([mockSearchParams]);
@@ -24,12 +40,15 @@ describe('useSignInWithGoogle', () => {
signInWithGoogle: signInWithGoogleMock,
});
- const { result } = renderHook(() => useSignInWithGoogle());
+ const { result } = renderHook(() => useSignInWithGoogle(), {
+ wrapper: Wrapper,
+ });
result.current.signInWithGoogle();
expect(signInWithGoogleMock).toHaveBeenCalledWith({
workspaceInviteHash: 'testHash',
workspacePersonalInviteToken: 'testToken',
+ billingCheckoutSession: mockBillingCheckoutSession,
});
});
@@ -44,12 +63,15 @@ describe('useSignInWithGoogle', () => {
signInWithGoogle: signInWithGoogleMock,
});
- const { result } = renderHook(() => useSignInWithGoogle());
+ const { result } = renderHook(() => useSignInWithGoogle(), {
+ wrapper: Wrapper,
+ });
result.current.signInWithGoogle();
expect(signInWithGoogleMock).toHaveBeenCalledWith({
workspaceInviteHash: 'testHash',
workspacePersonalInviteToken: undefined,
+ billingCheckoutSession: mockBillingCheckoutSession,
});
});
});
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts
index 3200e8a54..98c9ea527 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts
@@ -1,7 +1,8 @@
-import { renderHook } from '@testing-library/react';
-import { useParams, useSearchParams } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth';
import { useSignInWithMicrosoft } from '@/auth/sign-in-up/hooks/useSignInWithMicrosoft';
+import { renderHook } from '@testing-library/react';
+import { useParams, useSearchParams } from 'react-router-dom';
+import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
jest.mock('react-router-dom', () => ({
useParams: jest.fn(),
@@ -13,6 +14,17 @@ jest.mock('@/auth/hooks/useAuth', () => ({
}));
describe('useSignInWithMicrosoft', () => {
+ const Wrapper = getJestMetadataAndApolloMocksWrapper({
+ apolloMocks: [],
+ });
+
+ const mockBillingCheckoutSession = {
+ plan: 'PRO',
+ interval: 'Month',
+ requirePaymentMethod: true,
+ skipPlanPage: false,
+ };
+
it('should call signInWithMicrosoft with the correct parameters', () => {
const workspaceInviteHashMock = 'testHash';
const inviteTokenMock = 'testToken';
@@ -28,12 +40,15 @@ describe('useSignInWithMicrosoft', () => {
signInWithMicrosoft: signInWithMicrosoftMock,
});
- const { result } = renderHook(() => useSignInWithMicrosoft());
+ const { result } = renderHook(() => useSignInWithMicrosoft(), {
+ wrapper: Wrapper,
+ });
result.current.signInWithMicrosoft();
expect(signInWithMicrosoftMock).toHaveBeenCalledWith({
workspaceInviteHash: workspaceInviteHashMock,
workspacePersonalInviteToken: inviteTokenMock,
+ billingCheckoutSession: mockBillingCheckoutSession,
});
});
@@ -49,10 +64,13 @@ describe('useSignInWithMicrosoft', () => {
signInWithMicrosoft: signInWithMicrosoftMock,
});
- const { result } = renderHook(() => useSignInWithMicrosoft());
+ const { result } = renderHook(() => useSignInWithMicrosoft(), {
+ wrapper: Wrapper,
+ });
result.current.signInWithMicrosoft();
expect(signInWithMicrosoftMock).toHaveBeenCalledWith({
+ billingCheckoutSession: mockBillingCheckoutSession,
workspaceInviteHash: workspaceInviteHashMock,
workspacePersonalInviteToken: undefined,
});
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts
index 58ce165f7..26908402e 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts
@@ -1,15 +1,27 @@
import { useParams, useSearchParams } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth';
+import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
export const useSignInWithGoogle = () => {
const workspaceInviteHash = useParams().workspaceInviteHash;
const [searchParams] = useSearchParams();
const workspacePersonalInviteToken =
searchParams.get('inviteToken') ?? undefined;
+ const billingCheckoutSession = {
+ plan: 'PRO',
+ interval: 'Month',
+ requirePaymentMethod: true,
+ skipPlanPage: false,
+ } as BillingCheckoutSession;
+
const { signInWithGoogle } = useAuth();
return {
signInWithGoogle: () =>
- signInWithGoogle({ workspaceInviteHash, workspacePersonalInviteToken }),
+ signInWithGoogle({
+ workspaceInviteHash,
+ workspacePersonalInviteToken,
+ billingCheckoutSession,
+ }),
};
};
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithMicrosoft.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithMicrosoft.ts
index 2f471c176..513f77ac6 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithMicrosoft.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithMicrosoft.ts
@@ -1,18 +1,23 @@
import { useParams, useSearchParams } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth';
+import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
+import { useRecoilValue } from 'recoil';
export const useSignInWithMicrosoft = () => {
const workspaceInviteHash = useParams().workspaceInviteHash;
const [searchParams] = useSearchParams();
const workspacePersonalInviteToken =
searchParams.get('inviteToken') ?? undefined;
+ const billingCheckoutSession = useRecoilValue(billingCheckoutSessionState);
+
const { signInWithMicrosoft } = useAuth();
return {
signInWithMicrosoft: () =>
signInWithMicrosoft({
workspaceInviteHash,
workspacePersonalInviteToken,
+ billingCheckoutSession,
}),
};
};
diff --git a/packages/twenty-front/src/modules/auth/states/availableIdentityProviderForAuthState.ts b/packages/twenty-front/src/modules/auth/states/availableIdentityProviderForAuthState.ts
index d3100a826..a8ecdfd93 100644
--- a/packages/twenty-front/src/modules/auth/states/availableIdentityProviderForAuthState.ts
+++ b/packages/twenty-front/src/modules/auth/states/availableIdentityProviderForAuthState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { UserExists } from '~/generated/graphql';
export const availableSSOIdentityProvidersForAuthState = createState<
diff --git a/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
new file mode 100644
index 000000000..acfe231a9
--- /dev/null
+++ b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
@@ -0,0 +1,39 @@
+import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
+import { createState } from '@ui/utilities/state/utils/createState';
+import { syncEffect } from 'recoil-sync';
+import { BillingPlanKey, SubscriptionInterval } from '~/generated/graphql';
+
+export const billingCheckoutSessionState = createState({
+ key: 'billingCheckoutSessionState',
+ defaultValue: {
+ plan: BillingPlanKey.Pro,
+ interval: SubscriptionInterval.Month,
+ requirePaymentMethod: true,
+ skipPlanPage: false,
+ },
+ effects: [
+ syncEffect({
+ refine: (value: unknown) => {
+ if (
+ typeof value === 'object' &&
+ value !== null &&
+ 'plan' in value &&
+ 'interval' in value &&
+ 'requirePaymentMethod' in value &&
+ 'skipPlanPage' in value
+ ) {
+ return {
+ type: 'success',
+ value: value as BillingCheckoutSession,
+ warnings: [],
+ } as const;
+ }
+ return {
+ type: 'failure',
+ message: 'Invalid BillingCheckoutSessionState',
+ path: [] as any,
+ } as const;
+ },
+ }),
+ ],
+});
diff --git a/packages/twenty-front/src/modules/auth/states/currentUserState.ts b/packages/twenty-front/src/modules/auth/states/currentUserState.ts
index 26880ddae..72f11157a 100644
--- a/packages/twenty-front/src/modules/auth/states/currentUserState.ts
+++ b/packages/twenty-front/src/modules/auth/states/currentUserState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { User } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/auth/states/currentWorkspaceMemberState.ts b/packages/twenty-front/src/modules/auth/states/currentWorkspaceMemberState.ts
index 4af6c5b8f..33013f124 100644
--- a/packages/twenty-front/src/modules/auth/states/currentWorkspaceMemberState.ts
+++ b/packages/twenty-front/src/modules/auth/states/currentWorkspaceMemberState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
diff --git a/packages/twenty-front/src/modules/auth/states/currentWorkspaceMembersStates.ts b/packages/twenty-front/src/modules/auth/states/currentWorkspaceMembersStates.ts
index a8059259a..160e786b6 100644
--- a/packages/twenty-front/src/modules/auth/states/currentWorkspaceMembersStates.ts
+++ b/packages/twenty-front/src/modules/auth/states/currentWorkspaceMembersStates.ts
@@ -1,5 +1,5 @@
import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const currentWorkspaceMembersState = createState<
CurrentWorkspaceMember[]
diff --git a/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts b/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts
index c06276c12..872c6af93 100644
--- a/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts
+++ b/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Workspace } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/auth/states/isCurrentUserLoadingState.ts b/packages/twenty-front/src/modules/auth/states/isCurrentUserLoadingState.ts
index 0a62d92ab..242739b7b 100644
--- a/packages/twenty-front/src/modules/auth/states/isCurrentUserLoadingState.ts
+++ b/packages/twenty-front/src/modules/auth/states/isCurrentUserLoadingState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isCurrentUserLoadedState = createState({
key: 'isCurrentUserLoadedState',
diff --git a/packages/twenty-front/src/modules/auth/states/isVerifyPendingState.ts b/packages/twenty-front/src/modules/auth/states/isVerifyPendingState.ts
index 567910389..0e554b0c8 100644
--- a/packages/twenty-front/src/modules/auth/states/isVerifyPendingState.ts
+++ b/packages/twenty-front/src/modules/auth/states/isVerifyPendingState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isVerifyPendingState = createState({
key: 'isVerifyPendingState',
diff --git a/packages/twenty-front/src/modules/auth/states/previousUrlState.ts b/packages/twenty-front/src/modules/auth/states/previousUrlState.ts
index 1de274fb1..951436d33 100644
--- a/packages/twenty-front/src/modules/auth/states/previousUrlState.ts
+++ b/packages/twenty-front/src/modules/auth/states/previousUrlState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const previousUrlState = createState({
key: 'previousUrlState',
diff --git a/packages/twenty-front/src/modules/auth/states/signInUpModeState.ts b/packages/twenty-front/src/modules/auth/states/signInUpModeState.ts
index 98da3b608..398fe2604 100644
--- a/packages/twenty-front/src/modules/auth/states/signInUpModeState.ts
+++ b/packages/twenty-front/src/modules/auth/states/signInUpModeState.ts
@@ -1,5 +1,5 @@
-import { createState } from 'twenty-ui';
import { SignInUpMode } from '@/auth/types/signInUpMode';
+import { createState } from '@ui/utilities/state/utils/createState';
export const signInUpModeState = createState({
key: 'signInUpModeState',
diff --git a/packages/twenty-front/src/modules/auth/states/signInUpStepState.ts b/packages/twenty-front/src/modules/auth/states/signInUpStepState.ts
index b3f3c4081..81a402371 100644
--- a/packages/twenty-front/src/modules/auth/states/signInUpStepState.ts
+++ b/packages/twenty-front/src/modules/auth/states/signInUpStepState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export enum SignInUpStep {
Init = 'init',
diff --git a/packages/twenty-front/src/modules/auth/states/tokenPairState.ts b/packages/twenty-front/src/modules/auth/states/tokenPairState.ts
index 718e67ad6..2d8d597e4 100644
--- a/packages/twenty-front/src/modules/auth/states/tokenPairState.ts
+++ b/packages/twenty-front/src/modules/auth/states/tokenPairState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { AuthTokenPair } from '~/generated/graphql';
import { cookieStorageEffect } from '~/utils/recoil-effects';
diff --git a/packages/twenty-front/src/modules/auth/states/workspacePublicDataState.ts b/packages/twenty-front/src/modules/auth/states/workspacePublicDataState.ts
index f4866fa65..64bafed36 100644
--- a/packages/twenty-front/src/modules/auth/states/workspacePublicDataState.ts
+++ b/packages/twenty-front/src/modules/auth/states/workspacePublicDataState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { PublicWorkspaceDataOutput } from '~/generated/graphql';
export const workspacePublicDataState =
diff --git a/packages/twenty-front/src/modules/auth/states/workspaces.ts b/packages/twenty-front/src/modules/auth/states/workspaces.ts
index da0d270bb..e38a4a146 100644
--- a/packages/twenty-front/src/modules/auth/states/workspaces.ts
+++ b/packages/twenty-front/src/modules/auth/states/workspaces.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Workspace } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/auth/types/billingCheckoutSession.type.ts b/packages/twenty-front/src/modules/auth/types/billingCheckoutSession.type.ts
new file mode 100644
index 000000000..a634e17e2
--- /dev/null
+++ b/packages/twenty-front/src/modules/auth/types/billingCheckoutSession.type.ts
@@ -0,0 +1,9 @@
+import { SubscriptionInterval } from '~/generated-metadata/graphql';
+import { BillingPlanKey } from '~/generated/graphql';
+
+export type BillingCheckoutSession = {
+ plan: BillingPlanKey;
+ interval: SubscriptionInterval;
+ requirePaymentMethod: boolean;
+ skipPlanPage: boolean;
+};
diff --git a/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts b/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts
index 53821543c..91820e9f0 100644
--- a/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts
+++ b/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts
@@ -4,10 +4,14 @@ export const CHECKOUT_SESSION = gql`
mutation CheckoutSession(
$recurringInterval: SubscriptionInterval!
$successUrlPath: String
+ $plan: BillingPlanKey!
+ $requirePaymentMethod: Boolean!
) {
checkoutSession(
recurringInterval: $recurringInterval
successUrlPath: $successUrlPath
+ plan: $plan
+ requirePaymentMethod: $requirePaymentMethod
) {
url
}
diff --git a/packages/twenty-front/src/modules/captcha/states/captchaTokenState.ts b/packages/twenty-front/src/modules/captcha/states/captchaTokenState.ts
index 24289cac6..7ec02e494 100644
--- a/packages/twenty-front/src/modules/captcha/states/captchaTokenState.ts
+++ b/packages/twenty-front/src/modules/captcha/states/captchaTokenState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const captchaTokenState = createState({
key: 'captchaTokenState',
diff --git a/packages/twenty-front/src/modules/captcha/states/isCaptchaScriptLoadedState.ts b/packages/twenty-front/src/modules/captcha/states/isCaptchaScriptLoadedState.ts
index 0db486bbe..f6fa69169 100644
--- a/packages/twenty-front/src/modules/captcha/states/isCaptchaScriptLoadedState.ts
+++ b/packages/twenty-front/src/modules/captcha/states/isCaptchaScriptLoadedState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isCaptchaScriptLoadedState = createState({
key: 'isCaptchaScriptLoadedState',
diff --git a/packages/twenty-front/src/modules/captcha/states/isRequestingCaptchaTokenState.ts b/packages/twenty-front/src/modules/captcha/states/isRequestingCaptchaTokenState.ts
index df7daeb4d..86f4e9f6a 100644
--- a/packages/twenty-front/src/modules/captcha/states/isRequestingCaptchaTokenState.ts
+++ b/packages/twenty-front/src/modules/captcha/states/isRequestingCaptchaTokenState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isRequestingCaptchaTokenState = createState({
key: 'isRequestingCaptchaTokenState',
diff --git a/packages/twenty-front/src/modules/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts b/packages/twenty-front/src/modules/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts
index e04798f19..91525e562 100644
--- a/packages/twenty-front/src/modules/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts
+++ b/packages/twenty-front/src/modules/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isLoadingTokensFromExtensionState = createState({
key: 'isLoadingTokensFromExtensionState',
diff --git a/packages/twenty-front/src/modules/client-config/states/apiConfigState.ts b/packages/twenty-front/src/modules/client-config/states/apiConfigState.ts
index 9a01493e4..77e8295c7 100644
--- a/packages/twenty-front/src/modules/client-config/states/apiConfigState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/apiConfigState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { ApiConfig } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/client-config/states/authProvidersState.ts b/packages/twenty-front/src/modules/client-config/states/authProvidersState.ts
index d56572b5a..46646c40d 100644
--- a/packages/twenty-front/src/modules/client-config/states/authProvidersState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/authProvidersState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { AuthProviders } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/client-config/states/billingState.ts b/packages/twenty-front/src/modules/client-config/states/billingState.ts
index 5634c510b..b188943b0 100644
--- a/packages/twenty-front/src/modules/client-config/states/billingState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/billingState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Billing } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/client-config/states/captchaProviderState.ts b/packages/twenty-front/src/modules/client-config/states/captchaProviderState.ts
index 2440f11a4..ca312acf0 100644
--- a/packages/twenty-front/src/modules/client-config/states/captchaProviderState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/captchaProviderState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Captcha } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/client-config/states/chromeExtensionIdState.ts b/packages/twenty-front/src/modules/client-config/states/chromeExtensionIdState.ts
index bec5ae986..b29db3633 100644
--- a/packages/twenty-front/src/modules/client-config/states/chromeExtensionIdState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/chromeExtensionIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const chromeExtensionIdState = createState({
key: 'chromeExtensionIdState',
diff --git a/packages/twenty-front/src/modules/client-config/states/clientConfigApiStatusState.ts b/packages/twenty-front/src/modules/client-config/states/clientConfigApiStatusState.ts
index 08793188f..9f5f6f9e5 100644
--- a/packages/twenty-front/src/modules/client-config/states/clientConfigApiStatusState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/clientConfigApiStatusState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
type ClientConfigApiStatus = {
isLoaded: boolean;
diff --git a/packages/twenty-front/src/modules/client-config/states/isAnalyticsEnabledState.ts b/packages/twenty-front/src/modules/client-config/states/isAnalyticsEnabledState.ts
index 50c0f5c89..f21835209 100644
--- a/packages/twenty-front/src/modules/client-config/states/isAnalyticsEnabledState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/isAnalyticsEnabledState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isAnalyticsEnabledState = createState({
key: 'isAnalyticsEnabled',
diff --git a/packages/twenty-front/src/modules/client-config/states/isDebugModeState.ts b/packages/twenty-front/src/modules/client-config/states/isDebugModeState.ts
index b2efbf8fc..037661f1a 100644
--- a/packages/twenty-front/src/modules/client-config/states/isDebugModeState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/isDebugModeState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isDebugModeState = createState({
key: 'isDebugModeState',
diff --git a/packages/twenty-front/src/modules/client-config/states/isDeveloperDefaultSignInPrefilledState.ts b/packages/twenty-front/src/modules/client-config/states/isDeveloperDefaultSignInPrefilledState.ts
index 5f4d82bed..30839339a 100644
--- a/packages/twenty-front/src/modules/client-config/states/isDeveloperDefaultSignInPrefilledState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/isDeveloperDefaultSignInPrefilledState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isDeveloperDefaultSignInPrefilledState = createState({
key: 'isDeveloperDefaultSignInPrefilledState',
diff --git a/packages/twenty-front/src/modules/client-config/states/isMultiWorkspaceEnabledState.ts b/packages/twenty-front/src/modules/client-config/states/isMultiWorkspaceEnabledState.ts
index 4749eeab9..a18c415f5 100644
--- a/packages/twenty-front/src/modules/client-config/states/isMultiWorkspaceEnabledState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/isMultiWorkspaceEnabledState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isMultiWorkspaceEnabledState = createState({
key: 'isMultiWorkspaceEnabled',
diff --git a/packages/twenty-front/src/modules/client-config/states/isSSOEnabledState.ts b/packages/twenty-front/src/modules/client-config/states/isSSOEnabledState.ts
index 3242678c3..7d40b6733 100644
--- a/packages/twenty-front/src/modules/client-config/states/isSSOEnabledState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/isSSOEnabledState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isSSOEnabledState = createState({
key: 'isSSOEnabledState',
diff --git a/packages/twenty-front/src/modules/client-config/states/sentryConfigState.ts b/packages/twenty-front/src/modules/client-config/states/sentryConfigState.ts
index 19ca03599..5046ce9b6 100644
--- a/packages/twenty-front/src/modules/client-config/states/sentryConfigState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/sentryConfigState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Sentry } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/client-config/states/supportChatState.ts b/packages/twenty-front/src/modules/client-config/states/supportChatState.ts
index cca337ba5..81d7fb901 100644
--- a/packages/twenty-front/src/modules/client-config/states/supportChatState.ts
+++ b/packages/twenty-front/src/modules/client-config/states/supportChatState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Support } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/modules/command-menu/states/commandMenuSearchState.ts b/packages/twenty-front/src/modules/command-menu/states/commandMenuSearchState.ts
index 61f580a8a..b7315d5d0 100644
--- a/packages/twenty-front/src/modules/command-menu/states/commandMenuSearchState.ts
+++ b/packages/twenty-front/src/modules/command-menu/states/commandMenuSearchState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const commandMenuSearchState = createState({
key: 'command-menu/commandMenuSearchState',
diff --git a/packages/twenty-front/src/modules/context-store/states/mainContextStoreComponentInstanceId.ts b/packages/twenty-front/src/modules/context-store/states/mainContextStoreComponentInstanceId.ts
index 461c03be8..2a1fffbb2 100644
--- a/packages/twenty-front/src/modules/context-store/states/mainContextStoreComponentInstanceId.ts
+++ b/packages/twenty-front/src/modules/context-store/states/mainContextStoreComponentInstanceId.ts
@@ -1,5 +1,5 @@
import { CONTEXT_STORE_INSTANCE_ID_DEFAULT_VALUE } from '@/context-store/constants/ContextStoreInstanceIdDefaultValue';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const mainContextStoreComponentInstanceIdState = createState({
key: 'mainContextStoreComponentInstanceIdState',
diff --git a/packages/twenty-front/src/modules/domain-manager/states/domainConfigurationState.ts b/packages/twenty-front/src/modules/domain-manager/states/domainConfigurationState.ts
index 89dc12404..0ea281bce 100644
--- a/packages/twenty-front/src/modules/domain-manager/states/domainConfigurationState.ts
+++ b/packages/twenty-front/src/modules/domain-manager/states/domainConfigurationState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { ClientConfig } from '~/generated/graphql';
export const domainConfigurationState = createState<
diff --git a/packages/twenty-front/src/modules/domain-manager/states/lastAuthenticatedWorkspaceDomainState.ts b/packages/twenty-front/src/modules/domain-manager/states/lastAuthenticatedWorkspaceDomainState.ts
index 6d8e458a3..67c44f43f 100644
--- a/packages/twenty-front/src/modules/domain-manager/states/lastAuthenticatedWorkspaceDomainState.ts
+++ b/packages/twenty-front/src/modules/domain-manager/states/lastAuthenticatedWorkspaceDomainState.ts
@@ -1,5 +1,5 @@
+import { createState } from '@ui/utilities/state/utils/createState';
import { cookieStorageEffect } from '~/utils/recoil-effects';
-import { createState } from 'twenty-ui';
export const lastAuthenticatedWorkspaceDomainState = createState<
| {
diff --git a/packages/twenty-front/src/modules/localization/states/dateTimeFormatState.ts b/packages/twenty-front/src/modules/localization/states/dateTimeFormatState.ts
index 2392151e3..60bd66c61 100644
--- a/packages/twenty-front/src/modules/localization/states/dateTimeFormatState.ts
+++ b/packages/twenty-front/src/modules/localization/states/dateTimeFormatState.ts
@@ -1,7 +1,7 @@
import { DateFormat } from '@/localization/constants/DateFormat';
import { TimeFormat } from '@/localization/constants/TimeFormat';
import { detectTimeZone } from '@/localization/utils/detectTimeZone';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const dateTimeFormatState = createState<{
timeZone: string;
diff --git a/packages/twenty-front/src/modules/navigation/states/currentMobileNavigationDrawerState.ts b/packages/twenty-front/src/modules/navigation/states/currentMobileNavigationDrawerState.ts
index c376b1953..540c6fad1 100644
--- a/packages/twenty-front/src/modules/navigation/states/currentMobileNavigationDrawerState.ts
+++ b/packages/twenty-front/src/modules/navigation/states/currentMobileNavigationDrawerState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const currentMobileNavigationDrawerState = createState<
'main' | 'settings'
diff --git a/packages/twenty-front/src/modules/object-metadata/states/isAppWaitingForFreshObjectMetadataState.ts b/packages/twenty-front/src/modules/object-metadata/states/isAppWaitingForFreshObjectMetadataState.ts
index 2cbe59765..e5eb25380 100644
--- a/packages/twenty-front/src/modules/object-metadata/states/isAppWaitingForFreshObjectMetadataState.ts
+++ b/packages/twenty-front/src/modules/object-metadata/states/isAppWaitingForFreshObjectMetadataState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isAppWaitingForFreshObjectMetadataState = createState({
key: 'isAppWaitingForFreshObjectMetadataState',
diff --git a/packages/twenty-front/src/modules/object-metadata/states/objectMetadataItemsState.ts b/packages/twenty-front/src/modules/object-metadata/states/objectMetadataItemsState.ts
index 5c247fe0e..57545e5b1 100644
--- a/packages/twenty-front/src/modules/object-metadata/states/objectMetadataItemsState.ts
+++ b/packages/twenty-front/src/modules/object-metadata/states/objectMetadataItemsState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/states/aggregateDropdownState.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/states/aggregateDropdownState.ts
index 31eeb84fc..310f9db0d 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/states/aggregateDropdownState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/states/aggregateDropdownState.ts
@@ -1,5 +1,5 @@
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
type AggregateOperation = {
operation: AGGREGATE_OPERATIONS | null;
diff --git a/packages/twenty-front/src/modules/object-record/record-field/states/lastShowPageRecordId.ts b/packages/twenty-front/src/modules/object-record/record-field/states/lastShowPageRecordId.ts
index dce923b37..42e159bbd 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/states/lastShowPageRecordId.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/states/lastShowPageRecordId.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const lastShowPageRecordIdState = createState({
key: 'lastShowPageRecordIdState',
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts
index 1b260d3c9..dfd92d42e 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts
index fc3462109..6af604887 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexIsCompactModeActiveState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexIsCompactModeActiveState.ts
index cd1d83fbb..3cc57f14f 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexIsCompactModeActiveState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexIsCompactModeActiveState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const recordIndexIsCompactModeActiveState = createState({
key: 'recordIndexIsCompactModeActiveState',
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanAggregateOperationState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanAggregateOperationState.ts
index 9b60fae0c..664a6755c 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanAggregateOperationState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanAggregateOperationState.ts
@@ -1,5 +1,5 @@
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export type KanbanAggregateOperation = {
operation?: AGGREGATE_OPERATIONS | null;
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState.ts
index f1527934d..bb28f4ad0 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const recordIndexKanbanFieldMetadataIdState = createState(
{
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts
index 57088c8e5..2d839afdd 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewFilterGroupsState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewFilterGroupsState.ts
index 978614756..ea07e363d 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewFilterGroupsState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewFilterGroupsState.ts
@@ -1,5 +1,5 @@
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const recordIndexViewFilterGroupsState = createState({
key: 'recordIndexViewFilterGroupsState',
diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewTypeState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewTypeState.ts
index 49b032c86..ad8fbfc5c 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewTypeState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewTypeState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { ViewType } from '@/views/types/ViewType';
diff --git a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/isNewViewableRecordLoading.ts b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/isNewViewableRecordLoading.ts
index 904677204..7e5b6e2bc 100644
--- a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/isNewViewableRecordLoading.ts
+++ b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/isNewViewableRecordLoading.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isNewViewableRecordLoadingState = createState({
key: 'activities/is-new-viewable-record-loading',
diff --git a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordIdState.ts b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordIdState.ts
index c75e6cf8a..ae72a9435 100644
--- a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordIdState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const viewableRecordIdState = createState({
key: 'activities/viewable-record-id',
diff --git a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordNameSingularState.ts b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordNameSingularState.ts
index 3116430e1..6e0e5a6ea 100644
--- a/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordNameSingularState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-right-drawer/states/viewableRecordNameSingularState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const viewableRecordNameSingularState = createState({
key: 'activities/viewable-record-name-singular',
diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordInGroup.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordInGroup.test.tsx
index ee690ec18..84ab42477 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordInGroup.test.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordInGroup.test.tsx
@@ -1,7 +1,7 @@
import { renderHook } from '@testing-library/react';
+import { createState } from '@ui/utilities/state/utils/createState';
import { ReactNode, act } from 'react';
import { RecoilRoot } from 'recoil';
-import { createState } from 'twenty-ui';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordNoGroup.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordNoGroup.test.tsx
index d1f29e2c9..ac3e65e89 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordNoGroup.test.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/__tests__/useUpsertTableRecordNoGroup.test.tsx
@@ -1,7 +1,7 @@
import { renderHook } from '@testing-library/react';
+import { createState } from '@ui/utilities/state/utils/createState';
import { ReactNode, act } from 'react';
import { RecoilRoot } from 'recoil';
-import { createState } from 'twenty-ui';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isRemoveSortingModalOpenState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isRemoveSortingModalOpenState.ts
index 9f8627f2a..542c069d2 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/states/isRemoveSortingModalOpenState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-table/states/isRemoveSortingModalOpenState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isRemoveSortingModalOpenState = createState({
key: 'isRemoveSortingModalOpenState',
diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts
index d25f46faa..4d295db42 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts
+++ b/packages/twenty-front/src/modules/object-record/record-table/states/isSoftFocusUsingMouseState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isSoftFocusUsingMouseState = createState({
key: 'isSoftFocusUsingMouseState',
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/preview/states/settingsPreviewRecordIdState.ts b/packages/twenty-front/src/modules/settings/data-model/fields/preview/states/settingsPreviewRecordIdState.ts
index 68a5bbff9..a810c8c00 100644
--- a/packages/twenty-front/src/modules/settings/data-model/fields/preview/states/settingsPreviewRecordIdState.ts
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/preview/states/settingsPreviewRecordIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const settingsPreviewRecordIdState = createState({
key: 'settingsPreviewRecordIdState',
diff --git a/packages/twenty-front/src/modules/settings/developers/states/generatedApiKeyTokenState.ts b/packages/twenty-front/src/modules/settings/developers/states/generatedApiKeyTokenState.ts
index 6129e451c..0fc6a43a4 100644
--- a/packages/twenty-front/src/modules/settings/developers/states/generatedApiKeyTokenState.ts
+++ b/packages/twenty-front/src/modules/settings/developers/states/generatedApiKeyTokenState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const apiKeyTokenState = createState({
key: 'apiKeyTokenState',
diff --git a/packages/twenty-front/src/modules/settings/security/states/SSOIdentitiesProvidersState.ts b/packages/twenty-front/src/modules/settings/security/states/SSOIdentitiesProvidersState.ts
index 76dc7cfdf..db9cd89fb 100644
--- a/packages/twenty-front/src/modules/settings/security/states/SSOIdentitiesProvidersState.ts
+++ b/packages/twenty-front/src/modules/settings/security/states/SSOIdentitiesProvidersState.ts
@@ -1,7 +1,7 @@
/* @license Enterprise */
import { SSOIdentityProvider } from '@/settings/security/types/SSOIdentityProvider';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const SSOIdentitiesProvidersState = createState<
Omit[]
diff --git a/packages/twenty-front/src/modules/spreadsheet-import/states/spreadsheetImportDialogState.ts b/packages/twenty-front/src/modules/spreadsheet-import/states/spreadsheetImportDialogState.ts
index fceda6736..4aad187fa 100644
--- a/packages/twenty-front/src/modules/spreadsheet-import/states/spreadsheetImportDialogState.ts
+++ b/packages/twenty-front/src/modules/spreadsheet-import/states/spreadsheetImportDialogState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { SpreadsheetImportDialogOptions } from '../types';
diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/states/activeDropdownFocusIdState.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/states/activeDropdownFocusIdState.ts
index fca8a5ba6..17cf76ba6 100644
--- a/packages/twenty-front/src/modules/ui/layout/dropdown/states/activeDropdownFocusIdState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/dropdown/states/activeDropdownFocusIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const activeDropdownFocusIdState = createState({
key: 'activeDropdownFocusIdState',
diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/states/previousDropdownFocusIdState.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/states/previousDropdownFocusIdState.ts
index e311d1f3f..060d78a19 100644
--- a/packages/twenty-front/src/modules/ui/layout/dropdown/states/previousDropdownFocusIdState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/dropdown/states/previousDropdownFocusIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const previousDropdownFocusIdState = createState({
key: 'previousDropdownFocusIdState',
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerAnimationCompletedState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerAnimationCompletedState.ts
index 3e1cb030a..61d75e2d5 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerAnimationCompletedState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerAnimationCompletedState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isRightDrawerAnimationCompletedState = createState({
key: 'isRightDrawerAnimationCompletedState',
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerMinimizedState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerMinimizedState.ts
index 9b2124030..822f909c0 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerMinimizedState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerMinimizedState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isRightDrawerMinimizedState = createState({
key: 'ui/layout/is-right-drawer-minimized',
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts
index b0149a588..0fad6afab 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isRightDrawerOpenState = createState({
key: 'ui/layout/is-right-drawer-open',
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/messageThreadState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/messageThreadState.ts
index 2e1febcf6..77f9cd76a 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/messageThreadState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/messageThreadState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { MessageThread } from '@/activities/emails/types/MessageThread';
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerCloseEventsState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerCloseEventsState.ts
index ac0813d8e..9601f1572 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerCloseEventsState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerCloseEventsState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const rightDrawerCloseEventState = createState({
key: 'rightDrawerCloseEventState',
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerHeaderDropdownButtonState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerHeaderDropdownButtonState.ts
index a0f2a8496..e61330290 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerHeaderDropdownButtonState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerHeaderDropdownButtonState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { RightDrawerTopBarDropdownButtons } from '@/ui/layout/right-drawer/types/RightDrawerTopBarDropdownButtons';
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts
index 0cbcdee85..441988db3 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { RightDrawerPages } from '../types/RightDrawerPages';
diff --git a/packages/twenty-front/src/modules/ui/layout/states/isDefaultLayoutAuthModalVisibleState.ts b/packages/twenty-front/src/modules/ui/layout/states/isDefaultLayoutAuthModalVisibleState.ts
index c181a6a7d..7054e811e 100644
--- a/packages/twenty-front/src/modules/ui/layout/states/isDefaultLayoutAuthModalVisibleState.ts
+++ b/packages/twenty-front/src/modules/ui/layout/states/isDefaultLayoutAuthModalVisibleState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const isDefaultLayoutAuthModalVisibleState = createState({
key: 'isDefaultLayoutAuthModalVisibleState',
diff --git a/packages/twenty-front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts b/packages/twenty-front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts
index bb8318ecd..b8b681891 100644
--- a/packages/twenty-front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts
+++ b/packages/twenty-front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const navigationMemorizedUrlState = createState({
key: 'navigationMemorizedUrlState',
diff --git a/packages/twenty-front/src/modules/ui/navigation/step-bar/states/stepBarInternalState.ts b/packages/twenty-front/src/modules/ui/navigation/step-bar/states/stepBarInternalState.ts
index d2bf60d60..e25c33352 100644
--- a/packages/twenty-front/src/modules/ui/navigation/step-bar/states/stepBarInternalState.ts
+++ b/packages/twenty-front/src/modules/ui/navigation/step-bar/states/stepBarInternalState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export type StepsState = {
activeStep: number;
diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/currentHotkeyScopeState.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/currentHotkeyScopeState.ts
index 6b41a6705..61b8165f0 100644
--- a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/currentHotkeyScopeState.ts
+++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/currentHotkeyScopeState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { INITIAL_HOTKEYS_SCOPE } from '../../constants/InitialHotkeysScope';
import { HotkeyScope } from '../../types/HotkeyScope';
diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState.ts
index e2e06b62f..9478ec886 100644
--- a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState.ts
+++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const internalHotkeysEnabledScopesState = createState({
key: 'internalHotkeysEnabledScopesState',
diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts
index 13e1cc652..d4d8db3b1 100644
--- a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts
+++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts
@@ -1,5 +1,5 @@
+import { createState } from '@ui/utilities/state/utils/createState';
import { Keys } from 'react-hotkeys-hook/dist/types';
-import { createState } from 'twenty-ui';
export const pendingHotkeyState = createState({
key: 'pendingHotkeyState',
diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/previousHotkeyScopeState.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/previousHotkeyScopeState.ts
index 3e438fef1..4585d8f32 100644
--- a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/previousHotkeyScopeState.ts
+++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/previousHotkeyScopeState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { HotkeyScope } from '../../types/HotkeyScope';
diff --git a/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts b/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts
index 601831e5a..0deeeb3dd 100644
--- a/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts
+++ b/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const currentPageLocationState = createState({
key: 'currentPageLocationState',
diff --git a/packages/twenty-front/src/modules/workflow/states/openOverrideWorkflowDraftConfirmationModalState.ts b/packages/twenty-front/src/modules/workflow/states/openOverrideWorkflowDraftConfirmationModalState.ts
index 1320a9642..97da85be3 100644
--- a/packages/twenty-front/src/modules/workflow/states/openOverrideWorkflowDraftConfirmationModalState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/openOverrideWorkflowDraftConfirmationModalState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const openOverrideWorkflowDraftConfirmationModalState =
createState({
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowCreateStepFromParentStepIdState.ts b/packages/twenty-front/src/modules/workflow/states/workflowCreateStepFromParentStepIdState.ts
index 4e64010b9..95752ce60 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowCreateStepFromParentStepIdState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowCreateStepFromParentStepIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workflowCreateStepFromParentStepIdState = createState<
string | undefined
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowDiagramState.ts b/packages/twenty-front/src/modules/workflow/states/workflowDiagramState.ts
index 5f51bf85c..2d2f22a7c 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowDiagramState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowDiagramState.ts
@@ -1,5 +1,5 @@
import { WorkflowDiagram } from '@/workflow/types/WorkflowDiagram';
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workflowDiagramState = createState({
key: 'workflowDiagramState',
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowDiagramTriggerNodeSelectionState.ts b/packages/twenty-front/src/modules/workflow/states/workflowDiagramTriggerNodeSelectionState.ts
index 91630a300..06e64aded 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowDiagramTriggerNodeSelectionState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowDiagramTriggerNodeSelectionState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workflowDiagramTriggerNodeSelectionState = createState<
string | undefined
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowIdState.ts b/packages/twenty-front/src/modules/workflow/states/workflowIdState.ts
index 112729ea4..f107f5dd4 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowIdState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workflowIdState = createState({
key: 'workflowIdState',
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdState.ts b/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdState.ts
index e39718e9c..998ac7a86 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workflowLastCreatedStepIdState = createState({
key: 'workflowLastCreatedStepIdState',
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowReactFlowRefState.ts b/packages/twenty-front/src/modules/workflow/states/workflowReactFlowRefState.ts
index 7a56abac0..579eff7db 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowReactFlowRefState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowReactFlowRefState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { RefObject } from 'react';
export const workflowReactFlowRefState =
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowSelectedNodeState.ts b/packages/twenty-front/src/modules/workflow/states/workflowSelectedNodeState.ts
index d920489d6..b393f8627 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowSelectedNodeState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowSelectedNodeState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workflowSelectedNodeState = createState({
key: 'workflowSelectedNodeState',
diff --git a/packages/twenty-front/src/modules/workflow/states/workflowVersionIdState.ts b/packages/twenty-front/src/modules/workflow/states/workflowVersionIdState.ts
index 289469796..f72cb3933 100644
--- a/packages/twenty-front/src/modules/workflow/states/workflowVersionIdState.ts
+++ b/packages/twenty-front/src/modules/workflow/states/workflowVersionIdState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workflowVersionIdState = createState({
key: 'workflowVersionIdState',
diff --git a/packages/twenty-front/src/modules/workspace-invitation/states/workspaceInvitationsStates.ts b/packages/twenty-front/src/modules/workspace-invitation/states/workspaceInvitationsStates.ts
index 8f550a1ad..80b84c3ab 100644
--- a/packages/twenty-front/src/modules/workspace-invitation/states/workspaceInvitationsStates.ts
+++ b/packages/twenty-front/src/modules/workspace-invitation/states/workspaceInvitationsStates.ts
@@ -1,5 +1,5 @@
-import { createState } from 'twenty-ui';
import { WorkspaceInvitation } from '@/workspace-member/types/WorkspaceMember';
+import { createState } from '@ui/utilities/state/utils/createState';
export const workspaceInvitationsState = createState<
Omit[]
diff --git a/packages/twenty-front/src/modules/workspace/states/workspaceAuthProvidersState.ts b/packages/twenty-front/src/modules/workspace/states/workspaceAuthProvidersState.ts
index 43b360a0c..69384bbb5 100644
--- a/packages/twenty-front/src/modules/workspace/states/workspaceAuthProvidersState.ts
+++ b/packages/twenty-front/src/modules/workspace/states/workspaceAuthProvidersState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
import { AuthProviders } from '~/generated/graphql';
diff --git a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
index 15f8a4d79..493a8dfae 100644
--- a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
+++ b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
@@ -1,6 +1,7 @@
import { SubTitle } from '@/auth/components/SubTitle';
import { Title } from '@/auth/components/Title';
import { useAuth } from '@/auth/hooks/useAuth';
+import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
import { SubscriptionBenefit } from '@/billing/components/SubscriptionBenefit';
import { SubscriptionCard } from '@/billing/components/SubscriptionCard';
import { billingState } from '@/client-config/states/billingState';
@@ -10,7 +11,7 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import styled from '@emotion/styled';
import { isNonEmptyString, isNumber } from '@sniptt/guards';
import { useState } from 'react';
-import { useRecoilValue } from 'recoil';
+import { useRecoilState, useRecoilValue } from 'recoil';
import {
ActionLink,
CAL_LINK,
@@ -77,8 +78,6 @@ const benefits = [
export const ChooseYourPlan = () => {
const billing = useRecoilValue(billingState);
- const [planSelected, setPlanSelected] = useState(SubscriptionInterval.Month);
-
const [isSubmitting, setIsSubmitting] = useState(false);
const { enqueueSnackBar } = useSnackBar();
@@ -87,12 +86,44 @@ export const ChooseYourPlan = () => {
variables: { product: 'base-plan' },
});
+ const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState(
+ billingCheckoutSessionState,
+ );
+
const [checkoutSession] = useCheckoutSessionMutation();
- const handlePlanChange = (type?: SubscriptionInterval) => {
+ const handleCheckoutSession = async () => {
+ setIsSubmitting(true);
+ const { data } = await checkoutSession({
+ variables: {
+ recurringInterval: billingCheckoutSession.interval,
+ successUrlPath: AppPath.PlanRequiredSuccess,
+ plan: billingCheckoutSession.plan,
+ requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
+ },
+ });
+ setIsSubmitting(false);
+ if (!data?.checkoutSession.url) {
+ enqueueSnackBar(
+ 'Checkout session error. Please retry or contact Twenty team',
+ {
+ variant: SnackBarVariant.Error,
+ },
+ );
+ return;
+ }
+ window.location.replace(data.checkoutSession.url);
+ };
+
+ const handleIntervalChange = (type?: SubscriptionInterval) => {
return () => {
- if (isNonEmptyString(type) && planSelected !== type) {
- setPlanSelected(type);
+ if (isNonEmptyString(type) && billingCheckoutSession.interval !== type) {
+ setBillingCheckoutSession({
+ plan: billingCheckoutSession.plan,
+ interval: type,
+ requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
+ skipPlanPage: false,
+ });
}
};
};
@@ -121,26 +152,13 @@ export const ChooseYourPlan = () => {
return 'Cancel anytime';
};
- const handleButtonClick = async () => {
- setIsSubmitting(true);
- const { data } = await checkoutSession({
- variables: {
- recurringInterval: planSelected,
- successUrlPath: AppPath.PlanRequiredSuccess,
- },
- });
- setIsSubmitting(false);
- if (!data?.checkoutSession.url) {
- enqueueSnackBar(
- 'Checkout session error. Please retry or contact Twenty team',
- {
- variant: SnackBarVariant.Error,
- },
- );
- return;
- }
- window.location.replace(data.checkoutSession.url);
- };
+ if (billingCheckoutSession.skipPlanPage && !isSubmitting) {
+ handleCheckoutSession();
+ }
+
+ if (billingCheckoutSession.skipPlanPage || isSubmitting) {
+ return ;
+ }
return (
prices?.getProductPrices?.productPrices && (
@@ -152,8 +170,10 @@ export const ChooseYourPlan = () => {
{prices.getProductPrices.productPrices.map((price, index) => (
{
isSubmitting && }
disabled={isSubmitting}
diff --git a/packages/twenty-front/src/pages/settings/data-model/states/updatedObjectSlugState.ts b/packages/twenty-front/src/pages/settings/data-model/states/updatedObjectSlugState.ts
index be64d0b50..6140293dc 100644
--- a/packages/twenty-front/src/pages/settings/data-model/states/updatedObjectSlugState.ts
+++ b/packages/twenty-front/src/pages/settings/data-model/states/updatedObjectSlugState.ts
@@ -1,4 +1,4 @@
-import { createState } from 'twenty-ui';
+import { createState } from '@ui/utilities/state/utils/createState';
export const updatedObjectSlugState = createState({
key: 'updatedObjectSlugState',
diff --git a/packages/twenty-front/src/testing/decorators/PageDecorator.tsx b/packages/twenty-front/src/testing/decorators/PageDecorator.tsx
index 97232ffc1..1fcbce56f 100644
--- a/packages/twenty-front/src/testing/decorators/PageDecorator.tsx
+++ b/packages/twenty-front/src/testing/decorators/PageDecorator.tsx
@@ -23,9 +23,9 @@ import { mockedApolloClient } from '~/testing/mockedApolloClient';
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider';
+import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProviderEffect';
import { IconsProvider } from 'twenty-ui';
import { FullHeightStorybookLayout } from '../FullHeightStorybookLayout';
-import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProviderEffect';
export type PageDecoratorArgs = {
routePath: string;
diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts
index bb5414f08..31b83489f 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts
@@ -8,9 +8,10 @@ import {
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
-import { Repository } from 'typeorm';
import { Response } from 'express';
+import { Repository } from 'typeorm';
+import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
import { AuthOAuthExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-oauth-exception.filter';
import { AuthRestApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-rest-api-exception.filter';
import { GoogleOauthGuard } from 'src/engine/core-modules/auth/guards/google-oauth.guard';
@@ -18,9 +19,8 @@ import { GoogleProviderEnabledGuard } from 'src/engine/core-modules/auth/guards/
import { AuthService } from 'src/engine/core-modules/auth/services/auth.service';
import { GoogleRequest } from 'src/engine/core-modules/auth/strategies/google.auth.strategy';
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
-import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
-import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
+import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@Controller('auth/google')
@@ -55,6 +55,7 @@ export class GoogleAuthController {
workspaceInviteHash,
workspacePersonalInviteToken,
targetWorkspaceSubdomain,
+ billingCheckoutSessionState,
} = req.user;
const signInUpParams = {
@@ -106,6 +107,7 @@ export class GoogleAuthController {
this.authService.computeRedirectURI(
loginToken.token,
workspace.subdomain,
+ billingCheckoutSessionState,
),
);
} catch (err) {
diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts
index 3d12f2a91..131c56207 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts
@@ -11,15 +11,15 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Response } from 'express';
import { Repository } from 'typeorm';
+import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
import { AuthRestApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-rest-api-exception.filter';
import { MicrosoftOAuthGuard } from 'src/engine/core-modules/auth/guards/microsoft-oauth.guard';
import { MicrosoftProviderEnabledGuard } from 'src/engine/core-modules/auth/guards/microsoft-provider-enabled.guard';
import { AuthService } from 'src/engine/core-modules/auth/services/auth.service';
import { MicrosoftRequest } from 'src/engine/core-modules/auth/strategies/microsoft.auth.strategy';
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
-import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
-import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
+import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@Controller('auth/microsoft')
@@ -90,6 +90,7 @@ export class MicrosoftAuthController {
this.authService.computeRedirectURI(
loginToken.token,
workspace.subdomain,
+ signInUpParams.billingCheckoutSessionState,
),
);
} catch (err) {
diff --git a/packages/twenty-server/src/engine/core-modules/auth/guards/google-oauth.guard.ts b/packages/twenty-server/src/engine/core-modules/auth/guards/google-oauth.guard.ts
index 8f2f6b95c..2cb7e93a1 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/guards/google-oauth.guard.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/guards/google-oauth.guard.ts
@@ -45,6 +45,14 @@ export class GoogleOauthGuard extends AuthGuard('google') {
request.params.workspaceSubdomain = request.query.workspaceSubdomain;
}
+ if (
+ request.query.billingCheckoutSessionState &&
+ typeof request.query.billingCheckoutSessionState === 'string'
+ ) {
+ request.params.billingCheckoutSessionState =
+ request.query.billingCheckoutSessionState;
+ }
+
return (await super.canActivate(context)) as boolean;
}
}
diff --git a/packages/twenty-server/src/engine/core-modules/auth/guards/microsoft-oauth.guard.ts b/packages/twenty-server/src/engine/core-modules/auth/guards/microsoft-oauth.guard.ts
index 049f14789..6a2d40faf 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/guards/microsoft-oauth.guard.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/guards/microsoft-oauth.guard.ts
@@ -33,6 +33,14 @@ export class MicrosoftOAuthGuard extends AuthGuard('microsoft') {
request.params.workspaceSubdomain = request.query.workspaceSubdomain;
}
+ if (
+ request.query.billingCheckoutSessionState &&
+ typeof request.query.billingCheckoutSessionState === 'string'
+ ) {
+ request.params.billingCheckoutSessionState =
+ request.query.billingCheckoutSessionState;
+ }
+
return (await super.canActivate(context)) as boolean;
}
}
diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts
index a045f40de..9a17c69f8 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts
@@ -28,6 +28,7 @@ import { AuthorizeApp } from 'src/engine/core-modules/auth/dto/authorize-app.ent
import { AuthorizeAppInput } from 'src/engine/core-modules/auth/dto/authorize-app.input';
import { AvailableWorkspaceOutput } from 'src/engine/core-modules/auth/dto/available-workspaces.output';
import { ChallengeInput } from 'src/engine/core-modules/auth/dto/challenge.input';
+import { AuthTokens } from 'src/engine/core-modules/auth/dto/token.entity';
import { UpdatePassword } from 'src/engine/core-modules/auth/dto/update-password.entity';
import {
UserExists,
@@ -46,7 +47,6 @@ import { userValidator } from 'src/engine/core-modules/user/user.validate';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
-import { AuthTokens } from 'src/engine/core-modules/auth/dto/token.entity';
@Injectable()
// eslint-disable-next-line @nx/workspace-inject-workspace-repository
@@ -171,6 +171,7 @@ export class AuthService {
fromSSO: boolean;
targetWorkspaceSubdomain?: string;
authProvider?: WorkspaceAuthProvider;
+ billingCheckoutSessionState?: string;
}) {
return await this.signInUpService.signInUp({
email,
@@ -413,11 +414,18 @@ export class AuthService {
return workspace;
}
- computeRedirectURI(loginToken: string, subdomain?: string) {
+ computeRedirectURI(
+ loginToken: string,
+ subdomain?: string,
+ billingCheckoutSessionState?: string,
+ ) {
const url = this.domainManagerService.buildWorkspaceURL({
subdomain,
pathname: '/verify',
- searchParams: { loginToken },
+ searchParams: {
+ loginToken,
+ ...(billingCheckoutSessionState ? { billingCheckoutSessionState } : {}),
+ },
});
return url.toString();
diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts
index e86e60a7f..53a8028cb 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts
@@ -35,8 +35,8 @@ import {
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email';
import { getImageBufferFromUrl } from 'src/utils/image';
-import { isWorkEmail } from 'src/utils/is-work-email';
import { isDefined } from 'src/utils/is-defined';
+import { isWorkEmail } from 'src/utils/is-work-email';
export type SignInUpServiceInput = {
email: string;
diff --git a/packages/twenty-server/src/engine/core-modules/auth/strategies/google.auth.strategy.ts b/packages/twenty-server/src/engine/core-modules/auth/strategies/google.auth.strategy.ts
index 5e20e1a60..b673e7449 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/strategies/google.auth.strategy.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/strategies/google.auth.strategy.ts
@@ -18,6 +18,7 @@ export type GoogleRequest = Omit<
workspaceInviteHash?: string;
workspacePersonalInviteToken?: string;
targetWorkspaceSubdomain?: string;
+ billingCheckoutSessionState?: string;
};
};
@@ -39,6 +40,12 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
state: JSON.stringify({
workspaceInviteHash: req.params.workspaceInviteHash,
workspaceSubdomain: req.params.workspaceSubdomain,
+ ...(req.params.billingCheckoutSessionState
+ ? {
+ billingCheckoutSessionState:
+ req.params.billingCheckoutSessionState,
+ }
+ : {}),
...(req.params.workspacePersonalInviteToken
? {
workspacePersonalInviteToken:
@@ -72,6 +79,7 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
workspaceInviteHash: state.workspaceInviteHash,
workspacePersonalInviteToken: state.workspacePersonalInviteToken,
targetWorkspaceSubdomain: state.workspaceSubdomain,
+ billingCheckoutSessionState: state.billingCheckoutSessionState,
};
done(null, user);
diff --git a/packages/twenty-server/src/engine/core-modules/auth/strategies/microsoft.auth.strategy.ts b/packages/twenty-server/src/engine/core-modules/auth/strategies/microsoft.auth.strategy.ts
index 39077fb6d..1ae2afb3c 100644
--- a/packages/twenty-server/src/engine/core-modules/auth/strategies/microsoft.auth.strategy.ts
+++ b/packages/twenty-server/src/engine/core-modules/auth/strategies/microsoft.auth.strategy.ts
@@ -22,6 +22,7 @@ export type MicrosoftRequest = Omit<
workspaceInviteHash?: string;
workspacePersonalInviteToken?: string;
targetWorkspaceSubdomain?: string;
+ billingCheckoutSessionState?: string;
};
};
@@ -43,6 +44,12 @@ export class MicrosoftStrategy extends PassportStrategy(Strategy, 'microsoft') {
state: JSON.stringify({
workspaceInviteHash: req.params.workspaceInviteHash,
workspaceSubdomain: req.params.workspaceSubdomain,
+ ...(req.params.billingCheckoutSessionState
+ ? {
+ billingCheckoutSessionState:
+ req.params.billingCheckoutSessionState,
+ }
+ : {}),
...(req.params.workspacePersonalInviteToken
? {
workspacePersonalInviteToken:
@@ -86,6 +93,7 @@ export class MicrosoftStrategy extends PassportStrategy(Strategy, 'microsoft') {
workspaceInviteHash: state.workspaceInviteHash,
workspacePersonalInviteToken: state.workspacePersonalInviteToken,
targetWorkspaceSubdomain: state.workspaceSubdomain,
+ billingCheckoutSessionState: state.billingCheckoutSessionState,
};
done(null, user);
diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts
index 4dcc92f96..df2d4469d 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts
@@ -56,7 +56,13 @@ export class BillingResolver {
async checkoutSession(
@AuthWorkspace() workspace: Workspace,
@AuthUser() user: User,
- @Args() { recurringInterval, successUrlPath }: CheckoutSessionInput,
+ @Args()
+ {
+ recurringInterval,
+ successUrlPath,
+ plan,
+ requirePaymentMethod,
+ }: CheckoutSessionInput,
) {
const productPrice = await this.stripeService.getStripePrice(
AvailableProduct.BasePlan,
@@ -75,6 +81,8 @@ export class BillingResolver {
workspace,
productPrice.stripePriceId,
successUrlPath,
+ plan,
+ requirePaymentMethod,
),
};
}
diff --git a/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts b/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts
index 9371eee8f..b5c882be2 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts
@@ -1,16 +1,32 @@
import { ArgsType, Field } from '@nestjs/graphql';
-import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
-import Stripe from 'stripe';
+import {
+ IsBoolean,
+ IsEnum,
+ IsNotEmpty,
+ IsOptional,
+ IsString,
+} from 'class-validator';
+import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum';
import { SubscriptionInterval } from 'src/engine/core-modules/billing/enums/billing-subscription-interval.enum';
@ArgsType()
export class CheckoutSessionInput {
@Field(() => SubscriptionInterval)
- @IsString()
+ @IsEnum(SubscriptionInterval)
@IsNotEmpty()
- recurringInterval: Stripe.Price.Recurring.Interval;
+ recurringInterval: SubscriptionInterval;
+
+ @Field(() => BillingPlanKey, { defaultValue: BillingPlanKey.PRO })
+ @IsEnum(BillingPlanKey)
+ @IsOptional()
+ plan?: BillingPlanKey;
+
+ @Field(() => Boolean, { defaultValue: true })
+ @IsBoolean()
+ @IsOptional()
+ requirePaymentMethod?: boolean;
@Field(() => String, { nullable: true })
@IsString()
diff --git a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-plan-key.enum.ts b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-plan-key.enum.ts
index 2f8aa1eb1..55b4e2ad6 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/enums/billing-plan-key.enum.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/enums/billing-plan-key.enum.ts
@@ -1,4 +1,12 @@
+import { registerEnumType } from '@nestjs/graphql';
+
export enum BillingPlanKey {
- BASE_PLAN = 'BASE_PLAN',
- PRO_PLAN = 'PRO_PLAN',
+ BASE = 'BASE',
+ PRO = 'PRO',
+ ENTERPRISE = 'ENTERPRISE',
}
+
+registerEnumType(BillingPlanKey, {
+ name: 'BillingPlanKey',
+ description: 'The different billing plans available',
+});
diff --git a/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts b/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts
index 44b26626d..f422b07dc 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts
@@ -4,6 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.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 { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
@@ -30,6 +31,8 @@ export class BillingPortalWorkspaceService {
workspace: Workspace,
priceId: string,
successUrlPath?: string,
+ plan?: BillingPlanKey,
+ requirePaymentMethod?: boolean,
): Promise {
const frontBaseUrl = this.domainManagerService.getBaseUrl();
const cancelUrl = frontBaseUrl.toString();
@@ -57,6 +60,8 @@ export class BillingPortalWorkspaceService {
successUrl,
cancelUrl,
stripeCustomerId,
+ plan,
+ requirePaymentMethod,
);
assert(session.url, 'Error: missing checkout.session.url');
diff --git a/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook-product.service.ts b/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook-product.service.ts
index 1c9fcf859..084a4759b 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook-product.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/services/billing-webhook-product.service.ts
@@ -52,9 +52,9 @@ export class BillingWebhookProductService {
isValidBillingPlanKey(planKey?: string) {
switch (planKey) {
- case BillingPlanKey.BASE_PLAN:
+ case BillingPlanKey.BASE:
return true;
- case BillingPlanKey.PRO_PLAN:
+ case BillingPlanKey.PRO:
return true;
default:
return false;
diff --git a/packages/twenty-server/src/engine/core-modules/billing/stripe/stripe.service.ts b/packages/twenty-server/src/engine/core-modules/billing/stripe/stripe.service.ts
index 62d66fb79..cd837ae8f 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/stripe/stripe.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/stripe/stripe.service.ts
@@ -5,6 +5,7 @@ import Stripe from 'stripe';
import { ProductPriceEntity } from 'src/engine/core-modules/billing/dto/product-price.entity';
import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
import { AvailableProduct } from 'src/engine/core-modules/billing/enums/billing-available-product.enum';
+import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { User } from 'src/engine/core-modules/user/user.entity';
@@ -90,6 +91,8 @@ export class StripeService {
successUrl?: string,
cancelUrl?: string,
stripeCustomerId?: string,
+ plan: BillingPlanKey = BillingPlanKey.PRO,
+ requirePaymentMethod = true,
): Promise {
return await this.stripe.checkout.sessions.create({
line_items: [
@@ -102,18 +105,22 @@ export class StripeService {
subscription_data: {
metadata: {
workspaceId,
+ plan,
},
trial_period_days: this.environmentService.get(
'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
),
},
- automatic_tax: { enabled: true },
- tax_id_collection: { enabled: true },
+ automatic_tax: { enabled: !!requirePaymentMethod },
+ tax_id_collection: { enabled: !!requirePaymentMethod },
customer: stripeCustomerId,
customer_update: stripeCustomerId ? { name: 'auto' } : undefined,
customer_email: stripeCustomerId ? undefined : user.email,
success_url: successUrl,
cancel_url: cancelUrl,
+ payment_method_collection: requirePaymentMethod
+ ? 'always'
+ : 'if_required',
});
}
diff --git a/yarn.lock b/yarn.lock
index 00f102542..0b46335c9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11576,6 +11576,13 @@ __metadata:
languageName: node
linkType: hard
+"@recoiljs/refine@npm:^0.1.1":
+ version: 0.1.1
+ resolution: "@recoiljs/refine@npm:0.1.1"
+ checksum: 10c0/27ca1c4ea500b1b99a3af5ee48d6749310e5138e83b87ddfb41304e2222fa64567acb985f340334ab73980202ab277a0f133c40817fbec786076c06bfb3f5363
+ languageName: node
+ linkType: hard
+
"@redis/bloom@npm:1.2.0, @redis/bloom@npm:^1.2.0":
version: 1.2.0
resolution: "@redis/bloom@npm:1.2.0"
@@ -40639,6 +40646,18 @@ __metadata:
languageName: node
linkType: hard
+"recoil-sync@npm:^0.2.0":
+ version: 0.2.0
+ resolution: "recoil-sync@npm:0.2.0"
+ dependencies:
+ "@recoiljs/refine": "npm:^0.1.1"
+ transit-js: "npm:^0.8.874"
+ peerDependencies:
+ recoil: ">=0.7.3"
+ checksum: 10c0/f3a671a3cfcecadb5bbb22d47b3b040721be1013ee91d4fa0570a64ca1707eb68e818b291a82e133551adf4a2dd2fc9a0715d550c1f1e3db82f3b8d9169e2a5a
+ languageName: node
+ linkType: hard
+
"recoil@npm:^0.7.7":
version: 0.7.7
resolution: "recoil@npm:0.7.7"
@@ -44346,6 +44365,13 @@ __metadata:
languageName: node
linkType: hard
+"transit-js@npm:^0.8.874":
+ version: 0.8.874
+ resolution: "transit-js@npm:0.8.874"
+ checksum: 10c0/6ca0b413f1e3780a4a56b9bbde54b67a2ffefda1c052f8dc68bf28c1a3df4c29baa6f17f60484c9f2fb056a8c7dcd1fe04d24e091fb99afe1525e4d4406ad58a
+ languageName: node
+ linkType: hard
+
"transliteration@npm:^2.3.5":
version: 2.3.5
resolution: "transliteration@npm:2.3.5"
@@ -44879,6 +44905,7 @@ __metadata:
buffer: "npm:^6.0.3"
docx: "npm:^9.1.0"
file-saver: "npm:^2.0.5"
+ recoil-sync: "npm:^0.2.0"
transliteration: "npm:^2.3.5"
languageName: unknown
linkType: soft