fix: error layout global app

This commit is contained in:
Théo LAGACHE
2026-02-25 13:00:52 +01:00
parent 31285dd0d6
commit c8d390726c
5 changed files with 198 additions and 172 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ import { currentUser } from "@/lib/auth/current-user";
import { AuthLogoSection } from "@/components/wrappers/auth/auth-logo-section"; import { AuthLogoSection } from "@/components/wrappers/auth/auth-logo-section";
import { env } from "@/env.mjs"; import { env } from "@/env.mjs";
import { Heart } from "lucide-react"; 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({ export default async function Layout({
children, children,
@@ -24,7 +24,7 @@ export default async function Layout({
<AuthLogoSection /> <AuthLogoSection />
<div className="mt-4"> <div className="mt-4">
<Suspense> <Suspense>
<AuthLayout>{children}</AuthLayout> <ErrorLayout>{children}</ErrorLayout>
</Suspense> </Suspense>
</div> </div>
</div> </div>
+6 -2
View File
@@ -1,4 +1,4 @@
import React, {ReactNode} from "react"; import { ReactNode, Suspense } from "react";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
@@ -6,7 +6,7 @@ import {AppSidebar} from "@/components/wrappers/dashboard/common/sidebar/app-sid
import { Header } from "@/features/layout/Header"; import { Header } from "@/features/layout/Header";
import { currentUser } from "@/lib/auth/current-user"; import { currentUser } from "@/lib/auth/current-user";
import { ThemeMetaUpdater } from "@/features/browser/theme-meta-updater"; import { ThemeMetaUpdater } from "@/features/browser/theme-meta-updater";
import {BackupModalProvider} from "@/components/wrappers/dashboard/database/backup/backup-modal-context"; import { ErrorLayout } from "@/components/wrappers/common/error-layout";
export default async function Layout({ children }: { children: ReactNode }) { export default async function Layout({ children }: { children: ReactNode }) {
const user = await currentUser(); const user = await currentUser();
@@ -19,7 +19,11 @@ export default async function Layout({children}: { children: ReactNode }) {
<AppSidebar /> <AppSidebar />
<SidebarInset> <SidebarInset>
<Header /> <Header />
<Suspense>
<ErrorLayout>
<main className="h-full">{children}</main> <main className="h-full">{children}</main>
</ErrorLayout>
</Suspense>
</SidebarInset> </SidebarInset>
</div> </div>
</SidebarProvider> </SidebarProvider>
@@ -5,8 +5,16 @@ import {useMutation} from "@tanstack/react-query";
import { toast } from "sonner"; import { toast } from "sonner";
import { CardContent, CardHeader } from "@/components/ui/card"; import { CardContent, CardHeader } from "@/components/ui/card";
import { Form } from "@/components/ui/form"; import { Form } from "@/components/ui/form";
import {TooltipProvider, TooltipTrigger, Tooltip, TooltipContent} from "@/components/ui/tooltip"; import {
import {RegisterSchema, RegisterType} from "@/components/wrappers/auth/register/register-form/register-form.schema"; 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 { signUp } from "@/lib/auth/auth-client";
import { useZodForm } from "@/components/ui/form"; import { useZodForm } from "@/components/ui/form";
import { CardAuth } from "@/features/layout/card-auth"; import { CardAuth } from "@/features/layout/card-auth";
@@ -26,7 +34,6 @@ export type registerFormProps = {
}; };
export const RegisterForm = (props: registerFormProps) => { export const RegisterForm = (props: registerFormProps) => {
const form = useZodForm({ const form = useZodForm({
schema: RegisterSchema, schema: RegisterSchema,
}); });
@@ -34,7 +41,6 @@ export const RegisterForm = (props: registerFormProps) => {
const router = useRouter(); const router = useRouter();
const mutation = useMutation({ const mutation = useMutation({
mutationFn: async (values: RegisterType) => { mutationFn: async (values: RegisterType) => {
// @ts-ignore
await signUp.email(values, { await signUp.email(values, {
onSuccess: () => { onSuccess: () => {
toast.success(`Account successfully created`); toast.success(`Account successfully created`);
@@ -48,14 +54,15 @@ export const RegisterForm = (props: registerFormProps) => {
}, },
}); });
return ( return (
<TooltipProvider> <TooltipProvider>
<CardAuth> <CardAuth>
<CardHeader> <CardHeader>
<div className="grid gap-2 text-center mb-2"> <div className="grid gap-2 text-center mb-2">
<h1 className="text-3xl font-bold">Create an account</h1> <h1 className="text-3xl font-bold">Create an account</h1>
<p className="text-balance text-muted-foreground">Enter your informations below to register</p> <p className="text-balance text-muted-foreground">
Enter your informations below to register
</p>
</div> </div>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@@ -105,12 +112,12 @@ export const RegisterForm = (props: registerFormProps) => {
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Info className="ml-3" size="15"/> <Info size="15" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p> <p>
Min. 8 characters, 1 uppercase (A-Z), 1 lowercase (a-z), 1 Min. 8 characters, 1 uppercase (A-Z), 1 lowercase
number (0-9), 1 special character (!, @, (a-z), 1 number (0-9), 1 special character (!, @,
etc.) etc.)
</p> </p>
</TooltipContent> </TooltipContent>
@@ -132,13 +139,20 @@ export const RegisterForm = (props: registerFormProps) => {
<FormItem> <FormItem>
<FormLabel>Password Confirmation</FormLabel> <FormLabel>Password Confirmation</FormLabel>
<FormControl> <FormControl>
<PasswordInput placeholder="Conform your password" {...field} /> <PasswordInput
placeholder="Conform your password"
{...field}
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<Button type="submit" className="h-11" disabled={mutation.isPending}> <Button
type="submit"
className="h-11"
disabled={mutation.isPending}
>
Sign up Sign up
</Button> </Button>
<div className="mt-4 text-center text-sm"> <div className="mt-4 text-center text-sm">
@@ -147,7 +161,6 @@ export const RegisterForm = (props: registerFormProps) => {
Sign in Sign in
</Link> </Link>
</div> </div>
</Form> </Form>
</CardContent> </CardContent>
</CardAuth> </CardAuth>
@@ -4,7 +4,7 @@ import React, { useEffect } from "react";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import { toast } from "sonner"; import { toast } from "sonner";
export const AuthLayout = ({ children }: { children: React.ReactNode }) => { export const ErrorLayout = ({ children }: { children: React.ReactNode }) => {
const url = useSearchParams(); const url = useSearchParams();
useEffect(() => { useEffect(() => {
@@ -15,6 +15,8 @@ export const AuthLayout = ({ children }: { children: React.ReactNode }) => {
const errorMessages: Record<string, string> = { const errorMessages: Record<string, string> = {
pending: "Your account is not active.", pending: "Your account is not active.",
invalid_or_expired_token: "Password reset invalid token.", 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]) => const match = Object.entries(errorMessages).find(([key]) =>
@@ -28,7 +30,8 @@ export const AuthLayout = ({ children }: { children: React.ReactNode }) => {
const params = new URLSearchParams(url.toString()); const params = new URLSearchParams(url.toString());
params.delete("error"); params.delete("error");
const newUrl = const newUrl =
window.location.pathname + (params.toString() ? `?${params.toString()}` : ""); window.location.pathname +
(params.toString() ? `?${params.toString()}` : "");
window.history.replaceState({}, document.title, newUrl); window.history.replaceState({}, document.title, newUrl);
}, [url]); }, [url]);
+7 -1
View File
@@ -44,6 +44,12 @@ import { getOAuthProviders } from "./oauth";
const oidcProviders = getOidcProviders(); const oidcProviders = getOidcProviders();
export const auth = betterAuth({ export const auth = betterAuth({
onAPIError: {
errorURL: "/dashboard/home",
onError: (error, ctx) => {
//todo: capture errors in a monitoring service
},
},
database: drizzleAdapter(db, { database: drizzleAdapter(db, {
provider: "pg", provider: "pg",
schema: drizzleDb.schemas, schema: drizzleDb.schemas,
@@ -134,7 +140,7 @@ export const auth = betterAuth({
trustedProviders: [ trustedProviders: [
"credential", "credential",
...getOAuthProviders().map((p) => p.id), ...getOAuthProviders().map((p) => p.id),
...oidcProviders.map((p) => p.id) ...oidcProviders.map((p) => p.id),
], ],
allowDifferentEmails: true, allowDifferentEmails: true,
}, },