Refactor code structure for improved readability and maintainability

This commit is contained in:
Poyraz Avsever
2026-05-08 10:08:34 +03:00
parent 0235f82476
commit e98972597d
4 changed files with 270 additions and 126 deletions
+25 -51
View File
@@ -1,61 +1,31 @@
import { login } from './actions'
import { login } from '@/app/login/actions'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import Link from 'next/link'
import { ErrorToaster } from '@/components/error-toaster'
import Image from 'next/image'
import { Icon } from '@/components/ui/icon'
import { AuthPageShell } from '@/components/auth/auth-page-shell'
export default async function LoginPage({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const resolvedParams = await searchParams;
const error = resolvedParams?.error;
const message = resolvedParams?.message;
const resolvedParams = await searchParams
const error = resolvedParams?.error
const message = resolvedParams?.message
return (
<div className="flex min-h-screen bg-background">
<>
{error && message && <ErrorToaster message={String(message)} />}
{/* Sol Taraf - Resim Alanı */}
<div className="hidden lg:flex lg:w-1/2 p-4 relative">
<div className="relative w-full h-full overflow-hidden rounded-2xl">
<Image
src="/images/1830857_Image.png"
alt="MindSpace Background"
fill
className="object-cover"
priority
/>
<div className="absolute inset-0 bg-gradient-to-t from-background via-transparent to-background/30 opacity-90" />
{/* Logo */}
<div className="absolute bottom-0 flex items-center justify-center pointer-events-none z-10">
<div className="relative drop-shadow-2xl flex items-center gap-4">
<img
src="/logo/logo.png"
alt="MindSpace Logo"
className="object-contain w-64 h-auto"
/>
</div>
</div>
</div>
</div>
{/* Sağ Taraf - Form Alanı */}
<div className="flex flex-1 items-center justify-center p-8 w-full lg:w-1/2">
<div className="w-full max-w-sm space-y-8">
<div className="text-center lg:text-left">
<h1 className="text-3xl font-bold tracking-tight text-foreground">
Tekrar Hoş Geldiniz
</h1>
<p className="text-sm text-muted-foreground mt-2">
Kişisel alanınıza ulaşmak için giriş yapın
</p>
</div>
<AuthPageShell
title="Tekrar Hoş Geldiniz"
description="Kişisel alanınıza ulaşmak için giriş yapın"
imageSrc="/images/1830857_Image.png"
imageAlt="MindSpace Background"
imageSide="left"
form={
<form className="space-y-6">
<div className="space-y-4">
<div className="space-y-2">
@@ -96,7 +66,9 @@ export default async function LoginPage({
Giriş Yap
</Button>
</form>
}
secondaryAction={
<div className="space-y-6">
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t border-border" />
@@ -116,9 +88,11 @@ export default async function LoginPage({
<Icon icon="flat-color-icons:google" className="w-5 h-5 mr-2" />
Google ile Devam Et
</Button>
<div className="text-center text-sm mt-6">
Hesabınız yok mu?{" "}
</div>
}
footer={
<div className="text-center text-sm">
Hesabınız yok mu?{' '}
<Link
href="/register"
className="font-medium text-primary hover:text-primary-hover transition-colors"
@@ -126,8 +100,8 @@ export default async function LoginPage({
Kayıt Ol
</Link>
</div>
</div>
</div>
</div>
);
}
/>
</>
)
}
+18 -39
View File
@@ -4,8 +4,8 @@ import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import Link from 'next/link'
import { ErrorToaster } from '@/components/error-toaster'
import Image from 'next/image'
import { Icon } from '@/components/ui/icon'
import { AuthPageShell } from '@/components/auth/auth-page-shell'
export default async function RegisterPage({
searchParams,
@@ -17,20 +17,15 @@ export default async function RegisterPage({
const message = resolvedParams?.message
return (
<div className="flex min-h-screen bg-background">
<>
{error && message && <ErrorToaster message={String(message)} />}
<div className="flex flex-1 items-center justify-center p-8 w-full lg:w-1/2">
<div className="w-full max-w-sm space-y-8">
<div className="text-center lg:text-left">
<h1 className="text-3xl font-bold tracking-tight text-foreground">
Aramıza Katılın
</h1>
<p className="text-sm text-muted-foreground mt-2">
Kişisel yaşam alanınızı oluşturmak için kayıt olun
</p>
</div>
<AuthPageShell
title="Aramıza Katılın"
description="Kişisel yaşam alanınızı oluşturmak için kayıt olun"
imageSrc="/images/1830837_Image.png"
imageAlt="MindSpace Background"
imageSide="right"
form={
<form className="space-y-6">
<div className="space-y-4">
<div className="space-y-2">
@@ -63,7 +58,9 @@ export default async function RegisterPage({
Kayıt Ol
</Button>
</form>
}
secondaryAction={
<div className="space-y-6">
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t border-border" />
@@ -83,8 +80,10 @@ export default async function RegisterPage({
<Icon icon="flat-color-icons:google" className="w-5 h-5 mr-2" />
Google ile Devam Et
</Button>
<div className="text-center text-sm mt-6">
</div>
}
footer={
<div className="text-center text-sm">
Zaten hesabınız var mı?{' '}
<Link
href="/login"
@@ -93,28 +92,8 @@ export default async function RegisterPage({
Giriş Yap
</Link>
</div>
</div>
</div>
<div className="hidden lg:flex lg:w-1/2 p-4 relative">
<div className="relative w-full h-full overflow-hidden rounded-2xl">
<img
src="/images/1830837_Image.png"
alt="MindSpace Background"
className="w-full h-full object-cover"
}
/>
<div className="absolute inset-0 bg-gradient-to-t from-background via-transparent to-background/30 opacity-90" />
<div className="absolute bottom-0 flex items-center justify-center pointer-events-none z-10">
<div className="relative drop-shadow-2xl flex items-center gap-4">
<img
src="/logo/logo.png"
alt="MindSpace Logo"
className="object-contain w-64 h-auto"
/>
</div>
</div>
</div>
</div>
</div>
</>
)
}
+191
View File
@@ -0,0 +1,191 @@
"use client"
import type { ReactNode } from "react"
import Image from "next/image"
import { motion, useReducedMotion } from "framer-motion"
import { cn } from "@/lib/utils"
type AuthPageShellProps = {
title: string
description: string
imageSrc: string
imageAlt: string
imageSide: "left" | "right"
form: ReactNode
secondaryAction?: ReactNode
footer: ReactNode
}
export function AuthPageShell({
title,
description,
imageSrc,
imageAlt,
imageSide,
form,
secondaryAction,
footer,
}: AuthPageShellProps) {
const reducedMotion = useReducedMotion()
const stackVariants = {
hidden: {},
show: {
transition: reducedMotion
? undefined
: {
staggerChildren: 0.1,
delayChildren: 0.15,
},
},
}
const itemVariants = {
hidden: reducedMotion ? { opacity: 1 } : { opacity: 0, y: 24 },
show: {
opacity: 1,
y: 0,
transition: {
duration: reducedMotion ? 0 : 0.65,
ease: [0.22, 1, 0.36, 1] as const,
},
},
}
const formPanel = (
<motion.div
initial={reducedMotion ? false : { opacity: 0, x: imageSide === "left" ? 36 : -36 }}
animate={{ opacity: 1, x: 0 }}
transition={{
duration: reducedMotion ? 0 : 0.8,
ease: [0.22, 1, 0.36, 1],
}}
className="flex flex-1 items-center justify-center p-8 w-full lg:w-1/2"
>
<motion.div
variants={stackVariants}
initial="hidden"
animate="show"
className="w-full max-w-sm space-y-8"
>
<motion.div variants={itemVariants} className="text-center lg:text-left">
<h1 className="text-3xl font-bold tracking-tight text-foreground">
{title}
</h1>
<p className="text-sm text-muted-foreground mt-2">{description}</p>
</motion.div>
<motion.div variants={itemVariants}>{form}</motion.div>
{secondaryAction ? (
<motion.div variants={itemVariants}>{secondaryAction}</motion.div>
) : null}
<motion.div variants={itemVariants}>{footer}</motion.div>
</motion.div>
</motion.div>
)
const imagePanel = (
<motion.div
initial={reducedMotion ? false : { opacity: 0, x: imageSide === "left" ? -42 : 42 }}
animate={{ opacity: 1, x: 0 }}
transition={{
duration: reducedMotion ? 0 : 0.95,
ease: [0.22, 1, 0.36, 1],
}}
className="hidden lg:flex lg:w-1/2 p-4 relative"
>
<div className="relative w-full h-full overflow-hidden rounded-2xl">
<motion.div
initial={reducedMotion ? false : { scale: 1.08, filter: "blur(6px)" }}
animate={{ scale: 1, filter: "blur(0px)" }}
transition={{
duration: reducedMotion ? 0 : 1.2,
ease: [0.22, 1, 0.36, 1],
}}
className="absolute inset-0"
>
<Image src={imageSrc} alt={imageAlt} fill className="object-cover" priority />
</motion.div>
<motion.div
initial={reducedMotion ? false : { opacity: 0 }}
animate={{ opacity: 0.9 }}
transition={{ duration: reducedMotion ? 0 : 0.9, delay: reducedMotion ? 0 : 0.1 }}
className="absolute inset-0 bg-gradient-to-t from-background via-transparent to-background/30"
/>
<motion.div
initial={reducedMotion ? false : { opacity: 0, scale: 0.92 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
duration: reducedMotion ? 0 : 1.1,
delay: reducedMotion ? 0 : 0.2,
ease: [0.22, 1, 0.36, 1],
}}
className="absolute inset-0 bg-[radial-gradient(circle_at_50%_18%,rgba(255,255,255,0.18),transparent_46%)]"
/>
<motion.div
initial={reducedMotion ? false : { opacity: 0, y: 26 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: reducedMotion ? 0 : 0.85,
delay: reducedMotion ? 0 : 0.32,
ease: [0.22, 1, 0.36, 1],
}}
className="absolute bottom-0 left-0 right-0 flex items-center justify-center pointer-events-none z-10"
>
<motion.div
animate={
reducedMotion
? undefined
: {
y: [0, -8, 0],
scale: [1, 1.02, 1],
}
}
transition={
reducedMotion
? undefined
: {
duration: 8,
repeat: Infinity,
ease: "easeInOut",
}
}
className="relative drop-shadow-2xl flex items-center gap-4"
>
<Image
src="/logo/logo.png"
alt="MindSpace Logo"
width={256}
height={128}
className="object-contain w-64 h-auto"
priority
/>
</motion.div>
</motion.div>
</div>
</motion.div>
)
return (
<div className="relative isolate flex min-h-screen overflow-hidden bg-background">
{imageSide === "left" ? imagePanel : null}
{formPanel}
{imageSide === "right" ? imagePanel : null}
<motion.div
initial={reducedMotion ? false : { opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: reducedMotion ? 0 : 1.4 }}
className={cn(
"pointer-events-none absolute inset-0 -z-10",
"bg-[radial-gradient(circle_at_top,rgba(108,91,176,0.12),transparent_42%)]",
)}
/>
</div>
)
}
+1 -1
View File
File diff suppressed because one or more lines are too long