Remove redundant files and update pnpm lockfile

This commit is contained in:
giancarlo
2024-04-30 22:16:38 +07:00
parent 9eded69f15
commit 19aa40493f
78 changed files with 8835 additions and 10056 deletions

View File

@@ -0,0 +1,38 @@
'use client';
import { useRouter } from 'next/navigation';
import { AccountSelector } from '@kit/accounts/account-selector';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
const features = {
enableTeamCreation: featureFlagsConfig.enableTeamCreation,
};
export function HomeAccountSelector(props: {
accounts: Array<{
label: string | null;
value: string | null;
image: string | null;
}>;
collapsed: boolean;
}) {
const router = useRouter();
return (
<AccountSelector
collapsed={props.collapsed}
accounts={props.accounts}
features={features}
onAccountChange={(value) => {
if (value) {
const path = pathsConfig.app.accountHome.replace('[account]', value);
router.replace(path);
}
}}
/>
);
}

View File

@@ -0,0 +1,62 @@
import {
BorderedNavigationMenu,
BorderedNavigationMenuItem,
} from '@kit/ui/bordered-navigation-menu';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
// home imports
import { HomeAccountSelector } from '../_components/home-account-selector';
import { UserNotifications } from '../_components/user-notifications';
import { type UserWorkspace } from '../_lib/server/load-user-workspace';
export function HomeMenuNavigation(props: { workspace: UserWorkspace }) {
const { workspace, user, accounts } = props.workspace;
const routes = personalAccountNavigationConfig.routes.reduce<
Array<{
path: string;
label: string;
Icon?: React.ReactNode;
end?: boolean | undefined;
}>
>((acc, item) => {
if ('children' in item) {
return [...acc, ...item.children];
}
if ('divider' in item) {
return acc;
}
return [...acc, item];
}, []);
return (
<div className={'flex w-full flex-1 justify-between'}>
<div className={'flex items-center space-x-4'}>
<AppLogo />
<BorderedNavigationMenu>
{routes.map((route) => (
<BorderedNavigationMenuItem {...route} key={route.path} />
))}
</BorderedNavigationMenu>
</div>
<div className={'flex justify-end space-x-4'}>
<HomeAccountSelector accounts={accounts} collapsed={false} />
<UserNotifications userId={user.id} />
<ProfileAccountDropdownContainer
collapsed={true}
user={user}
account={workspace}
/>
</div>
</div>
);
}

View File

@@ -0,0 +1,130 @@
'use client';
import Link from 'next/link';
import { LogOut, Menu } from 'lucide-react';
import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@kit/ui/dropdown-menu';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import featuresFlagConfig from '~/config/feature-flags.config';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
// home imports
import { HomeAccountSelector } from '../_components/home-account-selector';
import type { UserWorkspace } from '../_lib/server/load-user-workspace';
export function HomeMobileNavigation(props: { workspace: UserWorkspace }) {
const signOut = useSignOut();
const Links = personalAccountNavigationConfig.routes.map((item, index) => {
if ('children' in item) {
return item.children.map((child) => {
return (
<DropdownLink
key={child.path}
Icon={child.Icon}
path={child.path}
label={child.label}
/>
);
});
}
if ('divider' in item) {
return <DropdownMenuSeparator key={index} />;
}
return (
<DropdownLink
key={item.path}
Icon={item.Icon}
path={item.path}
label={item.label}
/>
);
});
return (
<DropdownMenu>
<DropdownMenuTrigger>
<Menu className={'h-9'} />
</DropdownMenuTrigger>
<DropdownMenuContent sideOffset={10} className={'w-screen rounded-none'}>
<If condition={featuresFlagConfig.enableTeamAccounts}>
<DropdownMenuGroup>
<DropdownMenuLabel>
<Trans i18nKey={'common:yourAccounts'} />
</DropdownMenuLabel>
<HomeAccountSelector
accounts={props.workspace.accounts}
collapsed={false}
/>
</DropdownMenuGroup>
<DropdownMenuSeparator />
</If>
<DropdownMenuGroup>{Links}</DropdownMenuGroup>
<DropdownMenuSeparator />
<SignOutDropdownItem onSignOut={() => signOut.mutateAsync()} />
</DropdownMenuContent>
</DropdownMenu>
);
}
function DropdownLink(
props: React.PropsWithChildren<{
path: string;
label: string;
Icon: React.ReactNode;
}>,
) {
return (
<DropdownMenuItem asChild key={props.path}>
<Link
href={props.path}
className={'flex h-12 w-full items-center space-x-4'}
>
{props.Icon}
<span>
<Trans i18nKey={props.label} defaults={props.label} />
</span>
</Link>
</DropdownMenuItem>
);
}
function SignOutDropdownItem(
props: React.PropsWithChildren<{
onSignOut: () => unknown;
}>,
) {
return (
<DropdownMenuItem
className={'flex h-12 w-full items-center space-x-4'}
onClick={props.onSignOut}
>
<LogOut className={'h-6'} />
<span>
<Trans i18nKey={'common:signOut'} defaults={'Sign out'} />
</span>
</DropdownMenuItem>
);
}

