Added Loading Button to hub auth

This commit is contained in:
PxlLoewe
2025-03-11 22:49:54 -07:00
parent 3c26d5b476
commit 30b0caf19f
2 changed files with 297 additions and 297 deletions

View File

@@ -1,111 +1,113 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { signIn } from "next-auth/react"; import { signIn, useSession } from "next-auth/react";
import Link from "next/link"; import Link from "next/link";
import { redirect, useSearchParams } from "next/navigation"; import { redirect, useSearchParams } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { Toaster, toast } from "react-hot-toast"; import { Toaster, toast } from "react-hot-toast";
import { z } from "zod"; import { z } from "zod";
import { Button } from "../../../_components/ui/Button";
export const Login = () => { export const Login = () => {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const schema = z.object({ const schema = z.object({
email: z.string().email(), email: z.string().email(),
password: z.string(), password: z.string(),
}); });
type schemaType = z.infer<typeof schema>; type schemaType = z.infer<typeof schema>;
const form = useForm<schemaType>({ const form = useForm<schemaType>({
resolver: zodResolver(schema), resolver: zodResolver(schema),
}); });
return ( return (
<form <form
className="card-body" className="card-body"
onSubmit={form.handleSubmit(async () => { onSubmit={form.handleSubmit(async () => {
setIsLoading(true); setIsLoading(true);
const data = await signIn("credentials", { const data = await signIn("credentials", {
redirect: false, redirect: false,
email: form.getValues("email"), email: form.getValues("email"),
password: form.getValues("password"), password: form.getValues("password"),
}); });
setIsLoading(false); setIsLoading(false);
if (!data || data.error) { if (!data || data.error) {
toast.error("E-Mail / Passwort ist falsch!", { toast.error("E-Mail / Passwort ist falsch!", {
style: { style: {
background: "var(--color-base-100)", background: "var(--color-base-100)",
color: "var(--color-base-content)", color: "var(--color-base-content)",
}, },
}); });
return; return;
} }
redirect(searchParams.get("redirect") || "/"); redirect(searchParams.get("redirect") || "/");
})} })}
> >
<div> <div>
<Toaster position="top-center" reverseOrder={false} /> <Toaster position="top-center" reverseOrder={false} />
</div> </div>
<h1 className="text-3xl font-bold">Login</h1> <h1 className="text-3xl font-bold">Login</h1>
<span className="text-sm font-medium"> <span className="text-sm font-medium">
Noch keinen Account? Zur{" "} Noch keinen Account? Zur{" "}
<Link href="/register" className="link link-accent link-hover"> <Link href="/register" className="link link-accent link-hover">
Registrierung Registrierung
</Link> </Link>
</span> </span>
<label className="input input-bordered flex items-center gap-2 w-full"> <label className="input input-bordered flex items-center gap-2 w-full">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
className="h-4 w-4 opacity-70" className="h-4 w-4 opacity-70"
> >
<path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" /> <path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
<path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" /> <path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
</svg> </svg>
<input <input
type="text" type="text"
className="grow" className="grow"
{...form.register("email")} {...form.register("email")}
placeholder="Email" placeholder="Email"
/> />
</label> </label>
<p className="text-error"> <p className="text-error">
{typeof form.formState.errors.email?.message === "string" {typeof form.formState.errors.email?.message === "string"
? form.formState.errors.email.message ? form.formState.errors.email.message
: ""} : ""}
</p> </p>
<label className="input input-bordered flex items-center gap-2 mt-2 w-full"> <label className="input input-bordered flex items-center gap-2 mt-2 w-full">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
className="h-4 w-4 opacity-70" className="h-4 w-4 opacity-70"
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z" d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
clipRule="evenodd" clipRule="evenodd"
/> />
</svg> </svg>
<input <input
autoComplete="current-password" autoComplete="current-password"
type="password" type="password"
{...form.register("password")} {...form.register("password")}
placeholder="Passwort" placeholder="Passwort"
className="grow" className="grow"
/> />
</label> </label>
<div className="card-actions mt-6"> <div className="card-actions mt-6">
<button className="btn btn-primary btn-block" disabled={isLoading}> <Button
{isLoading && ( disabled={isLoading}
<span className="loading loading-spinner loading-sm"></span> isLoading={isLoading}
)} className="btn btn-primary btn-block"
Login{isLoading && "..."} >
</button> Login
</div> </Button>
</form> </div>
); </form>
);
}; };

