diff --git a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx index 9e91ce7c9..9cad8927d 100644 --- a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx +++ b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx @@ -1,11 +1,9 @@ import { useEffect, useState } from 'react'; import { - createSearchParams, matchPath, useLocation, useNavigate, useParams, - useSearchParams, } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; @@ -65,28 +63,11 @@ export const PageChangeEffect = () => { } }, [location, previousLocation, executeTasksOnAnyLocationChange]); - const [searchParams] = useSearchParams(); - useEffect(() => { if (isDefined(pageChangeEffectNavigateLocation)) { - const hasQueryParams = pageChangeEffectNavigateLocation.includes('?'); - - const navigationParams = createSearchParams({ - ...(searchParams.get('animateModal') - ? { animateModal: searchParams.get('animateModal') ?? 'false' } - : {}), - }); - - if (hasQueryParams) { - navigate(pageChangeEffectNavigateLocation); - } else { - navigate({ - pathname: pageChangeEffectNavigateLocation, - search: navigationParams.toString(), - }); - } + navigate(pageChangeEffectNavigateLocation); } - }, [navigate, pageChangeEffectNavigateLocation, searchParams]); + }, [navigate, pageChangeEffectNavigateLocation]); useEffect(() => { const isLeavingRecordIndexPage = !!matchPath( diff --git a/packages/twenty-front/src/modules/auth/components/Verify.tsx b/packages/twenty-front/src/modules/auth/components/Verify.tsx index 89f930b96..25a8a6a1f 100644 --- a/packages/twenty-front/src/modules/auth/components/Verify.tsx +++ b/packages/twenty-front/src/modules/auth/components/Verify.tsx @@ -3,9 +3,11 @@ import { useSearchParams } from 'react-router-dom'; import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin'; +import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState'; import { AppPath } from '@/types/AppPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { useNavigateApp } from '~/hooks/useNavigateApp'; import { SignInUpLoading } from '~/pages/auth/SignInUpLoading'; @@ -20,6 +22,10 @@ export const Verify = () => { const navigate = useNavigateApp(); const { verifyLoginToken } = useVerifyLogin(); + const { isLoaded: clientConfigLoaded } = useRecoilValue( + clientConfigApiStatusState, + ); + useEffect(() => { if (isDefined(errorMessage)) { enqueueSnackBar(errorMessage, { @@ -28,6 +34,8 @@ export const Verify = () => { }); } + if (!clientConfigLoaded) return; + if (isDefined(loginToken)) { verifyLoginToken(loginToken); } else if (!isLogged) { @@ -35,7 +43,7 @@ export const Verify = () => { } // Verify only needs to run once at mount // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [clientConfigLoaded]); return ; }; diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx index 066e25cd1..c0cb3d4a3 100644 --- a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx +++ b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx @@ -4,10 +4,12 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin'; +import { animateModalState } from '@/auth/states/animateModalState'; import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; import { useLingui } from '@lingui/react/macro'; import { useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; +import { useSetRecoilState } from 'recoil'; import { useNavigateApp } from '~/hooks/useNavigateApp'; import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl'; import { EmailVerificationSent } from '../sign-in-up/components/EmailVerificationSent'; @@ -19,6 +21,8 @@ export const VerifyEmailEffect = () => { const [searchParams] = useSearchParams(); const [isError, setIsError] = useState(false); + const setAnimateModal = useSetRecoilState(animateModalState); + const email = searchParams.get('email'); const emailVerificationToken = searchParams.get('emailVerificationToken'); @@ -48,9 +52,9 @@ export const VerifyEmailEffect = () => { const workspaceUrl = getWorkspaceUrl(workspaceUrls); if (workspaceUrl.slice(0, -1) !== window.location.origin) { - return redirectToWorkspaceDomain(workspaceUrl, AppPath.Verify, { + setAnimateModal(false); + return await redirectToWorkspaceDomain(workspaceUrl, AppPath.Verify, { loginToken: loginToken.token, - animateModal: false, }); } verifyLoginToken(loginToken.token); diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts index 8d970c2fd..4dae0dafb 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts @@ -41,6 +41,7 @@ import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTi import { currentUserState } from '../states/currentUserState'; import { tokenPairState } from '../states/tokenPairState'; +import { animateModalState } from '@/auth/states/animateModalState'; import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState'; import { SignInUpStep, @@ -114,6 +115,7 @@ export const useAuth = () => { const goToRecoilSnapshot = useGotoRecoilSnapshot(); const setDateTimeFormat = useSetRecoilState(dateTimeFormatState); + const setAnimateModal = useSetRecoilState(animateModalState); const [, setSearchParams] = useSearchParams(); @@ -420,14 +422,13 @@ export const useAuth = () => { } if (isMultiWorkspaceEnabled) { - return redirectToWorkspaceDomain( + setAnimateModal(false); + return await redirectToWorkspaceDomain( getWorkspaceUrl(signUpResult.data.signUp.workspace.workspaceUrls), - isEmailVerificationRequired ? AppPath.SignInUp : AppPath.Verify, { ...(!isEmailVerificationRequired && { loginToken: signUpResult.data.signUp.loginToken.token, - animateModal: false, }), email, }, @@ -445,6 +446,7 @@ export const useAuth = () => { handleGetAuthTokensFromLoginToken, setSignInUpStep, setSearchParams, + setAnimateModal, isEmailVerificationRequired, redirectToWorkspaceDomain, ], diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx index 8d9baac22..387dca2d7 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/SignInUpGlobalScopeForm.tsx @@ -90,13 +90,13 @@ export const SignInUpGlobalScopeForm = () => { variant: SnackBarVariant.Error, }); }, - onCompleted: (data) => { + onCompleted: async (data) => { requestFreshCaptchaToken(); const response = data.checkUserExists; if (response.__typename === 'UserExists') { if (response.availableWorkspaces.length >= 1) { const workspace = response.availableWorkspaces[0]; - return redirectToWorkspaceDomain( + return await redirectToWorkspaceDomain( getWorkspaceUrl(workspace.workspaceUrls), pathname, { diff --git a/packages/twenty-front/src/modules/auth/states/animateModalState.ts b/packages/twenty-front/src/modules/auth/states/animateModalState.ts new file mode 100644 index 000000000..6c2cd96da --- /dev/null +++ b/packages/twenty-front/src/modules/auth/states/animateModalState.ts @@ -0,0 +1,26 @@ +import { urlSyncEffect } from 'recoil-sync'; +import { createState } from 'twenty-ui/utilities'; + +export const animateModalState = createState({ + key: 'animateModalState', + defaultValue: true, + effects: [ + urlSyncEffect({ + itemKey: 'animateModal', + refine: (value: unknown) => { + if (typeof value === 'boolean') { + return { + type: 'success', + value: value as boolean, + warnings: [], + } as const; + } + return { + type: 'failure', + message: 'Invalid animateModalState', + path: [] as any, + } as const; + }, + }), + ], +}); diff --git a/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts index 2d52390df..dcb24afeb 100644 --- a/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts +++ b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts @@ -8,6 +8,7 @@ export const billingCheckoutSessionState = createState({ defaultValue: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE, effects: [ syncEffect({ + itemKey: 'billingCheckoutSession', refine: (value: unknown) => { if ( typeof value === 'object' && diff --git a/packages/twenty-front/src/modules/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates.ts b/packages/twenty-front/src/modules/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates.ts new file mode 100644 index 000000000..9996de191 --- /dev/null +++ b/packages/twenty-front/src/modules/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates.ts @@ -0,0 +1,32 @@ +import { animateModalState } from '@/auth/states/animateModalState'; +import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState'; +import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue'; +import { useRecoilCallback } from 'recoil'; + +export const useBuildSearchParamsFromUrlSyncedStates = () => { + const buildSearchParamsFromUrlSyncedStates = useRecoilCallback( + ({ snapshot }) => + async () => { + const animateModal = snapshot.getLoadable(animateModalState).getValue(); + const billingCheckoutSession = snapshot + .getLoadable(billingCheckoutSessionState) + .getValue(); + + const output = { + ...(billingCheckoutSession !== BILLING_CHECKOUT_SESSION_DEFAULT_VALUE + ? { + billingCheckoutSession: JSON.stringify(billingCheckoutSession), + } + : {}), + ...(animateModal === false ? { animateModal: 'false' } : {}), + }; + + return output; + }, + [], + ); + + return { + buildSearchParamsFromUrlSyncedStates, + }; +}; diff --git a/packages/twenty-front/src/modules/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain.ts b/packages/twenty-front/src/modules/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain.ts index f431176d9..8c04d9680 100644 --- a/packages/twenty-front/src/modules/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain.ts +++ b/packages/twenty-front/src/modules/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain.ts @@ -1,6 +1,6 @@ -import { useRecoilValue, useSetRecoilState } from 'recoil'; -import { lastAuthenticatedWorkspaceDomainState } from '@/domain-manager/states/lastAuthenticatedWorkspaceDomainState'; import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; +import { lastAuthenticatedWorkspaceDomainState } from '@/domain-manager/states/lastAuthenticatedWorkspaceDomainState'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; export const useLastAuthenticatedWorkspaceDomain = () => { const domainConfiguration = useRecoilValue(domainConfigurationState); diff --git a/packages/twenty-front/src/modules/domain-manager/hooks/useRedirectToWorkspaceDomain.ts b/packages/twenty-front/src/modules/domain-manager/hooks/useRedirectToWorkspaceDomain.ts index 1e7795dc8..922202cae 100644 --- a/packages/twenty-front/src/modules/domain-manager/hooks/useRedirectToWorkspaceDomain.ts +++ b/packages/twenty-front/src/modules/domain-manager/hooks/useRedirectToWorkspaceDomain.ts @@ -1,4 +1,5 @@ import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; +import { useBuildSearchParamsFromUrlSyncedStates } from '@/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates'; import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl'; import { useRedirect } from '@/domain-manager/hooks/useRedirect'; import { useRecoilValue } from 'recoil'; @@ -8,14 +9,23 @@ export const useRedirectToWorkspaceDomain = () => { const { buildWorkspaceUrl } = useBuildWorkspaceUrl(); const { redirect } = useRedirect(); - const redirectToWorkspaceDomain = ( + const { buildSearchParamsFromUrlSyncedStates } = + useBuildSearchParamsFromUrlSyncedStates(); + + const redirectToWorkspaceDomain = async ( baseUrl: string, pathname?: string, searchParams?: Record, target?: string, ) => { if (!isMultiWorkspaceEnabled) return; - redirect(buildWorkspaceUrl(baseUrl, pathname, searchParams), target); + redirect( + buildWorkspaceUrl(baseUrl, pathname, { + ...searchParams, + ...(await buildSearchParamsFromUrlSyncedStates()), + }), + target, + ); }; return { diff --git a/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts b/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts index 6bcf03f4a..8e4e4abae 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts +++ b/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts @@ -1,19 +1,23 @@ +import { animateModalState } from '@/auth/states/animateModalState'; import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; import { AppPath } from '@/types/AppPath'; +import { useSetRecoilState } from 'recoil'; import { WorkspaceUrls } from '~/generated/graphql'; import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl'; export const useImpersonationRedirect = () => { const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain(); + const setAnimateModal = useSetRecoilState(animateModalState); - const executeImpersonationRedirect = ( + const executeImpersonationRedirect = async ( workspaceUrls: WorkspaceUrls, loginToken: string, ) => { - return redirectToWorkspaceDomain( + setAnimateModal(false); + return await redirectToWorkspaceDomain( getWorkspaceUrl(workspaceUrls), AppPath.Verify, - { loginToken, animateModal: false }, + { loginToken }, ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx index f23c564ca..72b57946f 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx @@ -1,4 +1,5 @@ import { AuthModal } from '@/auth/components/AuthModal'; +import { animateModalState } from '@/auth/states/animateModalState'; import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter'; import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary'; import { AppFullScreenErrorFallback } from '@/error-handler/components/AppFullScreenErrorFallback'; @@ -17,7 +18,8 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { Global, css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { AnimatePresence, LayoutGroup, motion } from 'framer-motion'; -import { Outlet, useSearchParams } from 'react-router-dom'; +import { Outlet } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; import { useScreenSize } from 'twenty-ui/utilities'; const StyledLayout = styled.div` @@ -63,8 +65,7 @@ export const DefaultLayout = () => { const windowsWidth = useScreenSize().width; const showAuthModal = useShowAuthModal(); const useShowFullScreen = useShowFullscreen(); - const [searchParams] = useSearchParams(); - const animateModal = searchParams.get('animateModal') !== 'false'; + const animateModal = useRecoilValue(animateModalState); return ( <> diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx index fc36828b7..da422e79d 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx @@ -1,6 +1,7 @@ import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo'; import { useAuth } from '@/auth/hooks/useAuth'; +import { animateModalState } from '@/auth/states/animateModalState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { Workspaces, workspacesState } from '@/auth/states/workspaces'; import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl'; @@ -66,6 +67,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => { const setMultiWorkspaceDropdownState = useSetRecoilState( multiWorkspaceDropdownState, ); + const setAnimateModal = useSetRecoilState(animateModalState); const handleChange = async (workspace: Workspaces[0]) => { redirectToWorkspaceDomain(getWorkspaceUrl(workspace.workspaceUrls)); @@ -73,13 +75,13 @@ export const MultiWorkspaceDropdownDefaultComponents = () => { const createWorkspace = () => { signUpInNewWorkspaceMutation({ - onCompleted: (data) => { - return redirectToWorkspaceDomain( + onCompleted: async (data) => { + setAnimateModal(false); + return await redirectToWorkspaceDomain( getWorkspaceUrl(data.signUpInNewWorkspace.workspace.workspaceUrls), AppPath.Verify, { loginToken: data.signUpInNewWorkspace.loginToken.token, - animateModal: false, }, '_blank', ); diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownWorkspacesListComponents.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownWorkspacesListComponents.tsx index 475bf284a..f660a7d89 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownWorkspacesListComponents.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownWorkspacesListComponents.tsx @@ -1,20 +1,20 @@ -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo'; -import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { Workspaces, workspacesState } from '@/auth/states/workspaces'; import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; -import { useLingui } from '@lingui/react/macro'; -import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState'; -import { useState } from 'react'; -import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo'; +import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState'; +import { useLingui } from '@lingui/react/macro'; +import { useState } from 'react'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { Avatar, IconChevronLeft } from 'twenty-ui/display'; import { MenuItemSelectAvatar, UndecoratedLink } from 'twenty-ui/navigation'; +import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl'; export const MultiWorkspaceDropdownWorkspacesListComponents = () => { const currentWorkspace = useRecoilValue(currentWorkspaceState); @@ -24,7 +24,7 @@ export const MultiWorkspaceDropdownWorkspacesListComponents = () => { const { t } = useLingui(); const handleChange = async (workspace: Workspaces[0]) => { - redirectToWorkspaceDomain(getWorkspaceUrl(workspace.workspaceUrls)); + await redirectToWorkspaceDomain(getWorkspaceUrl(workspace.workspaceUrls)); }; const setMultiWorkspaceDropdownState = useSetRecoilState( multiWorkspaceDropdownState, diff --git a/packages/twenty-front/src/pages/auth/SignInUpLoading.tsx b/packages/twenty-front/src/pages/auth/SignInUpLoading.tsx index 00a18bc1a..42be13efb 100644 --- a/packages/twenty-front/src/pages/auth/SignInUpLoading.tsx +++ b/packages/twenty-front/src/pages/auth/SignInUpLoading.tsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { motion } from 'framer-motion'; const StyledContentContainer = styled(motion.div)` - height: 300px; + height: 480px; margin-bottom: ${({ theme }) => theme.spacing(8)}; margin-top: ${({ theme }) => theme.spacing(4)}; `; diff --git a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx index cff565b35..83400c7bf 100644 --- a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx +++ b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx @@ -14,11 +14,14 @@ import { useRecoilState, useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { Loader } from 'twenty-ui/feedback'; import { CardPicker, MainButton } from 'twenty-ui/input'; -import { CAL_LINK, ClickToActionLink } from 'twenty-ui/navigation'; +import { + CAL_LINK, + ClickToActionLink, + TWENTY_PRICING_LINK, +} from 'twenty-ui/navigation'; import { BillingPlanKey, BillingPriceLicensedDto, - SubscriptionInterval, useBillingBaseProductPricesQuery, } from '~/generated/graphql'; @@ -94,7 +97,7 @@ export const ChooseYourPlan = () => { const { data: plans } = useBillingBaseProductPricesQuery(); - const currentPlan = billingCheckoutSession.plan || BillingPlanKey.PRO; + const currentPlan = billingCheckoutSession.plan; const getPlanBenefits = (planKey: BillingPlanKey) => { if (planKey === BillingPlanKey.ENTERPRISE) { @@ -128,7 +131,7 @@ export const ChooseYourPlan = () => { const baseProductPrice = baseProduct?.prices?.find( (price): price is BillingPriceLicensedDto => isBillingPriceLicensed(price) && - price.recurringInterval === SubscriptionInterval.Month, + price.recurringInterval === billingCheckoutSession.interval, ); const hasWithoutCreditCardTrialPeriod = billing?.trialPeriods.some( @@ -160,27 +163,10 @@ export const ChooseYourPlan = () => { }; }; - const handleSwitchPlan = (planKey: BillingPlanKey) => { - return () => { - if (isDefined(baseProductPrice)) { - setBillingCheckoutSession({ - plan: planKey, - interval: baseProductPrice.recurringInterval, - requirePaymentMethod: billingCheckoutSession.requirePaymentMethod, - }); - } - }; - }; - const { signOut } = useAuth(); const withCreditCardTrialPeriodDuration = withCreditCardTrialPeriod?.duration; - const alternatePlan = - currentPlan === BillingPlanKey.PRO - ? BillingPlanKey.ENTERPRISE - : BillingPlanKey.PRO; - const planName = plans?.plans.find((plan) => plan.planKey === currentPlan) ?.baseProduct.name; @@ -252,8 +238,8 @@ export const ChooseYourPlan = () => { Log out - - Switch Plan + + Change Plan diff --git a/packages/twenty-front/src/pages/settings/workspace/SettingsDomain.tsx b/packages/twenty-front/src/pages/settings/workspace/SettingsDomain.tsx index 76cd0075f..dcf5720dc 100644 --- a/packages/twenty-front/src/pages/settings/workspace/SettingsDomain.tsx +++ b/packages/twenty-front/src/pages/settings/workspace/SettingsDomain.tsx @@ -1,30 +1,30 @@ -import { ApolloError } from '@apollo/client'; import { CurrentWorkspace, currentWorkspaceState, } from '@/auth/states/currentWorkspaceState'; -import { useRecoilState } from 'recoil'; +import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; +import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; +import { SettingsPath } from '@/types/SettingsPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; +import { ApolloError } from '@apollo/client'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Trans, useLingui } from '@lingui/react/macro'; +import { FormProvider, useForm } from 'react-hook-form'; +import { useRecoilState } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; +import { z } from 'zod'; import { FeatureFlagKey, useUpdateWorkspaceMutation, } from '~/generated/graphql'; -import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { SettingsCustomDomain } from '~/pages/settings/workspace/SettingsCustomDomain'; import { SettingsSubdomain } from '~/pages/settings/workspace/SettingsSubdomain'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; -import { Trans, useLingui } from '@lingui/react/macro'; -import { z } from 'zod'; -import { FormProvider, useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { SettingsPath } from '@/types/SettingsPath'; -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { isDefined } from 'twenty-shared/utils'; export const SettingsDomain = () => { const navigate = useNavigateSettings(); @@ -151,7 +151,7 @@ export const SettingsDomain = () => { variant: SnackBarVariant.Error, }); }, - onCompleted: () => { + onCompleted: async () => { const currentUrl = new URL(window.location.href); currentUrl.hostname = new URL( @@ -167,7 +167,7 @@ export const SettingsDomain = () => { variant: SnackBarVariant.Success, }); - redirectToWorkspaceDomain(currentUrl.toString()); + await redirectToWorkspaceDomain(currentUrl.toString()); }, }); }; diff --git a/packages/twenty-front/src/utils/recoil-effects.ts b/packages/twenty-front/src/utils/recoil-effects.ts index 47e9fd383..f618ef655 100644 --- a/packages/twenty-front/src/utils/recoil-effects.ts +++ b/packages/twenty-front/src/utils/recoil-effects.ts @@ -1,8 +1,8 @@ import omit from 'lodash.omit'; import { AtomEffect } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; import { z } from 'zod'; import { cookieStorage } from '~/utils/cookie-storage'; -import { isDefined } from 'twenty-shared/utils'; export const localStorageEffect = (key?: string): AtomEffect => diff --git a/packages/twenty-ui/src/navigation/index.ts b/packages/twenty-ui/src/navigation/index.ts index d6fca4f21..d35387101 100644 --- a/packages/twenty-ui/src/navigation/index.ts +++ b/packages/twenty-ui/src/navigation/index.ts @@ -17,6 +17,7 @@ export { LinkType, SocialLink } from './link/components/SocialLink'; export { UndecoratedLink } from './link/components/UndecoratedLink'; export { CAL_LINK } from './link/constants/Cal'; export { GITHUB_LINK } from './link/constants/GithubLink'; +export { TWENTY_PRICING_LINK } from './link/constants/TwentyPricingLink'; export type { MenuItemIconButton, MenuItemProps, diff --git a/packages/twenty-ui/src/navigation/link/constants/TwentyPricingLink.ts b/packages/twenty-ui/src/navigation/link/constants/TwentyPricingLink.ts new file mode 100644 index 000000000..9edff66e8 --- /dev/null +++ b/packages/twenty-ui/src/navigation/link/constants/TwentyPricingLink.ts @@ -0,0 +1 @@ +export const TWENTY_PRICING_LINK = 'https://twenty.com/pricing';