Files
menulio/apps/web/src/app/admin/login/page.tsx
T

206 lines
9.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useState, useEffect, Suspense } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import Link from "next/link";
import { loginAdmin, getAdminSession } from "@/lib/admin-auth";
function AdminLoginForm() {
const router = useRouter();
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl") || "/admin";
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [errorMsg, setErrorMsg] = useState("");
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
// If already logged in, redirect straight to admin dashboard
const session = getAdminSession();
if (session) {
router.replace(callbackUrl);
}
}, [router, callbackUrl]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!email.trim() || !password.trim()) {
setErrorMsg("Lütfen e-posta ve şifrenizi girin.");
return;
}
setIsLoading(true);
setErrorMsg("");
try {
const res = await loginAdmin(email, password);
if (res.success) {
router.replace(callbackUrl);
} else {
setErrorMsg(res.error || "Giriş başarısız.");
}
} catch {
setErrorMsg("Bir hata oluştu. Lütfen tekrar deneyin.");
} finally {
setIsLoading(false);
}
};
const handleQuickDemoLogin = () => {
setEmail("admin@menul.io");
setPassword("menulio2026");
};
return (
<div className="min-h-screen bg-[#09090b] text-white flex flex-col justify-center items-center p-4 relative overflow-hidden selection:bg-[#D4AF37]/30 selection:text-[#D4AF37]">
{/* Arka Plan Işık Efektleri */}
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-[#D4AF37]/10 rounded-full blur-3xl pointer-events-none" />
<div className="absolute bottom-10 right-10 w-72 h-72 bg-amber-500/5 rounded-full blur-3xl pointer-events-none" />
{/* Kart Konteynırı */}
<div className="w-full max-w-md bg-[#121215]/80 backdrop-blur-xl border border-white/10 rounded-3xl p-8 shadow-2xl shadow-black/80 relative z-10 space-y-6">
{/* Logo & Başlık */}
<div className="text-center space-y-2">
<Link href="/" className="inline-flex items-center gap-3 group mb-2">
<div className="w-12 h-12 rounded-2xl bg-gradient-to-tr from-[#D4AF37] to-[#F59E0B] flex items-center justify-center shadow-lg shadow-[#D4AF37]/30 group-hover:scale-105 transition-transform">
<span className="font-black text-[#0C0C0E] text-2xl tracking-wider">M</span>
</div>
</Link>
<h1 className="text-2xl font-black text-white tracking-tight">
Menulio Admin Girişi
</h1>
<p className="text-xs text-zinc-400">
QR Şablon Stüdyosu ve Sistem Yönetim Paneli
</p>
</div>
{/* Hata Bildirimi */}
{errorMsg && (
<div className="p-3.5 rounded-xl bg-red-500/10 border border-red-500/30 text-red-400 text-xs font-semibold flex items-center gap-2 animate-shake">
<svg className="w-4 h-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{errorMsg}
</div>
)}
{/* Giriş Formu */}
<form onSubmit={handleSubmit} className="space-y-4">
{/* E-posta */}
<div className="space-y-1.5">
<label className="text-xs font-bold text-zinc-300 uppercase tracking-wider block">
Yönetici E-Postası
</label>
<div className="relative">
<input
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="admin@menul.io"
className="w-full bg-white/[0.04] border border-white/10 rounded-xl px-4 py-3 text-sm text-white placeholder-zinc-500 focus:outline-none focus:border-[#D4AF37] focus:ring-1 focus:ring-[#D4AF37] transition-all"
required
/>
<div className="absolute right-3.5 top-1/2 -translate-y-1/2 text-zinc-500 pointer-events-none">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
</div>
</div>
{/* Şifre */}
<div className="space-y-1.5">
<label className="text-xs font-bold text-zinc-300 uppercase tracking-wider block">
Şifre
</label>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
className="w-full bg-white/[0.04] border border-white/10 rounded-xl px-4 py-3 text-sm text-white placeholder-zinc-500 focus:outline-none focus:border-[#D4AF37] focus:ring-1 focus:ring-[#D4AF37] transition-all pr-10"
required
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3.5 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-white transition-colors"
title={showPassword ? "Şifreyi gizle" : "Şifreyi göster"}
>
{showPassword ? (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858-5.908a10.05 10.05 0 012.122-.388c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21M3 3l18 18" />
</svg>
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
)}
</button>
</div>
</div>
{/* Giriş Yap Butonu */}
<button
type="submit"
disabled={isLoading}
className="w-full mt-2 bg-gradient-to-r from-[#D4AF37] to-[#F59E0B] text-[#0C0C0E] font-extrabold text-sm py-3.5 px-4 rounded-xl shadow-lg shadow-[#D4AF37]/20 hover:opacity-95 active:scale-[0.99] transition-all disabled:opacity-50 flex items-center justify-center gap-2 cursor-pointer"
>
{isLoading ? (
<>
<svg className="w-4 h-4 animate-spin text-[#0C0C0E]" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
Giriş Yapılıyor...
</>
) : (
<>
Giriş Yap
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
</>
)}
</button>
</form>
{/* Hızlı Demo Girişi */}
<div className="pt-4 border-t border-white/10 space-y-2 text-center">
<div className="text-[11px] text-zinc-500">
Varsayılan Admin Girişi: <span className="font-mono text-zinc-300">admin@menul.io / menulio2026</span>
</div>
<button
type="button"
onClick={handleQuickDemoLogin}
className="text-xs text-[#D4AF37] hover:underline font-medium transition-colors"
>
Formu Varsayılan Admin Bilgileri ile Doldur
</button>
</div>
</div>
{/* Alt Footer */}
<div className="mt-8 text-center text-xs text-zinc-600 relative z-10">
Menulio QR & Masa Kartı Yönetim Ekosistemi &copy; {new Date().getFullYear()}
</div>
</div>
);
}
export default function AdminLoginPage() {
return (
<Suspense fallback={
<div className="min-h-screen bg-[#09090b] flex items-center justify-center">
<div className="w-8 h-8 rounded-full border-2 border-[#D4AF37] border-t-transparent animate-spin" />
</div>
}>
<AdminLoginForm />
</Suspense>
);
}