fix(reports, students): fix reports yearly comparison layout overflow and students tab active year filter

This commit is contained in:
ssbg04 committed 2026-10-14 05:23:54 +08:00
1 parent bd1a712b40
commit c993937314
6 files changed
+74 -15

No files matched your search

+4
View File
@@ -1132,6 +1132,10 @@ Files backup/restore - mirror mode or zip -->
# REPORTS SCREEN
- [x] compliance tab student document compliance search bar not visible make it visible for all theme
- [x] make the space consume, make the analytics prominently visible with minimal scrolling
- [x] fix layout overflow in compliance tab yearly comparison chart on Windows app (adjusted card height to 360, dynamic chart height calculation, and wrapped scrollable chart in Expanded on desktop)
# STUDENT SCREEN
- [x] fix students tab showing all students on re-click: preserved active academic year in studentQueryProvider reset and fail-safe fallback in studentPageProvider for enrolled tab
---
@@ -383,6 +383,7 @@ class _AndroidBottomNavLayoutState extends ConsumerState<AndroidBottomNavLayout>
ref.invalidate(dashboardDataProvider);
break;
case 'Students':
ref.read(studentViewTabProvider.notifier).state = StudentViewTab.enrolled;
ref.read(studentQueryProvider.notifier).reset();
ref.invalidate(studentPageProvider);
break;
@@ -97,6 +97,7 @@ class _WindowsSidebarLayoutState extends ConsumerState<WindowsSidebarLayout> {
ref.invalidate(dashboardDataProvider);
break;
case 'Students':
ref.read(studentViewTabProvider.notifier).state = StudentViewTab.enrolled;
ref.read(studentQueryProvider.notifier).reset();
ref.invalidate(studentPageProvider);
break;
@@ -5,7 +5,9 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../../core/network/api_constants.dart';
import '../../domain/repositories/student_repository.dart';
import '../../domain/entities/student_model.dart';
import '../../domain/entities/setup_models.dart';
import '../../domain/entities/dashboard_models.dart' show RecentActivity;
import 'setup_provider.dart';
// ============================================================
// Academic Years Model
@@ -207,7 +209,21 @@ class StudentQueryNotifier extends Notifier<StudentQueryParams> {
);
}
void reset() => state = const StudentQueryParams();
void reset({String? schoolYear}) {
String sy = schoolYear ?? '';
if (sy.isEmpty) {
final tab = ref.read(studentViewTabProvider);
if (tab == StudentViewTab.enrolled) {
final years = ref.read(academicYearsListProvider).asData?.value ?? [];
try {
sy = years.firstWhere((y) => y.status.toLowerCase() == 'active').yearRange;
} catch (_) {
if (years.isNotEmpty) sy = years.first.yearRange;
}
}
}
state = StudentQueryParams(schoolYear: sy);
}
}
// ============================================================
@@ -258,6 +274,7 @@ final studentPageProvider = FutureProvider.autoDispose<StudentPage>((
ref,
) async {
final query = ref.watch(studentQueryProvider);
final tab = ref.watch(studentViewTabProvider);
final repo = ref.read(studentRepositoryProvider);
final sub = repo.onStudentChanged.listen((_) {
@@ -266,6 +283,25 @@ final studentPageProvider = FutureProvider.autoDispose<StudentPage>((
ref.onDispose(() => sub.cancel());
String effectiveSchoolYear = query.schoolYear;
if (tab == StudentViewTab.enrolled &&
effectiveSchoolYear.isEmpty &&
query.search.isEmpty) {
List<AcademicYearModel> years =
ref.watch(academicYearsListProvider).asData?.value ?? [];
if (years.isEmpty) {
try {
years = await ref.watch(academicYearsListProvider.future);
} catch (_) {}
}
try {
effectiveSchoolYear =
years.firstWhere((y) => y.status.toLowerCase() == 'active').yearRange;
} catch (_) {
if (years.isNotEmpty) effectiveSchoolYear = years.first.yearRange;
}
}
return repo.getStudents(
search: query.search,
page: query.page,
@@ -273,7 +309,7 @@ final studentPageProvider = FutureProvider.autoDispose<StudentPage>((
gradeLevel: query.gradeLevel,
status: query.status,
section: query.section,
schoolYear: query.schoolYear,
schoolYear: effectiveSchoolYear,
is4ps: query.is4Ps,
lrn: query.lrn,
sortBy: query.sortBy,
@@ -3691,7 +3691,7 @@ class _ReportsScreenState extends ConsumerState<ReportsScreen> {
return Container(
width: double.infinity,
height: isDesktop ? 300 : null,
height: isDesktop ? 360 : null,
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
decoration: _cardDecoration(),
child: Column(
@@ -4092,13 +4092,12 @@ class _ReportsScreenState extends ConsumerState<ReportsScreen> {
return Container(
width: double.infinity,
height: isDesktop ? 300 : null,
height: isDesktop ? 360 : null,
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
decoration: _cardDecoration(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ── Chart Title & Filter Row ──────────────────────────────────────
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -4266,14 +4265,15 @@ class _ReportsScreenState extends ConsumerState<ReportsScreen> {
final chartContent = LayoutBuilder(
builder: (context, constraints) {
final bool hasScroll = constraints.maxWidth < (data.length * 150.0);
final double minYearWidth = isDesktop ? 100.0 : 130.0;
final double requiredWidth = data.length * minYearWidth;
final bool hasScroll = constraints.maxWidth < requiredWidth;
final double chartWidth = hasScroll
? (data.length * 150.0)
? requiredWidth
: constraints.maxWidth;
final double scrollExtras = hasScroll ? 36.0 : 0.0;
final double chartHeight = isDesktop
? (hasScroll
? (constraints.maxHeight - 48).clamp(140.0, 220.0)
: constraints.maxHeight.clamp(160.0, 220.0))
? (constraints.maxHeight - scrollExtras).clamp(80.0, 220.0)
: 250;
final Widget chartWidget = SizedBox(
width: chartWidth,
@@ -4417,15 +4417,20 @@ class _ReportsScreenState extends ConsumerState<ReportsScreen> {
return Center(child: chartWidget);
}
final scrollableChart = SingleChildScrollView(
controller: _chartHorizontalScrollController,
scrollDirection: Axis.horizontal,
child: chartWidget,
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SingleChildScrollView(
controller: _chartHorizontalScrollController,
scrollDirection: Axis.horizontal,
child: chartWidget,
),
if (isDesktop)
Expanded(child: scrollableChart)
else
scrollableChart,
const SizedBox(height: 4),
_CustomHorizontalScrollBar(
controller: _chartHorizontalScrollController,
@@ -1053,6 +1053,18 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
}
});
ref.listen<StudentViewTab>(studentViewTabProvider, (previous, next) {
if (mounted && _viewTab != next) {
setState(() => _viewTab = next);
}
});
ref.listen<StudentQueryParams>(studentQueryProvider, (previous, next) {
if (next.search.isEmpty && _searchController.text.isNotEmpty) {
_searchController.clear();
}
});
ref.listen<AsyncValue<List<AcademicYearModel>>>(academicYearsListProvider, (prev, next) {
if (!mounted) return;
final years = next.asData?.value;