mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: error layout global app
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
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";
|
||||||
import {AppSidebar} from "@/components/wrappers/dashboard/common/sidebar/app-sidebar";
|
import { AppSidebar } from "@/components/wrappers/dashboard/common/sidebar/app-sidebar";
|
||||||
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();
|
||||||
if (!user) redirect("/login");
|
if (!user) redirect("/login");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<div className="flex flex-col lg:flex-row w-full">
|
<div className="flex flex-col lg:flex-row w-full">
|
||||||
<ThemeMetaUpdater/>
|
<ThemeMetaUpdater />
|
||||||
<AppSidebar/>
|
<AppSidebar />
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<Header/>
|
<Header />
|
||||||
<main className="h-full">{children}</main>
|
<Suspense>
|
||||||
</SidebarInset>
|
<ErrorLayout>
|
||||||
</div>
|
<main className="h-full">{children}</main>
|
||||||
</SidebarProvider>
|
</ErrorLayout>
|
||||||
);
|
</Suspense>
|
||||||
|
</SidebarInset>
|
||||||
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,156 +1,169 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {useRouter} from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {useMutation} from "@tanstack/react-query";
|
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,
|
||||||
import {signUp} from "@/lib/auth/auth-client";
|
TooltipTrigger,
|
||||||
import {useZodForm} from "@/components/ui/form";
|
Tooltip,
|
||||||
import {CardAuth} from "@/features/layout/card-auth";
|
TooltipContent,
|
||||||
import {FormField} from "@/components/ui/form";
|
} from "@/components/ui/tooltip";
|
||||||
import {FormItem} from "@/components/ui/form";
|
import {
|
||||||
import {FormLabel} from "@/components/ui/form";
|
RegisterSchema,
|
||||||
import {FormControl} from "@/components/ui/form";
|
RegisterType,
|
||||||
import {FormMessage} from "@/components/ui/form";
|
} from "@/components/wrappers/auth/register/register-form/register-form.schema";
|
||||||
import {Input} from "@/components/ui/input";
|
import { signUp } from "@/lib/auth/auth-client";
|
||||||
import {PasswordInput} from "@/components/ui/password-input";
|
import { useZodForm } from "@/components/ui/form";
|
||||||
import {Button} from "@/components/ui/button";
|
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 Link from "next/link";
|
||||||
import {Info} from "lucide-react";
|
import { Info } from "lucide-react";
|
||||||
|
|
||||||
export type registerFormProps = {
|
export type registerFormProps = {
|
||||||
defaultValues?: RegisterType;
|
defaultValues?: RegisterType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RegisterForm = (props: registerFormProps) => {
|
export const RegisterForm = (props: registerFormProps) => {
|
||||||
|
const form = useZodForm({
|
||||||
|
schema: RegisterSchema,
|
||||||
|
});
|
||||||
|
|
||||||
const form = useZodForm({
|
const router = useRouter();
|
||||||
schema: RegisterSchema,
|
const mutation = useMutation({
|
||||||
});
|
mutationFn: async (values: RegisterType) => {
|
||||||
|
await signUp.email(values, {
|
||||||
const router = useRouter();
|
onSuccess: () => {
|
||||||
const mutation = useMutation({
|
toast.success(`Account successfully created`);
|
||||||
mutationFn: async (values: RegisterType) => {
|
router.refresh();
|
||||||
// @ts-ignore
|
router.push(`/login`);
|
||||||
await signUp.email(values, {
|
|
||||||
onSuccess: () => {
|
|
||||||
toast.success(`Account successfully created`);
|
|
||||||
router.refresh();
|
|
||||||
router.push(`/login`);
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast.error(error.error.message);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
onError: (error) => {
|
||||||
|
toast.error(error.error.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
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">
|
||||||
<p className="text-balance text-muted-foreground">Enter your informations below to register</p>
|
Enter your informations below to register
|
||||||
</div>
|
</p>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent>
|
</CardHeader>
|
||||||
<Form
|
<CardContent>
|
||||||
form={form}
|
<Form
|
||||||
className="flex flex-col gap-4"
|
form={form}
|
||||||
onSubmit={async (values) => {
|
className="flex flex-col gap-4"
|
||||||
await mutation.mutateAsync(values);
|
onSubmit={async (values) => {
|
||||||
}}
|
await mutation.mutateAsync(values);
|
||||||
>
|
}}
|
||||||
<FormField
|
>
|
||||||
control={form.control}
|
<FormField
|
||||||
name="name"
|
control={form.control}
|
||||||
defaultValue=""
|
name="name"
|
||||||
render={({field}) => (
|
defaultValue=""
|
||||||
<FormItem>
|
render={({ field }) => (
|
||||||
<FormLabel>Name</FormLabel>
|
<FormItem>
|
||||||
<FormControl>
|
<FormLabel>Name</FormLabel>
|
||||||
<Input placeholder="Your name" {...field} />
|
<FormControl>
|
||||||
</FormControl>
|
<Input placeholder="Your name" {...field} />
|
||||||
<FormMessage/>
|
</FormControl>
|
||||||
</FormItem>
|
<FormMessage />
|
||||||
)}
|
</FormItem>
|
||||||
/>
|
)}
|
||||||
<FormField
|
/>
|
||||||
control={form.control}
|
<FormField
|
||||||
name="email"
|
control={form.control}
|
||||||
defaultValue=""
|
name="email"
|
||||||
render={({field}) => (
|
defaultValue=""
|
||||||
<FormItem>
|
render={({ field }) => (
|
||||||
<FormLabel>Email</FormLabel>
|
<FormItem>
|
||||||
<FormControl>
|
<FormLabel>Email</FormLabel>
|
||||||
<Input placeholder="example@portabase.io" {...field} />
|
<FormControl>
|
||||||
</FormControl>
|
<Input placeholder="example@portabase.io" {...field} />
|
||||||
<FormMessage/>
|
</FormControl>
|
||||||
</FormItem>
|
<FormMessage />
|
||||||
)}
|
</FormItem>
|
||||||
/>
|
)}
|
||||||
<FormField
|
/>
|
||||||
control={form.control}
|
<FormField
|
||||||
name="password"
|
control={form.control}
|
||||||
defaultValue=""
|
name="password"
|
||||||
render={({field}) => (
|
defaultValue=""
|
||||||
<FormItem>
|
render={({ field }) => (
|
||||||
<FormLabel className="flex">
|
<FormItem>
|
||||||
Password
|
<FormLabel className="flex">
|
||||||
<TooltipProvider>
|
Password
|
||||||
<Tooltip>
|
<TooltipProvider>
|
||||||
<TooltipTrigger asChild>
|
<Tooltip>
|
||||||
<Info className="ml-3" size="15"/>
|
<TooltipTrigger asChild>
|
||||||
</TooltipTrigger>
|
<Info size="15" />
|
||||||
<TooltipContent>
|
</TooltipTrigger>
|
||||||
<p>
|
<TooltipContent>
|
||||||
Min. 8 characters, 1 uppercase (A-Z), 1 lowercase (a-z), 1
|
<p>
|
||||||
number (0-9), 1 special character (!, @,
|
Min. 8 characters, 1 uppercase (A-Z), 1 lowercase
|
||||||
etc.)
|
(a-z), 1 number (0-9), 1 special character (!, @,
|
||||||
</p>
|
etc.)
|
||||||
</TooltipContent>
|
</p>
|
||||||
</Tooltip>
|
</TooltipContent>
|
||||||
</TooltipProvider>
|
</Tooltip>
|
||||||
</FormLabel>
|
</TooltipProvider>
|
||||||
<FormControl>
|
</FormLabel>
|
||||||
<PasswordInput placeholder="Your password" {...field} />
|
<FormControl>
|
||||||
</FormControl>
|
<PasswordInput placeholder="Your password" {...field} />
|
||||||
<FormMessage/>
|
</FormControl>
|
||||||
</FormItem>
|
<FormMessage />
|
||||||
)}
|
</FormItem>
|
||||||
/>
|
)}
|
||||||
<FormField
|
/>
|
||||||
control={form.control}
|
<FormField
|
||||||
name="confirmPassword"
|
control={form.control}
|
||||||
defaultValue=""
|
name="confirmPassword"
|
||||||
render={({field}) => (
|
defaultValue=""
|
||||||
<FormItem>
|
render={({ field }) => (
|
||||||
<FormLabel>Password Confirmation</FormLabel>
|
<FormItem>
|
||||||
<FormControl>
|
<FormLabel>Password Confirmation</FormLabel>
|
||||||
<PasswordInput placeholder="Conform your password" {...field} />
|
<FormControl>
|
||||||
</FormControl>
|
<PasswordInput
|
||||||
<FormMessage/>
|
placeholder="Conform your password"
|
||||||
</FormItem>
|
{...field}
|
||||||
)}
|
/>
|
||||||
/>
|
</FormControl>
|
||||||
<Button type="submit" className="h-11" disabled={mutation.isPending}>
|
<FormMessage />
|
||||||
Sign up
|
</FormItem>
|
||||||
</Button>
|
)}
|
||||||
<div className="mt-4 text-center text-sm">
|
/>
|
||||||
Already have an account ?{" "}
|
<Button
|
||||||
<Link href="/login" className="underline">
|
type="submit"
|
||||||
Sign in
|
className="h-11"
|
||||||
</Link>
|
disabled={mutation.isPending}
|
||||||
</div>
|
>
|
||||||
|
Sign up
|
||||||
</Form>
|
</Button>
|
||||||
</CardContent>
|
<div className="mt-4 text-center text-sm">
|
||||||
</CardAuth>
|
Already have an account ?{" "}
|
||||||
</TooltipProvider>
|
<Link href="/login" className="underline">
|
||||||
);
|
Sign in
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</CardContent>
|
||||||
|
</CardAuth>
|
||||||
|
</TooltipProvider>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-2
@@ -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]);
|
||||||
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user