const NAV = [
{ group: 'Me', items: [['home', 'Home', 'LayoutDashboard'], ['attendance', 'Attendance', 'CalendarCheck'], ['leave', 'Leave', 'CalendarDays'], ['tasks', 'Tasks', 'ListChecks'], ['performance', 'Performance', 'Target'], ['holidays', 'Holidays', 'PartyPopper']] },
{ group: 'My team', roles: ['manager', 'hr_admin', 'super_admin'], items: [['team', 'Team', 'Users']] },
{ group: 'Admin', roles: ['hr_admin', 'super_admin'], items: [['admin', 'Overview', 'LayoutGrid'], ['admin-employees', 'Employees', 'UserCheck'], ['admin-approvals', 'Approvals', 'Inbox'], ['admin-holidays', 'Holidays & festivals', 'CalendarHeart'], ['admin-outbox', 'Email outbox', 'Mail'], ['admin-roles', 'Roles & access', 'KeyRound', ['super_admin']]] },
];
const routeAllowed = (route, role) => NAV.some((g) => (!g.roles || g.roles.includes(role)) && g.items.some(([k, , , r]) => k === route && (!r || r.includes(role))));
const navFor = (role) => NAV.filter((g) => !g.roles || g.roles.includes(role)).map((g) => ({ ...g, items: g.items.filter(([, , , r]) => !r || r.includes(role)) }));
const ROUTE_LABEL = Object.fromEntries(NAV.flatMap((g) => g.items.map(([k, l]) => [k, l])));
function navCounts(S, me) {
const t = AKH.todayIso();
const reports = S.users.filter((u) => u.managerId === me.id).map((u) => u.id);
return {
tasks: S.tasks.filter((x) => x.assigneeId === me.id && x.status !== 'done' && x.due <= t).length,
team: S.leaves.filter((l) => l.status === 'pending' && l.approverId === me.id).length + S.corrections.filter((c) => c.status === 'pending' && reports.includes(c.userId)).length,
'admin-employees': S.users.filter((u) => u.status === 'pending').length,
'admin-approvals': S.corrections.filter((c) => c.status === 'pending').length + S.leaves.filter((l) => l.status === 'pending').length,
};
}
function CountDot({ n, light }) {
if (!n) return null;
return {n};
}
function NavItem({ k, label, icon, active, count, onClick }) {
const [h, setH] = React.useState(false);
return (
);
}
function Sidebar({ route, go }) {
const S = useStore(); const { me } = useApp(); const counts = navCounts(S, me);
return (
);
}
function NotifPanel({ go, onClose }) {
const S = useStore(); const { me, m } = useApp();
const list = S.notifications.filter((n) => n.userId === me.id).sort((a, b) => b.at.localeCompare(a.at)).slice(0, 12);
return (
Notifications
{list.length === 0 ?
: list.map((n) => (
))}
);
}
function Bell({ go }) {
const S = useStore(); const { me } = useApp(); const [open, setOpen] = React.useState(false);
const unread = S.notifications.filter((n) => n.userId === me.id && !n.read).length;
return (
<>
{open ? <> setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 30 }}>
setOpen(false)} />> : null}
>
);
}
function Topbar({ go }) {
const { me } = useApp();
return (
);
}
const TABS = [['home', 'Home', 'LayoutDashboard'], ['attendance', 'Attendance', 'CalendarCheck'], ['tasks', 'Tasks', 'ListChecks'], ['leave', 'Leave', 'CalendarDays']];
function MoreSheet({ route, go, onClose }) {
const S = useStore(); const { me } = useApp(); const counts = navCounts(S, me);
return (
{me.name}
{me.designation} · {AKStore.ROLES[me.role]}
{navFor(me.role).map((g) => (
{g.group}
{g.items.map(([k, label, icon]) =>
{ onClose(); go(k); }} />)}
))}
AKStore.act.signOut()}>Sign out
);
}
function AppShell({ route, go, children }) {
const { m } = useApp(); const [more, setMore] = React.useState(false);
const scroller = React.useRef(null);
React.useEffect(() => { if (scroller.current) scroller.current.scrollTop = 0; else window.scrollTo(0, 0); }, [route]);
if (m) {
const inTabs = TABS.some(([k]) => k === route);
return (
{children}
{more ?
setMore(false)} /> : null}
);
}
return (
);
}
Object.assign(window, { AppShell, routeAllowed, navCounts, ROUTE_LABEL });