This commit is contained in:
dimitar 2025-04-02 20:44:03 +02:00
parent 71b73d5fcb
commit 0269cefeeb
5 changed files with 150 additions and 57 deletions

View File

@ -153,7 +153,9 @@ export class AuthController {
@UseGuards(JwtAuthGuard)
@Get("user-info")
async getUserInfo(@Request() req) {
return this.authService.getUserInfo(req.user.userId);
return await this.authService.getUserInfo(req.user.userId);
// const user = await this.authService.getUserInfo(req.user.userId);
// return user;
}
@Post("forgot-password")

View File

@ -1,22 +1,22 @@
import { useState } from 'react';
import { useAuth } from '../../hooks/useAuth';
import { useNavigate } from 'react-router-dom';
import { Button, Card, Input, FormGroup, Label, ErrorMessage } from '../UI';
import { Link } from 'react-router-dom';
import { useState } from "react";
import { useAuth } from "../../hooks/useAuth";
import { useNavigate } from "react-router-dom";
import { Button, Card, Input, FormGroup, Label, ErrorMessage } from "../UI";
import { Link } from "react-router-dom";
export default function Login() {
const { login } = useAuth();
const navigate = useNavigate();
const [formData, setFormData] = useState({
username: '',
password: '',
username: "",
password: "",
});
const [error, setError] = useState('');
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
setError("");
setIsLoading(true);
try {
@ -24,13 +24,13 @@ export default function Login() {
// Navigate based on user role
if (userData.isAdmin) {
navigate('/admin');
navigate("/admin");
} else {
navigate('/dashboard'); // Navigate normal users to dashboard
navigate("/dashboard");
}
} catch (err) {
setError('Invalid username or password');
console.error('Login error:', err);
setError("Invalid username or password");
console.error("Login error:", err);
} finally {
setIsLoading(false);
}
@ -42,11 +42,17 @@ export default function Login() {
<div className="absolute inset-0 bg-pattern opacity-5 z-0"></div>
{/* Main content with higher z-index */}
<Card className="relative z-10 max-w-md w-full bg-neutral-900/80 backdrop-blur-lg shadow-2xl
border border-neutral-800 rounded-xl">
<Card
className="relative z-10 max-w-md w-full bg-neutral-900/80 backdrop-blur-lg shadow-2xl
border border-neutral-800 rounded-xl"
>
<div className="text-center mb-8">
<h2 className="text-2xl font-semibold text-white">Sign in to your account</h2>
<p className="text-sm text-neutral-300 mt-2">Welcome back! Please enter your details.</p>
<h2 className="text-2xl font-semibold text-white">
Sign in to your account
</h2>
<p className="text-sm text-neutral-300 mt-2">
Welcome back! Please enter your details.
</p>
</div>
{error && (
@ -66,7 +72,9 @@ export default function Login() {
required
placeholder="Enter your username"
value={formData.username}
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
onChange={(e) =>
setFormData({ ...formData, username: e.target.value })
}
error={error}
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
focus:border-primary-400 focus:ring-primary-400"
@ -93,7 +101,9 @@ export default function Login() {
required
placeholder="Enter your password"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
onChange={(e) =>
setFormData({ ...formData, password: e.target.value })
}
error={error}
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
focus:border-primary-400 focus:ring-primary-400"
@ -110,7 +120,7 @@ export default function Login() {
focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50
disabled:cursor-not-allowed transition-colors"
>
{isLoading ? 'Signing in...' : 'Sign in'}
{isLoading ? "Signing in..." : "Sign in"}
</button>
</div>

View File

@ -1,37 +1,70 @@
import { useState } from "react";
import { Dialog } from "@headlessui/react";
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
import { NavLink, Link } from "react-router-dom";
import { Button } from '../UI';
import { NavLink, Link, useNavigate } from "react-router-dom";
import { Button } from "../UI";
import { useAuth } from "../../hooks/useAuth";
const navigation = [
const publicNavigation = [
{ name: "Дома", href: "/" },
{ name: "За нас", href: "/about" },
{ name: "Галерија", href: "/gallery" },
{ name: "Контакт", href: "/contact" },
// { name: "Админ", href: "/admin" },
{ name: "Логин", href: "/login" },
];
const basePrivateNavigation = [
{ name: "Дома", href: "/" },
{ name: "За нас", href: "/about" },
{ name: "Галерија", href: "/gallery" },
{ name: "Контакт", href: "/contact" },
];
export default function Navbar() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const { user, logout } = useAuth();
const navigate = useNavigate();
const getNavigation = () => {
if (!user) return publicNavigation;
// Add appropriate dashboard link based on user role
const privateNavigation = [...basePrivateNavigation];
if (user.isAdmin) {
privateNavigation.push({ name: "Админ Панел", href: "/admin" });
} else {
privateNavigation.push({ name: "Клиент Зона", href: "/dashboard" });
}
return privateNavigation;
};
const navigation = getNavigation();
const handleLogout = async () => {
try {
await logout();
navigate("/");
} catch (error) {
console.error("Logout failed:", error);
}
};
return (
<div className="w-full bg-gradient-to-b from-primary-900 to-primary-800 border-b border-primary-700/50">
<header className="relative">
<nav className="container-base py-4" aria-label="Global">
<div className="flex items-center justify-between">
{/* Left Logo */}
<Link to="/" className="p-2 hover:bg-white/20 transition-colors">
<img
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
src="/imklogorgb.png"
alt="IMK logo"
/>
</Link>
<div className="flex-shrink-0">
<Link to="/" className="p-2 hover:bg-white/20 transition-colors">
<img
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
src="/imklogorgb.png"
alt="IMK logo"
/>
</Link>
</div>
{/* Centered Desktop Navigation */}
<div className="hidden lg:flex flex-grow justify-center mx-8">
<div className="hidden lg:flex flex-grow justify-center">
<div className="flex items-center gap-x-1">
{navigation.map((item) => (
<NavLink
@ -39,9 +72,11 @@ export default function Navbar() {
to={item.href}
className={({ isActive }) =>
`px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
${isActive
? 'bg-primary-700 text-white shadow-lg'
: 'text-neutral-200 hover:bg-primary-700/50 hover:text-white'}`
${
isActive
? "bg-primary-700 text-white shadow-lg"
: "text-neutral-200 hover:bg-primary-700/50 hover:text-white"
}`
}
>
{item.name}
@ -50,18 +85,36 @@ export default function Navbar() {
</div>
</div>
{/* Right Side - TUF Logo & Mobile Menu */}
{/* Right Side Content */}
<div className="flex items-center gap-x-4">
{/* TUF Logo */}
<a href="/" className="hidden lg:flex p-2 hover:bg-white/20 transition-colors">
<img
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
src="/tuf.png"
alt="TUF logo"
/>
</a>
{/* User Menu & Logout */}
{user && (
<div className="hidden lg:flex items-center gap-x-4">
<span className="text-white text-sm">
{user.name || user.email}
</span>
<Button
variant="ghost"
onClick={handleLogout}
className="text-white hover:bg-primary-700/50 px-4 py-2 text-sm"
>
Одјави се
</Button>
</div>
)}
{/* Mobile menu button */}
{/* TUF Logo */}
<div className="hidden lg:block">
<a href="/" className="p-2 hover:bg-white/20 transition-colors">
<img
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
src="/tuf.png"
alt="TUF logo"
/>
</a>
</div>
{/* Mobile Menu Button */}
<Button
variant="ghost"
className="lg:hidden text-white hover:bg-primary-700/50"
@ -74,7 +127,7 @@ export default function Navbar() {
</div>
</nav>
{/* Mobile menu */}
{/* Mobile Menu */}
<Dialog
as="div"
className="lg:hidden"
@ -100,24 +153,53 @@ export default function Navbar() {
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</Button>
</div>
<div className="mt-6 flow-root">
<div className="space-y-1 py-6">
{/* User Info in Mobile Menu */}
{user && (
<div className="px-3 py-2 text-white border-b border-primary-700/50 mb-4">
<span className="block text-sm font-medium">
{user.name || user.email}
</span>
<span className="block text-sm text-primary-400">
{user.isAdmin ? "Администратор" : "Корисник"}
</span>
</div>
)}
{/* Navigation Items */}
{navigation.map((item) => (
<NavLink
key={item.name}
to={item.href}
className={({ isActive }) =>
`block px-3 py-2 text-base font-medium rounded-lg transition-colors
${isActive
? 'bg-primary-700 text-white'
: 'text-neutral-200 hover:bg-primary-700/50 hover:text-white'
}`
${
isActive
? "bg-primary-700 text-white"
: "text-neutral-200 hover:bg-primary-700/50 hover:text-white"
}`
}
onClick={() => setMobileMenuOpen(false)}
>
{item.name}
</NavLink>
))}
{/* Logout in Mobile Menu */}
{user && (
<Button
variant="ghost"
onClick={() => {
handleLogout();
setMobileMenuOpen(false);
}}
className="block w-full text-left px-3 py-2 text-base font-medium text-neutral-200 hover:bg-primary-700/50 hover:text-white rounded-lg mt-4"
>
Одјави се
</Button>
)}
</div>
</div>
</Dialog.Panel>

View File

@ -1,4 +1,3 @@
// frontend/src/hooks/useAuth.jsx
import { createContext, useContext, useState, useEffect } from "react";
import api from "../services/api";
@ -51,6 +50,7 @@ export const AuthProvider = ({ children }) => {
};
const logout = () => {
//no session menager in DB, can add it later
localStorage.removeItem("token");
setUser(null);
};

View File

@ -122,5 +122,4 @@ api.interceptors.response.use(
return Promise.reject(error);
},
);
export const logout = () => api.post("/auth/logout");
export default api;