diff --git a/frontend/src/components/layout/Sidebar.test.tsx b/frontend/src/components/layout/Sidebar.test.tsx
index ee1bbbf..ff7dee3 100644
--- a/frontend/src/components/layout/Sidebar.test.tsx
+++ b/frontend/src/components/layout/Sidebar.test.tsx
@@ -21,34 +21,34 @@ function mockAuthenticatedAs(role: 'Owner' | 'Administrator' | 'User') {
}
describe('Sidebar role filtering (BR-U3-01 – BR-U3-06)', () => {
- it('Owner sees all nav items', async () => {
+ it('Owner sees Dashboard, Users, Settings, CMS', async () => {
mockAuthenticatedAs('Owner');
renderApp('/dashboard');
expect(await screen.findByTestId('nav-dashboard')).toBeInTheDocument();
expect(screen.getByTestId('nav-users')).toBeInTheDocument();
expect(screen.getByTestId('nav-settings')).toBeInTheDocument();
expect(screen.getByTestId('nav-cms')).toBeInTheDocument();
- expect(screen.getByTestId('nav-profile')).toBeInTheDocument();
+ expect(screen.queryByTestId('nav-profile')).not.toBeInTheDocument();
});
- it('Administrator sees Dashboard, Users, Profile — not Settings or CMS', async () => {
+ it('Administrator sees Dashboard and Users — not Settings or CMS', async () => {
mockAuthenticatedAs('Administrator');
renderApp('/dashboard');
expect(await screen.findByTestId('nav-dashboard')).toBeInTheDocument();
expect(screen.getByTestId('nav-users')).toBeInTheDocument();
- expect(screen.getByTestId('nav-profile')).toBeInTheDocument();
expect(screen.queryByTestId('nav-settings')).not.toBeInTheDocument();
expect(screen.queryByTestId('nav-cms')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('nav-profile')).not.toBeInTheDocument();
});
- it('User sees Dashboard and Profile only', async () => {
+ it('User sees Dashboard only', async () => {
mockAuthenticatedAs('User');
renderApp('/dashboard');
expect(await screen.findByTestId('nav-dashboard')).toBeInTheDocument();
- expect(screen.getByTestId('nav-profile')).toBeInTheDocument();
expect(screen.queryByTestId('nav-users')).not.toBeInTheDocument();
expect(screen.queryByTestId('nav-settings')).not.toBeInTheDocument();
expect(screen.queryByTestId('nav-cms')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('nav-profile')).not.toBeInTheDocument();
});
});
diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx
index f4af794..4481557 100644
--- a/frontend/src/components/layout/Sidebar.tsx
+++ b/frontend/src/components/layout/Sidebar.tsx
@@ -1,6 +1,6 @@
import { Link } from '@tanstack/react-router';
import { useTranslation } from 'react-i18next';
-import { LayoutDashboard, Users, FileText, Settings, User, X } from 'lucide-react';
+import { LayoutDashboard, Users, FileText, Settings, X } from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useAuth } from '@/contexts/auth-context';
@@ -23,7 +23,6 @@ const NAV_ITEMS: NavItem[] = [
{ to: '/users', labelKey: 'nav.users', icon: Users, testId: 'nav-users', roles: ['Owner', 'Administrator'] },
{ to: '/settings', labelKey: 'nav.settings', icon: Settings, testId: 'nav-settings', roles: ['Owner'] },
{ to: '/cms', labelKey: 'nav.cms', icon: FileText, testId: 'nav-cms', roles: ['Owner'] },
- { to: '/profile', labelKey: 'nav.profile', icon: User, testId: 'nav-profile' },
];
interface SidebarProps {
diff --git a/frontend/src/components/layout/UserMenu.tsx b/frontend/src/components/layout/UserMenu.tsx
index 289f3c5..969bd8b 100644
--- a/frontend/src/components/layout/UserMenu.tsx
+++ b/frontend/src/components/layout/UserMenu.tsx
@@ -1,6 +1,7 @@
import { useNavigate } from '@tanstack/react-router';
import { useTranslation } from 'react-i18next';
import { LogOut, User as UserIcon } from 'lucide-react';
+import { Link } from '@tanstack/react-router';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
@@ -46,6 +47,13 @@ export function UserMenu() {
+
+
+
+ {t('nav.profile')}
+
+
+
{
void handleLogout();
diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx
new file mode 100644
index 0000000..c0de36f
--- /dev/null
+++ b/frontend/src/pages/ProfilePage.tsx
@@ -0,0 +1,13 @@
+import { useTranslation } from 'react-i18next';
+
+export function ProfilePage() {
+ const { t } = useTranslation();
+ return (
+
+
+ {t('nav.profile')}
+
+
Coming soon.
+
+ );
+}
diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx
new file mode 100644
index 0000000..ecb83c9
--- /dev/null
+++ b/frontend/src/pages/SettingsPage.tsx
@@ -0,0 +1,13 @@
+import { useTranslation } from 'react-i18next';
+
+export function SettingsPage() {
+ const { t } = useTranslation();
+ return (
+
+
+ {t('nav.settings')}
+
+
Coming soon.
+
+ );
+}
diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx
index 3dbf2e6..86ee94f 100644
--- a/frontend/src/router.tsx
+++ b/frontend/src/router.tsx
@@ -189,12 +189,28 @@ const cmsRoute = createRoute({
),
});
+const settingsRoute = createRoute({
+ getParentRoute: () => authenticatedRoute,
+ path: '/settings',
+ component: () => (
+
+ {lazyPage(() => import('@/pages/SettingsPage'), 'SettingsPage')()}
+
+ ),
+});
+
+const profileRoute = createRoute({
+ getParentRoute: () => authenticatedRoute,
+ path: '/profile',
+ component: lazyPage(() => import('@/pages/ProfilePage'), 'ProfilePage'),
+});
+
export const routeTree = rootRoute.addChildren([
indexRoute,
loginRoute,
setupRoute,
inviteCompleteRoute,
- authenticatedRoute.addChildren([dashboardRoute, usersRoute, cmsRoute]),
+ authenticatedRoute.addChildren([dashboardRoute, usersRoute, cmsRoute, settingsRoute, profileRoute]),
]);
export const router = createRouter({