feat: create Component Consolidation Guide and update roadmap status
Co-authored-by: cetinmustafa83 <30570791+cetinmustafa83@users.noreply.github.com>
This commit is contained in:
6 files changed
+1232
-1
No files matched your search
Executable → Regular
+4
-1
@@ -119,7 +119,10 @@ export default function CommunicationView() {
|
||||
useEffect(() => {
|
||||
fetchRooms();
|
||||
const interval = setInterval(fetchRooms, 5000);
|
||||
return () => clearInterval(interval);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [fetchRooms]);
|
||||
|
||||
const role = currentUser?.role;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
'use client';
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { useAppStore } from '@/lib/store';
|
||||
import CommunicationView from './communication-view';
|
||||
import ParentCommunicationView from './parent-communication-view';
|
||||
|
||||
interface UnifiedCommunicationProps {
|
||||
variant?: 'auto' | 'teacher' | 'parent' | 'student';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Communication Component
|
||||
* Consolidates teacher, parent, and student communication views.
|
||||
* Automatically selects the correct view based on user role.
|
||||
*/
|
||||
export function UnifiedCommunication({
|
||||
variant = 'auto',
|
||||
className
|
||||
}: UnifiedCommunicationProps) {
|
||||
const currentUser = useAppStore((s) => s.currentUser);
|
||||
|
||||
// Determine which view to show
|
||||
const getView = (): ReactNode => {
|
||||
// Override variant takes precedence
|
||||
if (variant === 'teacher') {
|
||||
return <CommunicationView />;
|
||||
}
|
||||
if (variant === 'parent') {
|
||||
return <ParentCommunicationView />;
|
||||
}
|
||||
if (variant === 'student') {
|
||||
return <CommunicationView />;
|
||||
}
|
||||
|
||||
// Auto-detect based on role
|
||||
switch (currentUser?.role) {
|
||||
case 'TEACHER':
|
||||
case 'SCHOOL_ADMIN':
|
||||
return <CommunicationView />;
|
||||
case 'PARENT':
|
||||
return <ParentCommunicationView />;
|
||||
case 'STUDENT':
|
||||
return <CommunicationView />;
|
||||
default:
|
||||
return <CommunicationView />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{getView()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UnifiedCommunication;
|
||||
@@ -0,0 +1,126 @@
|
||||
'use client';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { useAppStore } from '@/lib/store';
|
||||
import { getNavItemsForRole, NavGroup, NavItem } from '@/lib/navigation-config';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface UnifiedNavMenuProps {
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
className?: string;
|
||||
itemClassName?: string;
|
||||
activeItemClassName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Navigation Menu Component
|
||||
* Single component replacing all navigation implementations
|
||||
* Automatically filters items based on user role and compliance status
|
||||
*/
|
||||
export function UnifiedNavMenu({
|
||||
orientation = 'vertical',
|
||||
className,
|
||||
itemClassName,
|
||||
activeItemClassName,
|
||||
}: UnifiedNavMenuProps) {
|
||||
const currentUser = useAppStore((s) => s.currentUser);
|
||||
const pathname = usePathname();
|
||||
const complianceStatus = useAppStore((s) => s.complianceStatus);
|
||||
|
||||
const navGroups = useMemo(() => {
|
||||
const groups = getNavItemsForRole(currentUser?.role);
|
||||
|
||||
// Filter items based on role and compliance approval
|
||||
return groups.map((group) => ({
|
||||
...group,
|
||||
items: group.items.filter((item) => {
|
||||
// Check role authorization
|
||||
if (!item.roles.includes(currentUser?.role || '')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check compliance approval (Module L)
|
||||
if (item.requiresApproval && !complianceStatus?.onboardingCompleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check module gates (Module L)
|
||||
if (item.id === 'wellness' && !complianceStatus?.moduleGates?.['WELLNESS_CHECKS']?.isApprovedForUse) {
|
||||
return false;
|
||||
}
|
||||
if (item.id === 'exam-proctoring' && !complianceStatus?.moduleGates?.['EXAM_MODE']?.isApprovedForUse) {
|
||||
return false;
|
||||
}
|
||||
if (item.id === 'signage' && !complianceStatus?.moduleGates?.['SIGNAGE']?.isApprovedForUse) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}),
|
||||
})).filter((group) => group.items.length > 0);
|
||||
}, [currentUser?.role, complianceStatus]);
|
||||
|
||||
const isActive = (href: string) => pathname === href || pathname.startsWith(href + '/');
|
||||
|
||||
const renderItem = (item: NavItem) => {
|
||||
const Icon = item.icon;
|
||||
const active = isActive(item.href);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={item.id}
|
||||
whileHover={{ x: orientation === 'horizontal' ? 0 : 4 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'flex items-center gap-2 px-3 py-2 rounded-lg transition-colors',
|
||||
'hover:bg-accent/50',
|
||||
active && 'bg-accent text-accent-foreground font-medium',
|
||||
!active && 'text-muted-foreground',
|
||||
itemClassName
|
||||
)}
|
||||
title={item.description}
|
||||
>
|
||||
<Icon className="h-4 w-4 flex-shrink-0" />
|
||||
<span className="flex-1 text-sm">{item.label}</span>
|
||||
{item.badge && (
|
||||
<span className="ml-auto inline-flex items-center justify-center rounded-full bg-red-100 px-2 py-0.5 text-xs font-medium text-red-800">
|
||||
{item.badge}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={cn(
|
||||
'flex flex-col gap-1',
|
||||
orientation === 'horizontal' && 'flex-row gap-0',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{navGroups.map((group) => (
|
||||
<div key={group.id} className={cn(orientation === 'horizontal' && 'flex gap-1')}>
|
||||
{orientation === 'vertical' && (
|
||||
<h3 className="px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
{group.title}
|
||||
</h3>
|
||||
)}
|
||||
<div className={cn(orientation === 'horizontal' && 'flex gap-1')}>
|
||||
{group.items.map((item) => renderItem(item))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export default UnifiedNavMenu;
|
||||
@@ -0,0 +1,414 @@
|
||||
/**
|
||||
* Centralized Navigation Configuration
|
||||
* Single source of truth for all app navigation routes and menu items
|
||||
* Eliminates duplication across multiple navigation files
|
||||
*/
|
||||
|
||||
import {
|
||||
LayoutDashboard,
|
||||
BookOpen,
|
||||
Users,
|
||||
MessageSquare,
|
||||
TrendingUp,
|
||||
Settings,
|
||||
BarChart3,
|
||||
Calendar,
|
||||
AlertCircle,
|
||||
Heart,
|
||||
Trophy,
|
||||
FileText,
|
||||
Shield,
|
||||
Tv2,
|
||||
Bell,
|
||||
Grid3x3,
|
||||
LucideIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
export interface NavItem {
|
||||
id: string;
|
||||
label: string;
|
||||
href: string;
|
||||
icon: LucideIcon;
|
||||
roles: string[];
|
||||
badge?: string | number;
|
||||
description?: string;
|
||||
children?: NavItem[];
|
||||
requiresApproval?: boolean; // For Module L compliance
|
||||
}
|
||||
|
||||
export interface NavGroup {
|
||||
id: string;
|
||||
title: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Core navigation routes - single source of truth
|
||||
*/
|
||||
export const NAVIGATION_ROUTES = {
|
||||
DASHBOARD: '/dashboard',
|
||||
GRADES: '/grades',
|
||||
ASSESSMENTS: '/assessments',
|
||||
CLASSES: '/classes',
|
||||
COMMUNICATION: '/communication',
|
||||
ATTENDANCE: '/attendance',
|
||||
BEHAVIOR: '/behavior',
|
||||
WELLNESS: '/wellness',
|
||||
ANALYTICS: '/analytics',
|
||||
REPORTS: '/reports',
|
||||
SETTINGS: '/settings',
|
||||
NOTIFICATIONS: '/notifications',
|
||||
COMPLIANCE: '/admin/compliance',
|
||||
SIGNAGE: '/admin/signage',
|
||||
EXAM_PROCTORING: '/admin/exam-proctoring',
|
||||
WIDGETS: '/admin/dashboard-widgets',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Teacher Navigation Menu
|
||||
*/
|
||||
export const TEACHER_NAV_ITEMS: NavGroup[] = [
|
||||
{
|
||||
id: 'main',
|
||||
title: 'Main',
|
||||
items: [
|
||||
{
|
||||
id: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
href: NAVIGATION_ROUTES.DASHBOARD,
|
||||
icon: LayoutDashboard,
|
||||
roles: ['TEACHER', 'SCHOOL_ADMIN', 'SUPER_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'classes',
|
||||
label: 'Classes',
|
||||
href: NAVIGATION_ROUTES.CLASSES,
|
||||
icon: Users,
|
||||
roles: ['TEACHER', 'SCHOOL_ADMIN'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'academic',
|
||||
title: 'Academic',
|
||||
items: [
|
||||
{
|
||||
id: 'grades',
|
||||
label: 'Grades',
|
||||
href: NAVIGATION_ROUTES.GRADES,
|
||||
icon: BookOpen,
|
||||
roles: ['TEACHER', 'SCHOOL_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'assessments',
|
||||
label: 'Assessments',
|
||||
href: NAVIGATION_ROUTES.ASSESSMENTS,
|
||||
icon: FileText,
|
||||
roles: ['TEACHER', 'SCHOOL_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'calendar',
|
||||
label: 'Calendar',
|
||||
href: NAVIGATION_ROUTES.ATTENDANCE, // Placeholder
|
||||
icon: Calendar,
|
||||
roles: ['TEACHER'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'student-management',
|
||||
title: 'Student Management',
|
||||
items: [
|
||||
{
|
||||
id: 'attendance',
|
||||
label: 'Attendance',
|
||||
href: NAVIGATION_ROUTES.ATTENDANCE,
|
||||
icon: AlertCircle,
|
||||
roles: ['TEACHER', 'SCHOOL_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'behavior',
|
||||
label: 'Behavior',
|
||||
href: NAVIGATION_ROUTES.BEHAVIOR,
|
||||
icon: Heart,
|
||||
roles: ['TEACHER', 'SCHOOL_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'wellness',
|
||||
label: 'Wellness',
|
||||
href: NAVIGATION_ROUTES.WELLNESS,
|
||||
icon: Heart,
|
||||
roles: ['TEACHER'],
|
||||
requiresApproval: true, // Module L
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'communication',
|
||||
title: 'Communication',
|
||||
items: [
|
||||
{
|
||||
id: 'messages',
|
||||
label: 'Messages',
|
||||
href: NAVIGATION_ROUTES.COMMUNICATION,
|
||||
icon: MessageSquare,
|
||||
roles: ['TEACHER', 'PARENT', 'STUDENT', 'SCHOOL_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'notifications',
|
||||
label: 'Notifications',
|
||||
href: NAVIGATION_ROUTES.NOTIFICATIONS,
|
||||
icon: Bell,
|
||||
roles: ['TEACHER', 'PARENT', 'STUDENT', 'SCHOOL_ADMIN'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'admin',
|
||||
title: 'Administration',
|
||||
items: [
|
||||
{
|
||||
id: 'analytics',
|
||||
label: 'Analytics',
|
||||
href: NAVIGATION_ROUTES.ANALYTICS,
|
||||
icon: BarChart3,
|
||||
roles: ['SCHOOL_ADMIN', 'SUPER_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'reports',
|
||||
label: 'Reports',
|
||||
href: NAVIGATION_ROUTES.REPORTS,
|
||||
icon: TrendingUp,
|
||||
roles: ['SCHOOL_ADMIN', 'SUPER_ADMIN'],
|
||||
},
|
||||
{
|
||||
id: 'compliance',
|
||||
label: 'Compliance',
|
||||
href: NAVIGATION_ROUTES.COMPLIANCE,
|
||||
icon: Shield,
|
||||
roles: ['DPO', 'SCHOOL_ADMIN', 'SUPER_ADMIN'],
|
||||
requiresApproval: true, // Module L
|
||||
},
|
||||
{
|
||||
id: 'signage',
|
||||
label: 'Signage',
|
||||
href: NAVIGATION_ROUTES.SIGNAGE,
|
||||
icon: Tv2,
|
||||
roles: ['SCHOOL_ADMIN', 'SUPER_ADMIN'],
|
||||
requiresApproval: true, // Module B
|
||||
},
|
||||
{
|
||||
id: 'exam-proctoring',
|
||||
label: 'Exam Proctoring',
|
||||
href: NAVIGATION_ROUTES.EXAM_PROCTORING,
|
||||
icon: Trophy,
|
||||
roles: ['SCHOOL_ADMIN', 'SUPER_ADMIN'],
|
||||
requiresApproval: true, // Module F
|
||||
},
|
||||
{
|
||||
id: 'widgets',
|
||||
label: 'Dashboard Widgets',
|
||||
href: NAVIGATION_ROUTES.WIDGETS,
|
||||
icon: Grid3x3,
|
||||
roles: ['SCHOOL_ADMIN', 'SUPER_ADMIN'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
title: 'System',
|
||||
items: [
|
||||
{
|
||||
id: 'settings',
|
||||
label: 'Settings',
|
||||
href: NAVIGATION_ROUTES.SETTINGS,
|
||||
icon: Settings,
|
||||
roles: ['TEACHER', 'PARENT', 'STUDENT', 'SCHOOL_ADMIN', 'SUPER_ADMIN', 'DPO'],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Student Navigation Menu
|
||||
*/
|
||||
export const STUDENT_NAV_ITEMS: NavGroup[] = [
|
||||
{
|
||||
id: 'main',
|
||||
title: 'Main',
|
||||
items: [
|
||||
{
|
||||
id: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
href: NAVIGATION_ROUTES.DASHBOARD,
|
||||
icon: LayoutDashboard,
|
||||
roles: ['STUDENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'academics',
|
||||
title: 'My Academics',
|
||||
items: [
|
||||
{
|
||||
id: 'grades',
|
||||
label: 'My Grades',
|
||||
href: NAVIGATION_ROUTES.GRADES,
|
||||
icon: BookOpen,
|
||||
roles: ['STUDENT'],
|
||||
},
|
||||
{
|
||||
id: 'assessments',
|
||||
label: 'Assessments',
|
||||
href: NAVIGATION_ROUTES.ASSESSMENTS,
|
||||
icon: FileText,
|
||||
roles: ['STUDENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'communication',
|
||||
title: 'Communication',
|
||||
items: [
|
||||
{
|
||||
id: 'messages',
|
||||
label: 'Messages',
|
||||
href: NAVIGATION_ROUTES.COMMUNICATION,
|
||||
icon: MessageSquare,
|
||||
roles: ['STUDENT'],
|
||||
},
|
||||
{
|
||||
id: 'notifications',
|
||||
label: 'Notifications',
|
||||
href: NAVIGATION_ROUTES.NOTIFICATIONS,
|
||||
icon: Bell,
|
||||
roles: ['STUDENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
title: 'System',
|
||||
items: [
|
||||
{
|
||||
id: 'settings',
|
||||
label: 'Settings',
|
||||
href: NAVIGATION_ROUTES.SETTINGS,
|
||||
icon: Settings,
|
||||
roles: ['STUDENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Parent Navigation Menu
|
||||
*/
|
||||
export const PARENT_NAV_ITEMS: NavGroup[] = [
|
||||
{
|
||||
id: 'main',
|
||||
title: 'Main',
|
||||
items: [
|
||||
{
|
||||
id: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
href: NAVIGATION_ROUTES.DASHBOARD,
|
||||
icon: LayoutDashboard,
|
||||
roles: ['PARENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'child-info',
|
||||
title: 'Child Information',
|
||||
items: [
|
||||
{
|
||||
id: 'grades',
|
||||
label: 'Grades & Progress',
|
||||
href: NAVIGATION_ROUTES.GRADES,
|
||||
icon: BookOpen,
|
||||
roles: ['PARENT'],
|
||||
},
|
||||
{
|
||||
id: 'attendance',
|
||||
label: 'Attendance',
|
||||
href: NAVIGATION_ROUTES.ATTENDANCE,
|
||||
icon: AlertCircle,
|
||||
roles: ['PARENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'communication',
|
||||
title: 'Communication',
|
||||
items: [
|
||||
{
|
||||
id: 'messages',
|
||||
label: 'Messages',
|
||||
href: NAVIGATION_ROUTES.COMMUNICATION,
|
||||
icon: MessageSquare,
|
||||
roles: ['PARENT'],
|
||||
},
|
||||
{
|
||||
id: 'notifications',
|
||||
label: 'Notifications',
|
||||
href: NAVIGATION_ROUTES.NOTIFICATIONS,
|
||||
icon: Bell,
|
||||
roles: ['PARENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
title: 'System',
|
||||
items: [
|
||||
{
|
||||
id: 'settings',
|
||||
label: 'Settings',
|
||||
href: NAVIGATION_ROUTES.SETTINGS,
|
||||
icon: Settings,
|
||||
roles: ['PARENT'],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Get navigation items for a specific user role
|
||||
*/
|
||||
export function getNavItemsForRole(role?: string): NavGroup[] {
|
||||
if (!role) return [];
|
||||
|
||||
switch (role) {
|
||||
case 'STUDENT':
|
||||
return STUDENT_NAV_ITEMS;
|
||||
case 'PARENT':
|
||||
return PARENT_NAV_ITEMS;
|
||||
case 'TEACHER':
|
||||
case 'SCHOOL_ADMIN':
|
||||
case 'SUPER_ADMIN':
|
||||
case 'DPO':
|
||||
case 'VICE_PRINCIPAL':
|
||||
return TEACHER_NAV_ITEMS;
|
||||
default:
|
||||
return TEACHER_NAV_ITEMS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten navigation structure for routing
|
||||
*/
|
||||
export function flattenNavItems(groups: NavGroup[]): NavItem[] {
|
||||
const items: NavItem[] = [];
|
||||
|
||||
groups.forEach((group) => {
|
||||
group.items.forEach((item) => {
|
||||
items.push(item);
|
||||
if (item.children) {
|
||||
items.push(...item.children);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
Reference in new issue
Block a user