onboarding - signout button on Choose your workspaces modal (#12794)

@AMoreaux changes - 
- Added secondary background color to `StyledWorkspaceContainer`.
- Updated border styles for child elements to handle bottom borders
correctly.
- Removed redundant border styling from `StyledWorkspaceItem`.
Fix #12859 

my changes - 
- Add a logout button on the `Choose your Workspace` modal
- Remove the footer from the `Choose your Workspace` modal

---------

Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com>
This commit is contained in:
nitin
2025-06-25 19:36:56 +05:30
committed by GitHub
parent 1ab51d41aa
commit e2febbccea
2 changed files with 88 additions and 64 deletions

View File

@ -1,42 +1,43 @@
import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState';
import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Trans, useLingui } from '@lingui/react/macro';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useState } from 'react'; import { useState } from 'react';
import { FormProvider } from 'react-hook-form'; import { FormProvider } from 'react-hook-form';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { useTheme } from '@emotion/react'; import { ClickToActionLink, UndecoratedLink } from 'twenty-ui/navigation';
import { useLingui } from '@lingui/react/macro';
import { UndecoratedLink } from 'twenty-ui/navigation';
import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState';
import { useAuth } from '@/auth/hooks/useAuth';
import { SignInUpEmailField } from '@/auth/sign-in-up/components/internal/SignInUpEmailField'; import { SignInUpEmailField } from '@/auth/sign-in-up/components/internal/SignInUpEmailField';
import { SignInUpPasswordField } from '@/auth/sign-in-up/components/internal/SignInUpPasswordField'; import { SignInUpPasswordField } from '@/auth/sign-in-up/components/internal/SignInUpPasswordField';
import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/internal/SignInUpWithGoogle'; import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/internal/SignInUpWithGoogle';
import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/internal/SignInUpWithMicrosoft'; import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/internal/SignInUpWithMicrosoft';
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp'; import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm'; import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
import { useSignUpInNewWorkspace } from '@/auth/sign-in-up/hooks/useSignUpInNewWorkspace';
import { signInUpModeState } from '@/auth/states/signInUpModeState'; import { signInUpModeState } from '@/auth/states/signInUpModeState';
import { import {
SignInUpStep, SignInUpStep,
signInUpStepState, signInUpStepState,
} from '@/auth/states/signInUpStepState'; } from '@/auth/states/signInUpStepState';
import { getAvailableWorkspacePathAndSearchParams } from '@/auth/utils/availableWorkspacesUtils';
import { SignInUpMode } from '@/auth/types/signInUpMode'; import { SignInUpMode } from '@/auth/types/signInUpMode';
import { getAvailableWorkspacePathAndSearchParams } from '@/auth/utils/availableWorkspacesUtils';
import { isRequestingCaptchaTokenState } from '@/captcha/states/isRequestingCaptchaTokenState'; import { isRequestingCaptchaTokenState } from '@/captcha/states/isRequestingCaptchaTokenState';
import { authProvidersState } from '@/client-config/states/authProvidersState'; import { authProvidersState } from '@/client-config/states/authProvidersState';
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { import {
Avatar,
HorizontalSeparator, HorizontalSeparator,
IconChevronRight, IconChevronRight,
IconPlus, IconPlus,
Avatar,
} from 'twenty-ui/display'; } from 'twenty-ui/display';
import { Loader } from 'twenty-ui/feedback'; import { Loader } from 'twenty-ui/feedback';
import { MainButton } from 'twenty-ui/input'; import { MainButton } from 'twenty-ui/input';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
import { useSignUpInNewWorkspace } from '@/auth/sign-in-up/hooks/useSignUpInNewWorkspace';
import { AvailableWorkspace } from '~/generated/graphql'; import { AvailableWorkspace } from '~/generated/graphql';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
const StyledContentContainer = styled(motion.div)` const StyledContentContainer = styled(motion.div)`
margin-bottom: ${({ theme }) => theme.spacing(8)}; margin-bottom: ${({ theme }) => theme.spacing(8)};
@ -52,6 +53,7 @@ const StyledForm = styled.form`
`; `;
const StyledWorkspaceContainer = styled.div` const StyledWorkspaceContainer = styled.div`
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.light}; border: 1px solid ${({ theme }) => theme.border.color.light};
border-radius: ${({ theme }) => theme.border.radius.md}; border-radius: ${({ theme }) => theme.border.radius.md};
display: flex; display: flex;
@ -60,6 +62,14 @@ const StyledWorkspaceContainer = styled.div`
margin-top: ${({ theme }) => theme.spacing(4)}; margin-top: ${({ theme }) => theme.spacing(4)};
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
> * {
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
&:last-child {
border-bottom: none;
}
}
`; `;
const StyledWorkspaceItem = styled.div` const StyledWorkspaceItem = styled.div`
@ -71,7 +81,6 @@ const StyledWorkspaceItem = styled.div`
padding: 0; padding: 0;
overflow: hidden; overflow: hidden;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
cursor: pointer; cursor: pointer;
justify-content: space-between; justify-content: space-between;
@ -125,10 +134,16 @@ const StyledChevronIcon = styled.div`
display: flex; display: flex;
`; `;
const StyledActionLinkContainer = styled.div`
display: flex;
justify-content: center;
`;
export const SignInUpGlobalScopeForm = () => { export const SignInUpGlobalScopeForm = () => {
const authProviders = useRecoilValue(authProvidersState); const authProviders = useRecoilValue(authProvidersState);
const signInUpStep = useRecoilValue(signInUpStepState); const signInUpStep = useRecoilValue(signInUpStepState);
const { buildWorkspaceUrl } = useBuildWorkspaceUrl(); const { buildWorkspaceUrl } = useBuildWorkspaceUrl();
const { signOut } = useAuth();
const { createWorkspace } = useSignUpInNewWorkspace(); const { createWorkspace } = useSignUpInNewWorkspace();
const setSignInUpStep = useSetRecoilState(signInUpStepState); const setSignInUpStep = useSetRecoilState(signInUpStepState);
@ -183,57 +198,65 @@ export const SignInUpGlobalScopeForm = () => {
return ( return (
<> <>
{signInUpStep === SignInUpStep.WorkspaceSelection && ( {signInUpStep === SignInUpStep.WorkspaceSelection && (
<StyledWorkspaceContainer> <>
{[ <StyledWorkspaceContainer>
...availableWorkspaces.availableWorkspacesForSignIn, {[
...availableWorkspaces.availableWorkspacesForSignUp, ...availableWorkspaces.availableWorkspacesForSignIn,
].map((availableWorkspace) => ( ...availableWorkspaces.availableWorkspacesForSignUp,
<UndecoratedLink ].map((availableWorkspace) => (
key={availableWorkspace.id} <UndecoratedLink
to={getAvailableWorkspaceUrl(availableWorkspace)} key={availableWorkspace.id}
> to={getAvailableWorkspaceUrl(availableWorkspace)}
<StyledWorkspaceItem> >
<StyledWorkspaceContent> <StyledWorkspaceItem>
<Avatar <StyledWorkspaceContent>
placeholder={availableWorkspace.displayName || ''} <Avatar
avatarUrl={ placeholder={availableWorkspace.displayName || ''}
availableWorkspace.logo ?? DEFAULT_WORKSPACE_LOGO avatarUrl={
} availableWorkspace.logo ?? DEFAULT_WORKSPACE_LOGO
size="lg"
/>
<StyledWorkspaceTextContainer>
<StyledWorkspaceName>
{availableWorkspace.displayName || availableWorkspace.id}
</StyledWorkspaceName>
<StyledWorkspaceUrl>
{
new URL(
getWorkspaceUrl(availableWorkspace.workspaceUrls),
).hostname
} }
</StyledWorkspaceUrl> size="lg"
</StyledWorkspaceTextContainer> />
<StyledChevronIcon> <StyledWorkspaceTextContainer>
<IconChevronRight size={theme.icon.size.md} /> <StyledWorkspaceName>
</StyledChevronIcon> {availableWorkspace.displayName ||
</StyledWorkspaceContent> availableWorkspace.id}
</StyledWorkspaceItem> </StyledWorkspaceName>
</UndecoratedLink> <StyledWorkspaceUrl>
))} {
<StyledWorkspaceItem onClick={() => createWorkspace()}> new URL(
<StyledWorkspaceContent> getWorkspaceUrl(availableWorkspace.workspaceUrls),
<StyledWorkspaceLogo> ).hostname
<IconPlus size={theme.icon.size.lg} /> }
</StyledWorkspaceLogo> </StyledWorkspaceUrl>
<StyledWorkspaceTextContainer> </StyledWorkspaceTextContainer>
<StyledWorkspaceName>{t`Create a workspace`}</StyledWorkspaceName> <StyledChevronIcon>
</StyledWorkspaceTextContainer> <IconChevronRight size={theme.icon.size.md} />
<StyledChevronIcon> </StyledChevronIcon>
<IconChevronRight size={theme.icon.size.md} /> </StyledWorkspaceContent>
</StyledChevronIcon> </StyledWorkspaceItem>
</StyledWorkspaceContent> </UndecoratedLink>
</StyledWorkspaceItem> ))}
</StyledWorkspaceContainer> <StyledWorkspaceItem onClick={() => createWorkspace()}>
<StyledWorkspaceContent>
<StyledWorkspaceLogo>
<IconPlus size={theme.icon.size.lg} />
</StyledWorkspaceLogo>
<StyledWorkspaceTextContainer>
<StyledWorkspaceName>{t`Create a workspace`}</StyledWorkspaceName>
</StyledWorkspaceTextContainer>
<StyledChevronIcon>
<IconChevronRight size={theme.icon.size.md} />
</StyledChevronIcon>
</StyledWorkspaceContent>
</StyledWorkspaceItem>
</StyledWorkspaceContainer>
<StyledActionLinkContainer>
<ClickToActionLink onClick={signOut}>
<Trans>Log out</Trans>
</ClickToActionLink>
</StyledActionLinkContainer>
</>
)} )}
{signInUpStep !== SignInUpStep.WorkspaceSelection && ( {signInUpStep !== SignInUpStep.WorkspaceSelection && (
<StyledContentContainer> <StyledContentContainer>

View File

@ -12,8 +12,8 @@ import { Title } from '@/auth/components/Title';
import { EmailVerificationSent } from '@/auth/sign-in-up/components/EmailVerificationSent'; import { EmailVerificationSent } from '@/auth/sign-in-up/components/EmailVerificationSent';
import { FooterNote } from '@/auth/sign-in-up/components/FooterNote'; import { FooterNote } from '@/auth/sign-in-up/components/FooterNote';
import { SignInUpGlobalScopeForm } from '@/auth/sign-in-up/components/SignInUpGlobalScopeForm'; import { SignInUpGlobalScopeForm } from '@/auth/sign-in-up/components/SignInUpGlobalScopeForm';
import { SignInUpSSOIdentityProviderSelection } from '@/auth/sign-in-up/components/internal/SignInUpSSOIdentityProviderSelection';
import { SignInUpWorkspaceScopeForm } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeForm'; import { SignInUpWorkspaceScopeForm } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeForm';
import { SignInUpSSOIdentityProviderSelection } from '@/auth/sign-in-up/components/internal/SignInUpSSOIdentityProviderSelection';
import { SignInUpWorkspaceScopeFormEffect } from '@/auth/sign-in-up/components/internal/SignInUpWorkspaceScopeFormEffect'; import { SignInUpWorkspaceScopeFormEffect } from '@/auth/sign-in-up/components/internal/SignInUpWorkspaceScopeFormEffect';
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
import { useGetPublicWorkspaceDataByDomain } from '@/domain-manager/hooks/useGetPublicWorkspaceDataByDomain'; import { useGetPublicWorkspaceDataByDomain } from '@/domain-manager/hooks/useGetPublicWorkspaceDataByDomain';
@ -22,6 +22,7 @@ import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useI
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName'; import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { SignInUpGlobalScopeFormEffect } from '@/auth/sign-in-up/components/internal/SignInUpGlobalScopeFormEffect';
import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash'; import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash';
import { Modal } from '@/ui/layout/modal/components/Modal'; import { Modal } from '@/ui/layout/modal/components/Modal';
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
@ -29,7 +30,6 @@ import { useSearchParams } from 'react-router-dom';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { AnimatedEaseIn } from 'twenty-ui/utilities'; import { AnimatedEaseIn } from 'twenty-ui/utilities';
import { PublicWorkspaceDataOutput } from '~/generated/graphql'; import { PublicWorkspaceDataOutput } from '~/generated/graphql';
import { SignInUpGlobalScopeFormEffect } from '@/auth/sign-in-up/components/internal/SignInUpGlobalScopeFormEffect';
const StandardContent = ({ const StandardContent = ({
workspacePublicData, workspacePublicData,
@ -55,7 +55,8 @@ const StandardContent = ({
</AnimatedEaseIn> </AnimatedEaseIn>
<Title animate>{title}</Title> <Title animate>{title}</Title>
{signInUpForm} {signInUpForm}
{signInUpStep !== SignInUpStep.Password && <FooterNote />} {signInUpStep !== SignInUpStep.Password &&
signInUpStep !== SignInUpStep.WorkspaceSelection && <FooterNote />}
</Modal.Content> </Modal.Content>
); );
}; };