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:
@@ -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>
|
||||
|
||||
@@ -11,7 +11,7 @@ export function EmbeddedCheckout(
|
||||
props: React.PropsWithChildren<{
|
||||
checkoutToken: string;
|
||||
provider: BillingProvider;
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
}>,
|
||||
) {
|
||||
const CheckoutComponent = useMemo(
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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,
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
1
packages/features/admin/src/index.ts
Normal file
1
packages/features/admin/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './lib/is-super-admin';
|
||||
19
packages/features/admin/src/lib/is-super-admin.ts
Normal file
19
packages/features/admin/src/lib/is-super-admin.ts
Normal 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';
|
||||
}
|
||||
@@ -72,7 +72,7 @@ export class AccountInvitationsService {
|
||||
role: params.role,
|
||||
})
|
||||
.match({
|
||||
id: params.id,
|
||||
id: params.invitationId,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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(), []);
|
||||
}
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<{
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user