feat: 3-role auth, role-based routing, role-specific sidebar navigation

Add cc-agent role alongside executive and admin. Login page now has 3 tabs
(Marketing Executive, Call Center, Admin). RoleRouter renders the appropriate
home page per role. Sidebar shows completely different nav items per role with
role subtitle. Placeholder pages added for Team Dashboard, Call Desk, Call
History, and Follow-ups.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-16 18:24:25 +05:30
parent 530dfa1aa4
commit 26c352e2cc
9 changed files with 208 additions and 48 deletions

View File

@@ -1,12 +1,16 @@
import type { FC, HTMLAttributes } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faBell,
faBullhorn,
faChartMixed,
faClockRotateLeft,
faCommentDots,
faGear,
faGrid2,
faPhone,
faPlug,
faUsers,
} from "@fortawesome/pro-duotone-svg-icons";
import { useNavigate } from "react-router";
import { MobileNavigationHeader } from "@/components/application/app-navigation/base-components/mobile-header";
@@ -36,28 +40,100 @@ const IconPlug: FC<HTMLAttributes<HTMLOrSVGElement>> = ({ className }) => (
const IconGear: FC<HTMLAttributes<HTMLOrSVGElement>> = ({ className }) => (
<FontAwesomeIcon icon={faGear} className={className} />
);
const IconPhone: FC<HTMLAttributes<HTMLOrSVGElement>> = ({ className }) => (
<FontAwesomeIcon icon={faPhone} className={className} />
);
const IconBell: FC<HTMLAttributes<HTMLOrSVGElement>> = ({ className }) => (
<FontAwesomeIcon icon={faBell} className={className} />
);
const IconClockRewind: FC<HTMLAttributes<HTMLOrSVGElement>> = ({ className }) => (
<FontAwesomeIcon icon={faClockRotateLeft} className={className} />
);
const IconUsers: FC<HTMLAttributes<HTMLOrSVGElement>> = ({ className }) => (
<FontAwesomeIcon icon={faUsers} className={className} />
);
const mainItems: NavItemType[] = [
{ label: "Lead Workspace", href: "/", icon: IconGrid2 },
{ label: "Campaigns", href: "/campaigns", icon: IconBullhorn },
{ label: "Outreach", href: "/outreach", icon: IconCommentDots },
];
type NavSection = {
label: string;
items: NavItemType[];
};
const insightsItems: NavItemType[] = [
{ label: "Analytics", href: "/analytics", icon: IconChartMixed },
];
const getNavSections = (role: string): NavSection[] => {
if (role === 'admin') {
return [
{
label: 'Overview',
items: [
{ label: 'Team Dashboard', href: '/', icon: IconGrid2 },
],
},
{
label: 'Management',
items: [
{ label: 'Campaigns', href: '/campaigns', icon: IconBullhorn },
{ label: 'Analytics', href: '/analytics', icon: IconChartMixed },
],
},
{
label: 'Admin',
items: [
{ label: 'Integrations', href: '/integrations', icon: IconPlug },
{ label: 'Settings', href: '/settings', icon: IconGear },
],
},
];
}
const adminItems: NavItemType[] = [
{ label: "Integrations", href: "/integrations", icon: IconPlug },
{ label: "Settings", href: "/settings", icon: IconGear },
];
if (role === 'cc-agent') {
return [
{
label: 'Call Center',
items: [
{ label: 'Call Desk', href: '/', icon: IconPhone },
{ label: 'Follow-ups', href: '/follow-ups', icon: IconBell },
{ label: 'Call History', href: '/call-history', icon: IconClockRewind },
],
},
];
}
// Executive (default)
return [
{
label: 'Main',
items: [
{ label: 'Lead Workspace', href: '/', icon: IconGrid2 },
{ label: 'All Leads', href: '/leads', icon: IconUsers },
{ label: 'Campaigns', href: '/campaigns', icon: IconBullhorn },
{ label: 'Outreach', href: '/outreach', icon: IconCommentDots },
],
},
{
label: 'Insights',
items: [
{ label: 'Analytics', href: '/analytics', icon: IconChartMixed },
],
},
];
};
const getRoleSubtitle = (role: string): string => {
switch (role) {
case 'admin':
return 'Marketing Admin';
case 'cc-agent':
return 'Call Center Agent';
default:
return 'Marketing Executive';
}
};
interface SidebarProps {
activeUrl?: string;
}
export const Sidebar = ({ activeUrl = "/" }: SidebarProps) => {
const { logout, isAdmin: authIsAdmin, user } = useAuth();
const { logout, user } = useAuth();
const navigate = useNavigate();
const handleSignOut = () => {
@@ -65,11 +141,7 @@ export const Sidebar = ({ activeUrl = "/" }: SidebarProps) => {
navigate('/login');
};
const navSections = [
{ label: "Main", items: mainItems },
{ label: "Insights", items: insightsItems },
...(authIsAdmin ? [{ label: "Admin", items: adminItems }] : []),
];
const navSections = getNavSections(user.role);
const content = (
<aside
@@ -79,7 +151,7 @@ export const Sidebar = ({ activeUrl = "/" }: SidebarProps) => {
{/* Logo */}
<div className="flex flex-col gap-1 px-4 lg:px-5">
<span className="text-md font-bold text-primary">Helix Engage</span>
<span className="text-xs text-tertiary">Ramaiah Memorial Hospital</span>
<span className="text-xs text-tertiary">Ramaiah Memorial Hospital &middot; {getRoleSubtitle(user.role)}</span>
</div>
{/* Nav sections */}