View File

@@ -1,207 +1,205 @@
'use client'; "use client";
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from 'react-hook-form'; import { useForm } from "react-hook-form";
import { z } from 'zod'; import { z } from "zod";
import { register } from '../action'; import { register } from "../action";
import { signIn } from 'next-auth/react'; import { signIn } from "next-auth/react";
import Link from 'next/link'; import Link from "next/link";
import { useState } from 'react'; import { useState } from "react";
import clsx, { ClassValue } from 'clsx'; import clsx, { ClassValue } from "clsx";
import { twMerge } from 'tailwind-merge'; import { twMerge } from "tailwind-merge";
import { Button } from "../../../_components/ui/Button";
export const Register = () => { export const Register = () => {
const schema = z const schema = z
.object({ .object({
email: z.string().email({ email: z.string().email({
message: 'Please enter a valid email', message: "Please enter a valid email",
}), }),
firstname: z.string().min(2).max(30), firstname: z.string().min(2).max(30),
lastname: z.string().min(2).max(30), lastname: z.string().min(2).max(30),
password: z.string(), password: z.string(),
passwordConfirm: z.string(), passwordConfirm: z.string(),
}) })
.superRefine(({ password, passwordConfirm }, ctx) => { .superRefine(({ password, passwordConfirm }, ctx) => {
if (password !== passwordConfirm) { if (password !== passwordConfirm) {
ctx.addIssue({ ctx.addIssue({
code: 'custom', code: "custom",
message: 'Die Passwörter stimmen nicht überein', message: "Die Passwörter stimmen nicht überein",
path: ['confirmPassword'], path: ["confirmPassword"],
}); });
} }
}); });
type IFormInput = z.infer<typeof schema>; type IFormInput = z.infer<typeof schema>;
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const form = useForm<IFormInput>({ const form = useForm<IFormInput>({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
email: '', email: "",
password: '', password: "",
passwordConfirm: '', passwordConfirm: "",
}, },
}); });
console.log(form.formState.errors); console.log(form.formState.errors);
return ( return (
<form <form
className="card-body" className="card-body"
onSubmit={form.handleSubmit(async () => { onSubmit={form.handleSubmit(async () => {
setIsLoading(true); setIsLoading(true);
const values = form.getValues(); const values = form.getValues();
const user = await register({ const user = await register({
email: form.getValues('email'), email: form.getValues("email"),
password: form.getValues('password'), password: form.getValues("password"),
firstname: form.getValues('firstname'), firstname: form.getValues("firstname"),
lastname: form.getValues('lastname'), lastname: form.getValues("lastname"),
}); });
await signIn('credentials', { await signIn("credentials", {
callbackUrl: '/', callbackUrl: "/",
email: user.email, email: user.email,
password: values.password, password: values.password,
}); });
setIsLoading(false); setIsLoading(false);
})} })}
> >
<h1 className="text-3xl font-bold">Registrierung</h1> <h1 className="text-3xl font-bold">Registrierung</h1>
<span className="text-sm font-medium"> <span className="text-sm font-medium">
Zurück zum{' '} Zurück zum{" "}
<Link href="/login" className="link link-accent link-hover"> <Link href="/login" className="link link-accent link-hover">
Login Login
</Link> </Link>
</span> </span>
<div className="mt-5 mb-2"> <div className="mt-5 mb-2">
<label className="input input-bordered flex items-center gap-2 mt-2 w-full"> <label className="input input-bordered flex items-center gap-2 mt-2 w-full">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
className="h-4 w-4 opacity-70" className="h-4 w-4 opacity-70"
> >
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" /> <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg> </svg>
<input <input
type="text" type="text"
className="grow" className="grow"
{...form.register('firstname')} {...form.register("firstname")}
placeholder="Vorname" placeholder="Vorname"
/> />
</label> </label>
<p className="text-error"> <p className="text-error">
{typeof form.formState.errors.firstname?.message === 'string' {typeof form.formState.errors.firstname?.message === "string"
? form.formState.errors.firstname.message ? form.formState.errors.firstname.message
: ''} : ""}
</p> </p>
<label className="input input-bordered flex items-center gap-2 mt-2 w-full"> <label className="input input-bordered flex items-center gap-2 mt-2 w-full">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
className="h-4 w-4 opacity-70" className="h-4 w-4 opacity-70"
> >
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" /> <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg> </svg>
<input <input
type="text" type="text"
className="grow" className="grow"
{...form.register('lastname')} {...form.register("lastname")}
placeholder="Nachname" placeholder="Nachname"
/> />
</label> </label>
<p className="text-error"> <p className="text-error">
{typeof form.formState.errors.lastname?.message === 'string' {typeof form.formState.errors.lastname?.message === "string"
? form.formState.errors.lastname.message ? form.formState.errors.lastname.message
: ''} : ""}
</p> </p>
<div className="divider">Account</div> <div className="divider">Account</div>
<label className="input input-bordered flex items-center gap-2 w-full"> <label className="input input-bordered flex items-center gap-2 w-full">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
className="h-4 w-4 opacity-70" className="h-4 w-4 opacity-70"
> >
<path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" /> <path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
<path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" /> <path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
</svg> </svg>
<input <input
type="text" type="text"
className="grow" className="grow"
{...form.register('email')} {...form.register("email")}
placeholder="Email" placeholder="Email"
/> />
</label> </label>
<p className="text-error"> <p className="text-error">
{typeof form.formState.errors.email?.message === 'string' {typeof form.formState.errors.email?.message === "string"
? form.formState.errors.email.message ? form.formState.errors.email.message
: ''} : ""}
</p> </p>
<label className="input input-bordered flex items-center gap-2 mt-2 w-full"> <label className="input input-bordered flex items-center gap-2 mt-2 w-full">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
className="h-4 w-4 opacity-70" className="h-4 w-4 opacity-70"
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z" d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
clipRule="evenodd" clipRule="evenodd"
/> />
</svg> </svg>
<input <input
type="password" type="password"
autoComplete="new-password" autoComplete="new-password"
{...form.register('password')} {...form.register("password")}
placeholder="Passwort" placeholder="Passwort"
className="grow" className="grow"
/> />
</label> </label>
<p className="text-error"> <p className="text-error">
{typeof form.formState.errors.password?.message === 'string' {typeof form.formState.errors.password?.message === "string"
? form.formState.errors.password.message ? form.formState.errors.password.message
: ''} : ""}
</p> </p>
<label className="input input-bordered flex items-center gap-2 mt-2 w-full"> <label className="input input-bordered flex items-center gap-2 mt-2 w-full">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
className="h-4 w-4 opacity-70" className="h-4 w-4 opacity-70"
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z" d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
clipRule="evenodd" clipRule="evenodd"
/> />
</svg> </svg>
<input <input
type="password" type="password"
autoComplete="new-password" autoComplete="new-password"
{...form.register('passwordConfirm')} {...form.register("passwordConfirm")}
placeholder="Passwort bestätigen" placeholder="Passwort bestätigen"
className="grow" className="grow"
/> />
</label> </label>
<p className="text-error"> <p className="text-error">
{typeof form.formState.errors.passwordConfirm?.message === 'string' {typeof form.formState.errors.passwordConfirm?.message === "string"
? form.formState.errors.passwordConfirm.message ? form.formState.errors.passwordConfirm.message
: ''} : ""}
</p> </p>
<div className="card-actions mt-6"> <div className="card-actions mt-6">
<button <Button
className="btn btn-primary btn-block" disabled={isLoading}
name="registerBtn" isLoading={isLoading}
disabled={isLoading} className="btn btn-primary btn-block"
> >
{isLoading && ( Registrieren
<span className="loading loading-spinner loading-sm"></span> </Button>
)} </div>
Registrieren{isLoading && '...'} </div>
</button> </form>
</div> );
</div>
</form>
);
}; };