206 lines
9.1 KiB
TypeScript
206 lines
9.1 KiB
TypeScript
"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 © {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>
|
||
);
|
||
}
|