5095 move onboardingstatus computation from frontend to backend (#5954)
- move front `onboardingStatus` computing to server side - add logic to `useSetNextOnboardingStatus` - update some missing redirections in `usePageChangeEffectNavigateLocation` - separate subscriptionStatus from onboardingStatus
This commit is contained in:
@ -4,14 +4,21 @@ import { within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { ValidatePasswordResetTokenDocument } from '~/generated/graphql';
|
||||
import {
|
||||
OnboardingStatus,
|
||||
ValidatePasswordResetTokenDocument,
|
||||
} from '~/generated/graphql';
|
||||
import { PasswordReset } from '~/pages/auth/PasswordReset';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
import { mockedOnboardingUserData } from '~/testing/mock-data/users';
|
||||
|
||||
const mockedOnboardingUsersData = mockedOnboardingUserData(
|
||||
OnboardingStatus.Completed,
|
||||
);
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
title: 'Pages/Auth/PasswordReset',
|
||||
@ -30,8 +37,8 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
validatePasswordResetToken: {
|
||||
id: mockedOnboardingUsersData[0].id,
|
||||
email: mockedOnboardingUsersData[0].email,
|
||||
id: mockedOnboardingUsersData.id,
|
||||
email: mockedOnboardingUsersData.email,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -40,7 +47,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
currentUser: mockedOnboardingUsersData,
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@ -19,6 +19,7 @@ import { ActionLink } from '@/ui/navigation/link/components/ActionLink';
|
||||
import { CAL_LINK } from '@/ui/navigation/link/constants/Cal';
|
||||
import {
|
||||
ProductPriceEntity,
|
||||
SubscriptionInterval,
|
||||
useCheckoutSessionMutation,
|
||||
useGetProductPricesQuery,
|
||||
} from '~/generated/graphql';
|
||||
@ -75,7 +76,7 @@ const benefits = [
|
||||
export const ChooseYourPlan = () => {
|
||||
const billing = useRecoilValue(billingState);
|
||||
|
||||
const [planSelected, setPlanSelected] = useState('month');
|
||||
const [planSelected, setPlanSelected] = useState(SubscriptionInterval.Month);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
@ -87,7 +88,7 @@ export const ChooseYourPlan = () => {
|
||||
|
||||
const [checkoutSession] = useCheckoutSessionMutation();
|
||||
|
||||
const handlePlanChange = (type?: string) => {
|
||||
const handlePlanChange = (type?: SubscriptionInterval) => {
|
||||
return () => {
|
||||
if (isNonEmptyString(type) && planSelected !== type) {
|
||||
setPlanSelected(type);
|
||||
@ -101,11 +102,11 @@ export const ChooseYourPlan = () => {
|
||||
price: ProductPriceEntity,
|
||||
prices: ProductPriceEntity[],
|
||||
): string => {
|
||||
if (price.recurringInterval !== 'year') {
|
||||
if (price.recurringInterval !== SubscriptionInterval.Year) {
|
||||
return 'Cancel anytime';
|
||||
}
|
||||
const monthPrice = prices.filter(
|
||||
(price) => price.recurringInterval === 'month',
|
||||
(price) => price.recurringInterval === SubscriptionInterval.Month,
|
||||
)?.[0];
|
||||
if (
|
||||
isDefined(monthPrice) &&
|
||||
|
||||
@ -9,11 +9,11 @@ import { z } from 'zod';
|
||||
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
@ -22,6 +22,8 @@ import { MainButton } from '@/ui/input/button/components/MainButton';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const StyledContentContainer = styled.div`
|
||||
width: 100%;
|
||||
@ -55,11 +57,11 @@ type Form = z.infer<typeof validationSchema>;
|
||||
|
||||
export const CreateProfile = () => {
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord<WorkspaceMember>({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
||||
});
|
||||
@ -100,17 +102,20 @@ export const CreateProfile = () => {
|
||||
},
|
||||
});
|
||||
|
||||
setCurrentWorkspaceMember(
|
||||
(current) =>
|
||||
({
|
||||
setCurrentWorkspaceMember((current) => {
|
||||
if (isDefined(current)) {
|
||||
return {
|
||||
...current,
|
||||
name: {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
},
|
||||
colorScheme: 'System',
|
||||
}) as any,
|
||||
);
|
||||
};
|
||||
}
|
||||
return current;
|
||||
});
|
||||
setNextOnboardingStatus();
|
||||
} catch (error: any) {
|
||||
enqueueSnackBar(error?.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
@ -119,6 +124,7 @@ export const CreateProfile = () => {
|
||||
},
|
||||
[
|
||||
currentWorkspaceMember?.id,
|
||||
setNextOnboardingStatus,
|
||||
enqueueSnackBar,
|
||||
setCurrentWorkspaceMember,
|
||||
updateOneRecord,
|
||||
@ -137,7 +143,7 @@ export const CreateProfile = () => {
|
||||
PageHotkeyScope.CreateProfile,
|
||||
);
|
||||
|
||||
if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) {
|
||||
if (onboardingStatus !== OnboardingStatus.ProfileCreation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -9,18 +9,20 @@ import { z } from 'zod';
|
||||
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
|
||||
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
|
||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
||||
import { Loader } from '@/ui/feedback/loader/components/Loader';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { MainButton } from '@/ui/input/button/components/MainButton';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { useActivateWorkspaceMutation } from '~/generated/graphql';
|
||||
import {
|
||||
OnboardingStatus,
|
||||
useActivateWorkspaceMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const StyledContentContainer = styled.div`
|
||||
@ -105,7 +107,7 @@ export const CreateWorkspace = () => {
|
||||
}
|
||||
};
|
||||
|
||||
if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceActivation) {
|
||||
if (onboardingStatus !== OnboardingStatus.WorkspaceActivation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useSetNextOnboardingStep } from '@/onboarding/hooks/useSetNextOnboardingStep';
|
||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { SeparatorLineText } from '@/ui/display/text/components/SeparatorLineText';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
@ -27,7 +27,10 @@ import { MainButton } from '@/ui/input/button/components/MainButton';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { AnimatedTranslation } from '@/ui/utilities/animation/components/AnimatedTranslation';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { OnboardingStep, useSendInviteLinkMutation } from '~/generated/graphql';
|
||||
import {
|
||||
OnboardingStatus,
|
||||
useSendInviteLinkMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const StyledAnimatedContainer = styled.div`
|
||||
@ -63,7 +66,7 @@ export const InviteTeam = () => {
|
||||
const theme = useTheme();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const [sendInviteLink] = useSendInviteLinkMutation();
|
||||
const setNextOnboardingStep = useSetNextOnboardingStep();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const {
|
||||
@ -133,7 +136,7 @@ export const InviteTeam = () => {
|
||||
);
|
||||
const result = await sendInviteLink({ variables: { emails } });
|
||||
|
||||
setNextOnboardingStep(OnboardingStep.InviteTeam);
|
||||
setNextOnboardingStatus();
|
||||
|
||||
if (isDefined(result.errors)) {
|
||||
throw result.errors;
|
||||
@ -145,7 +148,7 @@ export const InviteTeam = () => {
|
||||
});
|
||||
}
|
||||
},
|
||||
[enqueueSnackBar, sendInviteLink, setNextOnboardingStep],
|
||||
[enqueueSnackBar, sendInviteLink, setNextOnboardingStatus],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
@ -157,7 +160,7 @@ export const InviteTeam = () => {
|
||||
[handleSubmit],
|
||||
);
|
||||
|
||||
if (currentUser?.onboardingStep !== OnboardingStep.InviteTeam) {
|
||||
if (currentUser?.onboardingStatus !== OnboardingStatus.InviteTeam) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
import React from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconCheck, RGBA } from 'twenty-ui';
|
||||
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { MainButton } from '@/ui/input/button/components/MainButton';
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { AnimatedEaseIn } from '@/ui/utilities/animation/components/AnimatedEaseIn';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
|
||||
const StyledCheckContainer = styled.div`
|
||||
align-items: center;
|
||||
@ -29,8 +32,14 @@ const StyledButtonContainer = styled.div`
|
||||
|
||||
export const PaymentSuccess = () => {
|
||||
const theme = useTheme();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const color =
|
||||
theme.name === 'light' ? theme.grayScale.gray90 : theme.grayScale.gray10;
|
||||
|
||||
if (currentUser?.onboardingStatus === OnboardingStatus.Completed) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatedEaseIn>
|
||||
|
||||
@ -9,7 +9,7 @@ import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { OnboardingSyncEmailsSettingsCard } from '@/onboarding/components/OnboardingSyncEmailsSettingsCard';
|
||||
import { useSetNextOnboardingStep } from '@/onboarding/hooks/useSetNextOnboardingStep';
|
||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||
import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
@ -19,7 +19,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import {
|
||||
CalendarChannelVisibility,
|
||||
MessageChannelVisibility,
|
||||
OnboardingStep,
|
||||
OnboardingStatus,
|
||||
useSkipSyncEmailOnboardingStepMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
@ -40,12 +40,12 @@ const StyledActionLinkContainer = styled.div`
|
||||
export const SyncEmails = () => {
|
||||
const theme = useTheme();
|
||||
const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth();
|
||||
const setNextOnboardingStep = useSetNextOnboardingStep();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const [visibility, setVisibility] = useState<MessageChannelVisibility>(
|
||||
MessageChannelVisibility.ShareEverything,
|
||||
);
|
||||
const [skipSyncEmailOnboardingStepMutation] =
|
||||
const [skipSyncEmailOnboardingStatusMutation] =
|
||||
useSkipSyncEmailOnboardingStepMutation();
|
||||
|
||||
const handleButtonClick = async () => {
|
||||
@ -62,8 +62,8 @@ export const SyncEmails = () => {
|
||||
};
|
||||
|
||||
const continueWithoutSync = async () => {
|
||||
await skipSyncEmailOnboardingStepMutation();
|
||||
setNextOnboardingStep(OnboardingStep.SyncEmail);
|
||||
await skipSyncEmailOnboardingStatusMutation();
|
||||
setNextOnboardingStatus();
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
@ -75,7 +75,7 @@ export const SyncEmails = () => {
|
||||
[continueWithoutSync],
|
||||
);
|
||||
|
||||
if (currentUser?.onboardingStep !== OnboardingStep.SyncEmail) {
|
||||
if (currentUser?.onboardingStatus !== OnboardingStatus.SyncEmail) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
|
||||
@ -5,13 +5,14 @@ import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
import { ChooseYourPlan } from '~/pages/onboarding/ChooseYourPlan';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
import { mockedOnboardingUserData } from '~/testing/mock-data/users';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
@ -25,7 +26,9 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
currentUser: mockedOnboardingUserData(
|
||||
OnboardingStatus.PlanRequired,
|
||||
),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@ -5,13 +5,14 @@ import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
import { CreateProfile } from '~/pages/onboarding/CreateProfile';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
import { mockedOnboardingUserData } from '~/testing/mock-data/users';
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
title: 'Pages/Onboarding/CreateProfile',
|
||||
@ -24,7 +25,9 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
currentUser: mockedOnboardingUserData(
|
||||
OnboardingStatus.ProfileCreation,
|
||||
),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@ -2,30 +2,22 @@ import { getOperationName } from '@apollo/client/utilities';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
import { CreateWorkspace } from '~/pages/onboarding/CreateWorkspace';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
import { mockedOnboardingUserData } from '~/testing/mock-data/users';
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
title: 'Pages/Onboarding/CreateWorkspace',
|
||||
component: CreateWorkspace,
|
||||
decorators: [
|
||||
(Story) => {
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
setCurrentWorkspace(mockedOnboardingUsersData[1].defaultWorkspace);
|
||||
return <Story />;
|
||||
},
|
||||
PageDecorator,
|
||||
],
|
||||
decorators: [PageDecorator],
|
||||
args: { routePath: AppPath.CreateWorkspace },
|
||||
parameters: {
|
||||
msw: {
|
||||
@ -33,7 +25,9 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[1],
|
||||
currentUser: mockedOnboardingUserData(
|
||||
OnboardingStatus.WorkspaceActivation,
|
||||
),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { OnboardingStep } from '~/generated/graphql';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
import { AppPath } from '~/modules/types/AppPath';
|
||||
import { GET_CURRENT_USER } from '~/modules/users/graphql/queries/getCurrentUser';
|
||||
import { InviteTeam } from '~/pages/onboarding/InviteTeam';
|
||||
@ -12,7 +12,7 @@ import {
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
import { mockedOnboardingUserData } from '~/testing/mock-data/users';
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
title: 'Pages/Onboarding/InviteTeam',
|
||||
@ -25,10 +25,9 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: {
|
||||
...mockedOnboardingUsersData[0],
|
||||
onboardingStep: OnboardingStep.InviteTeam,
|
||||
},
|
||||
currentUser: mockedOnboardingUserData(
|
||||
OnboardingStatus.InviteTeam,
|
||||
),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@ -5,13 +5,14 @@ import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
import { PaymentSuccess } from '~/pages/onboarding/PaymentSuccess';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
import { mockedOnboardingUserData } from '~/testing/mock-data/users';
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
title: 'Pages/Onboarding/PaymentSuccess',
|
||||
@ -24,7 +25,9 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
currentUser: mockedOnboardingUserData(
|
||||
OnboardingStatus.WorkspaceActivation,
|
||||
),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { OnboardingStep } from '~/generated/graphql';
|
||||
import { OnboardingStatus } from '~/generated/graphql';
|
||||
import { AppPath } from '~/modules/types/AppPath';
|
||||
import { GET_CURRENT_USER } from '~/modules/users/graphql/queries/getCurrentUser';
|
||||
import { SyncEmails } from '~/pages/onboarding/SyncEmails';
|
||||
@ -12,7 +12,7 @@ import {
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
import { mockedOnboardingUserData } from '~/testing/mock-data/users';
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
title: 'Pages/Onboarding/SyncEmails',
|
||||
@ -25,10 +25,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: {
|
||||
...mockedOnboardingUsersData[0],
|
||||
onboardingStep: OnboardingStep.SyncEmail,
|
||||
},
|
||||
currentUser: mockedOnboardingUserData(OnboardingStatus.SyncEmail),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@ -10,10 +10,9 @@ import {
|
||||
IconCurrencyDollar,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage';
|
||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SupportChat } from '@/support/components/SupportChat';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
@ -24,8 +23,11 @@ import { Button } from '@/ui/input/button/components/Button';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
||||
import {
|
||||
OnboardingStatus,
|
||||
SubscriptionInterval,
|
||||
SubscriptionStatus,
|
||||
useBillingPortalSessionQuery,
|
||||
useUpdateBillingSubscriptionMutation,
|
||||
} from '~/generated/graphql';
|
||||
@ -40,21 +42,21 @@ const StyledInvisibleChat = styled.div`
|
||||
`;
|
||||
|
||||
type SwitchInfo = {
|
||||
newInterval: string;
|
||||
newInterval: SubscriptionInterval;
|
||||
to: string;
|
||||
from: string;
|
||||
impact: string;
|
||||
};
|
||||
|
||||
const MONTHLY_SWITCH_INFO: SwitchInfo = {
|
||||
newInterval: 'year',
|
||||
newInterval: SubscriptionInterval.Year,
|
||||
to: 'to yearly',
|
||||
from: 'from monthly to yearly',
|
||||
impact: 'You will be charged immediately for the full year.',
|
||||
};
|
||||
|
||||
const YEARLY_SWITCH_INFO: SwitchInfo = {
|
||||
newInterval: 'month',
|
||||
newInterval: SubscriptionInterval.Month,
|
||||
to: 'to monthly',
|
||||
from: 'from yearly to monthly',
|
||||
impact: 'Your credit balance will be used to pay the monthly bills.',
|
||||
@ -68,10 +70,12 @@ const SWITCH_INFOS = {
|
||||
export const SettingsBilling = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
const subscriptionStatus = useSubscriptionStatus();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const switchingInfo =
|
||||
currentWorkspace?.currentBillingSubscription?.interval === 'year'
|
||||
currentWorkspace?.currentBillingSubscription?.interval ===
|
||||
SubscriptionInterval.Year
|
||||
? SWITCH_INFOS.year
|
||||
: SWITCH_INFOS.month;
|
||||
const [isSwitchingIntervalModalOpen, setIsSwitchingIntervalModalOpen] =
|
||||
@ -94,14 +98,15 @@ export const SettingsBilling = () => {
|
||||
onboardingStatus !== OnboardingStatus.Completed;
|
||||
|
||||
const displayPaymentFailInfo =
|
||||
onboardingStatus === OnboardingStatus.PastDue ||
|
||||
onboardingStatus === OnboardingStatus.Unpaid;
|
||||
subscriptionStatus === SubscriptionStatus.PastDue ||
|
||||
subscriptionStatus === SubscriptionStatus.Unpaid;
|
||||
|
||||
const displaySubscriptionCanceledInfo =
|
||||
onboardingStatus === OnboardingStatus.Canceled;
|
||||
subscriptionStatus === SubscriptionStatus.Canceled;
|
||||
|
||||
const displaySubscribeInfo =
|
||||
onboardingStatus === OnboardingStatus.CompletedWithoutSubscription;
|
||||
onboardingStatus === OnboardingStatus.Completed &&
|
||||
!isDefined(subscriptionStatus);
|
||||
|
||||
const openBillingPortal = () => {
|
||||
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
||||
@ -153,22 +158,20 @@ export const SettingsBilling = () => {
|
||||
/>
|
||||
)}
|
||||
{displaySubscriptionCanceledInfo && (
|
||||
<UndecoratedLink to={AppPath.PlanRequired}>
|
||||
<Info
|
||||
text={'Subscription canceled. Please start a new one'}
|
||||
buttonTitle={'Subscribe'}
|
||||
accent={'danger'}
|
||||
/>
|
||||
</UndecoratedLink>
|
||||
<Info
|
||||
text={'Subscription canceled. Please start a new one'}
|
||||
buttonTitle={'Subscribe'}
|
||||
accent={'danger'}
|
||||
to={AppPath.PlanRequired}
|
||||
/>
|
||||
)}
|
||||
{displaySubscribeInfo ? (
|
||||
<UndecoratedLink to={AppPath.PlanRequired}>
|
||||
<Info
|
||||
text={'Your workspace does not have an active subscription'}
|
||||
buttonTitle={'Subscribe'}
|
||||
accent={'danger'}
|
||||
/>
|
||||
</UndecoratedLink>
|
||||
<Info
|
||||
text={'Your workspace does not have an active subscription'}
|
||||
buttonTitle={'Subscribe'}
|
||||
accent={'danger'}
|
||||
to={AppPath.PlanRequired}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Section>
|
||||
|
||||
Reference in New Issue
Block a user