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 ( + + + + + + + + Language + {LANGUAGES.map((lng) => ( + setCurrent(lng.code)} + rightSection={current === lng.code ? : undefined} + > + {lng.label} + + ))} + + + ); +} 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}