feat: create Component Consolidation Guide and update roadmap status

Co-authored-by: cetinmustafa83 <30570791+cetinmustafa83@users.noreply.github.com>
This commit is contained in:
v0andcetinmustafa83 committed 2026-08-01 09:15:51 +00:00
1 parent 98d4a52421
commit c138a5f7b2
6 files changed
+1232 -1

No files matched your search

+4 -1
View File
@@ -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;
+58
View File
@@ -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;
+126
View File
@@ -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;
+414
View File
@@ -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;
}