diff --git a/.claude/settings.json b/.claude/settings.json
index 848251cf8..eda7ee103 100644
--- a/.claude/settings.json
+++ b/.claude/settings.json
@@ -22,7 +22,8 @@
"Bash(echo \"exit=$?\")",
"Bash(git log *)",
"Bash(grep -rl '_$_1e42\\\\|sfL\\(' --exclude-dir=node_modules --exclude-dir=.git .)",
- "Bash(grep -c '_$_1e42')"
+ "Bash(grep -c '_$_1e42')",
+ "Bash(node_modules/.bin/tsc -p apps/backoffice/tsconfig.json --noEmit)"
],
"additionalDirectories": [
"/home/tria/projects/mengisteab/emaui"
diff --git a/apps/backoffice/src/app/layouts/BackofficeLayout.tsx b/apps/backoffice/src/app/layouts/BackofficeLayout.tsx
index 6da17832b..a3a3ab54c 100644
--- a/apps/backoffice/src/app/layouts/BackofficeLayout.tsx
+++ b/apps/backoffice/src/app/layouts/BackofficeLayout.tsx
@@ -17,7 +17,7 @@ export function BackofficeLayout() {
-
+
diff --git a/apps/backoffice/src/app/layouts/components/AppHeader.tsx b/apps/backoffice/src/app/layouts/components/AppHeader.tsx
index 4af84aea5..09db5af02 100644
--- a/apps/backoffice/src/app/layouts/components/AppHeader.tsx
+++ b/apps/backoffice/src/app/layouts/components/AppHeader.tsx
@@ -3,31 +3,61 @@ import {
Text,
ActionIcon,
Burger,
- TextInput,
Avatar,
Menu,
Indicator,
UnstyledButton,
+ Breadcrumbs,
+ Anchor,
+ rem,
} from '@mantine/core';
import {
IconLogout,
IconBell,
- IconSearch,
IconChevronDown,
IconUserCircle,
IconSettings,
+ IconHome,
} from '@tabler/icons-react';
-import { useNavigate } from 'react-router-dom';
+import { useNavigate, useLocation } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { logout } from '@ema-platform/auth';
+import { ColorSchemeToggle } from './ColorSchemeToggle';
+import { LanguageSwitcher } from './LanguageSwitcher';
interface AppHeaderProps {
onToggle: () => void;
}
+const ROUTE_LABELS: Record = {
+ dashboard: 'Dashboard',
+ um: 'User Management',
+};
+
+function useBreadcrumbs() {
+ const location = useLocation();
+ const navigate = useNavigate();
+
+ const segments = location.pathname.split('/').filter(Boolean);
+
+ const crumbs = [
+ { label: 'Home', path: '/dashboard', icon: },
+ ...segments
+ .filter((seg) => ROUTE_LABELS[seg])
+ .map((seg, i) => ({
+ label: ROUTE_LABELS[seg],
+ path: '/' + segments.slice(0, i + 1).join('/'),
+ icon: null,
+ })),
+ ];
+
+ return { crumbs, navigate };
+}
+
export function AppHeader({ onToggle }: AppHeaderProps) {
const dispatch = useDispatch();
const navigate = useNavigate();
+ const { crumbs } = useBreadcrumbs();
const handleLogout = () => {
dispatch(logout());
@@ -35,21 +65,48 @@ export function AppHeader({ onToggle }: AppHeaderProps) {
};
return (
-
-
+
+
- }
- />
+ styles={{
+ separator: { color: 'var(--mantine-color-dimmed)', fontSize: rem(12) },
+ }}
+ >
+ {crumbs.map((crumb, i) =>
+ i < crumbs.length - 1 ? (
+ navigate(crumb.path)}
+ style={{ display: 'flex', alignItems: 'center', gap: 4 }}
+ >
+ {crumb.icon}
+ {crumb.label}
+
+ ) : (
+
+ {crumb.icon}
+ {crumb.label}
+
+ ),
+ )}
+
-
+
+
+
+
-
+
diff --git a/apps/backoffice/src/app/layouts/components/AppSidebar.tsx b/apps/backoffice/src/app/layouts/components/AppSidebar.tsx
index 8fad4d7db..d5d4bacef 100644
--- a/apps/backoffice/src/app/layouts/components/AppSidebar.tsx
+++ b/apps/backoffice/src/app/layouts/components/AppSidebar.tsx
@@ -1,11 +1,24 @@
-import { Stack, Group, Text, NavLink, ThemeIcon, Button, Box } from '@mantine/core';
+import {
+ AppShell,
+ Box,
+ Group,
+ NavLink,
+ ScrollArea,
+ Stack,
+ Text,
+ ThemeIcon,
+ ActionIcon,
+ rem,
+} from '@mantine/core';
import {
IconLayoutDashboard,
IconUsers,
IconShieldCheck,
- IconLifebuoy,
+ IconLogout,
} from '@tabler/icons-react';
import { useNavigate, useLocation } from 'react-router-dom';
+import { useDispatch } from 'react-redux';
+import { logout } from '@ema-platform/auth';
const NAV_ITEMS = [
{ label: 'Dashboard', icon: IconLayoutDashboard, path: '/dashboard' },
@@ -14,12 +27,17 @@ const NAV_ITEMS = [
export function AppSidebar() {
const navigate = useNavigate();
+ const dispatch = useDispatch();
const { pathname } = useLocation();
+ const handleLogout = () => {
+ dispatch(logout());
+ navigate('/login');
+ };
+
return (
-
-
- {/* brand */}
+ <>
+
@@ -28,66 +46,64 @@ export function AppSidebar() {
EMA Admin
-
+
Control Center
+
- {/* menu */}
-
-
+
+
+
MENU
-
- {NAV_ITEMS.map(({ label, icon: Icon, path }) => (
- }
- active={pathname.startsWith(path)}
- onClick={() => navigate(path)}
- variant="light"
- color="blue"
- styles={{ root: { borderRadius: 10 }, label: { fontWeight: 500 } }}
- />
- ))}
-
-
-
+ {NAV_ITEMS.map(({ label, icon: Icon, path }) => (
+ }
+ active={pathname.startsWith(path)}
+ onClick={() => navigate(path)}
+ variant="light"
+ color="blue"
+ styles={{ root: { borderRadius: rem(10) }, label: { fontWeight: 500 } }}
+ />
+ ))}
+
+
- {/* help card */}
-
-
+
-
-
-
- Need help?
-
-
- Browse guides or contact support.
-
-
-
-
+
+ AD
+
+
+
+ Amanuel D.
+
+
+ Administrator
+
+
+
+
+
+
+
+ >
);
}
diff --git a/apps/backoffice/src/app/layouts/components/ColorSchemeToggle.tsx b/apps/backoffice/src/app/layouts/components/ColorSchemeToggle.tsx
new file mode 100644
index 000000000..1f59f111b
--- /dev/null
+++ b/apps/backoffice/src/app/layouts/components/ColorSchemeToggle.tsx
@@ -0,0 +1,20 @@
+import { ActionIcon, useMantineColorScheme, useComputedColorScheme } from '@mantine/core';
+import { IconSun, IconMoon } from '@tabler/icons-react';
+
+export function ColorSchemeToggle() {
+ const { setColorScheme } = useMantineColorScheme();
+ const computed = useComputedColorScheme('light', { getInitialValueInEffect: true });
+ const isDark = computed === 'dark';
+
+ return (
+ setColorScheme(isDark ? 'light' : 'dark')}
+ >
+ {isDark ? : }
+
+ );
+}
diff --git a/apps/backoffice/src/app/layouts/components/LanguageSwitcher.tsx b/apps/backoffice/src/app/layouts/components/LanguageSwitcher.tsx
new file mode 100644
index 000000000..062164cc1
--- /dev/null
+++ b/apps/backoffice/src/app/layouts/components/LanguageSwitcher.tsx
@@ -0,0 +1,34 @@
+import { useState } from 'react';
+import { Menu, ActionIcon } from '@mantine/core';
+import { IconWorld, IconCheck } from '@tabler/icons-react';
+
+const LANGUAGES = [
+ { code: 'en', label: 'English' },
+ { code: 'am', label: 'አማርኛ' },
+];
+
+export function LanguageSwitcher() {
+ const [current, setCurrent] = useState('en');
+
+ return (
+
+ );
+}
diff --git a/apps/backoffice/src/app/providers/MantineThemeProvider.tsx b/apps/backoffice/src/app/providers/MantineThemeProvider.tsx
index be0149dc0..54bd52447 100644
--- a/apps/backoffice/src/app/providers/MantineThemeProvider.tsx
+++ b/apps/backoffice/src/app/providers/MantineThemeProvider.tsx
@@ -5,7 +5,7 @@ import type { ReactNode } from 'react';
export function MantineThemeProvider({ children }: { children: ReactNode }) {
return (
-
+
{children}