diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx index a5a19baa..35e362a7 100644 --- a/app/(auth)/layout.tsx +++ b/app/(auth)/layout.tsx @@ -4,7 +4,7 @@ import { currentUser } from "@/lib/auth/current-user"; import { AuthLogoSection } from "@/components/wrappers/auth/auth-logo-section"; import { env } from "@/env.mjs"; import { Heart } from "lucide-react"; -import { AuthLayout } from "@/components/wrappers/auth/auth-layout"; +import { ErrorLayout } from "@/components/wrappers/common/error-layout"; export default async function Layout({ children, @@ -24,7 +24,7 @@ export default async function Layout({
- {children} + {children}
diff --git a/app/(customer)/dashboard/layout.tsx b/app/(customer)/dashboard/layout.tsx index bf04ccf7..f364638d 100644 --- a/app/(customer)/dashboard/layout.tsx +++ b/app/(customer)/dashboard/layout.tsx @@ -1,27 +1,31 @@ -import React, {ReactNode} from "react"; -import {redirect} from "next/navigation"; +import { ReactNode, Suspense } from "react"; +import { redirect } from "next/navigation"; -import {SidebarInset, SidebarProvider} from "@/components/ui/sidebar"; -import {AppSidebar} from "@/components/wrappers/dashboard/common/sidebar/app-sidebar"; -import {Header} from "@/features/layout/Header"; -import {currentUser} from "@/lib/auth/current-user"; -import {ThemeMetaUpdater} from "@/features/browser/theme-meta-updater"; -import {BackupModalProvider} from "@/components/wrappers/dashboard/database/backup/backup-modal-context"; +import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; +import { AppSidebar } from "@/components/wrappers/dashboard/common/sidebar/app-sidebar"; +import { Header } from "@/features/layout/Header"; +import { currentUser } from "@/lib/auth/current-user"; +import { ThemeMetaUpdater } from "@/features/browser/theme-meta-updater"; +import { ErrorLayout } from "@/components/wrappers/common/error-layout"; -export default async function Layout({children}: { children: ReactNode }) { - const user = await currentUser(); - if (!user) redirect("/login"); +export default async function Layout({ children }: { children: ReactNode }) { + const user = await currentUser(); + if (!user) redirect("/login"); - return ( - -
- - - -
-
{children}
- -
-
- ); + return ( + +
+ + + +
+ + +
{children}
+
+
+ +
+
+ ); } diff --git a/src/components/wrappers/auth/register/register-form/register-form.tsx b/src/components/wrappers/auth/register/register-form/register-form.tsx index 81e6ea9c..c00dc971 100644 --- a/src/components/wrappers/auth/register/register-form/register-form.tsx +++ b/src/components/wrappers/auth/register/register-form/register-form.tsx @@ -1,156 +1,169 @@ "use client"; -import {useRouter} from "next/navigation"; -import {useMutation} from "@tanstack/react-query"; -import {toast} from "sonner"; -import {CardContent, CardHeader} from "@/components/ui/card"; -import {Form} from "@/components/ui/form"; -import {TooltipProvider, TooltipTrigger, Tooltip, TooltipContent} from "@/components/ui/tooltip"; -import {RegisterSchema, RegisterType} from "@/components/wrappers/auth/register/register-form/register-form.schema"; -import {signUp} from "@/lib/auth/auth-client"; -import {useZodForm} from "@/components/ui/form"; -import {CardAuth} from "@/features/layout/card-auth"; -import {FormField} from "@/components/ui/form"; -import {FormItem} from "@/components/ui/form"; -import {FormLabel} from "@/components/ui/form"; -import {FormControl} from "@/components/ui/form"; -import {FormMessage} from "@/components/ui/form"; -import {Input} from "@/components/ui/input"; -import {PasswordInput} from "@/components/ui/password-input"; -import {Button} from "@/components/ui/button"; +import { useRouter } from "next/navigation"; +import { useMutation } from "@tanstack/react-query"; +import { toast } from "sonner"; +import { CardContent, CardHeader } from "@/components/ui/card"; +import { Form } from "@/components/ui/form"; +import { + TooltipProvider, + TooltipTrigger, + Tooltip, + TooltipContent, +} from "@/components/ui/tooltip"; +import { + RegisterSchema, + RegisterType, +} from "@/components/wrappers/auth/register/register-form/register-form.schema"; +import { signUp } from "@/lib/auth/auth-client"; +import { useZodForm } from "@/components/ui/form"; +import { CardAuth } from "@/features/layout/card-auth"; +import { FormField } from "@/components/ui/form"; +import { FormItem } from "@/components/ui/form"; +import { FormLabel } from "@/components/ui/form"; +import { FormControl } from "@/components/ui/form"; +import { FormMessage } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { PasswordInput } from "@/components/ui/password-input"; +import { Button } from "@/components/ui/button"; import Link from "next/link"; -import {Info} from "lucide-react"; +import { Info } from "lucide-react"; export type registerFormProps = { - defaultValues?: RegisterType; + defaultValues?: RegisterType; }; export const RegisterForm = (props: registerFormProps) => { + const form = useZodForm({ + schema: RegisterSchema, + }); - const form = useZodForm({ - schema: RegisterSchema, - }); - - const router = useRouter(); - const mutation = useMutation({ - mutationFn: async (values: RegisterType) => { - // @ts-ignore - await signUp.email(values, { - onSuccess: () => { - toast.success(`Account successfully created`); - router.refresh(); - router.push(`/login`); - }, - onError: (error) => { - toast.error(error.error.message); - }, - }); + const router = useRouter(); + const mutation = useMutation({ + mutationFn: async (values: RegisterType) => { + await signUp.email(values, { + onSuccess: () => { + toast.success(`Account successfully created`); + router.refresh(); + router.push(`/login`); }, - }); + onError: (error) => { + toast.error(error.error.message); + }, + }); + }, + }); - - return ( - - - -
-

Create an account

-

Enter your informations below to register

-
-
- -
{ - await mutation.mutateAsync(values); - }} - > - ( - - Name - - - - - - )} - /> - ( - - Email - - - - - - )} - /> - ( - - - Password - - - - - - -

- Min. 8 characters, 1 uppercase (A-Z), 1 lowercase (a-z), 1 - number (0-9), 1 special character (!, @, - etc.) -

-
-
-
-
- - - - -
- )} - /> - ( - - Password Confirmation - - - - - - )} - /> - -
- Already have an account ?{" "} - - Sign in - -
- - -
-
-
- ); + return ( + + + +
+

Create an account

+

+ Enter your informations below to register +

+
+
+ +
{ + await mutation.mutateAsync(values); + }} + > + ( + + Name + + + + + + )} + /> + ( + + Email + + + + + + )} + /> + ( + + + Password + + + + + + +

+ Min. 8 characters, 1 uppercase (A-Z), 1 lowercase + (a-z), 1 number (0-9), 1 special character (!, @, + etc.) +

+
+
+
+
+ + + + +
+ )} + /> + ( + + Password Confirmation + + + + + + )} + /> + +
+ Already have an account ?{" "} + + Sign in + +
+ +
+
+
+ ); }; diff --git a/src/components/wrappers/auth/auth-layout.tsx b/src/components/wrappers/common/error-layout.tsx similarity index 67% rename from src/components/wrappers/auth/auth-layout.tsx rename to src/components/wrappers/common/error-layout.tsx index 384c3835..078cc24f 100644 --- a/src/components/wrappers/auth/auth-layout.tsx +++ b/src/components/wrappers/common/error-layout.tsx @@ -4,7 +4,7 @@ import React, { useEffect } from "react"; import { useSearchParams } from "next/navigation"; import { toast } from "sonner"; -export const AuthLayout = ({ children }: { children: React.ReactNode }) => { +export const ErrorLayout = ({ children }: { children: React.ReactNode }) => { const url = useSearchParams(); useEffect(() => { @@ -15,6 +15,8 @@ export const AuthLayout = ({ children }: { children: React.ReactNode }) => { const errorMessages: Record = { pending: "Your account is not active.", invalid_or_expired_token: "Password reset invalid token.", + account_already_linked_to_different_user: + "An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.", }; const match = Object.entries(errorMessages).find(([key]) => @@ -28,7 +30,8 @@ export const AuthLayout = ({ children }: { children: React.ReactNode }) => { const params = new URLSearchParams(url.toString()); params.delete("error"); const newUrl = - window.location.pathname + (params.toString() ? `?${params.toString()}` : ""); + window.location.pathname + + (params.toString() ? `?${params.toString()}` : ""); window.history.replaceState({}, document.title, newUrl); }, [url]); diff --git a/src/lib/auth/auth.ts b/src/lib/auth/auth.ts index 44d79a3d..f2379f3a 100644 --- a/src/lib/auth/auth.ts +++ b/src/lib/auth/auth.ts @@ -44,6 +44,12 @@ import { getOAuthProviders } from "./oauth"; const oidcProviders = getOidcProviders(); export const auth = betterAuth({ + onAPIError: { + errorURL: "/dashboard/home", + onError: (error, ctx) => { + //todo: capture errors in a monitoring service + }, + }, database: drizzleAdapter(db, { provider: "pg", schema: drizzleDb.schemas, @@ -134,7 +140,7 @@ export const auth = betterAuth({ trustedProviders: [ "credential", ...getOAuthProviders().map((p) => p.id), - ...oidcProviders.map((p) => p.id) + ...oidcProviders.map((p) => p.id), ], allowDifferentEmails: true, },