391 lines
14 KiB
TypeScript
391 lines
14 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
|
import { Loader2, Eye, EyeOff } from "lucide-react";
|
|
import { ThemeProvider } from '@/components/theme-provider';
|
|
import { Toaster } from '@/components/ui/toaster';
|
|
|
|
export default function LandingPage() {
|
|
const router = useRouter();
|
|
const [isLoginOpen, setIsLoginOpen] = useState(true);
|
|
const [isRegisterOpen, setIsRegisterOpen] = useState(false);
|
|
const [activeTab, setActiveTab] = useState('dosen');
|
|
const [loading, setLoading] = useState(false);
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
// Admin form state
|
|
const [adminForm, setAdminForm] = useState({
|
|
username: '',
|
|
password: ''
|
|
});
|
|
|
|
// Dosen form state
|
|
const [dosenForm, setDosenForm] = useState({
|
|
nip: '',
|
|
password: ''
|
|
});
|
|
|
|
// Register form state
|
|
const [registerForm, setRegisterForm] = useState({
|
|
nip: '',
|
|
password: '',
|
|
confirmPassword: ''
|
|
});
|
|
|
|
const handleAdminLogin = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError('');
|
|
|
|
try {
|
|
const response = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
username: adminForm.username,
|
|
password: adminForm.password,
|
|
role: 'admin'
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
setIsLoginOpen(false);
|
|
router.push('/dashboard');
|
|
} else {
|
|
setError(data.error || 'Login gagal');
|
|
}
|
|
} catch (err) {
|
|
setError('Terjadi kesalahan saat login');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleDosenLogin = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError('');
|
|
|
|
try {
|
|
const response = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
nip: dosenForm.nip,
|
|
password: dosenForm.password,
|
|
role: 'dosen'
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
setIsLoginOpen(false);
|
|
router.push('/dashboard');
|
|
} else {
|
|
setError(data.error || 'Login gagal');
|
|
}
|
|
} catch (err) {
|
|
setError('Terjadi kesalahan saat login');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleRegister = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError('');
|
|
|
|
if (registerForm.password !== registerForm.confirmPassword) {
|
|
setError('Password dan konfirmasi password tidak cocok');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/auth/register', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
nip: registerForm.nip,
|
|
password: registerForm.password
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
setIsRegisterOpen(false);
|
|
setIsLoginOpen(true);
|
|
setActiveTab('dosen');
|
|
setError('');
|
|
} else {
|
|
setError(data.error || 'Registrasi gagal');
|
|
}
|
|
} catch (err) {
|
|
setError('Terjadi kesalahan saat registrasi');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const openRegister = () => {
|
|
setIsLoginOpen(false);
|
|
setIsRegisterOpen(true);
|
|
};
|
|
|
|
const openLogin = () => {
|
|
setIsRegisterOpen(false);
|
|
setIsLoginOpen(true);
|
|
};
|
|
|
|
return (
|
|
<ThemeProvider
|
|
attribute="class"
|
|
defaultTheme="system"
|
|
enableSystem
|
|
disableTransitionOnChange
|
|
>
|
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-gray-800">
|
|
{/* Login Dialog */}
|
|
<Dialog open={isLoginOpen} onOpenChange={() => {}}>
|
|
<DialogContent className="sm:max-w-md" hideClose>
|
|
<DialogHeader>
|
|
<DialogTitle>Login Portal Data Informatika</DialogTitle>
|
|
<DialogDescription>
|
|
Silakan login sesuai dengan role Anda
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
|
<TabsList className="grid w-full grid-cols-2">
|
|
<TabsTrigger value="dosen">Dosen</TabsTrigger>
|
|
<TabsTrigger value="admin">Admin</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="dosen" className="space-y-4">
|
|
<form onSubmit={handleDosenLogin} className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="nip">NIP</Label>
|
|
<Input
|
|
id="nip"
|
|
type="text"
|
|
placeholder="Masukkan NIP"
|
|
value={dosenForm.nip}
|
|
onChange={(e) => setDosenForm({ ...dosenForm, nip: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="dosen-password">Password</Label>
|
|
<div className="relative">
|
|
<Input
|
|
id="dosen-password"
|
|
type={showPassword ? "text" : "password"}
|
|
placeholder="Masukkan password"
|
|
value={dosenForm.password}
|
|
onChange={(e) => setDosenForm({ ...dosenForm, password: e.target.value })}
|
|
required
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
>
|
|
{showPassword ? (
|
|
<EyeOff className="h-4 w-4" />
|
|
) : (
|
|
<Eye className="h-4 w-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<Button type="submit" className="w-full" disabled={loading}>
|
|
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
Login sebagai Dosen
|
|
</Button>
|
|
</form>
|
|
|
|
<div className="text-center pt-4 border-t">
|
|
<p className="text-sm text-gray-600 dark:text-gray-400 inline">
|
|
Belum punya akun?{' '}
|
|
</p>
|
|
<Button
|
|
type="button"
|
|
variant="link"
|
|
className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 p-0 h-auto inline"
|
|
onClick={openRegister}
|
|
>
|
|
Daftar disini
|
|
</Button>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="admin" className="space-y-4">
|
|
<form onSubmit={handleAdminLogin} className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="username">Username</Label>
|
|
<Input
|
|
id="username"
|
|
type="text"
|
|
placeholder="Masukkan username"
|
|
value={adminForm.username}
|
|
onChange={(e) => setAdminForm({ ...adminForm, username: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="admin-password">Password</Label>
|
|
<div className="relative">
|
|
<Input
|
|
id="admin-password"
|
|
type={showPassword ? "text" : "password"}
|
|
placeholder="Masukkan password"
|
|
value={adminForm.password}
|
|
onChange={(e) => setAdminForm({ ...adminForm, password: e.target.value })}
|
|
required
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
>
|
|
{showPassword ? (
|
|
<EyeOff className="h-4 w-4" />
|
|
) : (
|
|
<Eye className="h-4 w-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<Button type="submit" className="w-full" disabled={loading}>
|
|
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
Login sebagai Admin
|
|
</Button>
|
|
</form>
|
|
</TabsContent>
|
|
</Tabs>
|
|
|
|
{error && (
|
|
<Alert className="mt-4">
|
|
<AlertDescription className="text-red-600">
|
|
{error}
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
{/* Register Dialog */}
|
|
<Dialog open={isRegisterOpen} onOpenChange={() => {}}>
|
|
<DialogContent className="sm:max-w-md" hideClose>
|
|
<DialogHeader>
|
|
<DialogTitle>Registrasi Dosen</DialogTitle>
|
|
<DialogDescription>
|
|
Daftar akun baru untuk dosen Portal Data Informatika
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<form onSubmit={handleRegister} className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="register-nip">NIP</Label>
|
|
<Input
|
|
id="register-nip"
|
|
type="text"
|
|
placeholder="Masukkan NIP"
|
|
value={registerForm.nip}
|
|
onChange={(e) => setRegisterForm({ ...registerForm, nip: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="register-password">Password</Label>
|
|
<div className="relative">
|
|
<Input
|
|
id="register-password"
|
|
type={showPassword ? "text" : "password"}
|
|
placeholder="Masukkan password (min. 6 karakter)"
|
|
value={registerForm.password}
|
|
onChange={(e) => setRegisterForm({ ...registerForm, password: e.target.value })}
|
|
required
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
>
|
|
{showPassword ? (
|
|
<EyeOff className="h-4 w-4" />
|
|
) : (
|
|
<Eye className="h-4 w-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="confirm-password">Konfirmasi Password</Label>
|
|
<Input
|
|
id="confirm-password"
|
|
type="password"
|
|
placeholder="Konfirmasi password"
|
|
value={registerForm.confirmPassword}
|
|
onChange={(e) => setRegisterForm({ ...registerForm, confirmPassword: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
<Button type="submit" className="w-full" disabled={loading}>
|
|
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
Daftar sebagai Dosen
|
|
</Button>
|
|
</form>
|
|
|
|
<div className="text-center pt-4 border-t">
|
|
<p className="text-sm text-gray-600 dark:text-gray-400 inline">
|
|
Sudah punya akun?{' '}
|
|
</p>
|
|
<Button
|
|
type="button"
|
|
variant="link"
|
|
className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 p-0 h-auto inline"
|
|
onClick={openLogin}
|
|
>
|
|
Login
|
|
</Button>
|
|
</div>
|
|
|
|
{error && (
|
|
<Alert className="mt-4">
|
|
<AlertDescription className="text-red-600">
|
|
{error}
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
<Toaster />
|
|
</ThemeProvider>
|
|
);
|
|
}
|