View File

@@ -0,0 +1,14 @@
import { PageHeader } from '@kit/ui/page';
export function HomeLayoutPageHeader(
props: React.PropsWithChildren<{
title: string | React.ReactNode;
description: string | React.ReactNode;
}>,
) {
return (
<PageHeader title={props.title} description={props.description}>
{props.children}
</PageHeader>
);
}

View File

@@ -0,0 +1,42 @@
import { If } from '@kit/ui/if';
import { Sidebar, SidebarContent, SidebarNavigation } from '@kit/ui/sidebar';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import featuresFlagConfig from '~/config/feature-flags.config';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
// home imports
import type { UserWorkspace } from '../_lib/server/load-user-workspace';
import { HomeAccountSelector } from './home-account-selector';
export function HomeSidebar(props: { workspace: UserWorkspace }) {
const { workspace, user, accounts } = props.workspace;
return (
<Sidebar>
<SidebarContent className={'h-16 justify-center'}>
<If
condition={featuresFlagConfig.enableTeamAccounts}
fallback={<AppLogo className={'py-2'} />}
>
<HomeAccountSelector collapsed={false} accounts={accounts} />
</If>
</SidebarContent>
<SidebarContent className={`mt-5 h-[calc(100%-160px)] overflow-y-auto`}>
<SidebarNavigation config={personalAccountNavigationConfig} />
</SidebarContent>
<div className={'absolute bottom-4 left-0 w-full'}>
<SidebarContent>
<ProfileAccountDropdownContainer
collapsed={false}
user={user}
account={workspace}
/>
</SidebarContent>
</div>
</Sidebar>
);
}

View File

@@ -0,0 +1,16 @@
import { NotificationsPopover } from '@kit/notifications/components';
import featuresFlagConfig from '~/config/feature-flags.config';
export function UserNotifications(props: { userId: string }) {
if (!featuresFlagConfig.enableNotifications) {
return null;
}
return (
<NotificationsPopover
accountIds={[props.userId]}
realtime={featuresFlagConfig.realtimeNotifications}
/>
);
}

View File

@@ -0,0 +1,46 @@
import { cache } from 'react';
import { createAccountsApi } from '@kit/accounts/api';
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
import featureFlagsConfig from '~/config/feature-flags.config';
const shouldLoadAccounts = featureFlagsConfig.enableTeamAccounts;
export type UserWorkspace = Awaited<ReturnType<typeof loadUserWorkspace>>;
/**
* @name loadUserWorkspace
* @description
* Load the user workspace data. It's a cached per-request function that fetches the user workspace data.
* It can be used across the server components to load the user workspace data.
*/
export const loadUserWorkspace = cache(async () => {
const client = getSupabaseServerComponentClient();
const api = createAccountsApi(client);
const accountsPromise = shouldLoadAccounts
? () => api.loadUserAccounts()
: () => Promise.resolve([]);
const workspacePromise = api.getAccountWorkspace();
const userPromise = client.auth.getUser();
const [accounts, workspace, userResult] = await Promise.all([
accountsPromise(),
workspacePromise,
userPromise,
]);
const user = userResult.data.user;
if (!user) {
throw new Error('User is not logged in');
}
return {
accounts,
workspace,
user,
};
});

