Refactor authentication flow and improve code organization

The update implemented a redirect functionality in the multi-factor authentication flow for a better user experience. It also involved a refactoring of some parts of the code, substituting direct routing paths with path configs for easier future modifications. Import statements were adjusted for better code organization and readability.
This commit is contained in:
giancarlo
2024-03-27 15:07:15 +08:00
parent f0883c19ef
commit 7579ee9a2c
33 changed files with 103 additions and 151 deletions

View File

@@ -3,6 +3,7 @@ import { BadgeCheck, CheckCircle2 } from 'lucide-react';
import { z } from 'zod';
import { BillingSchema, getProductPlanPairFromId } from '@kit/billing';
import { formatCurrency } from '@kit/shared/utils';
import { Database } from '@kit/supabase/database';
import {
Accordion,
@@ -65,8 +66,7 @@ export function CurrentPlanCard({
i18nKey="billing:planRenewal"
values={{
interval: subscription.interval,
currency: product.currency,
price: plan.price,
price: formatCurrency(product.currency, plan.price),
}}
/>
</div>

View File

@@ -11,7 +11,7 @@ export function EmbeddedCheckout(
props: React.PropsWithChildren<{
checkoutToken: string;
provider: BillingProvider;
onClose: () => void;
onClose?: () => void;
}>,
) {
const CheckoutComponent = useMemo(

View File

@@ -3,3 +3,4 @@ export * from './current-plan-card';
export * from './embedded-checkout';
export * from './billing-session-status';
export * from './billing-portal-card';
export * from './pricing-table';

View File

@@ -6,6 +6,7 @@ import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { BillingSchema } from '@kit/billing';
import { formatCurrency } from '@kit/shared/utils';
import { Button } from '@kit/ui/button';
import {
Form,
@@ -233,10 +234,3 @@ function Price(props: React.PropsWithChildren) {
</span>
);
}
function formatCurrency(currencyCode: string, value: string) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currencyCode,
}).format(value);
}

View File

@@ -7,14 +7,13 @@ import Link from 'next/link';
import { CheckCircle, Sparkles } from 'lucide-react';
import { z } from 'zod';
import { BillingSchema, getPlanIntervals } from '@kit/billing';
import { Button } from '@kit/ui/button';
import { Heading } from '@kit/ui/heading';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import { cn } from '@kit/ui/utils';
import { BillingSchema, getPlanIntervals } from '../create-billing-schema';
type Config = z.infer<typeof BillingSchema>;
interface Paths {

View File

@@ -17,10 +17,14 @@
"@kit/prettier-config": "0.1.0",
"@kit/tailwind-config": "0.1.0",
"@kit/tsconfig": "0.1.0",
"@kit/ui": "*"
"@kit/ui": "*",
"@kit/supabase": "*",
"@supabase/supabase-js": "2.40.0",
"lucide-react": "^0.363.0"
},
"exports": {
".": "./src/index.ts"
".": "./src/index.ts",
"./components/*": "./src/components/*"
},
"eslintConfig": {
"root": true,

View File

@@ -7,7 +7,7 @@ interface Data {
trialSubscriptions: number;
}
function AdminDashboard({
export function AdminDashboard({
data,
}: React.PropsWithChildren<{
data: Data;
@@ -70,8 +70,6 @@ function AdminDashboard({
);
}
export default AdminDashboard;
function Figure(props: React.PropsWithChildren) {
return <div className={'text-3xl font-bold'}>{props.children}</div>;
}

View File

@@ -1,22 +1,23 @@
import { notFound } from 'next/navigation';
import isUserSuperAdmin from '../../../app/admin/utils/is-user-super-admin';
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
import { isSuperAdmin } from '../lib/is-super-admin';
type LayoutOrPageComponent<Params> = React.ComponentType<Params>;
function AdminGuard<Params extends object>(
export function AdminGuard<Params extends object>(
Component: LayoutOrPageComponent<Params>,
) {
return async function AdminGuardServerComponentWrapper(params: Params) {
const isAdmin = await isUserSuperAdmin();
const client = getSupabaseServerComponentClient();
const isUserSuperAdmin = await isSuperAdmin(client);
// if the user is not a super-admin, we redirect to a 404
if (!isAdmin) {
if (!isUserSuperAdmin) {
notFound();
}
return <Component {...params} />;
};
}
export default AdminGuard;

View File

@@ -5,11 +5,10 @@ import { ArrowLeft } from 'lucide-react';
import { Button } from '@kit/ui/button';
import { PageHeader } from '@kit/ui/page';
function AdminHeader({
export function AdminHeader({
children,
paths,
}: React.PropsWithChildren<{
appHome: string;
paths: {
appHome: string;
};
@@ -28,5 +27,3 @@ function AdminHeader({
</PageHeader>
);
}
export default AdminHeader;

View File

@@ -1,10 +1,8 @@
'use client';
import { Home, User, Users } from 'lucide-react';
import { Sidebar, SidebarContent, SidebarItem } from '@kit/ui/sidebar';
function AdminSidebar(props: { Logo: React.ReactNode }) {
export function AdminSidebar(props: { Logo: React.ReactNode }) {
return (
<Sidebar>
<SidebarContent className={'mb-6 mt-4 pt-2'}>{props.Logo}</SidebarContent>
@@ -28,5 +26,3 @@ function AdminSidebar(props: { Logo: React.ReactNode }) {
</Sidebar>
);
}
export default AdminSidebar;

View File

@@ -0,0 +1 @@
export * from './lib/is-super-admin';

View File

@@ -0,0 +1,19 @@
import { SupabaseClient } from '@supabase/supabase-js';
import { Database } from '@kit/supabase/database';
export async function isSuperAdmin(client: SupabaseClient<Database>) {
const { data, error } = await client.auth.getUser();
if (error) {
throw error;
}
if (!data.user) {
return false;
}
const appMetadata = data.user.app_metadata;
return appMetadata?.role === 'super-admin';
}

View File

@@ -72,7 +72,7 @@ export class AccountInvitationsService {
role: params.role,
})
.match({
id: params.id,
id: params.invitationId,
});
if (error) {

View File

@@ -1,3 +1,10 @@
export function isBrowser() {
return typeof window !== 'undefined';
}
export function formatCurrency(currencyCode: string, value: string | number) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currencyCode,
}).format(Number(value));
}

View File

@@ -1,8 +1,7 @@
import { useMemo } from 'react';
import { getSupabaseBrowserClient } from '../clients/browser.client';
import { Database } from '../database.types';
export function useSupabase<Schema = Database>() {
return useMemo(() => getSupabaseBrowserClient<Schema>(), []);
export function useSupabase() {
return useMemo(() => getSupabaseBrowserClient(), []);
}

View File

@@ -36,7 +36,7 @@ import {
TableRow,
} from '../shadcn/table';
import { cn } from '../utils';
import Trans from './trans';
import { Trans } from './trans';
interface ReactTableProps<T extends object> {
data: T[];

View File

@@ -49,6 +49,11 @@ export const ImageUploadInput = forwardRef<React.ElementRef<'input'>, Props>(
if (files?.length) {
const file = files[0];
if (!file) {
return;
}
const data = URL.createObjectURL(file);
setState({

View File

@@ -14,7 +14,7 @@ export default function isRouteActive(
depth: number,
) {
// we remove any eventual query param from the route's URL
const currentRoutePath = currentRoute.split('?')[0];
const currentRoutePath = currentRoute.split('?')[0] ?? '';
if (!isRoot(currentRoutePath) && isRoot(targetLink)) {
return false;

View File

@@ -2,7 +2,7 @@ import { forwardRef } from 'react';
import Image from 'next/image';
import { cn } from '../../shadcn';
import { cn } from '../../utils';
import { LazyRender } from '../lazy-render';
const NextImage: React.FC<{

View File

@@ -2,6 +2,7 @@ import type { MDXComponents } from 'mdx/types';
import { getMDXComponent } from 'next-contentlayer/hooks';
import Components from './mdx-components';
// @ts-ignore
import styles from './mdx-renderer.module.css';
export function Mdx({