Správa používateľov + notifikačný systém

- Pridaná kompletná správa používateľov (CRUD, reset hesla, zmena roly) pre ROOT/ADMIN
- Backend: POST /users endpoint, createUser controller, validácia
- Frontend: UserManagement, UserForm, PasswordResetModal komponenty
- Settings prístupné pre ROOT aj ADMIN (AdminRoute)
- Notifikačný systém s snooze funkcionalitou
- Aktualizácia HELPDESK_INIT_V2.md dokumentácie

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 15:30:27 +01:00
parent cbdd952bc1
commit 2ca0c4f4d8
36 changed files with 3116 additions and 522 deletions

View File

@@ -1,10 +1,13 @@
import { Link } from 'react-router-dom';
import { LogOut, User, Settings } from 'lucide-react';
import { LogOut, User, Settings, Menu } from 'lucide-react';
import { useAuthStore } from '@/store/authStore';
import { useSidebarStore } from '@/store/sidebarStore';
import { Button } from '@/components/ui';
import { NotificationCenter } from '@/components/NotificationCenter';
export function Header() {
const { user, logout } = useAuthStore();
const { toggle } = useSidebarStore();
const handleLogout = async () => {
await logout();
@@ -13,9 +16,19 @@ export function Header() {
return (
<header className="sticky top-0 z-40 border-b bg-background">
<div className="flex h-14 items-center justify-between px-4">
<Link to="/" className="flex items-center gap-2 font-semibold">
<span className="text-lg">Helpdesk</span>
</Link>
<div className="flex items-center gap-2">
<Button
variant="ghost"
size="sm"
onClick={toggle}
className="md:hidden"
>
<Menu className="h-5 w-5" />
</Button>
<Link to="/" className="flex items-center gap-2 font-semibold">
<span className="text-lg">Helpdesk</span>
</Link>
</div>
<div className="flex items-center gap-4">
{user && (
@@ -26,7 +39,9 @@ export function Header() {
<span className="text-muted-foreground">({user.role.name})</span>
</div>
{user.role.code === 'ROOT' && (
<NotificationCenter />
{(user.role.code === 'ROOT' || user.role.code === 'ADMIN') && (
<Link to="/settings">
<Button variant="ghost" size="sm">
<Settings className="h-4 w-4" />

View File

@@ -7,7 +7,7 @@ export function MainLayout() {
<div className="min-h-screen bg-background">
<Header />
<Sidebar />
<main className="ml-56 p-6">
<main className="p-4 md:ml-56 md:p-6">
<Outlet />
</main>
</div>

View File

@@ -6,8 +6,10 @@ import {
Users,
Wrench,
RotateCcw,
X,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { useSidebarStore } from '@/store/sidebarStore';
const navItems = [
{ to: '/', icon: LayoutDashboard, label: 'Dashboard' },
@@ -19,28 +21,57 @@ const navItems = [
];
export function Sidebar() {
const { isOpen, close } = useSidebarStore();
return (
<aside className="fixed left-0 top-14 z-30 h-[calc(100vh-3.5rem)] w-56 border-r bg-background">
<nav className="flex flex-col gap-1 p-4">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
end={item.to === '/'}
className={({ isActive }) =>
cn(
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
isActive
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
)
}
<>
{/* Overlay pre mobile */}
{isOpen && (
<div
className="fixed inset-0 z-40 bg-black/50 md:hidden"
onClick={close}
/>
)}
{/* Sidebar */}
<aside
className={cn(
'fixed left-0 top-14 z-50 h-[calc(100vh-3.5rem)] w-56 border-r bg-background transition-transform duration-200',
'md:translate-x-0 md:z-30',
isOpen ? 'translate-x-0' : '-translate-x-full'
)}
>
<div className="flex items-center justify-between p-4 md:hidden">
<span className="font-semibold">Menu</span>
<button
onClick={close}
className="rounded-md p-1 hover:bg-accent"
>
<item.icon className="h-4 w-4" />
{item.label}
</NavLink>
))}
</nav>
</aside>
<X className="h-5 w-5" />
</button>
</div>
<nav className="flex flex-col gap-1 p-4 pt-0 md:pt-4">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
end={item.to === '/'}
onClick={close}
className={({ isActive }) =>
cn(
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
isActive
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
)
}
>
<item.icon className="h-4 w-4" />
{item.label}
</NavLink>
))}
</nav>
</aside>
</>
);
}