View File

@@ -0,0 +1,121 @@
'use client';
import { useState, useTransition } from 'react';
import dynamic from 'next/dynamic';
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import { PlanPicker } from '@kit/billing-gateway/components';
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@kit/ui/card';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import billingConfig from '~/config/billing.config';
import { createPersonalAccountCheckoutSession } from '../_lib/server/server-actions';
const EmbeddedCheckout = dynamic(
async () => {
const { EmbeddedCheckout } = await import('@kit/billing-gateway/checkout');
return {
default: EmbeddedCheckout,
};
},
{
ssr: false,
},
);
export function PersonalAccountCheckoutForm(props: {
customerId: string | null | undefined;
}) {
const [pending, startTransition] = useTransition();
const [error, setError] = useState(false);
const [checkoutToken, setCheckoutToken] = useState<string | undefined>(
undefined,
);
// only allow trial if the user is not already a customer
const canStartTrial = !props.customerId;
// If the checkout token is set, render the embedded checkout component
if (checkoutToken) {
return (
<EmbeddedCheckout
checkoutToken={checkoutToken}
provider={billingConfig.provider}
onClose={() => setCheckoutToken(undefined)}
/>
);
}
// Otherwise, render the plan picker component
return (
<div>
<Card>
<CardHeader>
<CardTitle>
<Trans i18nKey={'common:planCardLabel'} />
</CardTitle>
<CardDescription>
<Trans i18nKey={'common:planCardDescription'} />
</CardDescription>
</CardHeader>
<CardContent className={'space-y-4'}>
<If condition={error}>
<ErrorAlert />
</If>
<PlanPicker
pending={pending}
config={billingConfig}
canStartTrial={canStartTrial}
onSubmit={({ planId, productId }) => {
startTransition(async () => {
try {
const { checkoutToken } =
await createPersonalAccountCheckoutSession({
planId,
productId,
});
setCheckoutToken(checkoutToken);
} catch (e) {
setError(true);
}
});
}}
/>
</CardContent>
</Card>
</div>
);
}
function ErrorAlert() {
return (
<Alert variant={'destructive'}>
<ExclamationTriangleIcon className={'h-4'} />
<AlertTitle>
<Trans i18nKey={'common:planPickerAlertErrorTitle'} />
</AlertTitle>
<AlertDescription>
<Trans i18nKey={'common:planPickerAlertErrorDescription'} />
</AlertDescription>
</Alert>
);
}

View File

@@ -0,0 +1,6 @@
import { z } from 'zod';
export const PersonalAccountCheckoutSchema = z.object({
planId: z.string().min(1),
productId: z.string().min(1),
});

View File

@@ -0,0 +1,45 @@
import 'server-only';
import { cache } from 'react';
import { SupabaseClient } from '@supabase/supabase-js';
import { z } from 'zod';
import { createAccountsApi } from '@kit/accounts/api';
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
/**
* The variable BILLING_MODE represents the billing mode for a service. It can
* have either the value 'subscription' or 'one-time'. If not provided, the default
* value is 'subscription'. The value can be overridden by the environment variable
* BILLING_MODE.
*
* If the value is 'subscription', we fetch the subscription data for the user.
* If the value is 'one-time', we fetch the orders data for the user.
* if none of these suits your needs, please override the below function.
*/
const BILLING_MODE = z
.enum(['subscription', 'one-time'])
.default('subscription')
.parse(process.env.BILLING_MODE);
/**
* Load the personal account billing page data for the given user.
* @param userId
* @returns The subscription data or the orders data and the billing customer ID.
* This function is cached per-request.
*/
export const loadPersonalAccountBillingPageData = cache((userId: string) => {
const client = getSupabaseServerComponentClient();
const api = createAccountsApi(client);
const data =
BILLING_MODE === 'subscription'
? api.getSubscription(userId)
: api.getOrder(userId);
const customerId = api.getCustomerId(userId);
return Promise.all([data, customerId]);
});

