tidy
This commit is contained in:
parent
71b73d5fcb
commit
0269cefeeb
@ -153,7 +153,9 @@ export class AuthController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get("user-info")
|
@Get("user-info")
|
||||||
async getUserInfo(@Request() req) {
|
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")
|
@Post("forgot-password")
|
||||||
|
|||||||
@ -1,22 +1,22 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from "react";
|
||||||
import { useAuth } from '../../hooks/useAuth';
|
import { useAuth } from "../../hooks/useAuth";
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Button, Card, Input, FormGroup, Label, ErrorMessage } from '../UI';
|
import { Button, Card, Input, FormGroup, Label, ErrorMessage } from "../UI";
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
username: '',
|
username: "",
|
||||||
password: '',
|
password: "",
|
||||||
});
|
});
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setError('');
|
setError("");
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -24,13 +24,13 @@ export default function Login() {
|
|||||||
|
|
||||||
// Navigate based on user role
|
// Navigate based on user role
|
||||||
if (userData.isAdmin) {
|
if (userData.isAdmin) {
|
||||||
navigate('/admin');
|
navigate("/admin");
|
||||||
} else {
|
} else {
|
||||||
navigate('/dashboard'); // Navigate normal users to dashboard
|
navigate("/dashboard");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError('Invalid username or password');
|
setError("Invalid username or password");
|
||||||
console.error('Login error:', err);
|
console.error("Login error:", err);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@ -42,11 +42,17 @@ export default function Login() {
|
|||||||
<div className="absolute inset-0 bg-pattern opacity-5 z-0"></div>
|
<div className="absolute inset-0 bg-pattern opacity-5 z-0"></div>
|
||||||
|
|
||||||
{/* Main content with higher z-index */}
|
{/* 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
|
<Card
|
||||||
border border-neutral-800 rounded-xl">
|
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">
|
<div className="text-center mb-8">
|
||||||
<h2 className="text-2xl font-semibold text-white">Sign in to your account</h2>
|
<h2 className="text-2xl font-semibold text-white">
|
||||||
<p className="text-sm text-neutral-300 mt-2">Welcome back! Please enter your details.</p>
|
Sign in to your account
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-neutral-300 mt-2">
|
||||||
|
Welcome back! Please enter your details.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
@ -66,7 +72,9 @@ export default function Login() {
|
|||||||
required
|
required
|
||||||
placeholder="Enter your username"
|
placeholder="Enter your username"
|
||||||
value={formData.username}
|
value={formData.username}
|
||||||
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setFormData({ ...formData, username: e.target.value })
|
||||||
|
}
|
||||||
error={error}
|
error={error}
|
||||||
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
||||||
focus:border-primary-400 focus:ring-primary-400"
|
focus:border-primary-400 focus:ring-primary-400"
|
||||||
@ -93,7 +101,9 @@ export default function Login() {
|
|||||||
required
|
required
|
||||||
placeholder="Enter your password"
|
placeholder="Enter your password"
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setFormData({ ...formData, password: e.target.value })
|
||||||
|
}
|
||||||
error={error}
|
error={error}
|
||||||
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
||||||
focus:border-primary-400 focus:ring-primary-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
|
focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50
|
||||||
disabled:cursor-not-allowed transition-colors"
|
disabled:cursor-not-allowed transition-colors"
|
||||||
>
|
>
|
||||||
{isLoading ? 'Signing in...' : 'Sign in'}
|
{isLoading ? "Signing in..." : "Sign in"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,37 +1,70 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Dialog } from "@headlessui/react";
|
import { Dialog } from "@headlessui/react";
|
||||||
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
|
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||||
import { NavLink, Link } from "react-router-dom";
|
import { NavLink, Link, useNavigate } from "react-router-dom";
|
||||||
import { Button } from '../UI';
|
import { Button } from "../UI";
|
||||||
|
import { useAuth } from "../../hooks/useAuth";
|
||||||
|
|
||||||
const navigation = [
|
const publicNavigation = [
|
||||||
{ name: "Дома", href: "/" },
|
{ name: "Дома", href: "/" },
|
||||||
{ name: "За нас", href: "/about" },
|
{ name: "За нас", href: "/about" },
|
||||||
{ name: "Галерија", href: "/gallery" },
|
{ name: "Галерија", href: "/gallery" },
|
||||||
{ name: "Контакт", href: "/contact" },
|
{ name: "Контакт", href: "/contact" },
|
||||||
// { name: "Админ", href: "/admin" },
|
|
||||||
{ name: "Логин", href: "/login" },
|
{ name: "Логин", href: "/login" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const basePrivateNavigation = [
|
||||||
|
{ name: "Дома", href: "/" },
|
||||||
|
{ name: "За нас", href: "/about" },
|
||||||
|
{ name: "Галерија", href: "/gallery" },
|
||||||
|
{ name: "Контакт", href: "/contact" },
|
||||||
|
];
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
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 (
|
return (
|
||||||
<div className="w-full bg-gradient-to-b from-primary-900 to-primary-800 border-b border-primary-700/50">
|
<div className="w-full bg-gradient-to-b from-primary-900 to-primary-800 border-b border-primary-700/50">
|
||||||
<header className="relative">
|
<header className="relative">
|
||||||
<nav className="container-base py-4" aria-label="Global">
|
<nav className="container-base py-4" aria-label="Global">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
{/* Left Logo */}
|
{/* Left Logo */}
|
||||||
<Link to="/" className="p-2 hover:bg-white/20 transition-colors">
|
<div className="flex-shrink-0">
|
||||||
<img
|
<Link to="/" className="p-2 hover:bg-white/20 transition-colors">
|
||||||
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
|
<img
|
||||||
src="/imklogorgb.png"
|
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
|
||||||
alt="IMK logo"
|
src="/imklogorgb.png"
|
||||||
/>
|
alt="IMK logo"
|
||||||
</Link>
|
/>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Centered Desktop Navigation */}
|
{/* 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">
|
<div className="flex items-center gap-x-1">
|
||||||
{navigation.map((item) => (
|
{navigation.map((item) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
@ -39,9 +72,11 @@ export default function Navbar() {
|
|||||||
to={item.href}
|
to={item.href}
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
|
`px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
|
||||||
${isActive
|
${
|
||||||
? 'bg-primary-700 text-white shadow-lg'
|
isActive
|
||||||
: 'text-neutral-200 hover:bg-primary-700/50 hover:text-white'}`
|
? "bg-primary-700 text-white shadow-lg"
|
||||||
|
: "text-neutral-200 hover:bg-primary-700/50 hover:text-white"
|
||||||
|
}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
@ -50,18 +85,36 @@ export default function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Side - TUF Logo & Mobile Menu */}
|
{/* Right Side Content */}
|
||||||
<div className="flex items-center gap-x-4">
|
<div className="flex items-center gap-x-4">
|
||||||
{/* TUF Logo */}
|
{/* User Menu & Logout */}
|
||||||
<a href="/" className="hidden lg:flex p-2 hover:bg-white/20 transition-colors">
|
{user && (
|
||||||
<img
|
<div className="hidden lg:flex items-center gap-x-4">
|
||||||
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
|
<span className="text-white text-sm">
|
||||||
src="/tuf.png"
|
{user.name || user.email}
|
||||||
alt="TUF logo"
|
</span>
|
||||||
/>
|
<Button
|
||||||
</a>
|
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
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="lg:hidden text-white hover:bg-primary-700/50"
|
className="lg:hidden text-white hover:bg-primary-700/50"
|
||||||
@ -74,7 +127,7 @@ export default function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Mobile menu */}
|
{/* Mobile Menu */}
|
||||||
<Dialog
|
<Dialog
|
||||||
as="div"
|
as="div"
|
||||||
className="lg:hidden"
|
className="lg:hidden"
|
||||||
@ -100,24 +153,53 @@ export default function Navbar() {
|
|||||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flow-root">
|
<div className="mt-6 flow-root">
|
||||||
<div className="space-y-1 py-6">
|
<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) => (
|
{navigation.map((item) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
key={item.name}
|
key={item.name}
|
||||||
to={item.href}
|
to={item.href}
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`block px-3 py-2 text-base font-medium rounded-lg transition-colors
|
`block px-3 py-2 text-base font-medium rounded-lg transition-colors
|
||||||
${isActive
|
${
|
||||||
? 'bg-primary-700 text-white'
|
isActive
|
||||||
: 'text-neutral-200 hover:bg-primary-700/50 hover:text-white'
|
? "bg-primary-700 text-white"
|
||||||
}`
|
: "text-neutral-200 hover:bg-primary-700/50 hover:text-white"
|
||||||
|
}`
|
||||||
}
|
}
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</NavLink>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// frontend/src/hooks/useAuth.jsx
|
|
||||||
import { createContext, useContext, useState, useEffect } from "react";
|
import { createContext, useContext, useState, useEffect } from "react";
|
||||||
import api from "../services/api";
|
import api from "../services/api";
|
||||||
|
|
||||||
@ -51,6 +50,7 @@ export const AuthProvider = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
|
//no session menager in DB, can add it later
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
setUser(null);
|
setUser(null);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -122,5 +122,4 @@ api.interceptors.response.use(
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
export const logout = () => api.post("/auth/logout");
|
|
||||||
export default api;
|
export default api;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user