feat: add categories to windows sidebar and refresh data on stat card navigation

This commit is contained in:
ssbg04 committed 2026-06-28 13:44:34 +08:00
1 parent dae724a41d
commit 7fa6c1140e
2 files changed
+93 -37

No files matched your search

@@ -125,13 +125,13 @@ class _WindowsSidebarLayoutState extends ConsumerState<WindowsSidebarLayout> {
@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<String>).contains(widget.userRole)).toList();
@@ -286,35 +286,62 @@ class _WindowsSidebarLayoutState extends ConsumerState<WindowsSidebarLayout> {
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);
},
),
),
],
);
},
),
@@ -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<DashboardScreen> {
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<DashboardScreen> {
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');
},
),
],
);
});