diff --git a/frontend/lib/ui/layouts/windows_sidebar_layout.dart b/frontend/lib/ui/layouts/windows_sidebar_layout.dart index a98c18d..675e005 100644 --- a/frontend/lib/ui/layouts/windows_sidebar_layout.dart +++ b/frontend/lib/ui/layouts/windows_sidebar_layout.dart @@ -125,13 +125,13 @@ class _WindowsSidebarLayoutState extends ConsumerState { @override Widget build(BuildContext context) { final allTabs = [ - {'label': 'Dashboard', 'icon': Icons.dashboard, 'activeIcon': Icons.dashboard, 'screen': const DashboardScreen(), 'roles': ['admin', 'teacher']}, - {'label': 'Students', 'icon': Icons.people, 'activeIcon': Icons.people, 'screen': StudentsScreen(userRole: widget.userRole), 'roles': ['admin', 'teacher']}, - {'label': 'Documents', 'icon': Icons.folder, 'activeIcon': Icons.folder, 'screen': DocumentsScreen(userRole: widget.userRole), 'roles': ['admin', 'teacher']}, - {'label': 'Archives', 'icon': Icons.archive, 'activeIcon': Icons.archive, 'screen': ArchivesScreen(userRole: widget.userRole), 'roles': ['admin']}, - {'label': 'Reports', 'icon': Icons.bar_chart, 'activeIcon': Icons.bar_chart, 'screen': ReportsScreen(userRole: widget.userRole), 'roles': ['admin']}, - {'label': 'Users', 'icon': Icons.manage_accounts, 'activeIcon': Icons.manage_accounts, 'screen': const UsersScreen(), 'roles': ['admin']}, - {'label': 'Settings', 'icon': Icons.settings, 'activeIcon': Icons.settings, 'screen': SettingsScreen(userRole: widget.userRole), 'roles': ['admin', 'teacher']}, + {'category': 'Main Menu', 'label': 'Dashboard', 'icon': Icons.dashboard, 'activeIcon': Icons.dashboard, 'screen': const DashboardScreen(), 'roles': ['admin', 'teacher']}, + {'category': 'Main Menu', 'label': 'Students', 'icon': Icons.people, 'activeIcon': Icons.people, 'screen': StudentsScreen(userRole: widget.userRole), 'roles': ['admin', 'teacher']}, + {'category': 'Main Menu', 'label': 'Documents', 'icon': Icons.folder, 'activeIcon': Icons.folder, 'screen': DocumentsScreen(userRole: widget.userRole), 'roles': ['admin', 'teacher']}, + {'category': 'Management', 'label': 'Archives', 'icon': Icons.archive, 'activeIcon': Icons.archive, 'screen': ArchivesScreen(userRole: widget.userRole), 'roles': ['admin']}, + {'category': 'Management', 'label': 'Reports', 'icon': Icons.bar_chart, 'activeIcon': Icons.bar_chart, 'screen': ReportsScreen(userRole: widget.userRole), 'roles': ['admin']}, + {'category': 'Management', 'label': 'Users', 'icon': Icons.manage_accounts, 'activeIcon': Icons.manage_accounts, 'screen': const UsersScreen(), 'roles': ['admin']}, + {'category': 'System', 'label': 'Settings', 'icon': Icons.settings, 'activeIcon': Icons.settings, 'screen': SettingsScreen(userRole: widget.userRole), 'roles': ['admin', 'teacher']}, ]; final tabs = allTabs.where((tab) => (tab['roles'] as List).contains(widget.userRole)).toList(); @@ -286,35 +286,62 @@ class _WindowsSidebarLayoutState extends ConsumerState { final tab = tabs[index]; final isSelected = currentIndex == index; - return Padding( - padding: const EdgeInsets.only(bottom: AppSizes.p8), - child: Material( - color: Colors.transparent, - child: ListTile( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppSizes.radiusMedium), - ), - selected: isSelected, - selectedTileColor: Colors.white.withOpacity(0.15), // Soft highlight - leading: Icon( - (isSelected ? tab['activeIcon'] ?? tab['icon'] : tab['icon']) as IconData, - color: isSelected ? Colors.white : Colors.white70, - ), - title: Text( - tab['label'] as String, - style: TextStyle( - fontWeight: isSelected ? FontWeight.bold : FontWeight.w500, - color: isSelected ? Colors.white : Colors.white70, - fontSize: 15, + final String? category = tab['category'] as String?; + final String? previousCategory = index > 0 ? tabs[index - 1]['category'] as String? : null; + + final bool showCategory = category != null && category != previousCategory; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (showCategory) ...[ + if (index > 0) const SizedBox(height: 8), + if (index > 0) const Divider(height: 1, color: Colors.white24), + if (index > 0) const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.only(left: 12, bottom: 8), + child: Text( + category.toUpperCase(), + style: const TextStyle( + color: Colors.white54, + fontSize: 11, + fontWeight: FontWeight.bold, + letterSpacing: 1.2, + ), + ), + ), + ], + Padding( + padding: const EdgeInsets.only(bottom: AppSizes.p8), + child: Material( + color: Colors.transparent, + child: ListTile( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppSizes.radiusMedium), + ), + selected: isSelected, + selectedTileColor: Colors.white.withOpacity(0.15), // Soft highlight + leading: Icon( + (isSelected ? tab['activeIcon'] ?? tab['icon'] : tab['icon']) as IconData, + color: isSelected ? Colors.white : Colors.white70, + ), + title: Text( + tab['label'] as String, + style: TextStyle( + fontWeight: isSelected ? FontWeight.bold : FontWeight.w500, + color: isSelected ? Colors.white : Colors.white70, + fontSize: 15, + ), + ), + onTap: () { + ref.read(activeTabProvider.notifier).setTab(tab['label'] as String); + + _reloadTabContent(tab['label'] as String); + }, ), ), - onTap: () { - ref.read(activeTabProvider.notifier).setTab(tab['label'] as String); - - _reloadTabContent(tab['label'] as String); - }, ), - ), + ], ); }, ), diff --git a/frontend/lib/ui/screens/dashboard/dashboard_screen.dart b/frontend/lib/ui/screens/dashboard/dashboard_screen.dart index 63d1c54..934108a 100644 --- a/frontend/lib/ui/screens/dashboard/dashboard_screen.dart +++ b/frontend/lib/ui/screens/dashboard/dashboard_screen.dart @@ -6,6 +6,8 @@ import '../../shared/cards/stat_card.dart'; import '../../providers/dashboard_provider.dart'; import '../../providers/auth_provider.dart'; import '../../providers/student_provider.dart'; +import '../../providers/users_provider.dart'; +import '../../providers/document_provider.dart'; import '../../providers/navigation_provider.dart'; import '../../providers/notification_provider.dart'; import '../../../domain/entities/dashboard_models.dart'; @@ -631,7 +633,11 @@ class _DashboardScreenState extends ConsumerState { title: 'Total Students', value: stats.totalStudents.toString(), icon: Icons.school, - onTap: () => ref.read(activeTabProvider.notifier).setTab('Students'), + onTap: () { + ref.read(studentQueryProvider.notifier).reset(); + ref.invalidate(studentPageProvider); + ref.read(activeTabProvider.notifier).setTab('Students'); + }, ), if (isAdmin) StatCard( @@ -639,10 +645,33 @@ class _DashboardScreenState extends ConsumerState { value: stats.activeUsers.toString(), icon: Icons.badge, iconColor: Colors.blue, - onTap: () => ref.read(activeTabProvider.notifier).setTab('Users'), + onTap: () { + ref.invalidate(usersProvider); + ref.read(activeTabProvider.notifier).setTab('Users'); + }, ), - StatCard(title: 'Complete Docs', value: stats.completedDocuments.toString(), icon: Icons.task_alt, iconColor: AppColors.primaryGreen), - StatCard(title: 'Missing Docs', value: stats.missingDocuments.toString(), icon: Icons.folder_off, iconColor: Colors.orange), + StatCard( + title: 'Complete Docs', + value: stats.completedDocuments.toString(), + icon: Icons.task_alt, + iconColor: AppColors.primaryGreen, + onTap: () { + ref.invalidate(foldersProvider); + ref.invalidate(documentPageProvider); + ref.read(activeTabProvider.notifier).setTab('Documents'); + }, + ), + StatCard( + title: 'Missing Docs', + value: stats.missingDocuments.toString(), + icon: Icons.folder_off, + iconColor: Colors.orange, + onTap: () { + ref.invalidate(foldersProvider); + ref.invalidate(documentPageProvider); + ref.read(activeTabProvider.notifier).setTab('Documents'); + }, + ), ], ); });