diff --git a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx
index 16eb64bde..a63f7f575 100644
--- a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx
+++ b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx
@@ -16,8 +16,8 @@ import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider
import { DialogManager } from '@/ui/feedback/dialog-manager/components/DialogManager';
import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogManagerScope';
import { SnackBarProvider } from '@/ui/feedback/snack-bar-manager/components/SnackBarProvider';
-import { UserThemeProviderEffect } from '@/ui/theme/components/AppThemeProvider';
import { BaseThemeProvider } from '@/ui/theme/components/BaseThemeProvider';
+import { UserThemeProviderEffect } from '@/ui/theme/components/UserThemeProviderEffect';
import { PageFavicon } from '@/ui/utilities/page-favicon/components/PageFavicon';
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
import { ServerPreconnect } from '@/ui/utilities/server-preconnect/components/ServerPreconnect';
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 7ebbe1ada..f94017524 100644
--- a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
+++ b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
@@ -4,6 +4,7 @@ import {
useLocation,
useNavigate,
useParams,
+ useSearchParams,
} from 'react-router-dom';
import { useRecoilValue } from 'recoil';
@@ -13,6 +14,7 @@ import {
} from '@/analytics/hooks/useEventTracker';
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
+import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
@@ -21,10 +23,9 @@ import { AppPath } from '@/types/AppPath';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { SettingsPath } from '@/types/SettingsPath';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
+import { isDefined } from 'twenty-shared/utils';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
-import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
-import { isDefined } from 'twenty-shared/utils';
// TODO: break down into smaller functions and / or hooks
// - moved usePageChangeEffectNavigateLocation into dedicated hook
@@ -58,11 +59,16 @@ export const PageChangeEffect = () => {
}
}, [location, previousLocation]);
+ const [searchParams] = useSearchParams();
+ const navigationParams = searchParams.get('animateModal')
+ ? `?animateModal=${searchParams.get('animateModal')}`
+ : '';
+
useEffect(() => {
if (isDefined(pageChangeEffectNavigateLocation)) {
- navigate(pageChangeEffectNavigateLocation);
+ navigate(pageChangeEffectNavigateLocation + navigationParams);
}
- }, [navigate, pageChangeEffectNavigateLocation]);
+ }, [navigate, pageChangeEffectNavigateLocation, navigationParams]);
useEffect(() => {
const isLeavingRecordIndexPage = !!matchPath(
diff --git a/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx b/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx
index f2ef1bec8..d0ff55684 100644
--- a/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx
+++ b/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx
@@ -1,12 +1,13 @@
import { AppRouterProviders } from '@/app/components/AppRouterProviders';
import { SettingsRoutes } from '@/app/components/SettingsRoutes';
+import { Verify } from '@/auth/components/Verify';
-import { VerifyEffect } from '@/auth/components/VerifyEffect';
import { VerifyEmailEffect } from '@/auth/components/VerifyEmailEffect';
import indexAppPath from '@/navigation/utils/indexAppPath';
import { AppPath } from '@/types/AppPath';
import { BlankLayout } from '@/ui/layout/page/components/BlankLayout';
import { DefaultLayout } from '@/ui/layout/page/components/DefaultLayout';
+
import {
createBrowserRouter,
createRoutesFromElements,
@@ -38,7 +39,7 @@ export const useCreateAppRouter = (
loader={async () => Promise.resolve(null)}
>
}>
- } />
+ } />
} />
} />
} />
diff --git a/packages/twenty-front/src/modules/auth/components/AuthModal.tsx b/packages/twenty-front/src/modules/auth/components/AuthModal.tsx
index e50386d83..97734af3a 100644
--- a/packages/twenty-front/src/modules/auth/components/AuthModal.tsx
+++ b/packages/twenty-front/src/modules/auth/components/AuthModal.tsx
@@ -8,10 +8,20 @@ const StyledContent = styled(Modal.Content)`
justify-content: center;
`;
-type AuthModalProps = { children: React.ReactNode };
+type AuthModalProps = {
+ children: React.ReactNode;
+ isOpenAnimated?: boolean;
+};
-export const AuthModal = ({ children }: AuthModalProps) => (
-
+export const AuthModal = ({
+ children,
+ isOpenAnimated = true,
+}: AuthModalProps) => (
+
{children}
diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEffect.tsx b/packages/twenty-front/src/modules/auth/components/Verify.tsx
similarity index 91%
rename from packages/twenty-front/src/modules/auth/components/VerifyEffect.tsx
rename to packages/twenty-front/src/modules/auth/components/Verify.tsx
index f5ecefb0a..89f930b96 100644
--- a/packages/twenty-front/src/modules/auth/components/VerifyEffect.tsx
+++ b/packages/twenty-front/src/modules/auth/components/Verify.tsx
@@ -6,10 +6,11 @@ import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin';
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 { useNavigateApp } from '~/hooks/useNavigateApp';
import { isDefined } from 'twenty-shared/utils';
+import { useNavigateApp } from '~/hooks/useNavigateApp';
+import { SignInUpLoading } from '~/pages/auth/SignInUpLoading';
-export const VerifyEffect = () => {
+export const Verify = () => {
const [searchParams] = useSearchParams();
const loginToken = searchParams.get('loginToken');
const errorMessage = searchParams.get('errorMessage');
@@ -36,5 +37,5 @@ export const VerifyEffect = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
- return <>>;
+ return ;
};
diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx
index 991b552a6..066e25cd1 100644
--- a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx
+++ b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx
@@ -3,14 +3,14 @@ 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 { useVerifyLogin } from '@/auth/hooks/useVerifyLogin';
+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 { useNavigateApp } from '~/hooks/useNavigateApp';
-import { EmailVerificationSent } from '../sign-in-up/components/EmailVerificationSent';
-import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
-import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
+import { EmailVerificationSent } from '../sign-in-up/components/EmailVerificationSent';
export const VerifyEmailEffect = () => {
const { getLoginTokenFromEmailVerificationToken } = useAuth();
@@ -50,6 +50,7 @@ export const VerifyEmailEffect = () => {
if (workspaceUrl.slice(0, -1) !== window.location.origin) {
return 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 64bf4eab1..7edee4a62 100644
--- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
+++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
@@ -416,6 +416,7 @@ export const useAuth = () => {
{
...(!isEmailVerificationRequired && {
loginToken: signUpResult.data.signUp.loginToken.token,
+ animateModal: false,
}),
email,
},
diff --git a/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx b/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx
index f88b9652f..4ff304e77 100644
--- a/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx
+++ b/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx
@@ -2,6 +2,8 @@ import { useRecoilValue } from 'recoil';
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
import { AppFullScreenErrorFallback } from '@/error-handler/components/AppFullScreenErrorFallback';
+import { AppPath } from '@/types/AppPath';
+import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
export const ClientConfigProvider: React.FC = ({
children,
@@ -10,8 +12,15 @@ export const ClientConfigProvider: React.FC = ({
clientConfigApiStatusState,
);
+ const { isMatchingLocation } = useIsMatchingLocation();
+
// TODO: Implement a better loading strategy
- if (!isLoaded) return null;
+ if (
+ !isLoaded &&
+ !isMatchingLocation(AppPath.Verify) &&
+ !isMatchingLocation(AppPath.VerifyEmail)
+ )
+ return null;
return isErrored && error instanceof Error ? (
{
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
-
const shouldDisplayChildren = objectMetadataItems.length > 0;
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 437873821..6bcf03f4a 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
@@ -13,7 +13,7 @@ export const useImpersonationRedirect = () => {
return redirectToWorkspaceDomain(
getWorkspaceUrl(workspaceUrls),
AppPath.Verify,
- { loginToken },
+ { loginToken, animateModal: false },
);
};
diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx
index 613192b41..04ae0d4d8 100644
--- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx
@@ -56,16 +56,16 @@ const getResult = (isDefaultLayoutAuthModalVisible = true) =>
// prettier-ignore
const testCases = [
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
- { loc: AppPath.Verify, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: false },
- { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
+ { loc: AppPath.Verify, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: true },
+ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
{ loc: AppPath.VerifyEmail, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
{ loc: AppPath.VerifyEmail, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts
index 39e59630f..cfcf3af19 100644
--- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts
+++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts
@@ -6,9 +6,9 @@ import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { AppPath } from '@/types/AppPath';
import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState';
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
+import { isDefined } from 'twenty-shared/utils';
import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
-import { isDefined } from 'twenty-shared/utils';
export const useShowAuthModal = () => {
const { isMatchingLocation } = useIsMatchingLocation();
@@ -21,8 +21,11 @@ export const useShowAuthModal = () => {
);
return useMemo(() => {
- if (isMatchingLocation(AppPath.Verify)) {
- return false;
+ if (
+ isMatchingLocation(AppPath.Verify) ||
+ isMatchingLocation(AppPath.VerifyEmail)
+ ) {
+ return true;
}
if (
diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx
index ac3279a3d..094548fdb 100644
--- a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx
@@ -7,6 +7,7 @@ import {
useListenClickOutside,
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
+import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import React, { useEffect, useRef } from 'react';
@@ -148,6 +149,7 @@ export type ModalProps = React.PropsWithChildren & {
className?: string;
hotkeyScope?: ModalHotkeyScope;
onEnter?: () => void;
+ isOpenAnimated?: boolean;
modalVariant?: ModalVariants;
} & (
| { isClosable: true; onClose: () => void }
@@ -170,6 +172,7 @@ export const Modal = ({
isClosable = false,
onClose,
modalVariant = 'primary',
+ isOpenAnimated = true,
}: ModalProps) => {
const isMobile = useIsMobile();
const modalRef = useRef(null);
@@ -223,6 +226,8 @@ export const Modal = ({
e.stopPropagation();
};
+ const theme = useTheme();
+
return (
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 0512c1212..032891614 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
@@ -17,7 +17,7 @@ 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 } from 'react-router-dom';
+import { Outlet, useSearchParams } from 'react-router-dom';
import { useScreenSize } from 'twenty-ui/utilities';
const StyledLayout = styled.div`
@@ -63,6 +63,8 @@ export const DefaultLayout = () => {
const windowsWidth = useScreenSize().width;
const showAuthModal = useShowAuthModal();
const useShowFullScreen = useShowFullscreen();
+ const [searchParams] = useSearchParams();
+ const animateModal = searchParams.get('animateModal') !== 'false';
return (
<>
@@ -86,7 +88,9 @@ export const DefaultLayout = () => {
2
: 0,
}}
- transition={{ duration: theme.animation.duration.normal }}
+ transition={{
+ duration: theme.animation.duration.normal,
+ }}
>
{!showAuthModal && (
<>
@@ -104,7 +108,7 @@ export const DefaultLayout = () => {
-
+
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 f500172e2..e4e1db512 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,30 +1,27 @@
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
-import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
-import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
-import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
-import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
-import { useRecoilValue, useSetRecoilState } from 'recoil';
-import { Workspaces, workspacesState } from '@/auth/states/workspaces';
-import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
-import { useLingui } from '@lingui/react/macro';
-import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
-import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState';
-import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
-import { SettingsPath } from '@/types/SettingsPath';
-import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
-import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MultiWorkspaceDropdownId';
import { useAuth } from '@/auth/hooks/useAuth';
+import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
+import { Workspaces, workspacesState } from '@/auth/states/workspaces';
+import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
+import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { AppPath } from '@/types/AppPath';
-import { useSignUpInNewWorkspaceMutation } from '~/generated/graphql';
+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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
-import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
+import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
+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 { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
+import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
+import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MultiWorkspaceDropdownId';
+import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState';
import { useColorScheme } from '@/ui/theme/hooks/useColorScheme';
import styled from '@emotion/styled';
-import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
+import { useLingui } from '@lingui/react/macro';
+import { useRecoilValue, useSetRecoilState } from 'recoil';
import {
Avatar,
IconDotsVertical,
@@ -39,6 +36,9 @@ import {
MenuItemSelectAvatar,
UndecoratedLink,
} from 'twenty-ui/navigation';
+import { useSignUpInNewWorkspaceMutation } from '~/generated/graphql';
+import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
+import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
const StyledDescription = styled.div`
color: ${({ theme }) => theme.font.color.light};
@@ -79,6 +79,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
AppPath.Verify,
{
loginToken: data.signUpInNewWorkspace.loginToken.token,
+ animateModal: false,
},
'_blank',
);
diff --git a/packages/twenty-front/src/modules/ui/theme/components/AppThemeProvider.tsx b/packages/twenty-front/src/modules/ui/theme/components/UserThemeProviderEffect.tsx
similarity index 100%
rename from packages/twenty-front/src/modules/ui/theme/components/AppThemeProvider.tsx
rename to packages/twenty-front/src/modules/ui/theme/components/UserThemeProviderEffect.tsx
diff --git a/packages/twenty-front/src/modules/users/components/UserProvider.tsx b/packages/twenty-front/src/modules/users/components/UserProvider.tsx
index e3cdede57..a506bf1a4 100644
--- a/packages/twenty-front/src/modules/users/components/UserProvider.tsx
+++ b/packages/twenty-front/src/modules/users/components/UserProvider.tsx
@@ -15,6 +15,8 @@ export const UserProvider = ({ children }: React.PropsWithChildren) => {
const dateTimeFormat = useRecoilValue(dateTimeFormatState);
return !isCurrentUserLoaded &&
+ !isMatchingLocation(AppPath.Verify) &&
+ !isMatchingLocation(AppPath.VerifyEmail) &&
!isMatchingLocation(AppPath.CreateWorkspace) ? (
) : (
diff --git a/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx b/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx
index 0f4390c23..a8229c460 100644
--- a/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx
+++ b/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx
@@ -3,8 +3,8 @@ import { useRecoilState, useSetRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
-import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersStates';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
+import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersStates';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
import { workspacesState } from '@/auth/states/workspaces';
@@ -16,15 +16,18 @@ import { detectTimeFormat } from '@/localization/utils/detectTimeFormat';
import { detectTimeZone } from '@/localization/utils/detectTimeZone';
import { getDateFormatFromWorkspaceDateFormat } from '@/localization/utils/getDateFormatFromWorkspaceDateFormat';
import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTimeFormatFromWorkspaceTimeFormat';
+import { AppPath } from '@/types/AppPath';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
-import { WorkspaceMember } from '~/generated-metadata/graphql';
-import { useGetCurrentUserQuery } from '~/generated/graphql';
-import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils';
+import { WorkspaceMember } from '~/generated-metadata/graphql';
+import { useGetCurrentUserQuery } from '~/generated/graphql';
+import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
+import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
export const UserProviderEffect = () => {
const [isLoading, setIsLoading] = useState(true);
+ const { isMatchingLocation } = useIsMatchingLocation();
const [isCurrentUserLoaded, setIsCurrentUserLoaded] = useRecoilState(
isCurrentUserLoadedState,
@@ -44,7 +47,10 @@ export const UserProviderEffect = () => {
);
const { loading: queryLoading, data: queryData } = useGetCurrentUserQuery({
- skip: isCurrentUserLoaded,
+ skip:
+ isCurrentUserLoaded ||
+ isMatchingLocation(AppPath.Verify) ||
+ isMatchingLocation(AppPath.VerifyEmail),
});
useEffect(() => {
diff --git a/packages/twenty-front/src/pages/auth/SignInUpLoading.tsx b/packages/twenty-front/src/pages/auth/SignInUpLoading.tsx
new file mode 100644
index 000000000..099f5b402
--- /dev/null
+++ b/packages/twenty-front/src/pages/auth/SignInUpLoading.tsx
@@ -0,0 +1,100 @@
+import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
+import { useRecoilValue } from 'recoil';
+
+import { Logo } from '@/auth/components/Logo';
+import { Title } from '@/auth/components/Title';
+import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
+import { useMemo } from 'react';
+
+import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash';
+import styled from '@emotion/styled';
+import { useLingui } from '@lingui/react/macro';
+import { isNonEmptyString } from '@sniptt/guards';
+import { motion } from 'framer-motion';
+import { isDefined } from 'twenty-shared/utils';
+import { Loader } from 'twenty-ui/feedback';
+import { MainButton } from 'twenty-ui/input';
+import { PublicWorkspaceDataOutput } from '~/generated/graphql';
+
+const StyledContentContainer = styled(motion.div)`
+ margin-bottom: ${({ theme }) => theme.spacing(8)};
+ margin-top: ${({ theme }) => theme.spacing(4)};
+`;
+
+const StyledForm = styled.form`
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ margin-top: ${({ theme }) => theme.spacing(10)};
+`;
+
+const StandardContent = ({
+ workspacePublicData,
+ signInUpForm,
+ title,
+}: {
+ workspacePublicData: PublicWorkspaceDataOutput | null;
+ signInUpForm: JSX.Element | null;
+ title: string;
+}) => {
+ return (
+ <>
+
+ {title}
+ {signInUpForm}
+ >
+ );
+};
+
+export const SignInUpLoading = () => {
+ const { t } = useLingui();
+ const workspacePublicData = useRecoilValue(workspacePublicDataState);
+
+ const { workspaceInviteHash, workspace: workspaceFromInviteHash } =
+ useWorkspaceFromInviteHash();
+
+ const title = useMemo(() => {
+ if (isDefined(workspaceInviteHash)) {
+ return `Join ${workspaceFromInviteHash?.displayName ?? ''} team`;
+ }
+ const workspaceName = !isDefined(workspacePublicData?.displayName)
+ ? DEFAULT_WORKSPACE_NAME
+ : !isNonEmptyString(workspacePublicData?.displayName)
+ ? t`Your Workspace`
+ : workspacePublicData?.displayName;
+
+ return t`Welcome to ${workspaceName}`;
+ }, [
+ workspaceFromInviteHash?.displayName,
+ workspaceInviteHash,
+ workspacePublicData?.displayName,
+ t,
+ ]);
+
+ return (
+
+
+ SignInUpLoading
+
+
+
+ }
+ fullWidth
+ />
+
+
+ >
+ }
+ title={title}
+ />
+ );
+};
diff --git a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
index 131ac864c..00e198045 100644
--- a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
+++ b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
@@ -11,16 +11,16 @@ import { billingState } from '@/client-config/states/billingState';
import styled from '@emotion/styled';
import { Trans, useLingui } from '@lingui/react/macro';
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 { ActionLink, CAL_LINK } from 'twenty-ui/navigation';
import {
BillingPlanKey,
BillingPriceLicensedDto,
SubscriptionInterval,
useBillingBaseProductPricesQuery,
} from '~/generated/graphql';
-import { isDefined } from 'twenty-shared/utils';
-import { ActionLink, CAL_LINK } from 'twenty-ui/navigation';
-import { CardPicker, MainButton } from 'twenty-ui/input';
-import { Loader } from 'twenty-ui/feedback';
const StyledSubscriptionContainer = styled.div<{
withLongerMarginBottom: boolean;
@@ -80,6 +80,10 @@ const StyledLinkGroup = styled.div`
}
`;
+const StyledChooseYourPlanPlaceholder = styled.div`
+ height: 566px;
+`;
+
export const ChooseYourPlan = () => {
const billing = useRecoilValue(billingState);
const { t } = useLingui();
@@ -182,82 +186,87 @@ export const ChooseYourPlan = () => {
)?.baseProduct.name;
return (
- isDefined(baseProductPrice) &&
- isDefined(billing) && (
- <>
-
- {hasWithoutCreditCardTrialPeriod
- ? t`Choose your Trial`
- : t`Get your subscription`}
-
- {hasWithoutCreditCardTrialPeriod ? (
-
- Cancel anytime
-
- ) : (
- withCreditCardTrialPeriod && (
+ <>
+ {isDefined(baseProductPrice) && isDefined(billing) ? (
+ <>
+
+ {hasWithoutCreditCardTrialPeriod
+ ? t`Choose your Trial`
+ : t`Get your subscription`}
+
+ {hasWithoutCreditCardTrialPeriod ? (
- {t`Enjoy a ${withCreditCardTrialPeriodDuration}-days free trial`}
+ Cancel anytime
- )
- )}
-
-
-
-
-
- {benefits.map((benefit) => (
- {benefit}
- ))}
-
-
- {hasWithoutCreditCardTrialPeriod && (
-
- {billing.trialPeriods.map((trialPeriod) => (
-
-
-
- ))}
-
- )}
- isSubmitting && }
- disabled={isSubmitting}
- />
-
-
- Log out
-
-
-
- Switch to {alternatePlanName}
-
-
-
- Book a Call
-
-
- >
- )
+ ) : (
+ withCreditCardTrialPeriod && (
+
+ {t`Enjoy a ${withCreditCardTrialPeriodDuration}-days free trial`}
+
+ )
+ )}
+
+
+
+
+
+ {benefits.map((benefit) => (
+
+ {benefit}
+
+ ))}
+
+
+ {hasWithoutCreditCardTrialPeriod && (
+
+ {billing.trialPeriods.map((trialPeriod) => (
+
+
+
+ ))}
+
+ )}
+ isSubmitting && }
+ disabled={isSubmitting}
+ />
+
+
+ Log out
+
+
+
+ Switch to {alternatePlanName}
+
+
+
+ Book a Call
+
+
+ >
+ ) : (
+
+ )}
+ >
);
};
diff --git a/packages/twenty-server/src/engine/core-modules/domain-manager/services/domain-manager.service.ts b/packages/twenty-server/src/engine/core-modules/domain-manager/services/domain-manager.service.ts
index 58864662b..22b2103eb 100644
--- a/packages/twenty-server/src/engine/core-modules/domain-manager/services/domain-manager.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/domain-manager/services/domain-manager.service.ts
@@ -1,8 +1,8 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
-import { Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
+import { Repository } from 'typeorm';
import { SEED_APPLE_WORKSPACE_ID } from 'src/database/typeorm-seeds/core/workspaces';
import { WorkspaceSubdomainCustomDomainAndIsCustomDomainEnabledType } from 'src/engine/core-modules/domain-manager/domain-manager.type';
@@ -44,7 +44,7 @@ export class DomainManagerService {
private appendSearchParams(
url: URL,
- searchParams: Record,
+ searchParams: Record,
) {
Object.entries(searchParams).forEach(([key, value]) => {
url.searchParams.set(key, value.toString());
@@ -78,7 +78,7 @@ export class DomainManagerService {
}: {
workspace: WorkspaceSubdomainCustomDomainAndIsCustomDomainEnabledType;
pathname?: string;
- searchParams?: Record;
+ searchParams?: Record;
}) {
const workspaceUrls = this.getWorkspaceUrls(workspace);
diff --git a/packages/twenty-server/src/engine/core-modules/email/email.module.ts b/packages/twenty-server/src/engine/core-modules/email/email.module.ts
index 0a6b5c3a5..6410facaf 100644
--- a/packages/twenty-server/src/engine/core-modules/email/email.module.ts
+++ b/packages/twenty-server/src/engine/core-modules/email/email.module.ts
@@ -2,11 +2,11 @@ import { DynamicModule, Global } from '@nestjs/common';
import { EmailModuleAsyncOptions } from 'src/engine/core-modules/email/interfaces/email.interface';
-import { EMAIL_DRIVER } from 'src/engine/core-modules/email/email.constants';
import { LoggerDriver } from 'src/engine/core-modules/email/drivers/logger.driver';
import { SmtpDriver } from 'src/engine/core-modules/email/drivers/smtp.driver';
-import { EmailService } from 'src/engine/core-modules/email/email.service';
import { EmailSenderService } from 'src/engine/core-modules/email/email-sender.service';
+import { EMAIL_DRIVER } from 'src/engine/core-modules/email/email.constants';
+import { EmailService } from 'src/engine/core-modules/email/email.service';
@Global()
export class EmailModule {