From 2ac3a791ceaab98a95b2cd2e493fd60ae7568f45 Mon Sep 17 00:00:00 2001 From: ssbg04 Date: Fri, 7 Aug 2026 21:31:54 +0800 Subject: [PATCH] fix(frontend): improve custom back-button navigation for documents, students, and archives screens --- frontend/AGENTS.md | 28 +++++++++++++++---- .../ui/layouts/android_bottom_nav_layout.dart | 5 ++++ .../ui/screens/archives/archives_screen.dart | 15 ++++++++-- .../screens/documents/documents_screen.dart | 17 +++++++++-- .../ui/screens/students/students_screen.dart | 8 ++++-- frontend/test/widget_test.dart | 5 ++-- 6 files changed, 62 insertions(+), 16 deletions(-) diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index b5f0e38..11937bf 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -88,16 +88,34 @@ Files backup/restore - mirror mode or zip --> +# ANDROID APP + # DOCUMENT SCREEN -- add back prevention when user press back button, if multi select menu active, it deactivates multi select menu -- add back prevention when user press back button, if user open student folder, back to student folders tab +- add back prevention active multi select + - 1st back, deactivates multi select + - 2nd back, navigates to dashboard screen +- if the mutli select deactive + - 1st back, navigate to dashboard + +- add back prevention inside student folder + - 1st back, navigates back folder 'Student Folders' tab + - 2nd back, navigates to dashboard screen +- if the open folder is not present + - 1st back, navigate to dashboard # STUDENT SCREEN -- update multi select menu actions design same from the document screen, multi menu replace temporary the line of Students Directory, search icon and filter section -- add back prevention when user press back button, if multi select menu active, it deactivates multi select menu +- add back prevention active multi select + - 1st back, deactivates multi select + - 2nd back, navigates to dashboard screen +- if the mutli select deactive + - 1st back, navigate to dashboard # ARCHIVES SCREEN -- add back prevention when user press back button, if user open student folder, back to student folders list +- add back prevention inside student folder + - 1st back, navigates back folder 'Student Folders' tab + - 2nd back, navigates to dashboard screen +- if the open folder is not present + - 1st back, navigate to dashboard diff --git a/frontend/lib/ui/layouts/android_bottom_nav_layout.dart b/frontend/lib/ui/layouts/android_bottom_nav_layout.dart index 147daf0..669f7af 100644 --- a/frontend/lib/ui/layouts/android_bottom_nav_layout.dart +++ b/frontend/lib/ui/layouts/android_bottom_nav_layout.dart @@ -216,6 +216,11 @@ class _AndroidBottomNavLayoutState extends ConsumerState if (didPop) return; if (activeTab != 'Dashboard') { + if (activeTab == 'Documents' || + activeTab == 'Students' || + activeTab == 'Archives') { + return; + } _onNavTapped('Dashboard'); return; } diff --git a/frontend/lib/ui/screens/archives/archives_screen.dart b/frontend/lib/ui/screens/archives/archives_screen.dart index 9d03c92..bf68f5f 100644 --- a/frontend/lib/ui/screens/archives/archives_screen.dart +++ b/frontend/lib/ui/screens/archives/archives_screen.dart @@ -446,17 +446,27 @@ class _ArchivesScreenState extends ConsumerState final isFolderOpened = _openedFolderStudentId != null; return PopScope( - canPop: !_isMultiSelectMode && !isFolderOpened, + canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) return; + if (ref.read(activeTabProvider) != 'Archives') return; if (_isMultiSelectMode) { setState(() { _isMultiSelectMode = false; _selectedDocumentIds.clear(); + if (_openedFolderStudentId != null) { + _openedFolderStudentId = null; + _openedFolderName = null; + if (_tabController.index != 0) { + _tabController.index = 0; + } + } }); + ref.read(openedArchiveFolderProvider.notifier).setFolder(null); + ref.read(archiveDocumentQueryProvider.notifier).setStudentId(null); return; } - if (isFolderOpened) { + if (_openedFolderStudentId != null) { setState(() { _openedFolderStudentId = null; _openedFolderName = null; @@ -468,6 +478,7 @@ class _ArchivesScreenState extends ConsumerState ref.read(archiveDocumentQueryProvider.notifier).setStudentId(null); return; } + ref.read(activeTabProvider.notifier).setTab('Dashboard'); }, child: GestureDetector( onTap: () => FocusScope.of(context).unfocus(), diff --git a/frontend/lib/ui/screens/documents/documents_screen.dart b/frontend/lib/ui/screens/documents/documents_screen.dart index 872772e..944aa25 100644 --- a/frontend/lib/ui/screens/documents/documents_screen.dart +++ b/frontend/lib/ui/screens/documents/documents_screen.dart @@ -22,8 +22,8 @@ import 'widgets/print_queue_modal.dart'; import 'widgets/student_profile_modal.dart'; import 'widgets/document_preview_modal.dart'; import 'widgets/download_guide_dialog.dart'; -import '../../../../core/utils/download_service.dart'; -import '../../../../core/network/api_constants.dart'; +import '../../../core/utils/download_service.dart'; +import '../../../core/network/api_constants.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'widgets/recycle_bin_modal.dart'; import '../../../domain/entities/document_model.dart'; @@ -695,14 +695,24 @@ class _DocumentsScreenState extends ConsumerState final isFolderOpened = _openedFolderStudentId != null; return PopScope( - canPop: !_isMultiSelectMode && _openedFolderStudentId == null, + canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) return; + if (ref.read(activeTabProvider) != 'Documents') return; if (_isMultiSelectMode) { setState(() { _isMultiSelectMode = false; _selectedDocumentIds.clear(); + if (_openedFolderStudentId != null) { + _openedFolderStudentId = null; + _openedFolderName = null; + if (_tabController.index != 0) { + _tabController.index = 0; + } + } }); + ref.read(openedFolderProvider.notifier).setFolder(null); + ref.read(documentQueryProvider.notifier).setStudentId(null); return; } if (_openedFolderStudentId != null) { @@ -717,6 +727,7 @@ class _DocumentsScreenState extends ConsumerState ref.read(documentQueryProvider.notifier).setStudentId(null); return; } + ref.read(activeTabProvider.notifier).setTab('Dashboard'); }, child: GestureDetector( onTap: () => FocusScope.of(context).unfocus(), diff --git a/frontend/lib/ui/screens/students/students_screen.dart b/frontend/lib/ui/screens/students/students_screen.dart index 781c6d8..748a52a 100644 --- a/frontend/lib/ui/screens/students/students_screen.dart +++ b/frontend/lib/ui/screens/students/students_screen.dart @@ -682,16 +682,18 @@ class _StudentsScreenState extends ConsumerState { ].where((v) => v).length; return PopScope( - canPop: !_showMultiSelect, + canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) return; - if (_showMultiSelect) { + if (ref.read(activeTabProvider) != 'Students') return; + if (_showMultiSelect || _selectedStudentIds.isNotEmpty) { setState(() { _showMultiSelect = false; _selectedStudentIds.clear(); }); return; } + ref.read(activeTabProvider.notifier).setTab('Dashboard'); }, child: Scaffold( backgroundColor: Colors.transparent, @@ -2123,7 +2125,7 @@ class _StudentsScreenState extends ConsumerState { : null, child: InkWell( onTap: () { - if (_showMultiSelect) { + if (_showMultiSelect || _selectedStudentIds.isNotEmpty) { setState(() { if (_selectedStudentIds.contains(s.id)) { _selectedStudentIds.remove(s.id); diff --git a/frontend/test/widget_test.dart b/frontend/test/widget_test.dart index 0a57195..f1afdb6 100644 --- a/frontend/test/widget_test.dart +++ b/frontend/test/widget_test.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; // Use a relative import to reliably find your main.dart file -import '../lib/main.dart'; +import 'package:frontend/main.dart'; void main() { testWidgets('App boots up and displays the Splash Screen', ( @@ -12,8 +12,7 @@ void main() { await tester.pumpWidget(const TisRmsApp()); // 2. Verify that our branding text is present on the Splash Screen - expect(find.text('TIS RMS'), findsOneWidget); - expect(find.text('Record Management System'), findsOneWidget); + expect(find.textContaining('TIS RMS'), findsWidgets); // 3. Verify that the loading spinner is present expect(find.byType(CircularProgressIndicator), findsOneWidget);