Various fixes (#11448)
# Scrollbar fix Fixes https://github.com/twentyhq/twenty/issues/11403 <img width="1512" alt="image" src="https://github.com/user-attachments/assets/b13fe0f2-8c61-4ea8-9ea1-e61e571a90da" /> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@ -60,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body style="width: 100%; height: 100vh">
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script
|
<script
|
||||||
|
|||||||
@ -91,23 +91,23 @@ const testCases: {
|
|||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: AppPath.InviteTeam },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: defaultHomePagePath },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: defaultHomePagePath },
|
||||||
|
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: '/plan-required' },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: '/create/workspace' },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: '/create/profile' },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: '/sync/emails' },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: '/invite-team' },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: '/objects/companies' },
|
||||||
|
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: '/plan-required' },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: '/create/workspace' },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: '/create/profile' },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: '/sync/emails' },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: '/invite-team' },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: '/objects/companies' },
|
||||||
|
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: AppPath.PlanRequired },
|
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' },
|
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' },
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import { SettingsPath } from '@/types/SettingsPath';
|
|||||||
import { useIsWorkspaceActivationStatusEqualsTo } from '@/workspace/hooks/useIsWorkspaceActivationStatusEqualsTo';
|
import { useIsWorkspaceActivationStatusEqualsTo } from '@/workspace/hooks/useIsWorkspaceActivationStatusEqualsTo';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||||
import { OnboardingStatus } from '~/generated/graphql';
|
import { OnboardingStatus } from '~/generated/graphql';
|
||||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
|
|
||||||
export const usePageChangeEffectNavigateLocation = () => {
|
export const usePageChangeEffectNavigateLocation = () => {
|
||||||
const { isMatchingLocation } = useIsMatchingLocation();
|
const { isMatchingLocation } = useIsMatchingLocation();
|
||||||
@ -47,10 +47,6 @@ export const usePageChangeEffectNavigateLocation = () => {
|
|||||||
(objectMetadataItem) => objectMetadataItem.namePlural === objectNamePlural,
|
(objectMetadataItem) => objectMetadataItem.namePlural === objectNamePlural,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isMatchingOpenRoute) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isLoggedIn && !isMatchingOngoingUserCreationRoute) {
|
if (!isLoggedIn && !isMatchingOngoingUserCreationRoute) {
|
||||||
return AppPath.SignInUp;
|
return AppPath.SignInUp;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||||
|
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||||
import { NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/NavDrawerWidths';
|
import { NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/NavDrawerWidths';
|
||||||
|
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||||
import { LeftPanelSkeletonLoader } from '~/loading/components/LeftPanelSkeletonLoader';
|
import { LeftPanelSkeletonLoader } from '~/loading/components/LeftPanelSkeletonLoader';
|
||||||
import { RightPanelSkeletonLoader } from '~/loading/components/RightPanelSkeletonLoader';
|
import { RightPanelSkeletonLoader } from '~/loading/components/RightPanelSkeletonLoader';
|
||||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
background: ${({ theme }) => theme.background.noisy};
|
background: ${({ theme }) => theme.background.noisy};
|
||||||
@ -23,8 +25,11 @@ const StyledContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const UserOrMetadataLoader = () => {
|
export const UserOrMetadataLoader = () => {
|
||||||
|
const showAuthModal = useShowAuthModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
|
{showAuthModal && <Modal.Backdrop modalVariant="primary" />}
|
||||||
<LeftPanelSkeletonLoader />
|
<LeftPanelSkeletonLoader />
|
||||||
<RightPanelSkeletonLoader />
|
<RightPanelSkeletonLoader />
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { ClientConfigProviderEffect } from '@/client-config/components/ClientCon
|
|||||||
import { MainContextStoreProvider } from '@/context-store/components/MainContextStoreProvider';
|
import { MainContextStoreProvider } from '@/context-store/components/MainContextStoreProvider';
|
||||||
import { PromiseRejectionEffect } from '@/error-handler/components/PromiseRejectionEffect';
|
import { PromiseRejectionEffect } from '@/error-handler/components/PromiseRejectionEffect';
|
||||||
import { ApolloMetadataClientProvider } from '@/object-metadata/components/ApolloMetadataClientProvider';
|
import { ApolloMetadataClientProvider } from '@/object-metadata/components/ApolloMetadataClientProvider';
|
||||||
import { ObjectMetadataItemsGater } from '@/object-metadata/components/ObjectMetadataItemsGater';
|
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
|
||||||
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
||||||
import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider';
|
import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider';
|
||||||
import { DialogManager } from '@/ui/feedback/dialog-manager/components/DialogManager';
|
import { DialogManager } from '@/ui/feedback/dialog-manager/components/DialogManager';
|
||||||
@ -45,28 +45,27 @@ export const AppRouterProviders = () => {
|
|||||||
<UserProvider>
|
<UserProvider>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<ApolloMetadataClientProvider>
|
<ApolloMetadataClientProvider>
|
||||||
|
<ObjectMetadataItemsLoadEffect />
|
||||||
<ObjectMetadataItemsProvider>
|
<ObjectMetadataItemsProvider>
|
||||||
<ObjectMetadataItemsGater>
|
<PrefetchDataProvider>
|
||||||
<PrefetchDataProvider>
|
<UserThemeProviderEffect />
|
||||||
<UserThemeProviderEffect />
|
<SnackBarProvider>
|
||||||
<SnackBarProvider>
|
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
<DialogManager>
|
||||||
<DialogManager>
|
<StrictMode>
|
||||||
<StrictMode>
|
<PromiseRejectionEffect />
|
||||||
<PromiseRejectionEffect />
|
<GotoHotkeysEffectsProvider />
|
||||||
<GotoHotkeysEffectsProvider />
|
<ServerPreconnect />
|
||||||
<ServerPreconnect />
|
<PageTitle title={pageTitle} />
|
||||||
<PageTitle title={pageTitle} />
|
<PageFavicon />
|
||||||
<PageFavicon />
|
<Outlet />
|
||||||
<Outlet />
|
</StrictMode>
|
||||||
</StrictMode>
|
</DialogManager>
|
||||||
</DialogManager>
|
</DialogManagerScope>
|
||||||
</DialogManagerScope>
|
</SnackBarProvider>
|
||||||
</SnackBarProvider>
|
<MainContextStoreProvider />
|
||||||
<MainContextStoreProvider />
|
</PrefetchDataProvider>
|
||||||
</PrefetchDataProvider>
|
<PageChangeEffect />
|
||||||
<PageChangeEffect />
|
|
||||||
</ObjectMetadataItemsGater>
|
|
||||||
</ObjectMetadataItemsProvider>
|
</ObjectMetadataItemsProvider>
|
||||||
</ApolloMetadataClientProvider>
|
</ApolloMetadataClientProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
|
createSearchParams,
|
||||||
matchPath,
|
matchPath,
|
||||||
useLocation,
|
useLocation,
|
||||||
useNavigate,
|
useNavigate,
|
||||||
@ -60,15 +61,27 @@ export const PageChangeEffect = () => {
|
|||||||
}, [location, previousLocation]);
|
}, [location, previousLocation]);
|
||||||
|
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const navigationParams = searchParams.get('animateModal')
|
|
||||||
? `?animateModal=${searchParams.get('animateModal')}`
|
|
||||||
: '';
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDefined(pageChangeEffectNavigateLocation)) {
|
if (isDefined(pageChangeEffectNavigateLocation)) {
|
||||||
navigate(pageChangeEffectNavigateLocation + navigationParams);
|
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, navigationParams]);
|
}, [navigate, pageChangeEffectNavigateLocation, searchParams]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isLeavingRecordIndexPage = !!matchPath(
|
const isLeavingRecordIndexPage = !!matchPath(
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import { expect } from '@storybook/test';
|
|
||||||
import { ReactNode, act } from 'react';
|
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
|
||||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
|
||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
import { billingState } from '@/client-config/states/billingState';
|
import { billingState } from '@/client-config/states/billingState';
|
||||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { ReactNode, act } from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||||
import { renderHook } from '@testing-library/react';
|
import { renderHook } from '@testing-library/react';
|
||||||
import { email, mocks, password, results, token } from '../__mocks__/useAuth';
|
|
||||||
import { iconsState } from 'twenty-ui/display';
|
import { iconsState } from 'twenty-ui/display';
|
||||||
|
import { email, mocks, password, results, token } from '../__mocks__/useAuth';
|
||||||
|
|
||||||
const redirectSpy = jest.fn();
|
const redirectSpy = jest.fn();
|
||||||
|
|
||||||
@ -24,6 +24,12 @@ jest.mock('@/domain-manager/hooks/useRedirect', () => ({
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/object-metadata/hooks/useRefreshObjectMetadataItem', () => ({
|
||||||
|
useRefreshObjectMetadataItems: jest.fn().mockImplementation(() => ({
|
||||||
|
refreshObjectMetadataItems: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
<MockedProvider mocks={mocks} addTypename={false}>
|
<MockedProvider mocks={mocks} addTypename={false}>
|
||||||
<RecoilRoot>
|
<RecoilRoot>
|
||||||
|
|||||||
@ -56,15 +56,15 @@ import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useL
|
|||||||
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||||
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
||||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||||
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
|
||||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
import { i18n } from '@lingui/core';
|
import { i18n } from '@lingui/core';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { APP_LOCALES } from 'twenty-shared/translations';
|
import { APP_LOCALES } from 'twenty-shared/translations';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { iconsState } from 'twenty-ui/display';
|
||||||
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
|
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
|
||||||
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
|
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
|
||||||
import { iconsState } from 'twenty-ui/display';
|
|
||||||
|
|
||||||
export const useAuth = () => {
|
export const useAuth = () => {
|
||||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||||
@ -73,9 +73,7 @@ export const useAuth = () => {
|
|||||||
currentWorkspaceMemberState,
|
currentWorkspaceMemberState,
|
||||||
);
|
);
|
||||||
const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState);
|
const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState);
|
||||||
const setIsAppWaitingForFreshObjectMetadataState = useSetRecoilState(
|
|
||||||
isAppWaitingForFreshObjectMetadataState,
|
|
||||||
);
|
|
||||||
const setCurrentWorkspaceMembers = useSetRecoilState(
|
const setCurrentWorkspaceMembers = useSetRecoilState(
|
||||||
currentWorkspaceMembersState,
|
currentWorkspaceMembersState,
|
||||||
);
|
);
|
||||||
@ -84,6 +82,8 @@ export const useAuth = () => {
|
|||||||
isEmailVerificationRequiredState,
|
isEmailVerificationRequiredState,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
|
||||||
|
|
||||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||||
const setWorkspaces = useSetRecoilState(workspacesState);
|
const setWorkspaces = useSetRecoilState(workspacesState);
|
||||||
@ -312,7 +312,6 @@ export const useAuth = () => {
|
|||||||
|
|
||||||
setWorkspaces(validWorkspaces);
|
setWorkspaces(validWorkspaces);
|
||||||
}
|
}
|
||||||
setIsAppWaitingForFreshObjectMetadataState(true);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
@ -328,7 +327,6 @@ export const useAuth = () => {
|
|||||||
setCurrentWorkspaceMember,
|
setCurrentWorkspaceMember,
|
||||||
setCurrentWorkspaceMembers,
|
setCurrentWorkspaceMembers,
|
||||||
setDateTimeFormat,
|
setDateTimeFormat,
|
||||||
setIsAppWaitingForFreshObjectMetadataState,
|
|
||||||
setLastAuthenticateWorkspaceDomain,
|
setLastAuthenticateWorkspaceDomain,
|
||||||
setWorkspaces,
|
setWorkspaces,
|
||||||
]);
|
]);
|
||||||
@ -351,9 +349,15 @@ export const useAuth = () => {
|
|||||||
getAuthTokensResult.data?.getAuthTokensFromLoginToken.tokens,
|
getAuthTokensResult.data?.getAuthTokensFromLoginToken.tokens,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await refreshObjectMetadataItems();
|
||||||
await loadCurrentUser();
|
await loadCurrentUser();
|
||||||
},
|
},
|
||||||
[getAuthTokensFromLoginToken, setTokenPair, loadCurrentUser],
|
[
|
||||||
|
getAuthTokensFromLoginToken,
|
||||||
|
setTokenPair,
|
||||||
|
refreshObjectMetadataItems,
|
||||||
|
loadCurrentUser,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCredentialsSignIn = useCallback(
|
const handleCredentialsSignIn = useCallback(
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
|
|
||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { useLingui } from '@lingui/react/macro';
|
import { useLingui } from '@lingui/react/macro';
|
||||||
import { useSetRecoilState } from 'recoil';
|
|
||||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||||
|
|
||||||
export const useVerifyLogin = () => {
|
export const useVerifyLogin = () => {
|
||||||
@ -14,21 +12,14 @@ export const useVerifyLogin = () => {
|
|||||||
const { getAuthTokensFromLoginToken } = useAuth();
|
const { getAuthTokensFromLoginToken } = useAuth();
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
const setIsAppWaitingForFreshObjectMetadata = useSetRecoilState(
|
|
||||||
isAppWaitingForFreshObjectMetadataState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const verifyLoginToken = async (loginToken: string) => {
|
const verifyLoginToken = async (loginToken: string) => {
|
||||||
try {
|
try {
|
||||||
setIsAppWaitingForFreshObjectMetadata(true);
|
|
||||||
await getAuthTokensFromLoginToken(loginToken);
|
await getAuthTokensFromLoginToken(loginToken);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
enqueueSnackBar(t`Authentication failed`, {
|
enqueueSnackBar(t`Authentication failed`, {
|
||||||
variant: SnackBarVariant.Error,
|
variant: SnackBarVariant.Error,
|
||||||
});
|
});
|
||||||
navigate(AppPath.SignInUp);
|
navigate(AppPath.SignInUp);
|
||||||
} finally {
|
|
||||||
setIsAppWaitingForFreshObjectMetadata(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,12 +11,13 @@ import styled from '@emotion/styled';
|
|||||||
import { Trans } from '@lingui/react/macro';
|
import { Trans } from '@lingui/react/macro';
|
||||||
import { FormProvider } from 'react-hook-form';
|
import { FormProvider } from 'react-hook-form';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { ActionLink } from 'twenty-ui/navigation';
|
|
||||||
import { HorizontalSeparator } from 'twenty-ui/display';
|
import { HorizontalSeparator } from 'twenty-ui/display';
|
||||||
|
import { ActionLink } from 'twenty-ui/navigation';
|
||||||
|
|
||||||
const StyledContentContainer = styled.div`
|
const StyledContentContainer = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||||
margin-top: ${({ theme }) => theme.spacing(4)};
|
margin-top: ${({ theme }) => theme.spacing(4)};
|
||||||
|
width: 200px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SignInUpWorkspaceScopeForm = () => {
|
export const SignInUpWorkspaceScopeForm = () => {
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||||
|
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState';
|
|
||||||
|
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { useGetWorkspaceFromInviteHashQuery } from '~/generated/graphql';
|
import { useGetWorkspaceFromInviteHashQuery } from '~/generated/graphql';
|
||||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
|
|
||||||
export const useWorkspaceFromInviteHash = () => {
|
export const useWorkspaceFromInviteHash = () => {
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
@ -19,9 +18,7 @@ export const useWorkspaceFromInviteHash = () => {
|
|||||||
const workspaceInviteHash = useParams().workspaceInviteHash;
|
const workspaceInviteHash = useParams().workspaceInviteHash;
|
||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||||
const [initiallyLoggedIn] = useState(isDefined(currentWorkspace));
|
const [initiallyLoggedIn] = useState(isDefined(currentWorkspace));
|
||||||
const setIsDefaultLayoutAuthModalVisible = useSetRecoilState(
|
|
||||||
isDefaultLayoutAuthModalVisibleState,
|
|
||||||
);
|
|
||||||
const { data: workspaceFromInviteHash, loading } =
|
const { data: workspaceFromInviteHash, loading } =
|
||||||
useGetWorkspaceFromInviteHashQuery({
|
useGetWorkspaceFromInviteHashQuery({
|
||||||
skip: !workspaceInviteHash,
|
skip: !workspaceInviteHash,
|
||||||
@ -46,8 +43,6 @@ export const useWorkspaceFromInviteHash = () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
navigate(AppPath.Index);
|
navigate(AppPath.Index);
|
||||||
} else {
|
|
||||||
setIsDefaultLayoutAuthModalVisible(true);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
|
|||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { prefetchIndexViewIdFromObjectMetadataItemFamilySelector } from '@/prefetch/states/selector/prefetchIndexViewIdFromObjectMetadataItemFamilySelector';
|
import { prefetchIndexViewIdFromObjectMetadataItemFamilySelector } from '@/prefetch/states/selector/prefetchIndexViewIdFromObjectMetadataItemFamilySelector';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
|
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||||
import { useParams, useSearchParams } from 'react-router-dom';
|
import { useParams, useSearchParams } from 'react-router-dom';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
@ -62,9 +63,10 @@ export const MainContextStoreProvider = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const viewId = getViewId(viewIdQueryParam, indexViewId, lastVisitedViewId);
|
const viewId = getViewId(viewIdQueryParam, indexViewId, lastVisitedViewId);
|
||||||
|
const showAuthModal = useShowAuthModal();
|
||||||
|
|
||||||
const shouldComputeContextStore =
|
const shouldComputeContextStore =
|
||||||
isRecordIndexPage || isRecordShowPage || isSettingsPage;
|
(isRecordIndexPage || isRecordShowPage || isSettingsPage) && !showAuthModal;
|
||||||
|
|
||||||
if (!shouldComputeContextStore) {
|
if (!shouldComputeContextStore) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -61,13 +61,12 @@ export const MainContextStoreProviderEffect = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!objectMetadataItem) {
|
if (contextStoreCurrentObjectMetadataItemId !== objectMetadataItem?.id) {
|
||||||
setContextStoreCurrentObjectMetadataItemId(undefined);
|
setContextStoreCurrentObjectMetadataItemId(objectMetadataItem?.id);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contextStoreCurrentObjectMetadataItemId !== objectMetadataItem.id) {
|
if (!objectMetadataItem) {
|
||||||
setContextStoreCurrentObjectMetadataItemId(objectMetadataItem.id);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLastVisitedViewForObjectMetadataNamePlural({
|
setLastVisitedViewForObjectMetadataNamePlural({
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
|
||||||
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
|
|
||||||
|
|
||||||
export const ObjectMetadataItemsGater = ({
|
|
||||||
children,
|
|
||||||
}: React.PropsWithChildren) => {
|
|
||||||
const isAppWaitingForFreshObjectMetadata = useRecoilValue(
|
|
||||||
isAppWaitingForFreshObjectMetadataState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const shouldDisplayChildren = !isAppWaitingForFreshObjectMetadata;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>{shouldDisplayChildren ? <>{children}</> : <UserOrMetadataLoader />}</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
|
|
||||||
import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider';
|
import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider';
|
||||||
|
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
|
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
|
||||||
|
|
||||||
@ -10,11 +10,16 @@ export const ObjectMetadataItemsProvider = ({
|
|||||||
children,
|
children,
|
||||||
}: React.PropsWithChildren) => {
|
}: React.PropsWithChildren) => {
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
const shouldDisplayChildren = objectMetadataItems.length > 0;
|
|
||||||
|
const isAppWaitingForFreshObjectMetadata = useRecoilValue(
|
||||||
|
isAppWaitingForFreshObjectMetadataState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const shouldDisplayChildren =
|
||||||
|
!isAppWaitingForFreshObjectMetadata && objectMetadataItems.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ObjectMetadataItemsLoadEffect />
|
|
||||||
{shouldDisplayChildren ? (
|
{shouldDisplayChildren ? (
|
||||||
<PreComputedChipGeneratorsProvider>
|
<PreComputedChipGeneratorsProvider>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -39,7 +39,8 @@ export const useRefreshObjectMetadataItems = (
|
|||||||
!isDeeplyEqual(
|
!isDeeplyEqual(
|
||||||
snapshot.getLoadable(objectMetadataItemsState).getValue(),
|
snapshot.getLoadable(objectMetadataItemsState).getValue(),
|
||||||
toSetObjectMetadataItems,
|
toSetObjectMetadataItems,
|
||||||
)
|
) &&
|
||||||
|
toSetObjectMetadataItems.length > 0
|
||||||
) {
|
) {
|
||||||
set(objectMetadataItemsState, toSetObjectMetadataItems);
|
set(objectMetadataItemsState, toSetObjectMetadataItems);
|
||||||
set(isAppWaitingForFreshObjectMetadataState, false);
|
set(isAppWaitingForFreshObjectMetadataState, false);
|
||||||
|
|||||||
@ -433,6 +433,17 @@ const HookMockWrapper = getJestMetadataAndApolloMocksWrapper({
|
|||||||
apolloMocks: mocks,
|
apolloMocks: mocks,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useLocation: jest.fn().mockReturnValue({
|
||||||
|
pathname: '/',
|
||||||
|
search: '',
|
||||||
|
hash: '',
|
||||||
|
state: null,
|
||||||
|
key: 'default',
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
const Wrapper = ({ children }: { children: ReactNode }) => {
|
const Wrapper = ({ children }: { children: ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
<HookMockWrapper>
|
<HookMockWrapper>
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActionMenu';
|
import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActionMenu';
|
||||||
|
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||||
|
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||||
@ -7,7 +9,7 @@ import { PageHeader } from '@/ui/layout/page/components/PageHeader';
|
|||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { capitalize } from 'twenty-shared/utils';
|
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||||
import { useIcons } from 'twenty-ui/display';
|
import { useIcons } from 'twenty-ui/display';
|
||||||
|
|
||||||
const StyledTitleWithSelectedRecords = styled.div`
|
const StyledTitleWithSelectedRecords = styled.div`
|
||||||
@ -59,10 +61,19 @@ export const RecordIndexPageHeader = () => {
|
|||||||
label
|
label
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||||
|
contextStoreCurrentViewIdComponentState,
|
||||||
|
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader title={pageHeaderTitle} Icon={Icon}>
|
<PageHeader title={pageHeaderTitle} Icon={Icon}>
|
||||||
<RecordIndexActionMenu indexId={recordIndexId} />
|
{isDefined(contextStoreCurrentViewId) && (
|
||||||
<PageHeaderToggleCommandMenuButton />
|
<>
|
||||||
|
<RecordIndexActionMenu indexId={recordIndexId} />
|
||||||
|
<PageHeaderToggleCommandMenuButton />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
|
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
|
||||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||||
import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields';
|
import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies';
|
import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies';
|
||||||
import { isWorkspaceActiveOrSuspended } from 'twenty-shared/workspace';
|
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||||
|
|
||||||
export const useLazyLoadRecordIndexTable = (objectNameSingular: string) => {
|
export const useLazyLoadRecordIndexTable = (objectNameSingular: string) => {
|
||||||
|
const showAuthModal = useShowAuthModal();
|
||||||
|
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
@ -17,8 +16,6 @@ export const useLazyLoadRecordIndexTable = (objectNameSingular: string) => {
|
|||||||
const { setRecordTableData, setIsRecordTableInitialLoading } =
|
const { setRecordTableData, setIsRecordTableInitialLoading } =
|
||||||
useRecordTable();
|
useRecordTable();
|
||||||
|
|
||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
|
||||||
|
|
||||||
const params = useFindManyRecordIndexTableParams(objectNameSingular);
|
const params = useFindManyRecordIndexTableParams(objectNameSingular);
|
||||||
|
|
||||||
const recordGqlFields = useRecordTableRecordGqlFields({ objectMetadataItem });
|
const recordGqlFields = useRecordTableRecordGqlFields({ objectMetadataItem });
|
||||||
@ -44,9 +41,7 @@ export const useLazyLoadRecordIndexTable = (objectNameSingular: string) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
findManyRecords,
|
findManyRecords,
|
||||||
records: isWorkspaceActiveOrSuspended(currentWorkspace)
|
records: !showAuthModal ? records : SIGN_IN_BACKGROUND_MOCK_COMPANIES,
|
||||||
? records
|
|
||||||
: SIGN_IN_BACKGROUND_MOCK_COMPANIES,
|
|
||||||
totalCount: totalCount,
|
totalCount: totalCount,
|
||||||
loading,
|
loading,
|
||||||
fetchMoreRecords,
|
fetchMoreRecords,
|
||||||
|
|||||||
@ -2,25 +2,28 @@ import { useEffect, useState } from 'react';
|
|||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
|
||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
|
||||||
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
|
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
|
||||||
import { useLazyLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLazyLoadRecordIndexTable';
|
import { useLazyLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLazyLoadRecordIndexTable';
|
||||||
import { ROW_HEIGHT } from '@/object-record/record-table/constants/RowHeight';
|
import { ROW_HEIGHT } from '@/object-record/record-table/constants/RowHeight';
|
||||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { hasRecordTableFetchedAllRecordsComponentStateV2 } from '@/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2';
|
import { hasRecordTableFetchedAllRecordsComponentStateV2 } from '@/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2';
|
||||||
import { tableEncounteredUnrecoverableErrorComponentState } from '@/object-record/record-table/states/tableEncounteredUnrecoverableErrorComponentState';
|
import { tableEncounteredUnrecoverableErrorComponentState } from '@/object-record/record-table/states/tableEncounteredUnrecoverableErrorComponentState';
|
||||||
import { tableLastRowVisibleComponentState } from '@/object-record/record-table/states/tableLastRowVisibleComponentState';
|
import { tableLastRowVisibleComponentState } from '@/object-record/record-table/states/tableLastRowVisibleComponentState';
|
||||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||||
|
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||||
import { useScrollToPosition } from '@/ui/utilities/scroll/hooks/useScrollToPosition';
|
import { useScrollToPosition } from '@/ui/utilities/scroll/hooks/useScrollToPosition';
|
||||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
import { isNonEmptyString, isNull } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
export const RecordTableNoRecordGroupBodyEffect = () => {
|
export const RecordTableNoRecordGroupBodyEffect = () => {
|
||||||
const { objectNameSingular } = useRecordTableContextOrThrow();
|
const { objectNameSingular } = useRecordTableContextOrThrow();
|
||||||
|
|
||||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
const { setIsRecordTableInitialLoading } = useRecordTable();
|
||||||
|
|
||||||
|
const showAuthModal = useShowAuthModal();
|
||||||
|
|
||||||
const [hasInitializedScroll, setHasInitializedScroll] = useState(false);
|
const [hasInitializedScroll, setHasInitializedScroll] = useState(false);
|
||||||
|
|
||||||
@ -139,7 +142,8 @@ export const RecordTableNoRecordGroupBodyEffect = () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isNull(currentWorkspaceMember)) {
|
if (showAuthModal) {
|
||||||
|
setIsRecordTableInitialLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +151,12 @@ export const RecordTableNoRecordGroupBodyEffect = () => {
|
|||||||
findManyRecords();
|
findManyRecords();
|
||||||
setHasInitialized(true);
|
setHasInitialized(true);
|
||||||
}
|
}
|
||||||
}, [currentWorkspaceMember, findManyRecords, hasInitialized]);
|
}, [
|
||||||
|
findManyRecords,
|
||||||
|
hasInitialized,
|
||||||
|
setIsRecordTableInitialLoading,
|
||||||
|
showAuthModal,
|
||||||
|
]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
|
||||||
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
|
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
|
||||||
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
|
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
|
||||||
import { useLazyLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLazyLoadRecordIndexTable';
|
import { useLazyLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLazyLoadRecordIndexTable';
|
||||||
import { recordIndexHasFetchedAllRecordsByGroupComponentState } from '@/object-record/record-index/states/recordIndexHasFetchedAllRecordsByGroupComponentState';
|
import { recordIndexHasFetchedAllRecordsByGroupComponentState } from '@/object-record/record-index/states/recordIndexHasFetchedAllRecordsByGroupComponentState';
|
||||||
import { ROW_HEIGHT } from '@/object-record/record-table/constants/RowHeight';
|
import { ROW_HEIGHT } from '@/object-record/record-table/constants/RowHeight';
|
||||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
|
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||||
import { useScrollToPosition } from '@/ui/utilities/scroll/hooks/useScrollToPosition';
|
import { useScrollToPosition } from '@/ui/utilities/scroll/hooks/useScrollToPosition';
|
||||||
import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2';
|
import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2';
|
||||||
import { isNonEmptyString, isNull } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
import { OnboardingStatus } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
export const RecordTableRecordGroupBodyEffect = () => {
|
export const RecordTableRecordGroupBodyEffect = () => {
|
||||||
const { objectNameSingular } = useRecordTableContextOrThrow();
|
const { objectNameSingular } = useRecordTableContextOrThrow();
|
||||||
|
|
||||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
const onboardingStatus = useOnboardingStatus();
|
||||||
|
|
||||||
const recordGroupId = useCurrentRecordGroupId();
|
const recordGroupId = useCurrentRecordGroupId();
|
||||||
|
|
||||||
@ -79,12 +80,12 @@ export const RecordTableRecordGroupBodyEffect = () => {
|
|||||||
}, [hasNextPage, setHasRecordFetchedAllRecordsComponents]);
|
}, [hasNextPage, setHasRecordFetchedAllRecordsComponents]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isNull(currentWorkspaceMember)) {
|
if (onboardingStatus !== OnboardingStatus.COMPLETED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
findManyRecords();
|
findManyRecords();
|
||||||
}, [currentWorkspaceMember, findManyRecords]);
|
}, [onboardingStatus, findManyRecords]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { ReactElement, useContext } from 'react';
|
|||||||
|
|
||||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||||
import { RecordTableCellBaseContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer';
|
import { RecordTableCellBaseContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer';
|
||||||
import { RecordTableCellSoftFocusMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusMode';
|
|
||||||
|
|
||||||
import { RecordTableCellSoftFocusModeHotkeysSetterEffect } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect';
|
import { RecordTableCellSoftFocusModeHotkeysSetterEffect } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect';
|
||||||
import { RecordTableCellDisplayMode } from './RecordTableCellDisplayMode';
|
import { RecordTableCellDisplayMode } from './RecordTableCellDisplayMode';
|
||||||
@ -27,19 +26,14 @@ export const RecordTableCellContainer = ({
|
|||||||
<RecordTableCellBaseContainer>
|
<RecordTableCellBaseContainer>
|
||||||
{isInEditMode ? (
|
{isInEditMode ? (
|
||||||
<RecordTableCellEditMode>{editModeContent}</RecordTableCellEditMode>
|
<RecordTableCellEditMode>{editModeContent}</RecordTableCellEditMode>
|
||||||
) : hasSoftFocus ? (
|
|
||||||
<>
|
|
||||||
<RecordTableCellSoftFocusModeHotkeysSetterEffect />
|
|
||||||
<RecordTableCellSoftFocusMode
|
|
||||||
editModeContent={editModeContent}
|
|
||||||
nonEditModeContent={nonEditModeContent}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<RecordTableCellDisplayMode>
|
<RecordTableCellDisplayMode>
|
||||||
{nonEditModeContent}
|
{nonEditModeContent}
|
||||||
</RecordTableCellDisplayMode>
|
</RecordTableCellDisplayMode>
|
||||||
)}
|
)}
|
||||||
|
{hasSoftFocus ? (
|
||||||
|
<RecordTableCellSoftFocusModeHotkeysSetterEffect />
|
||||||
|
) : null}
|
||||||
</RecordTableCellBaseContainer>
|
</RecordTableCellBaseContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -41,11 +41,11 @@ export const RecordTableCellFieldContextWrapper = ({
|
|||||||
return (
|
return (
|
||||||
<RecordFieldComponentInstanceContext.Provider value={{ instanceId }}>
|
<RecordFieldComponentInstanceContext.Provider value={{ instanceId }}>
|
||||||
{isLabelIdentifier ? (
|
{isLabelIdentifier ? (
|
||||||
<RecordTableCellFieldContextLabelIdentifier>
|
<RecordTableCellFieldContextLabelIdentifier key={instanceId}>
|
||||||
{children}
|
{children}
|
||||||
</RecordTableCellFieldContextLabelIdentifier>
|
</RecordTableCellFieldContextLabelIdentifier>
|
||||||
) : (
|
) : (
|
||||||
<RecordTableCellFieldContextGeneric>
|
<RecordTableCellFieldContextGeneric key={instanceId}>
|
||||||
{children}
|
{children}
|
||||||
</RecordTableCellFieldContextGeneric>
|
</RecordTableCellFieldContextGeneric>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,117 +0,0 @@
|
|||||||
import { ReactElement, useContext, useEffect, useRef } from 'react';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { useGetButtonIcon } from '@/object-record/record-field/hooks/useGetButtonIcon';
|
|
||||||
import { useIsFieldEmpty } from '@/object-record/record-field/hooks/useIsFieldEmpty';
|
|
||||||
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
|
|
||||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
|
||||||
import { RecordTableCellButton } from '@/object-record/record-table/record-table-cell/components/RecordTableCellButton';
|
|
||||||
import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell';
|
|
||||||
import { isSoftFocusUsingMouseState } from '@/object-record/record-table/states/isSoftFocusUsingMouseState';
|
|
||||||
|
|
||||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
|
||||||
import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
import { IconArrowUpRight } from 'twenty-ui/display';
|
|
||||||
import { useIsMobile } from 'twenty-ui/utilities';
|
|
||||||
import { RecordTableCellDisplayContainer } from './RecordTableCellDisplayContainer';
|
|
||||||
|
|
||||||
type RecordTableCellSoftFocusModeProps = {
|
|
||||||
editModeContent: ReactElement;
|
|
||||||
nonEditModeContent: ReactElement;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RecordTableCellSoftFocusMode = ({
|
|
||||||
editModeContent,
|
|
||||||
nonEditModeContent,
|
|
||||||
}: RecordTableCellSoftFocusModeProps) => {
|
|
||||||
const { columnIndex, columnDefinition } = useContext(RecordTableCellContext);
|
|
||||||
const { recordId, isReadOnly } = useContext(FieldContext);
|
|
||||||
|
|
||||||
const { onActionMenuDropdownOpened } = useRecordTableBodyContextOrThrow();
|
|
||||||
|
|
||||||
const { openTableCell } = useOpenRecordTableCellFromCell();
|
|
||||||
|
|
||||||
const editModeContentOnly = useIsFieldInputOnly();
|
|
||||||
|
|
||||||
const isFieldInputOnly = useIsFieldInputOnly();
|
|
||||||
|
|
||||||
const isEmpty = useIsFieldEmpty();
|
|
||||||
|
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
const isSoftFocusUsingMouse = useRecoilValue(isSoftFocusUsingMouseState);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isSoftFocusUsingMouse) {
|
|
||||||
scrollRef.current?.scrollIntoView({ block: 'nearest' });
|
|
||||||
}
|
|
||||||
}, [isSoftFocusUsingMouse]);
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
if (!isFieldInputOnly && !isReadOnly) {
|
|
||||||
openTableCell();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleButtonClick = () => {
|
|
||||||
if (!isFieldInputOnly && isFirstColumn) {
|
|
||||||
openTableCell(undefined, false, true);
|
|
||||||
} else {
|
|
||||||
openTableCell();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Disabling sidepanel access for now, TODO: launch
|
|
||||||
if (!isFieldInputOnly) {
|
|
||||||
openTableCell(undefined, true);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleActionMenuDropdown = (event: React.MouseEvent) => {
|
|
||||||
onActionMenuDropdownOpened(event, recordId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isFirstColumn = columnIndex === 0;
|
|
||||||
const customButtonIcon = useGetButtonIcon();
|
|
||||||
const buttonIcon = isFirstColumn
|
|
||||||
? IconArrowUpRight // IconLayoutSidebarRightExpand - Disabling sidepanel access for now
|
|
||||||
: customButtonIcon;
|
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
|
||||||
const showButton =
|
|
||||||
isDefined(buttonIcon) &&
|
|
||||||
!editModeContentOnly &&
|
|
||||||
!isReadOnly &&
|
|
||||||
!(isMobile && isFirstColumn);
|
|
||||||
|
|
||||||
const dontShowContent = isEmpty && isReadOnly;
|
|
||||||
|
|
||||||
const showPlaceholder =
|
|
||||||
!editModeContentOnly && !isReadOnly && isFirstColumn && isEmpty;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<RecordTableCellDisplayContainer
|
|
||||||
onClick={handleClick}
|
|
||||||
scrollRef={scrollRef}
|
|
||||||
softFocus
|
|
||||||
onContextMenu={handleActionMenuDropdown}
|
|
||||||
placeholderForEmptyCell={
|
|
||||||
showPlaceholder ? columnDefinition.label : undefined
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{dontShowContent ? (
|
|
||||||
<></>
|
|
||||||
) : editModeContentOnly ? (
|
|
||||||
editModeContent
|
|
||||||
) : (
|
|
||||||
nonEditModeContent
|
|
||||||
)}
|
|
||||||
</RecordTableCellDisplayContainer>
|
|
||||||
{showButton && (
|
|
||||||
<RecordTableCellButton onClick={handleButtonClick} Icon={buttonIcon} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { currentUserState } from '@/auth/states/currentUserState';
|
|
||||||
import { Favorite } from '@/favorites/types/Favorite';
|
import { Favorite } from '@/favorites/types/Favorite';
|
||||||
import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
@ -13,17 +12,12 @@ import { prefetchFavoriteFoldersState } from '@/prefetch/states/prefetchFavorite
|
|||||||
import { prefetchFavoritesState } from '@/prefetch/states/prefetchFavoritesState';
|
import { prefetchFavoritesState } from '@/prefetch/states/prefetchFavoritesState';
|
||||||
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
|
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
import { useIsWorkspaceActivationStatusEqualsTo } from '@/workspace/hooks/useIsWorkspaceActivationStatusEqualsTo';
|
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
|
||||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
export const PrefetchRunFavoriteQueriesEffect = () => {
|
export const PrefetchRunFavoriteQueriesEffect = () => {
|
||||||
const currentUser = useRecoilValue(currentUserState);
|
const showAuthModal = useShowAuthModal();
|
||||||
|
|
||||||
const isWorkspaceActive = useIsWorkspaceActivationStatusEqualsTo(
|
|
||||||
WorkspaceActivationStatus.ACTIVE,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
@ -53,14 +47,14 @@ export const PrefetchRunFavoriteQueriesEffect = () => {
|
|||||||
objectNameSingular: CoreObjectNameSingular.Favorite,
|
objectNameSingular: CoreObjectNameSingular.Favorite,
|
||||||
filter: findAllFavoritesOperationSignature.variables.filter,
|
filter: findAllFavoritesOperationSignature.variables.filter,
|
||||||
recordGqlFields: findAllFavoritesOperationSignature.fields,
|
recordGqlFields: findAllFavoritesOperationSignature.fields,
|
||||||
skip: !currentUser || !isWorkspaceActive,
|
skip: showAuthModal,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { records: favoriteFolders } = useFindManyRecords({
|
const { records: favoriteFolders } = useFindManyRecords({
|
||||||
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
|
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
|
||||||
filter: findAllFavoriteFoldersOperationSignature.variables.filter,
|
filter: findAllFavoriteFoldersOperationSignature.variables.filter,
|
||||||
recordGqlFields: findAllFavoriteFoldersOperationSignature.fields,
|
recordGqlFields: findAllFavoriteFoldersOperationSignature.fields,
|
||||||
skip: !currentUser || !isWorkspaceActive,
|
skip: showAuthModal,
|
||||||
});
|
});
|
||||||
|
|
||||||
const setPrefetchFavoritesState = useRecoilCallback(
|
const setPrefetchFavoritesState = useRecoilCallback(
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTabl
|
|||||||
import { useSetTableColumns } from '@/object-record/record-table/hooks/useSetTableColumns';
|
import { useSetTableColumns } from '@/object-record/record-table/hooks/useSetTableColumns';
|
||||||
import { SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions';
|
import { SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions';
|
||||||
import { SIGN_IN_BACKGROUND_MOCK_VIEW_FIELDS } from '@/sign-in-background-mock/constants/SignInBackgroundMockViewFields';
|
import { SIGN_IN_BACKGROUND_MOCK_VIEW_FIELDS } from '@/sign-in-background-mock/constants/SignInBackgroundMockViewFields';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
import { useInitViewBar } from '@/views/hooks/useInitViewBar';
|
import { useInitViewBar } from '@/views/hooks/useInitViewBar';
|
||||||
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
|
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
|
||||||
|
|
||||||
@ -23,11 +23,13 @@ export const SignInBackgroundMockContainerEffect = ({
|
|||||||
recordTableId,
|
recordTableId,
|
||||||
viewId,
|
viewId,
|
||||||
}: SignInBackgroundMockContainerEffectProps) => {
|
}: SignInBackgroundMockContainerEffectProps) => {
|
||||||
const setContextStoreCurrentObjectMetadataItemId =
|
const [
|
||||||
useSetRecoilComponentStateV2(
|
contextStoreCurrentObjectMetadataItemId,
|
||||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
setContextStoreCurrentObjectMetadataItemId,
|
||||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
] = useRecoilComponentStateV2(
|
||||||
);
|
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||||
|
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||||
|
);
|
||||||
|
|
||||||
const { setAvailableTableColumns } = useRecordTable({
|
const { setAvailableTableColumns } = useRecordTable({
|
||||||
recordTableId,
|
recordTableId,
|
||||||
@ -61,7 +63,9 @@ export const SignInBackgroundMockContainerEffect = ({
|
|||||||
recordTableId,
|
recordTableId,
|
||||||
);
|
);
|
||||||
|
|
||||||
setContextStoreCurrentObjectMetadataItemId(objectMetadataItem.id);
|
if (contextStoreCurrentObjectMetadataItemId !== objectMetadataItem.id) {
|
||||||
|
setContextStoreCurrentObjectMetadataItemId(objectMetadataItem.id);
|
||||||
|
}
|
||||||
}, [
|
}, [
|
||||||
setViewObjectMetadataId,
|
setViewObjectMetadataId,
|
||||||
setAvailableFieldDefinitions,
|
setAvailableFieldDefinitions,
|
||||||
@ -70,6 +74,7 @@ export const SignInBackgroundMockContainerEffect = ({
|
|||||||
setTableColumns,
|
setTableColumns,
|
||||||
recordTableId,
|
recordTableId,
|
||||||
setContextStoreCurrentObjectMetadataItemId,
|
setContextStoreCurrentObjectMetadataItemId,
|
||||||
|
contextStoreCurrentObjectMetadataItemId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@ -4,8 +4,6 @@ import React from 'react';
|
|||||||
const StyledInputErrorHelper = styled.div`
|
const StyledInputErrorHelper = styled.div`
|
||||||
color: ${({ theme }) => theme.color.red};
|
color: ${({ theme }) => theme.color.red};
|
||||||
font-size: ${({ theme }) => theme.font.size.xs};
|
font-size: ${({ theme }) => theme.font.size.xs};
|
||||||
position: absolute;
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(0.25)};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const InputErrorHelper = ({
|
export const InputErrorHelper = ({
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { renderHook } from '@testing-library/react';
|
import { renderHook } from '@testing-library/react';
|
||||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
import { RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||||
import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState';
|
|
||||||
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
||||||
import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
|
import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
|
||||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||||
@ -39,14 +38,9 @@ const setupMockIsMatchingLocation = (pathname: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getResult = (isDefaultLayoutAuthModalVisible = true) =>
|
const getResult = () =>
|
||||||
renderHook(
|
renderHook(
|
||||||
() => {
|
() => {
|
||||||
const setIsDefaultLayoutAuthModalVisible = useSetRecoilState(
|
|
||||||
isDefaultLayoutAuthModalVisibleState,
|
|
||||||
);
|
|
||||||
setIsDefaultLayoutAuthModalVisible(isDefaultLayoutAuthModalVisible);
|
|
||||||
|
|
||||||
return useShowAuthModal();
|
return useShowAuthModal();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -112,15 +106,15 @@ const testCases = [
|
|||||||
{ loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
{ loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
|
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true },
|
{ loc: AppPath.CreateWorkspace, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: true },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: true },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: true },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: true },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: true },
|
||||||
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
|
|
||||||
{ loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
{ loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
||||||
{ loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
||||||
@ -157,14 +151,14 @@ const testCases = [
|
|||||||
|
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true },
|
{ loc: AppPath.PlanRequired, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: true },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: true },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: true },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: true },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: true },
|
||||||
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.COMPLETED, res: true },
|
||||||
|
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
{ loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: true },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
{ loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.COMPLETED, res: false },
|
||||||
@ -290,7 +284,7 @@ const testCases = [
|
|||||||
|
|
||||||
describe('useShowAuthModal', () => {
|
describe('useShowAuthModal', () => {
|
||||||
testCases.forEach((testCase) => {
|
testCases.forEach((testCase) => {
|
||||||
it(`testCase for location ${testCase.loc} with onboardingStatus ${testCase.onboardingStatus} should return ${testCase.res}`, () => {
|
it(`testCase for location ${testCase.loc} with onboardingStatus ${testCase.onboardingStatus} and subscriptionStatus ${testCase.subscriptionStatus} should return ${testCase.res}`, () => {
|
||||||
setupMockOnboardingStatus(testCase.onboardingStatus);
|
setupMockOnboardingStatus(testCase.onboardingStatus);
|
||||||
setupMockSubscriptionStatus(testCase.subscriptionStatus);
|
setupMockSubscriptionStatus(testCase.subscriptionStatus);
|
||||||
setupMockIsMatchingLocation(testCase.loc);
|
setupMockIsMatchingLocation(testCase.loc);
|
||||||
@ -305,21 +299,21 @@ describe('useShowAuthModal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('test with token validation loading', () => {
|
describe('test with token validation loading', () => {
|
||||||
it(`with appPath ${AppPath.Invite} and isDefaultLayoutAuthModalVisible=false`, () => {
|
it(`with appPath ${AppPath.Invite} `, () => {
|
||||||
setupMockOnboardingStatus(OnboardingStatus.COMPLETED);
|
setupMockOnboardingStatus(OnboardingStatus.COMPLETED);
|
||||||
setupMockSubscriptionStatus(SubscriptionStatus.Active);
|
setupMockSubscriptionStatus(SubscriptionStatus.Active);
|
||||||
setupMockIsMatchingLocation(AppPath.Invite);
|
setupMockIsMatchingLocation(AppPath.Invite);
|
||||||
setupMockIsLogged(true);
|
setupMockIsLogged(true);
|
||||||
const { result } = getResult(false);
|
const { result } = getResult();
|
||||||
expect(result.current).toBeFalsy();
|
expect(result.current).toBeTruthy();
|
||||||
});
|
});
|
||||||
it(`with appPath ${AppPath.ResetPassword} and isDefaultLayoutAuthModalVisible=false`, () => {
|
it(`with appPath ${AppPath.ResetPassword} `, () => {
|
||||||
setupMockOnboardingStatus(OnboardingStatus.COMPLETED);
|
setupMockOnboardingStatus(OnboardingStatus.COMPLETED);
|
||||||
setupMockSubscriptionStatus(SubscriptionStatus.Active);
|
setupMockSubscriptionStatus(SubscriptionStatus.Active);
|
||||||
setupMockIsMatchingLocation(AppPath.ResetPassword);
|
setupMockIsMatchingLocation(AppPath.ResetPassword);
|
||||||
setupMockIsLogged(true);
|
setupMockIsLogged(true);
|
||||||
const { result } = getResult(false);
|
const { result } = getResult();
|
||||||
expect(result.current).toBeFalsy();
|
expect(result.current).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,40 +1,27 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState';
|
import { OnboardingStatus } from '~/generated/graphql';
|
||||||
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
|
|
||||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||||
|
|
||||||
export const useShowAuthModal = () => {
|
export const useShowAuthModal = () => {
|
||||||
const { isMatchingLocation } = useIsMatchingLocation();
|
const { isMatchingLocation } = useIsMatchingLocation();
|
||||||
const isLoggedIn = useIsLogged();
|
const isLoggedIn = useIsLogged();
|
||||||
const onboardingStatus = useOnboardingStatus();
|
const onboardingStatus = useOnboardingStatus();
|
||||||
const subscriptionStatus = useSubscriptionStatus();
|
|
||||||
|
|
||||||
const isDefaultLayoutAuthModalVisible = useRecoilValue(
|
|
||||||
isDefaultLayoutAuthModalVisibleState,
|
|
||||||
);
|
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (
|
|
||||||
isMatchingLocation(AppPath.Verify) ||
|
|
||||||
isMatchingLocation(AppPath.VerifyEmail)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isMatchingLocation(AppPath.Invite) ||
|
isMatchingLocation(AppPath.Invite) ||
|
||||||
isMatchingLocation(AppPath.ResetPassword) ||
|
isMatchingLocation(AppPath.ResetPassword) ||
|
||||||
isMatchingLocation(AppPath.VerifyEmail) ||
|
isMatchingLocation(AppPath.VerifyEmail) ||
|
||||||
isMatchingLocation(AppPath.SignInUp)
|
isMatchingLocation(AppPath.Verify) ||
|
||||||
|
isMatchingLocation(AppPath.SignInUp) ||
|
||||||
|
isMatchingLocation(AppPath.CreateWorkspace) ||
|
||||||
|
isMatchingLocation(AppPath.PlanRequired)
|
||||||
) {
|
) {
|
||||||
return isDefaultLayoutAuthModalVisible;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -48,20 +35,6 @@ export const useShowAuthModal = () => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMatchingLocation(AppPath.PlanRequired)) {
|
|
||||||
return (
|
|
||||||
(onboardingStatus === OnboardingStatus.COMPLETED &&
|
|
||||||
!isDefined(subscriptionStatus)) ||
|
|
||||||
subscriptionStatus === SubscriptionStatus.Canceled
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}, [
|
}, [isLoggedIn, isMatchingLocation, onboardingStatus]);
|
||||||
isLoggedIn,
|
|
||||||
isDefaultLayoutAuthModalVisible,
|
|
||||||
isMatchingLocation,
|
|
||||||
onboardingStatus,
|
|
||||||
subscriptionStatus,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -257,3 +257,4 @@ export const Modal = ({
|
|||||||
Modal.Header = ModalHeader;
|
Modal.Header = ModalHeader;
|
||||||
Modal.Content = ModalContent;
|
Modal.Content = ModalContent;
|
||||||
Modal.Footer = ModalFooter;
|
Modal.Footer = ModalFooter;
|
||||||
|
Modal.Backdrop = StyledBackDrop;
|
||||||
|
|||||||
@ -26,7 +26,7 @@ const StyledLayout = styled.div`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
position: relative;
|
position: relative;
|
||||||
scrollbar-color: ${({ theme }) => theme.border.color.medium};
|
scrollbar-color: ${({ theme }) => theme.border.color.medium} transparent;
|
||||||
scrollbar-width: 4px;
|
scrollbar-width: 4px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@ -105,7 +105,9 @@ export const DefaultLayout = () => {
|
|||||||
)}
|
)}
|
||||||
{showAuthModal ? (
|
{showAuthModal ? (
|
||||||
<>
|
<>
|
||||||
<SignInBackgroundMockPage />
|
<StyledMainContainer>
|
||||||
|
<SignInBackgroundMockPage />
|
||||||
|
</StyledMainContainer>
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
<LayoutGroup>
|
<LayoutGroup>
|
||||||
<AuthModal isOpenAnimated={animateModal}>
|
<AuthModal isOpenAnimated={animateModal}>
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
import { createState } from 'twenty-ui/utilities';
|
|
||||||
export const isDefaultLayoutAuthModalVisibleState = createState<boolean>({
|
|
||||||
key: 'isDefaultLayoutAuthModalVisibleState',
|
|
||||||
defaultValue: true,
|
|
||||||
});
|
|
||||||
@ -9,10 +9,10 @@ import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-sta
|
|||||||
|
|
||||||
const StyledScrollWrapper = styled.div`
|
const StyledScrollWrapper = styled.div`
|
||||||
&.scroll-wrapper-x-enabled {
|
&.scroll-wrapper-x-enabled {
|
||||||
overflow-x: scroll;
|
overflow-x: overlay;
|
||||||
}
|
}
|
||||||
&.scroll-wrapper-y-enabled {
|
&.scroll-wrapper-y-enabled {
|
||||||
overflow-y: scroll;
|
overflow-y: overlay;
|
||||||
}
|
}
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { AppPath } from '@/types/AppPath';
|
|||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||||
import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState';
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
@ -21,7 +20,9 @@ import { useState } from 'react';
|
|||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { MainButton } from 'twenty-ui/input';
|
||||||
|
import { AnimatedEaseIn } from 'twenty-ui/utilities';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import {
|
import {
|
||||||
useUpdatePasswordViaResetTokenMutation,
|
useUpdatePasswordViaResetTokenMutation,
|
||||||
@ -29,8 +30,6 @@ import {
|
|||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||||
import { logError } from '~/utils/logError';
|
import { logError } from '~/utils/logError';
|
||||||
import { AnimatedEaseIn } from 'twenty-ui/utilities';
|
|
||||||
import { MainButton } from 'twenty-ui/input';
|
|
||||||
|
|
||||||
const validationSchema = z
|
const validationSchema = z
|
||||||
.object({
|
.object({
|
||||||
@ -89,9 +88,6 @@ export const PasswordReset = () => {
|
|||||||
|
|
||||||
const isLoggedIn = useIsLogged();
|
const isLoggedIn = useIsLogged();
|
||||||
|
|
||||||
const setIsDefaultLayoutAuthModalVisibleState = useSetRecoilState(
|
|
||||||
isDefaultLayoutAuthModalVisibleState,
|
|
||||||
);
|
|
||||||
const { control, handleSubmit } = useForm<Form>({
|
const { control, handleSubmit } = useForm<Form>({
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@ -105,7 +101,7 @@ export const PasswordReset = () => {
|
|||||||
variables: {
|
variables: {
|
||||||
token: passwordResetToken ?? '',
|
token: passwordResetToken ?? '',
|
||||||
},
|
},
|
||||||
skip: !passwordResetToken,
|
skip: !passwordResetToken || isTokenValid,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
enqueueSnackBar(error?.message ?? 'Token Invalid', {
|
enqueueSnackBar(error?.message ?? 'Token Invalid', {
|
||||||
variant: SnackBarVariant.Error,
|
variant: SnackBarVariant.Error,
|
||||||
@ -114,7 +110,6 @@ export const PasswordReset = () => {
|
|||||||
},
|
},
|
||||||
onCompleted: (data) => {
|
onCompleted: (data) => {
|
||||||
setIsTokenValid(true);
|
setIsTokenValid(true);
|
||||||
setIsDefaultLayoutAuthModalVisibleState(true);
|
|
||||||
if (isNonEmptyString(data?.validatePasswordResetToken?.email)) {
|
if (isNonEmptyString(data?.validatePasswordResetToken?.email)) {
|
||||||
setEmail(data.validatePasswordResetToken.email);
|
setEmail(data.validatePasswordResetToken.email);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,100 +1,12 @@
|
|||||||
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 styled from '@emotion/styled';
|
||||||
import { useLingui } from '@lingui/react/macro';
|
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
|
||||||
import { motion } from 'framer-motion';
|
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)`
|
const StyledContentContainer = styled(motion.div)`
|
||||||
|
height: 300px;
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||||
margin-top: ${({ theme }) => theme.spacing(4)};
|
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 (
|
|
||||||
<>
|
|
||||||
<Logo secondaryLogo={workspacePublicData?.logo} />
|
|
||||||
<Title animate={false}>{title}</Title>
|
|
||||||
{signInUpForm}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SignInUpLoading = () => {
|
export const SignInUpLoading = () => {
|
||||||
const { t } = useLingui();
|
return <StyledContentContainer />;
|
||||||
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 (
|
|
||||||
<StandardContent
|
|
||||||
workspacePublicData={workspacePublicData}
|
|
||||||
signInUpForm={
|
|
||||||
<>
|
|
||||||
<p style={{ color: 'red', backgroundColor: 'blue' }}>
|
|
||||||
SignInUpLoading
|
|
||||||
</p>
|
|
||||||
<StyledContentContainer>
|
|
||||||
<StyledForm>
|
|
||||||
<MainButton
|
|
||||||
disabled={true}
|
|
||||||
title={t`Continue`}
|
|
||||||
type="submit"
|
|
||||||
variant={'primary'}
|
|
||||||
Icon={() => <Loader />}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</StyledForm>
|
|
||||||
</StyledContentContainer>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
title={title}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,30 +1,32 @@
|
|||||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { RecordIndexContainerGater } from '@/object-record/record-index/components/RecordIndexContainerGater';
|
import { RecordIndexContainerGater } from '@/object-record/record-index/components/RecordIndexContainerGater';
|
||||||
import { PageContainer } from '@/ui/layout/page/components/PageContainer';
|
import { PageContainer } from '@/ui/layout/page/components/PageContainer';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { isNonEmptyString, isUndefined } from '@sniptt/guards';
|
import { isUndefined } from '@sniptt/guards';
|
||||||
|
|
||||||
export const RecordIndexPage = () => {
|
export const RecordIndexPage = () => {
|
||||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
|
||||||
contextStoreCurrentViewIdComponentState,
|
|
||||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
|
||||||
);
|
|
||||||
|
|
||||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
isUndefined(contextStoreCurrentObjectMetadataItemId) ||
|
|
||||||
!isNonEmptyString(contextStoreCurrentViewId)
|
if (isUndefined(contextStoreCurrentObjectMetadataItemId)) {
|
||||||
) {
|
return <></>;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const objectMetadataItem = objectMetadataItems.find(
|
||||||
|
(objectMetadataItem) =>
|
||||||
|
objectMetadataItem.id === contextStoreCurrentObjectMetadataItemId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isUndefined(objectMetadataItem)) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { z } from 'zod';
|
|||||||
import { SubTitle } from '@/auth/components/SubTitle';
|
import { SubTitle } from '@/auth/components/SubTitle';
|
||||||
import { Title } from '@/auth/components/Title';
|
import { Title } from '@/auth/components/Title';
|
||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
|
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
|
||||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||||
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
||||||
@ -15,14 +16,14 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
|
|||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||||
import { Trans, useLingui } from '@lingui/react/macro';
|
import { Trans, useLingui } from '@lingui/react/macro';
|
||||||
import {
|
|
||||||
OnboardingStatus,
|
|
||||||
useActivateWorkspaceMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { H2Title } from 'twenty-ui/display';
|
import { H2Title } 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 {
|
||||||
|
OnboardingStatus,
|
||||||
|
useActivateWorkspaceMutation,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
const StyledContentContainer = styled.div`
|
const StyledContentContainer = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -42,6 +43,7 @@ export const CreateWorkspace = () => {
|
|||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
const onboardingStatus = useOnboardingStatus();
|
const onboardingStatus = useOnboardingStatus();
|
||||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||||
|
const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
|
||||||
|
|
||||||
const { loadCurrentUser } = useAuth();
|
const { loadCurrentUser } = useAuth();
|
||||||
const [activateWorkspace] = useActivateWorkspaceMutation();
|
const [activateWorkspace] = useActivateWorkspaceMutation();
|
||||||
@ -81,6 +83,8 @@ export const CreateWorkspace = () => {
|
|||||||
if (isDefined(result.errors)) {
|
if (isDefined(result.errors)) {
|
||||||
throw result.errors ?? new Error(t`Unknown error`);
|
throw result.errors ?? new Error(t`Unknown error`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await refreshObjectMetadataItems();
|
||||||
await loadCurrentUser();
|
await loadCurrentUser();
|
||||||
setNextOnboardingStatus();
|
setNextOnboardingStatus();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@ -93,6 +97,7 @@ export const CreateWorkspace = () => {
|
|||||||
activateWorkspace,
|
activateWorkspace,
|
||||||
enqueueSnackBar,
|
enqueueSnackBar,
|
||||||
loadCurrentUser,
|
loadCurrentUser,
|
||||||
|
refreshObjectMetadataItems,
|
||||||
setNextOnboardingStatus,
|
setNextOnboardingStatus,
|
||||||
t,
|
t,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { UserProvider } from '~/modules/users/components/UserProvider';
|
|||||||
import { mockedApolloClient } from '~/testing/mockedApolloClient';
|
import { mockedApolloClient } from '~/testing/mockedApolloClient';
|
||||||
|
|
||||||
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
||||||
|
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
|
||||||
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
||||||
import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext';
|
import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext';
|
||||||
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
@ -29,10 +30,10 @@ import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider
|
|||||||
import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProviderEffect';
|
import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProviderEffect';
|
||||||
import { i18n } from '@lingui/core';
|
import { i18n } from '@lingui/core';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
|
|
||||||
import { FullHeightStorybookLayout } from '../FullHeightStorybookLayout';
|
|
||||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||||
import { IconsProvider } from 'twenty-ui/display';
|
import { IconsProvider } from 'twenty-ui/display';
|
||||||
|
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
|
||||||
|
import { FullHeightStorybookLayout } from '../FullHeightStorybookLayout';
|
||||||
|
|
||||||
export type PageDecoratorArgs = {
|
export type PageDecoratorArgs = {
|
||||||
routePath: string;
|
routePath: string;
|
||||||
@ -86,6 +87,7 @@ const Providers = () => {
|
|||||||
<WorkspaceProviderEffect />
|
<WorkspaceProviderEffect />
|
||||||
<UserProvider>
|
<UserProvider>
|
||||||
<ApolloMetadataClientMockedProvider>
|
<ApolloMetadataClientMockedProvider>
|
||||||
|
<ObjectMetadataItemsLoadEffect />
|
||||||
<ObjectMetadataItemsProvider>
|
<ObjectMetadataItemsProvider>
|
||||||
<FullHeightStorybookLayout>
|
<FullHeightStorybookLayout>
|
||||||
<HelmetProvider>
|
<HelmetProvider>
|
||||||
|
|||||||
@ -12,10 +12,14 @@ export const emailModuleFactory = (
|
|||||||
|
|
||||||
switch (driver) {
|
switch (driver) {
|
||||||
case EmailDriver.Logger:
|
case EmailDriver.Logger:
|
||||||
return {};
|
return {
|
||||||
|
type: EmailDriver.Logger,
|
||||||
|
};
|
||||||
|
|
||||||
case EmailDriver.Smtp: {
|
case EmailDriver.Smtp: {
|
||||||
const options: EmailModuleOptions = {};
|
const options: EmailModuleOptions = {
|
||||||
|
type: EmailDriver.Smtp,
|
||||||
|
};
|
||||||
|
|
||||||
const host = environmentService.get('EMAIL_SMTP_HOST');
|
const host = environmentService.get('EMAIL_SMTP_HOST');
|
||||||
const port = environmentService.get('EMAIL_SMTP_PORT');
|
const port = environmentService.get('EMAIL_SMTP_PORT');
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import { DynamicModule, Global } from '@nestjs/common';
|
import { DynamicModule, Global } from '@nestjs/common';
|
||||||
|
|
||||||
import { EmailModuleAsyncOptions } from 'src/engine/core-modules/email/interfaces/email.interface';
|
import {
|
||||||
|
EmailDriver,
|
||||||
|
EmailModuleAsyncOptions,
|
||||||
|
} from 'src/engine/core-modules/email/interfaces/email.interface';
|
||||||
|
|
||||||
import { LoggerDriver } from 'src/engine/core-modules/email/drivers/logger.driver';
|
import { LoggerDriver } from 'src/engine/core-modules/email/drivers/logger.driver';
|
||||||
import { SmtpDriver } from 'src/engine/core-modules/email/drivers/smtp.driver';
|
import { SmtpDriver } from 'src/engine/core-modules/email/drivers/smtp.driver';
|
||||||
@ -16,7 +19,9 @@ export class EmailModule {
|
|||||||
useFactory: (...args: any[]) => {
|
useFactory: (...args: any[]) => {
|
||||||
const config = options.useFactory(...args);
|
const config = options.useFactory(...args);
|
||||||
|
|
||||||
return config ? new SmtpDriver(config) : new LoggerDriver();
|
return config.type === EmailDriver.Smtp
|
||||||
|
? new SmtpDriver(config)
|
||||||
|
: new LoggerDriver();
|
||||||
},
|
},
|
||||||
inject: options.inject || [],
|
inject: options.inject || [],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,7 +7,13 @@ export enum EmailDriver {
|
|||||||
Smtp = 'smtp',
|
Smtp = 'smtp',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EmailModuleOptions = SMTPConnection.Options | undefined;
|
export type EmailModuleOptions =
|
||||||
|
| (SMTPConnection.Options & {
|
||||||
|
type: EmailDriver.Smtp;
|
||||||
|
})
|
||||||
|
| {
|
||||||
|
type: EmailDriver.Logger;
|
||||||
|
};
|
||||||
|
|
||||||
export type EmailModuleAsyncOptions = {
|
export type EmailModuleAsyncOptions = {
|
||||||
useFactory: (...args: any[]) => EmailModuleOptions;
|
useFactory: (...args: any[]) => EmailModuleOptions;
|
||||||
|
|||||||
Reference in New Issue
Block a user