View File

@@ -0,0 +1,42 @@
'use server';
import { redirect } from 'next/navigation';
import { enhanceAction } from '@kit/next/actions';
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema';
import { createUserBillingService } from './user-billing.service';
/**
* @name createPersonalAccountCheckoutSession
* @description Creates a checkout session for a personal account.
*/
export const createPersonalAccountCheckoutSession = enhanceAction(
async function (data) {
const client = getSupabaseServerActionClient();
const service = createUserBillingService(client);
return await service.createCheckoutSession(data);
},
{
schema: PersonalAccountCheckoutSchema,
},
);
/**
* @name createPersonalAccountBillingPortalSession
* @description Creates a billing Portal session for a personal account
*/
export const createPersonalAccountBillingPortalSession = enhanceAction(
async () => {
const client = getSupabaseServerActionClient();
const service = createUserBillingService(client);
// get url to billing portal
const url = await service.createBillingPortalSession();
return redirect(url);
},
{},
);

View File

@@ -0,0 +1,202 @@
import 'server-only';
import { SupabaseClient } from '@supabase/supabase-js';
import { z } from 'zod';
import { createAccountsApi } from '@kit/accounts/api';
import { getProductPlanPair } from '@kit/billing';
import { getBillingGatewayProvider } from '@kit/billing-gateway';
import { getLogger } from '@kit/shared/logger';
import { requireUser } from '@kit/supabase/require-user';
import appConfig from '~/config/app.config';
import billingConfig from '~/config/billing.config';
import pathsConfig from '~/config/paths.config';
import { Database } from '~/lib/database.types';
import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema';
export function createUserBillingService(client: SupabaseClient<Database>) {
return new UserBillingService(client);
}
/**
* @name UserBillingService
* @description Service for managing billing for personal accounts.
*/
class UserBillingService {
private readonly namespace = 'billing.personal-account';
constructor(private readonly client: SupabaseClient<Database>) {}
/**
* @name createCheckoutSession
* @description Create a checkout session for the user
* @param planId
* @param productId
*/
async createCheckoutSession({
planId,
productId,
}: z.infer<typeof PersonalAccountCheckoutSchema>) {
// get the authenticated user
const { data: user, error } = await requireUser(this.client);
if (error ?? !user) {
throw new Error('Authentication required');
}
const service = await getBillingGatewayProvider(this.client);
// in the case of personal accounts
// the account ID is the same as the user ID
const accountId = user.id;
// the return URL for the checkout session
const returnUrl = getCheckoutSessionReturnUrl();
// find the customer ID for the account if it exists
// (eg. if the account has been billed before)
const api = createAccountsApi(this.client);
const customerId = await api.getCustomerId(accountId);
const product = billingConfig.products.find(
(item) => item.id === productId,
);
if (!product) {
throw new Error('Product not found');
}
const { plan } = getProductPlanPair(billingConfig, planId);
const logger = await getLogger();
logger.info(
{
name: `billing.personal-account`,
planId,
customerId,
accountId,
},
`User requested a personal account checkout session. Contacting provider...`,
);
try {
// call the payment gateway to create the checkout session
const { checkoutToken } = await service.createCheckoutSession({
returnUrl,
accountId,
customerEmail: user.email,
customerId,
plan,
variantQuantities: [],
enableDiscountField: product.enableDiscountField,
});
logger.info(
{
userId: user.id,
},
`Checkout session created. Returning checkout token to client...`,
);
// return the checkout token to the client
// so we can call the payment gateway to complete the checkout
return {
checkoutToken,
};
} catch (error) {
logger.error(
{
name: `billing.personal-account`,
planId,
customerId,
accountId,
error,
},
`Checkout session not created due to an error`,
);
throw new Error(`Failed to create a checkout session`);
}
}
/**
* @name createBillingPortalSession
* @description Create a billing portal session for the user
* @returns The URL to redirect the user to the billing portal
*/
async createBillingPortalSession() {
const { data, error } = await requireUser(this.client);
if (error ?? !data) {
throw new Error('Authentication required');
}
const service = await getBillingGatewayProvider(this.client);
const logger = await getLogger();
const accountId = data.id;
const api = createAccountsApi(this.client);
const customerId = await api.getCustomerId(accountId);
const returnUrl = getBillingPortalReturnUrl();
if (!customerId) {
throw new Error('Customer not found');
}
const ctx = {
name: this.namespace,
customerId,
accountId,
};
logger.info(
ctx,
`User requested a Billing Portal session. Contacting provider...`,
);
let url: string;
try {
const session = await service.createBillingPortalSession({
customerId,
returnUrl,
});
url = session.url;
} catch (error) {
logger.error(
{
error,
...ctx,
},
`Failed to create a Billing Portal session`,
);
throw new Error(
`Encountered an error creating the Billing Portal session`,
);
}
logger.info(ctx, `Session successfully created.`);
// redirect user to billing portal
return url;
}
}
function getCheckoutSessionReturnUrl() {
return new URL(
pathsConfig.app.personalAccountBillingReturn,
appConfig.url,
).toString();
}
function getBillingPortalReturnUrl() {
return new URL(
pathsConfig.app.personalAccountBilling,
appConfig.url,
).toString();
}

