Initial commit

This commit is contained in:
twotalesanimation
2026-05-19 22:20:29 +02:00
commit 0fbe856dce
173 changed files with 38316 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
import { redirect } from 'next/navigation';
import { auth } from '@/auth';
import Image from 'next/image';
import { Montserrat } from 'next/font/google';
const montserrat = Montserrat({
subsets: ['latin'],
weight: ['200', '500', '600'],
});
export default async function AuthLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await auth();
if (session?.user) redirect('/dashboard');
return (
<div className="min-h-screen flex items-center justify-center bg-zinc-950">
<div className="w-full max-w-md px-4">
{/* Logo / Brand */}
<div className="text-center mb-8">
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-md bg-black border border-zinc-800 shadow-2xl">
<Image src="/logo.svg" alt="Logo" width={32} height={32} />
</div>
<div className={`${montserrat.className} mt-4`}>
<span className="block text-2xl font-light tracking-wide text-white leading-none">
TWO TALES
</span>
<span className="block text-[11px] tracking-[0.28em] italic uppercase text-zinc-500 leading-none mt-1">
vfx review
</span>
</div>
</div>
{children}
</div>
</div>
);
}
+124
View File
@@ -0,0 +1,124 @@
"use client";
import { useState } from "react";
import { signIn } from "next-auth/react";
import { useRouter, useSearchParams } from "next/navigation";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Eye, EyeOff, LogIn } from "lucide-react";
const loginSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(1, "Password is required"),
});
type LoginFormValues = z.infer<typeof loginSchema>;
export default function LoginPage() {
const router = useRouter();
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl") ?? "/dashboard";
const [showPassword, setShowPassword] = useState(false);
const [authError, setAuthError] = useState<string | null>(null);
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginFormValues>({
resolver: zodResolver(loginSchema),
});
const onSubmit = async (data: LoginFormValues) => {
setAuthError(null);
const result = await signIn("credentials", {
email: data.email,
password: data.password,
redirect: false,
});
if (result?.error) {
setAuthError("Invalid email or password");
return;
}
router.push(callbackUrl);
router.refresh();
};
return (
<Card>
<CardHeader className="pb-4">
<CardTitle className="text-xl align-middle text-center">Sign in</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="space-y-1.5">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@studio.com"
autoComplete="email"
autoFocus
{...register("email")}
/>
{errors.email && (
<p className="text-xs text-red-400">{errors.email.message}</p>
)}
</div>
<div className="space-y-1.5">
<Label htmlFor="password">Password</Label>
<div className="relative">
<Input
id="password"
type={showPassword ? "text" : "password"}
placeholder="••••••••"
autoComplete="current-password"
className="pr-10"
{...register("password")}
/>
<button
type="button"
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
onClick={() => setShowPassword((v) => !v)}
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
{errors.password && (
<p className="text-xs text-red-400">{errors.password.message}</p>
)}
</div>
{authError && (
<p className="text-sm text-red-400 text-center">{authError}</p>
)}
<Button type="submit" className="w-full gap-2" disabled={isSubmitting}>
<LogIn className="h-4 w-4" />
{isSubmitting ? "Signing in..." : "Sign in"}
</Button>
</form>
</CardContent>
</Card>
);
}