Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 22 additions & 65 deletions src/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,28 @@
import React, {useContext, useState} from 'react';
import {Link, useLocation} from 'react-router-dom';
import {Building, Calendar, CalendarCheck, Home, Settings, TreePalm, User, Users} from 'lucide-react';
import {UserContext} from "@/contexts/UserContext.tsx";
import SidebarAccountDropdown from "@/components/sidebar/SidebarAccountDropdown.tsx";
import Logo from "@/components/icon/Logo.tsx";
import {NotificationBell} from "@/modules/notification/components/NotificationBell.tsx";
import {UserRole} from "@/core/types/enum.ts";
import {NavigationItem, navigationItems} from "@/core/types/NavigationItem.ts";

const navigationItems = {
main: [
{name: "Home", href: "/", icon: Home},
{name: "Calendar", href: "/calendar", icon: Calendar},
{name: "Settings", href: "/settings", icon: Settings},
],
admin: [
{name: "Leaves", href: "/leaves", icon: CalendarCheck},
{name: "Organization", href: "/organization", icon: Building},
{name: "Leave Policy", href: "/leaves/policies", icon: TreePalm},
{name: "Users", href: "/users", icon: User},
{name: "Teams", href: "/teams", icon: Users},
],
teamAdmin: [
{name: "Leaves", href: "/leaves", icon: CalendarCheck},
{name: "Users", href: "/users", icon: User},
],
};

const getAccessibleNavigationItems = (role: UserRole): NavigationItem[] => {
return navigationItems.filter(item => item.accessLevel.includes(role))
}

export default function Sidebar() {
const [selectedOption, setSelectedOption] = useState<string>('');
const location = useLocation();
const {user} = useContext(UserContext);
const accessibleItems = user ? getAccessibleNavigationItems(user.role) : [];

const handleNavigation = (path: string) => {
setSelectedOption(path);
};

const isNavigationActive = (href: string) => {
return location.pathname === href || selectedOption === href;
const isNavigationActive = (path: string) => {
return location.pathname === path || selectedOption === path;
};

return (
Expand All @@ -52,23 +37,9 @@ export default function Sidebar() {
<NotificationBell/>
</div>

<div className="flex-1 overflow-y-auto px-4">
<NavigationSection items={navigationItems.main} isNavigationActive={isNavigationActive}
onClick={handleNavigation}/>
{user?.role === UserRole.ORGANIZATION_ADMIN && (
<NavigationSection title="Management" items={navigationItems.admin}
isNavigationActive={isNavigationActive} onClick={handleNavigation}/>
)}
{user?.role === UserRole.TEAM_ADMIN && (
<NavigationSection title="Management" items={navigationItems.teamAdmin}
isNavigationActive={isNavigationActive} onClick={handleNavigation}/>
)}
</div>
<NavigationSection items={accessibleItems} isNavigationActive={isNavigationActive} onClick={handleNavigation}/>

<SidebarAccountDropdown
isActive={isNavigationActive('/profile')}
onClick={handleNavigation}
/>
<SidebarAccountDropdown isActive={isNavigationActive('/profile')} onClick={handleNavigation}/>
</div>
</div>
</div>
Expand All @@ -77,41 +48,27 @@ export default function Sidebar() {

type NavigationSectionProps = {
title?: string;
items: { name: string, href: string, icon: React.ComponentType<React.SVGProps<SVGSVGElement>> }[];
isNavigationActive: (href: string) => boolean;
items: NavigationItem[];
isNavigationActive: (path: string) => boolean;
onClick: (page: string) => void;
}

function NavigationSection({title, items, isNavigationActive, onClick}: NavigationSectionProps) {
return (
<div className="">
<div className="flex-1 overflow-y-auto px-4">
<h2 className="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider my-3">{title}</h2>
{items.map((item) => (
<NavigationLink key={item.name} {...item} isActive={isNavigationActive(item.href)} onClick={onClick}/>
<Link
key={item.title}
to={item.path}
onClick={() => onClick(item.path)}
className={`group flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${
isNavigationActive(item.path) ? "bg-indigo-50 text-indigo-600" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"}`}
>
<item.icon className={`h-5 w-5 ${isNavigationActive(item.path) ? "text-indigo-600" : "text-gray-400 group-hover:text-gray-500"}`}/>
{item.title}
</Link>
))}
</div>
);
}

type NavigationLinkProps = {
href: string;
name: string;
isActive: boolean;
onClick: (page: string) => void;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}

function NavigationLink({href, name, icon: Icon, isActive, onClick}: NavigationLinkProps) {
return (
<Link
to={href}
onClick={() => onClick(href)}
className={`group flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${
isActive ? "bg-indigo-50 text-indigo-600" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
}`}
>
<Icon className={`h-5 w-5 ${isActive ? "text-indigo-600" : "text-gray-400 group-hover:text-gray-500"}`}/>
{name}
</Link>
);
}
69 changes: 69 additions & 0 deletions src/core/types/NavigationItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {Building, Calendar, CalendarCheck, Home, LucideIcon, Settings, TreePalm, User, Users} from "lucide-react";
import {UserRole} from "@/core/types/enum.ts";

export interface NavigationItem {
title: string;
path: string;
icon: LucideIcon;
description: string;
accessLevel: UserRole[];
}

export const navigationItems: NavigationItem[] = [
{
title: "Home",
path: "/",
icon: Home,
description: "View your leave balance, and leave history.",
accessLevel: [UserRole.ORGANIZATION_ADMIN, UserRole.TEAM_ADMIN, UserRole.EMPLOYEE]
},
{
title: "Calendar",
path: "/calendar",
icon: Calendar,
description: "View the organization calendar with pending and approved leaves.",
accessLevel: [UserRole.ORGANIZATION_ADMIN, UserRole.TEAM_ADMIN, UserRole.EMPLOYEE]
},
{
title: "Settings",
path: "/settings",
icon: Settings,
description: "Manage your account settings, holidays, and notification.",
accessLevel: [UserRole.ORGANIZATION_ADMIN, UserRole.TEAM_ADMIN, UserRole.EMPLOYEE]
},
{
title: "Leaves",
path: "/leaves",
icon: CalendarCheck,
description: "Review and manage leave requests, and access user profiles.",
accessLevel: [UserRole.ORGANIZATION_ADMIN, UserRole.TEAM_ADMIN]
},
{
title: "Organization",
path: "/organization",
icon: Building,
description: "Configure organization settings like start of the week and working days.",
accessLevel: [UserRole.ORGANIZATION_ADMIN]
},
{
title: "Leave Policy",
path: "/leaves/policies",
icon: TreePalm,
description: "Create and manage custom leave types and policies for your organization.",
accessLevel: [UserRole.ORGANIZATION_ADMIN]
},
{
title: "Users",
path: "/users",
icon: User,
description: "Add, edit, or remove users from the organization.",
accessLevel: [UserRole.ORGANIZATION_ADMIN, UserRole.TEAM_ADMIN]
},
{
title: "Teams",
path: "/teams",
icon: Users,
description: "Add, edit, or remove teams from the organization.",
accessLevel: [UserRole.ORGANIZATION_ADMIN]
}
]
5 changes: 0 additions & 5 deletions src/core/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,4 @@ export type PagedResponse<T> = {
pageSize: number;
totalPages: number;
totalContents: number
}

export type Country = {
name: string;
code: string
}
5 changes: 4 additions & 1 deletion src/core/types/country.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {Country} from '@/core/types/common.ts';
export type Country = {
name: string;
code: string
}

export const country: Country[] = [
{name: "Afghanistan", code: "AF"},
Expand Down