Files
twenty_crm/packages/twenty-front/src/pages/settings/SettingsBilling.tsx
Félix Malfait f38a25412e Add more translations (#9707)
As per title
2025-01-17 12:50:28 +01:00

200 lines
6.4 KiB
TypeScript

import { Trans, useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import {
Button,
H2Title,
IconCalendarEvent,
IconCircleX,
IconCreditCard,
Section,
} from 'twenty-ui';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage';
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import {
OnboardingStatus,
SubscriptionInterval,
useBillingPortalSessionQuery,
useUpdateBillingSubscriptionMutation,
} from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
type SwitchInfo = {
newInterval: SubscriptionInterval;
to: string;
from: string;
impact: string;
};
export const SettingsBilling = () => {
const { t } = useLingui();
const MONTHLY_SWITCH_INFO: SwitchInfo = {
newInterval: SubscriptionInterval.Year,
to: t`to yearly`,
from: t`from monthly to yearly`,
impact: t`You will be charged immediately for the full year.`,
};
const YEARLY_SWITCH_INFO: SwitchInfo = {
newInterval: SubscriptionInterval.Month,
to: t`to monthly`,
from: t`from yearly to monthly`,
impact: t`Your credit balance will be used to pay the monthly bills.`,
};
const SWITCH_INFOS = {
year: YEARLY_SWITCH_INFO,
month: MONTHLY_SWITCH_INFO,
};
const { enqueueSnackBar } = useSnackBar();
const onboardingStatus = useOnboardingStatus();
const subscriptionStatus = useSubscriptionStatus();
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
const switchingInfo =
currentWorkspace?.currentBillingSubscription?.interval ===
SubscriptionInterval.Year
? SWITCH_INFOS.year
: SWITCH_INFOS.month;
const [isSwitchingIntervalModalOpen, setIsSwitchingIntervalModalOpen] =
useState(false);
const [updateBillingSubscription] = useUpdateBillingSubscriptionMutation();
const { data, loading } = useBillingPortalSessionQuery({
variables: {
returnUrlPath: '/settings/billing',
},
});
const billingPortalButtonDisabled =
loading || !isDefined(data) || !isDefined(data.billingPortalSession.url);
const switchIntervalButtonDisabled =
onboardingStatus !== OnboardingStatus.Completed;
const cancelPlanButtonDisabled =
billingPortalButtonDisabled ||
onboardingStatus !== OnboardingStatus.Completed;
const openBillingPortal = () => {
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
window.location.replace(data.billingPortalSession.url);
}
};
const openSwitchingIntervalModal = () => {
setIsSwitchingIntervalModalOpen(true);
};
const from = switchingInfo.from;
const to = switchingInfo.to;
const impact = switchingInfo.impact;
const switchInterval = async () => {
try {
await updateBillingSubscription();
if (isDefined(currentWorkspace?.currentBillingSubscription)) {
const newCurrentWorkspace = {
...currentWorkspace,
currentBillingSubscription: {
...currentWorkspace?.currentBillingSubscription,
interval: switchingInfo.newInterval,
},
};
setCurrentWorkspace(newCurrentWorkspace);
}
enqueueSnackBar(t`Subscription has been switched ${to}`, {
variant: SnackBarVariant.Success,
});
} catch (error: any) {
enqueueSnackBar(t`Error while switching subscription ${to}.`, {
variant: SnackBarVariant.Error,
});
}
};
return (
<SubMenuTopBarContainer
title={t`Billing`}
links={[
{
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: <Trans>Billing</Trans> },
]}
>
<SettingsPageContainer>
<SettingsBillingCoverImage />
{isDefined(subscriptionStatus) && (
<>
<Section>
<H2Title
title={t`Manage your subscription`}
description={t`Edit payment method, see your invoices and more`}
/>
<Button
Icon={IconCreditCard}
title={t`View billing details`}
variant="secondary"
onClick={openBillingPortal}
disabled={billingPortalButtonDisabled}
/>
</Section>
<Section>
<H2Title
title={t`Edit billing interval`}
description={t`Switch ${from}`}
/>
<Button
Icon={IconCalendarEvent}
title={t`Switch ${to}`}
variant="secondary"
onClick={openSwitchingIntervalModal}
disabled={switchIntervalButtonDisabled}
/>
</Section>
<Section>
<H2Title
title={t`Cancel your subscription`}
description={t`Your workspace will be disabled`}
/>
<Button
Icon={IconCircleX}
title={t`Cancel Plan`}
variant="secondary"
accent="danger"
onClick={openBillingPortal}
disabled={cancelPlanButtonDisabled}
/>
</Section>
</>
)}
</SettingsPageContainer>
<ConfirmationModal
isOpen={isSwitchingIntervalModalOpen}
setIsOpen={setIsSwitchingIntervalModalOpen}
title={t`Switch billing ${to}`}
subtitle={
t`Are you sure that you want to change your billing interval?` +
` ${impact}`
}
onConfirmClick={switchInterval}
deleteButtonText={t`Change ${to}`}
confirmButtonAccent={'blue'}
/>
</SubMenuTopBarContainer>
);
};