View File

@@ -0,0 +1,7 @@
'use client';
// We reuse the page from the billing module
// as there is no need to create a new one.
import BillingErrorPage from '~/home/[account]/billing/error';
export default BillingErrorPage;

View File

@@ -0,0 +1,15 @@
import { notFound } from 'next/navigation';
import featureFlagsConfig from '~/config/feature-flags.config';
function UserBillingLayout(props: React.PropsWithChildren) {
const isEnabled = featureFlagsConfig.enablePersonalAccountBilling;
if (!isEnabled) {
notFound();
}
return <>{props.children}</>;
}
export default UserBillingLayout;

View File

@@ -0,0 +1,103 @@
import { redirect } from 'next/navigation';
import {
BillingPortalCard,
CurrentLifetimeOrderCard,
CurrentSubscriptionCard,
} from '@kit/billing-gateway/components';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
import { If } from '@kit/ui/if';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import billingConfig from '~/config/billing.config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
// local imports
import { HomeLayoutPageHeader } from '../_components/home-page-header';
import { createPersonalAccountBillingPortalSession } from '../billing/_lib/server/server-actions';
import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form';
import { loadPersonalAccountBillingPageData } from './_lib/server/personal-account-billing-page.loader';
export const generateMetadata = async () => {
const i18n = await createI18nServerInstance();
const title = i18n.t('account:billingTab');
return {
title,
};
};
async function PersonalAccountBillingPage() {
const client = getSupabaseServerComponentClient();
const auth = await requireUser(client);
if (auth.error) {
redirect(auth.redirectTo);
}
const [data, customerId] = await loadPersonalAccountBillingPageData(
auth.data.id,
);
return (
<>
<HomeLayoutPageHeader
title={<Trans i18nKey={'common:billingTabLabel'} />}
description={<Trans i18nKey={'common:billingTabDescription'} />}
/>
<PageBody>
<div className={'flex flex-col space-y-4'}>
<If condition={!data}>
<PersonalAccountCheckoutForm customerId={customerId} />
<If condition={customerId}>
<CustomerBillingPortalForm />
</If>
</If>
<If condition={data}>
{(data) => (
<div
className={'mx-auto flex w-full max-w-2xl flex-col space-y-6'}
>
{'active' in data ? (
<CurrentSubscriptionCard
subscription={data}
config={billingConfig}
/>
) : (
<CurrentLifetimeOrderCard
order={data}
config={billingConfig}
/>
)}
<If condition={!data}>
<PersonalAccountCheckoutForm customerId={customerId} />
</If>
<If condition={customerId}>
<CustomerBillingPortalForm />
</If>
</div>
)}
</If>
</div>
</PageBody>
</>
);
}
export default withI18n(PersonalAccountBillingPage);
function CustomerBillingPortalForm() {
return (
<form action={createPersonalAccountBillingPortalSession}>
<BillingPortalCard />
</form>
);
}

