fix: dashboard notification and refresh bugs, student filter and preview updates
This commit is contained in:
1 parent
fb4f62b528
commit
871541885c
7 files changed
+123
-39
No files matched your search
@@ -176,7 +176,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
ref.read(notificationsProvider.notifier).markAsRead(note.id);
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
if (context.mounted) {
|
||||
if (note.title.toLowerCase().contains('password')) {
|
||||
final role = ref.read(authProvider).value?.role;
|
||||
if (note.title.toLowerCase().contains('password') && role == 'admin') {
|
||||
ResetRequestsModal.show(context);
|
||||
} else {
|
||||
ViewActivityModal.show(
|
||||
@@ -670,7 +671,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
child: TextButton.icon(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => const UserHistoryScreen()),
|
||||
),
|
||||
).then((_) => _handleRefresh()),
|
||||
icon: const Icon(Icons.history, size: 18),
|
||||
label: const Text('User History'),
|
||||
style: TextButton.styleFrom(foregroundColor: AppColors.primaryGreen, padding: EdgeInsets.zero),
|
||||
@@ -679,7 +680,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
TextButton.icon(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => const RecentActivitiesScreen()),
|
||||
),
|
||||
).then((_) => _handleRefresh()),
|
||||
icon: const Icon(Icons.open_in_new, size: 18),
|
||||
label: const Text('View All'),
|
||||
style: TextButton.styleFrom(foregroundColor: AppColors.primaryGreen, padding: EdgeInsets.zero),
|
||||
@@ -701,7 +702,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
TextButton.icon(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => const UserHistoryScreen()),
|
||||
),
|
||||
).then((_) => _handleRefresh()),
|
||||
icon: const Icon(Icons.history, size: 18),
|
||||
label: const Text('User History'),
|
||||
style: TextButton.styleFrom(foregroundColor: AppColors.primaryGreen),
|
||||
@@ -709,7 +710,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
TextButton.icon(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => const RecentActivitiesScreen()),
|
||||
),
|
||||
).then((_) => _handleRefresh()),
|
||||
icon: const Icon(Icons.open_in_new, size: 18),
|
||||
label: const Text('View All'),
|
||||
style: TextButton.styleFrom(foregroundColor: AppColors.primaryGreen),
|
||||
|
||||
@@ -191,17 +191,12 @@ class _RecentActivitiesScreenState
|
||||
backgroundColor: AppColors.primaryGreen,
|
||||
foregroundColor: Colors.white,
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () => ref.invalidate(recentActivitiesPageProvider),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
// ── Filter Bar ───────────────────────────────────────────────────
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Wrap(
|
||||
|
||||
@@ -110,8 +110,11 @@ class _UserHistoryScreenState extends ConsumerState<UserHistoryScreen> {
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
items: ['All Actions', 'CREATED', 'UPDATED', 'DELETED']
|
||||
.map((v) => DropdownMenuItem(value: v, child: Text(v)))
|
||||
items: ['All Actions', 'created', 'updated', 'deleted']
|
||||
.map((v) => DropdownMenuItem(
|
||||
value: v,
|
||||
child: Text(v == 'All Actions' ? v : v.toUpperCase()),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (v) => setDialogState(() => pendingAction = v!),
|
||||
),
|
||||
@@ -185,17 +188,12 @@ class _UserHistoryScreenState extends ConsumerState<UserHistoryScreen> {
|
||||
backgroundColor: AppColors.primaryGreen,
|
||||
foregroundColor: Colors.white,
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () => ref.invalidate(userHistoryPageProvider),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
// ── Filter Bar ───────────────────────────────────────────────────
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Wrap(
|
||||
|
||||
@@ -1146,7 +1146,7 @@ class _DocumentsScreenState extends ConsumerState<DocumentsScreen>
|
||||
if (!isIconOnly) ...[
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Multi-Select',
|
||||
'Select',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
@@ -479,20 +479,23 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
|
||||
],
|
||||
if (isDesktop) ...[
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => _openFilterDialog(query, ref.read(academicYearsListProvider), ref.read(sectionsListProvider)),
|
||||
icon: const Icon(Icons.tune_rounded, size: 20),
|
||||
label: const Text('Filter'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.primaryGreen,
|
||||
side: const BorderSide(color: AppColors.primaryGreen),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
SizedBox(
|
||||
height: 42,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _openFilterDialog(query, ref.read(academicYearsListProvider), ref.read(sectionsListProvider)),
|
||||
icon: const Icon(Icons.tune_rounded, size: 20),
|
||||
label: const Text('Filter'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.primaryGreen,
|
||||
side: const BorderSide(color: AppColors.primaryGreen),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.userRole != 'teacher') ...[
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
height: 42,
|
||||
child: PrimaryButton(
|
||||
label: 'ADD',
|
||||
onPressed: () => _openModal(),
|
||||
@@ -739,12 +742,38 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
|
||||
|
||||
final sectionItems = sectionsAsync.whenOrNull(
|
||||
data: (sections) {
|
||||
final raw = ['All Sections', ...(sections as List).map((s) => s.name as String)];
|
||||
final filteredSections = (sections as List).where((s) {
|
||||
if (_pendingGradeLevel != 'All Grades' && s.gradeLevel.toString() != _pendingGradeLevel) return false;
|
||||
if (_pendingSchoolYear != 'All School Years' && s.academicYearRange != _pendingSchoolYear) return false;
|
||||
return true;
|
||||
});
|
||||
final raw = ['All Sections', ...filteredSections.map((s) => s.name as String)];
|
||||
return raw.toSet().toList();
|
||||
},
|
||||
) ??
|
||||
['All Sections'];
|
||||
|
||||
// Precompute counts for badges
|
||||
final Map<String, int> syGradeCount = {};
|
||||
if (academicYearsAsync.value != null && sectionsAsync.value != null) {
|
||||
final sList = sectionsAsync.value as List;
|
||||
for (final sy in syItems.skip(1)) {
|
||||
syGradeCount[sy] = sList.where((s) => s.academicYearRange == sy).map((s) => s.gradeLevel).toSet().length;
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, int> gradeSectionCount = {};
|
||||
if (sectionsAsync.value != null) {
|
||||
final sList = sectionsAsync.value as List;
|
||||
for (final grade in _gradeLevels.skip(1)) {
|
||||
gradeSectionCount[grade] = sList.where((s) {
|
||||
final matchGrade = s.gradeLevel.toString() == grade;
|
||||
final matchSy = _pendingSchoolYear == 'All School Years' || s.academicYearRange == _pendingSchoolYear;
|
||||
return matchGrade && matchSy;
|
||||
}).length;
|
||||
}
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
decoration: BoxDecoration(
|
||||
@@ -787,11 +816,18 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
|
||||
// School Year
|
||||
_buildFilterSection(
|
||||
label: 'School Year',
|
||||
onReset: () => setDialogState(() => _pendingSchoolYear = 'All School Years'),
|
||||
onReset: () => setDialogState(() {
|
||||
_pendingSchoolYear = 'All School Years';
|
||||
_pendingSection = 'All Sections';
|
||||
}),
|
||||
child: _buildFilterDropdown(
|
||||
value: syItems.contains(_pendingSchoolYear) ? _pendingSchoolYear : 'All School Years',
|
||||
items: syItems,
|
||||
onChanged: (v) => setDialogState(() => _pendingSchoolYear = v!),
|
||||
counts: syGradeCount,
|
||||
onChanged: (v) => setDialogState(() {
|
||||
_pendingSchoolYear = v!;
|
||||
_pendingSection = 'All Sections';
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -807,6 +843,7 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
|
||||
child: _buildFilterDropdown(
|
||||
value: _gradeLevels.contains(_pendingGradeLevel) ? _pendingGradeLevel : 'All Grades',
|
||||
items: _gradeLevels.toList(),
|
||||
counts: gradeSectionCount,
|
||||
onChanged: (v) => setDialogState(() {
|
||||
_pendingGradeLevel = v!;
|
||||
_pendingSection = 'All Sections';
|
||||
@@ -949,6 +986,7 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
|
||||
Widget _buildFilterDropdown({
|
||||
required String value,
|
||||
required List<String> items,
|
||||
Map<String, int>? counts,
|
||||
ValueChanged<String?>? onChanged,
|
||||
String? hint,
|
||||
bool enabled = true,
|
||||
@@ -981,7 +1019,28 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
|
||||
items: items
|
||||
.map((i) => DropdownMenuItem(
|
||||
value: i,
|
||||
child: Text(i, style: const TextStyle(fontSize: 14)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(i, style: const TextStyle(fontSize: 14)),
|
||||
if (counts != null && counts[i] != null && counts[i]! > 0)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primaryGreen.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
counts[i].toString(),
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: AppColors.primaryGreen,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: enabled ? onChanged : null,
|
||||
@@ -1237,10 +1296,9 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
|
||||
),
|
||||
|
||||
// BOTTOM SECTION
|
||||
Wrap(
|
||||
spacing: AppSizes.p12,
|
||||
runSpacing: AppSizes.p12,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@@ -49,6 +49,7 @@ class _AddEditStudentModalState extends ConsumerState<AddEditStudentModal>
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _scrollCtrl = ScrollController();
|
||||
late TabController _tabController;
|
||||
final TransformationController _previewTransformationController = TransformationController();
|
||||
|
||||
late TextEditingController _lrnController;
|
||||
late TextEditingController _firstNameController;
|
||||
@@ -97,6 +98,7 @@ class _AddEditStudentModalState extends ConsumerState<AddEditStudentModal>
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
_previewTransformationController.dispose();
|
||||
_lrnController.dispose();
|
||||
_firstNameController.dispose();
|
||||
_middleNameController.dispose();
|
||||
@@ -506,11 +508,38 @@ class _AddEditStudentModalState extends ConsumerState<AddEditStudentModal>
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
if (!isPdf) ...[
|
||||
IconButton(
|
||||
icon: const Icon(Icons.zoom_in, size: 20, color: AppColors.textSecondary),
|
||||
tooltip: 'Zoom In',
|
||||
onPressed: () {
|
||||
_previewTransformationController.value =
|
||||
_previewTransformationController.value.clone()..scale(1.5);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.zoom_out, size: 20, color: AppColors.textSecondary),
|
||||
tooltip: 'Zoom Out',
|
||||
onPressed: () {
|
||||
_previewTransformationController.value =
|
||||
_previewTransformationController.value.clone()..scale(0.666);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.center_focus_strong, size: 20, color: AppColors.textSecondary),
|
||||
tooltip: 'Reset View',
|
||||
onPressed: () {
|
||||
_previewTransformationController.value = Matrix4.identity();
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(AppSizes.radiusMedium),
|
||||
@@ -525,8 +554,11 @@ class _AddEditStudentModalState extends ConsumerState<AddEditStudentModal>
|
||||
canShowScrollStatus: false,
|
||||
)
|
||||
: InteractiveViewer(
|
||||
minScale: 0.5,
|
||||
maxScale: 4.0,
|
||||
transformationController: _previewTransformationController,
|
||||
minScale: 0.1,
|
||||
maxScale: 10.0,
|
||||
boundaryMargin: const EdgeInsets.all(double.infinity),
|
||||
constrained: true,
|
||||
child: Center(
|
||||
child: Image.file(file, fit: BoxFit.contain),
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user