diff --git a/packages/frontend/src/components/RoleGuard.tsx b/packages/frontend/src/components/RoleGuard.tsx
new file mode 100644
index 0000000..dc1ef1a
--- /dev/null
+++ b/packages/frontend/src/components/RoleGuard.tsx
@@ -0,0 +1,17 @@
+import { Navigate, Outlet } from 'react-router-dom';
+import type { Role } from '@flashcard/shared';
+import { useAuth } from '../stores/authStore.js';
+
+export function RoleGuard({ role }: { role: Role }) {
+ const user = useAuth((s) => s.user);
+ if (!user) return ;
+ if (user.role !== role) {
+ return (
+ ;
+}
diff --git a/packages/frontend/src/components/UserMenu.tsx b/packages/frontend/src/components/UserMenu.tsx
new file mode 100644
index 0000000..ea64030
--- /dev/null
+++ b/packages/frontend/src/components/UserMenu.tsx
@@ -0,0 +1,55 @@
+import { useState, useRef, useEffect } from 'react';
+import { Link, useNavigate } from 'react-router-dom';
+import { useAuth } from '../stores/authStore.js';
+
+export function UserMenu() {
+ const { user, logout } = useAuth();
+ const [open, setOpen] = useState(false);
+ const ref = useRef(null);
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ function onClick(e: MouseEvent) {
+ if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
+ }
+ document.addEventListener('mousedown', onClick);
+ return () => document.removeEventListener('mousedown', onClick);
+ }, []);
+
+ if (!user) return null;
+
+ const initials = user.displayName.trim().split(/\s+/).map((p) => p[0]).slice(0, 2).join('').toUpperCase();
+
+ async function handleLogout() {
+ await logout();
+ navigate('/login');
+ }
+
+ return (
+
+
+ );
+ }
+ return Geen toegang
+Deze pagina is alleen voor beheerders.
+
+
+ {open && (
+
+ );
+}
diff --git a/packages/frontend/src/main.tsx b/packages/frontend/src/main.tsx
index 853e39f..7f4e8cc 100644
--- a/packages/frontend/src/main.tsx
+++ b/packages/frontend/src/main.tsx
@@ -3,10 +3,16 @@ import { createRoot } from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import { router } from './router.js';
import { useSettings } from './stores/settingsStore.js';
+import { useAuth } from './stores/authStore.js';
+import { onUnauthorized } from './api/client.js';
import './styles.css';
useSettings.getState().hydrate();
+onUnauthorized(() => {
+ useAuth.setState({ user: null, ready: true });
+});
+
const root = createRoot(document.getElementById('root')!);
root.render(
+
+ setOpen(false)}>Profiel + {user.role === 'sysadmin' && ( + setOpen(false)}>👑 Systeembeheer + )} + +
+ )}
+
+
+ {user.displayName}
+ {user.email}
+ + setOpen(false)}>Profiel + {user.role === 'sysadmin' && ( + setOpen(false)}>👑 Systeembeheer + )} + +