View File

@@ -0,0 +1,5 @@
// We reuse the page from the billing module
// as there is no need to create a new one.
import ReturnCheckoutSessionPage from '~/home/[account]/billing/return/page';
export default ReturnCheckoutSessionPage;

View File

@@ -0,0 +1,48 @@
import { use } from 'react';
import { If } from '@kit/ui/if';
import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
import { AppLogo } from '~/components/app-logo';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
import { withI18n } from '~/lib/i18n/with-i18n';
// home imports
import { HomeMenuNavigation } from './_components/home-menu-navigation';
import { HomeMobileNavigation } from './_components/home-mobile-navigation';
import { HomeSidebar } from './_components/home-sidebar';
import { loadUserWorkspace } from './_lib/server/load-user-workspace';
const style = personalAccountNavigationConfig.style;
function UserHomeLayout({ children }: React.PropsWithChildren) {
const workspace = use(loadUserWorkspace());
return (
<Page style={style}>
<PageNavigation>
<If condition={style === 'header'}>
<HomeMenuNavigation workspace={workspace} />
</If>
<If condition={style === 'sidebar'}>
<HomeSidebar workspace={workspace} />
</If>
</PageNavigation>
<PageMobileNavigation className={'flex items-center justify-between'}>
<div>
<AppLogo />
</div>
<div>
<HomeMobileNavigation workspace={workspace} />
</div>
</PageMobileNavigation>
{children}
</Page>
);
}
export default withI18n(UserHomeLayout);

View File

@@ -0,0 +1,3 @@
import { GlobalLoader } from '@kit/ui/global-loader';
export default GlobalLoader;

View File

@@ -0,0 +1,32 @@
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
// local imports
import { HomeLayoutPageHeader } from './_components/home-page-header';
export const generateMetadata = async () => {
const i18n = await createI18nServerInstance();
const title = i18n.t('account:homePage');
return {
title,
};
};
function UserHomePage() {
return (
<>
<HomeLayoutPageHeader
title={<Trans i18nKey={'common:homeTabLabel'} />}
description={<Trans i18nKey={'common:homeTabDescription'} />}
/>
<PageBody></PageBody>
</>
);
}
export default withI18n(UserHomePage);

View File

@@ -0,0 +1,21 @@
import { Trans } from '@kit/ui/trans';
import { withI18n } from '~/lib/i18n/with-i18n';
// local imports
import { HomeLayoutPageHeader } from '../_components/home-page-header';
function UserSettingsLayout(props: React.PropsWithChildren) {
return (
<>
<HomeLayoutPageHeader
title={<Trans i18nKey={'account:accountTabLabel'} />}
description={<Trans i18nKey={'account:accountTabDescription'} />}
/>
{props.children}
</>
);
}
export default withI18n(UserSettingsLayout);

View File

@@ -0,0 +1,36 @@
import { PersonalAccountSettingsContainer } from '@kit/accounts/personal-account-settings';
import { PageBody } from '@kit/ui/page';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
const features = {
enableAccountDeletion: featureFlagsConfig.enableAccountDeletion,
};
const paths = {
callback: pathsConfig.auth.callback + `?next=${pathsConfig.app.accountHome}`,
};
export const generateMetadata = async () => {
const i18n = await createI18nServerInstance();
const title = i18n.t('account:settingsTab');
return {
title,
};
};
function PersonalAccountSettingsPage() {
return (
<PageBody>
<div className={'flex w-full flex-1 flex-col lg:max-w-2xl'}>
<PersonalAccountSettingsContainer features={features} paths={paths} />
</div>
</PageBody>
);
}
export default withI18n(PersonalAccountSettingsPage);