375 lines
12 KiB
TypeScript
375 lines
12 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
|
|
import { ThemeToggle } from '@/components/theme-toggle';
|
|
import { Menu, User } from 'lucide-react';
|
|
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
|
import SidebarContent from '@/components/ui/SidebarContent';
|
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from '@/components/ui/dropdown-menu';
|
|
import { useToast } from '@/components/ui/use-toast';
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/navigation';
|
|
|
|
const Navbar = () => {
|
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
const [loginData, setLoginData] = useState({ nim: '', password: '' });
|
|
const [registerData, setRegisterData] = useState({ username: '', nim: '', password: '', confirmPassword: '' });
|
|
const [userData, setUserData] = useState<any>(null);
|
|
const { toast } = useToast();
|
|
const router = useRouter();
|
|
|
|
// Check login status on component mount and when route changes
|
|
useEffect(() => {
|
|
const checkAuth = async () => {
|
|
try {
|
|
const response = await fetch('/api/auth/check');
|
|
const data = await response.json();
|
|
|
|
if (response.ok && data.user) {
|
|
setUserData(data.user);
|
|
setIsLoggedIn(true);
|
|
} else {
|
|
setUserData(null);
|
|
setIsLoggedIn(false);
|
|
}
|
|
} catch (error) {
|
|
console.error('Auth check failed:', error);
|
|
setUserData(null);
|
|
setIsLoggedIn(false);
|
|
}
|
|
};
|
|
|
|
checkAuth();
|
|
}, [router]);
|
|
|
|
const handleLogin = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
// Validate input
|
|
if (!loginData.nim || !loginData.password) {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Login gagal",
|
|
description: "NIM dan password harus diisi",
|
|
});
|
|
return;
|
|
}
|
|
|
|
console.log('Login attempt with data:', {
|
|
nim: loginData.nim,
|
|
password: '***'
|
|
});
|
|
|
|
try {
|
|
const response = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
nim: loginData.nim.trim(),
|
|
password: loginData.password
|
|
}),
|
|
});
|
|
|
|
console.log('Login response status:', response.status);
|
|
const data = await response.json();
|
|
console.log('Login response data:', data);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.error || 'Login gagal');
|
|
}
|
|
|
|
toast({
|
|
title: "Login berhasil",
|
|
description: "Selamat datang kembali!",
|
|
});
|
|
|
|
setUserData(data.user);
|
|
setIsLoggedIn(true);
|
|
setDialogOpen(false);
|
|
router.refresh();
|
|
} catch (error) {
|
|
console.error('Login error:', error);
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Login gagal",
|
|
description: error instanceof Error ? error.message : 'Terjadi kesalahan saat login',
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleRegister = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
// Validate passwords match
|
|
if (registerData.password !== registerData.confirmPassword) {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Registrasi gagal",
|
|
description: "Password dan konfirmasi password tidak cocok",
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/auth/register', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
username: registerData.username,
|
|
nim: registerData.nim,
|
|
password: registerData.password,
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.error || 'Registrasi gagal');
|
|
}
|
|
|
|
toast({
|
|
title: "Registrasi berhasil",
|
|
description: "Silakan login dengan akun Anda",
|
|
});
|
|
|
|
// Reset form and switch to login tab
|
|
setRegisterData({ username: '', nim: '', password: '', confirmPassword: '' });
|
|
const tabsList = document.querySelector('[role="tablist"]');
|
|
if (tabsList) {
|
|
const loginTab = tabsList.querySelector('[value="login"]');
|
|
if (loginTab) {
|
|
(loginTab as HTMLElement).click();
|
|
}
|
|
}
|
|
} catch (error) {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Registrasi gagal",
|
|
description: error instanceof Error ? error.message : 'Terjadi kesalahan saat registrasi',
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
try {
|
|
const response = await fetch('/api/auth/logout', {
|
|
method: 'POST',
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Logout gagal');
|
|
}
|
|
|
|
toast({
|
|
title: "Logout berhasil",
|
|
description: "Sampai jumpa lagi!",
|
|
});
|
|
|
|
setUserData(null);
|
|
setIsLoggedIn(false);
|
|
router.push('/');
|
|
router.refresh();
|
|
} catch (error) {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Logout gagal",
|
|
description: error instanceof Error ? error.message : 'Terjadi kesalahan saat logout',
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleProfileClick = async () => {
|
|
try {
|
|
const response = await fetch('/api/auth/check');
|
|
const data = await response.json();
|
|
|
|
if (response.ok && data.user) {
|
|
router.push('/mahasiswa/profile');
|
|
} else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Akses Ditolak",
|
|
description: "Silakan login terlebih dahulu untuk mengakses profil",
|
|
});
|
|
setDialogOpen(true);
|
|
router.push('/'); // Redirect to home if not logged in
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking auth status:', error);
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Error",
|
|
description: "Terjadi kesalahan saat memeriksa status login",
|
|
});
|
|
router.push('/');
|
|
}
|
|
};
|
|
|
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const { name, value } = e.target;
|
|
if (name.startsWith('login')) {
|
|
const loginField = name.replace('login', '').toLowerCase();
|
|
setLoginData(prev => ({
|
|
...prev,
|
|
[loginField]: value
|
|
}));
|
|
} else {
|
|
setRegisterData(prev => ({
|
|
...prev,
|
|
[name]: value
|
|
}));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="bg-background border-b sticky top-0 z-50 py-2 px-5 flex justify-between items-center">
|
|
<div className="flex items-center gap-2">
|
|
{/* Mobile Menu Button */}
|
|
<div className="md:hidden">
|
|
<Sheet>
|
|
<SheetTrigger asChild>
|
|
<Button variant="outline" size="icon">
|
|
<Menu className="h-5 w-5" />
|
|
<span className="sr-only">Toggle menu</span>
|
|
</Button>
|
|
</SheetTrigger>
|
|
<SheetContent side="left" className="p-0 w-[250px] overflow-y-auto">
|
|
<DialogTitle className="sr-only">Menu Navigasi</DialogTitle>
|
|
<SidebarContent />
|
|
</SheetContent>
|
|
</Sheet>
|
|
</div>
|
|
<Link href="/" className="flex items-center text-lg font-semibold hover:text-primary transition-colors">
|
|
<img src="/podif-icon.png" alt="PODIF Logo" className="h-6 w-auto mr-2" />
|
|
PODIF
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
{isLoggedIn ? (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger className="focus:outline-none">
|
|
<Avatar>
|
|
<AvatarImage src="" alt={userData?.username || 'User'} />
|
|
<AvatarFallback className="bg-primary/10">
|
|
<User className="h-5 w-5" />
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" side="bottom" sideOffset={9} alignOffset={0}>
|
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem onClick={handleProfileClick}>
|
|
Profile
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={handleLogout}>Logout</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
) : (
|
|
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button variant="secondary">
|
|
Login
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="sm:max-w-md">
|
|
<DialogHeader>
|
|
<DialogTitle>Portal Data Informatika</DialogTitle>
|
|
<DialogDescription>Masuk atau daftar untuk mengakses portal</DialogDescription>
|
|
</DialogHeader>
|
|
<Tabs defaultValue="login" className="w-full mt-4">
|
|
<TabsList className="grid w-full grid-cols-2">
|
|
<TabsTrigger value="login">Login</TabsTrigger>
|
|
<TabsTrigger value="register">Register</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="login">
|
|
<form onSubmit={handleLogin} className="space-y-4">
|
|
<Input
|
|
type="text"
|
|
name="loginNim"
|
|
placeholder="NIM"
|
|
value={loginData.nim}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<Input
|
|
type="password"
|
|
name="loginPassword"
|
|
placeholder="Password"
|
|
value={loginData.password}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<Button className="w-full" type="submit">
|
|
Login
|
|
</Button>
|
|
</form>
|
|
</TabsContent>
|
|
<TabsContent value="register">
|
|
<form onSubmit={handleRegister} className="space-y-4">
|
|
<Input
|
|
type="text"
|
|
name="username"
|
|
placeholder="Nama Lengkap"
|
|
value={registerData.username}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<Input
|
|
type="text"
|
|
name="nim"
|
|
placeholder="NIM"
|
|
value={registerData.nim}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<Input
|
|
type="password"
|
|
name="password"
|
|
placeholder="Password"
|
|
value={registerData.password}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<Input
|
|
type="password"
|
|
name="confirmPassword"
|
|
placeholder="Konfirmasi Password"
|
|
value={registerData.confirmPassword}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<Button className="w-full" type="submit">
|
|
Register
|
|
</Button>
|
|
</form>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)}
|
|
<ThemeToggle />
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Navbar;
|