diff --git a/.gitignore b/.gitignore index 6eb12d8..039d049 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ TIS* backend/.env backend/.env.* !backend/.example.env +backend/brain +frontend/brain # Database (runtime data — never commit) backend/*.db diff --git a/backend/src/controllers/studentController.js b/backend/src/controllers/studentController.js index e5c6d9c..9005662 100644 --- a/backend/src/controllers/studentController.js +++ b/backend/src/controllers/studentController.js @@ -874,8 +874,14 @@ exports.scanEnrollmentFromSF = async (req, res) => { AND ( document_type LIKE '%SF10%' OR document_type LIKE '%SF9%' OR document_type LIKE '%Form 10%' OR document_type LIKE '%Form 9%' + OR document_type LIKE '%Form 137%' OR document_type LIKE '%Form 138%' + OR document_type LIKE '%School Form 10%' OR document_type LIKE '%School Form 9%' OR document_type LIKE '%Report Card%' OR document_type LIKE '%Permanent Record%' + OR document_type LIKE '%SF1%' OR document_type LIKE '%SF-1%' OR file_name LIKE '%sf10%' OR file_name LIKE '%sf9%' + OR file_name LIKE '%form 137%' OR file_name LIKE '%form 138%' + OR file_name LIKE '%form 10%' OR file_name LIKE '%form 9%' + OR file_name LIKE '%sf1%' OR file_name LIKE '%report card%' ) ORDER BY id DESC `).all(studentId); @@ -883,7 +889,7 @@ exports.scanEnrollmentFromSF = async (req, res) => { if (!docs || docs.length === 0) { return res.status(404).json({ success: false, - message: 'No SF10 or SF9 document found for this student. Please upload an SF10 or SF9 document first.' + message: 'No SF10 or SF9 document found for this student. Auto Enrollment supports only SF9 and SF10 documents.' }); } diff --git a/backend/src/services/sfEnrollmentService.js b/backend/src/services/sfEnrollmentService.js index a437fbe..e8d5417 100644 --- a/backend/src/services/sfEnrollmentService.js +++ b/backend/src/services/sfEnrollmentService.js @@ -37,16 +37,16 @@ exports.extractEnrollmentData = async ({ file, documentType = '', manual = false // 1. Determine if SF10 or SF9 const docTypeStr = String(documentType || ''); const fileNameStr = String(file?.originalname || file?.name || ''); - const isSF10 = /sf10|sf-10|sf 10|permanent record|form 10/i.test(docTypeStr) || - /sf10|sf-10|sf 10|permanent record|form 10/i.test(fileNameStr); - const isSF9 = /sf9|sf-9|sf 9|report card|form 9/i.test(docTypeStr) || - /sf9|sf-9|sf 9|report card|form 9/i.test(fileNameStr); + const isSF10 = /sf10|sf-10|sf 10|permanent record|form 10|school form 10|form 137|form-137|form 137-a|form 137a|student permanent record/i.test(docTypeStr) || + /sf10|sf-10|sf 10|permanent record|form 10|school form 10|form 137|form-137|form 137-a|form 137a|student permanent record/i.test(fileNameStr); + const isSF9 = /sf9|sf-9|sf 9|report card|form 9|school form 9|form 138|form-138|form 138|student report card|sf1 for jhs\/sf9 for shs|sf1\b|sf-1\b|sf 1\b/i.test(docTypeStr) || + /sf9|sf-9|sf 9|report card|form 9|school form 9|form 138|form-138|form 138|student report card|sf1 for jhs\/sf9 for shs|sf1\b|sf-1\b|sf 1\b/i.test(fileNameStr); if (!isSF10 && !isSF9 && !manual) { return { ok: false, skipped: true, - message: 'Document is not an SF10 or SF9.' + message: 'Auto Enrollment supports only SF9 and SF10 documents.' }; } diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index 7336de9..b17f38e 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -141,9 +141,23 @@ Files backup/restore - mirror mode or zip --> 2. Add new student screen: - fix drag and drop not working in windows app --> -Make a new plan for light and dark theme colors -- keep the light theme colors, its the schools trademark colors -- for dark theme + + + +# STUDENT SCREEN +- bring back background color chip for status in students table (windows) and card (android) + +# DOCUMENT SCREEN +- remove background color and border of the chips + - file status inside of student folder +- bring back the background color chip for requirements progress + +# ARCHIVES SCREEN +- bring back the background color chip for status in root student folders tab + --- diff --git a/frontend/lib/ui/screens/documents/documents_screen.dart b/frontend/lib/ui/screens/documents/documents_screen.dart index 709d0f7..f470d48 100644 --- a/frontend/lib/ui/screens/documents/documents_screen.dart +++ b/frontend/lib/ui/screens/documents/documents_screen.dart @@ -2580,7 +2580,6 @@ class _DocumentsScreenState extends ConsumerState } Color statusColor; - final isDark = Theme.of(context).brightness == Brightness.dark; switch (doc.status as String) { case 'Completed': statusColor = isDark ? Colors.lightGreenAccent.shade400 : AppColors.success; diff --git a/frontend/lib/ui/screens/documents/widgets/upload_ocr_modal.dart b/frontend/lib/ui/screens/documents/widgets/upload_ocr_modal.dart index 2910112..361d4ad 100644 --- a/frontend/lib/ui/screens/documents/widgets/upload_ocr_modal.dart +++ b/frontend/lib/ui/screens/documents/widgets/upload_ocr_modal.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; @@ -110,6 +111,7 @@ class _UploadOcrModalState extends ConsumerState { List<_UploadEntry> _entries = []; bool _isUploading = false; bool _isDragOver = false; + Timer? _dragResetTimer; @override void initState() { @@ -144,6 +146,7 @@ class _UploadOcrModalState extends ConsumerState { @override void dispose() { + _dragResetTimer?.cancel(); _lrnController.removeListener(_onLrnChanged); _lrnController.dispose(); super.dispose(); @@ -676,10 +679,22 @@ class _UploadOcrModalState extends ConsumerState { if (isWindows) { content = DropTarget( - onDragEntered: (_) => setState(() => _isDragOver = true), - onDragExited: (_) => setState(() => _isDragOver = false), + onDragEntered: (_) { + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = true); + _dragResetTimer = Timer(const Duration(seconds: 3), () { + if (mounted && _isDragOver) { + setState(() => _isDragOver = false); + } + }); + }, + onDragExited: (_) { + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = false); + }, onDragDone: (details) { - setState(() => _isDragOver = false); + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = false); _handleDroppedFiles(details.files); }, child: content, diff --git a/frontend/lib/ui/screens/students/widgets/add_student_modal.dart b/frontend/lib/ui/screens/students/widgets/add_student_modal.dart index 6519dc9..b7890e7 100644 --- a/frontend/lib/ui/screens/students/widgets/add_student_modal.dart +++ b/frontend/lib/ui/screens/students/widgets/add_student_modal.dart @@ -129,7 +129,17 @@ class _AddStudentModalState extends ConsumerState { lower.contains('sf-9') || lower.contains('sf 9') || lower.contains('report card') || - lower.contains('reportcard')) { + lower.contains('reportcard') || + lower.contains('student report card') || + lower.contains('form 138') || + lower.contains('form-138') || + lower.contains('form138') || + lower.contains('school form 9') || + lower.contains('school-form-9') || + lower.contains('sf1 for jhs') || + lower.contains('sf1') || + lower.contains('sf-1') || + lower.contains('sf 1')) { return 'SF9'; } if (lower.contains('sf10') || @@ -137,8 +147,16 @@ class _AddStudentModalState extends ConsumerState { lower.contains('sf 10') || lower.contains('permanent record') || lower.contains('permanentrecord') || + lower.contains('student permanent record') || lower.contains('school form 10') || - lower.contains('school-form-10')) { + lower.contains('school-form-10') || + lower.contains('form 137') || + lower.contains('form-137') || + lower.contains('form137') || + lower.contains('form 137-a') || + lower.contains('form 137a') || + lower.contains('form 10') || + lower.contains('form-10')) { return 'SF10'; } return null; diff --git a/frontend/lib/ui/screens/students/widgets/bulk_ocr_import_dialog.dart b/frontend/lib/ui/screens/students/widgets/bulk_ocr_import_dialog.dart index 78f9d4c..bd3c887 100644 --- a/frontend/lib/ui/screens/students/widgets/bulk_ocr_import_dialog.dart +++ b/frontend/lib/ui/screens/students/widgets/bulk_ocr_import_dialog.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -99,7 +100,14 @@ class _BulkOcrImportDialogState extends ConsumerState { final List<_OcrItem> _items = []; bool _isDragOver = false; + Timer? _dragResetTimer; bool _isProcessing = false; + + @override + void dispose() { + _dragResetTimer?.cancel(); + super.dispose(); + } int _processingIndex = -1; @override @@ -165,15 +173,34 @@ class _BulkOcrImportDialogState extends ConsumerState { l.contains('sf-9') || l.contains('sf 9') || l.contains('report card') || - l.contains('reportcard')) { + l.contains('reportcard') || + l.contains('student report card') || + l.contains('form 138') || + l.contains('form-138') || + l.contains('form138') || + l.contains('school form 9') || + l.contains('school-form-9') || + l.contains('sf1 for jhs') || + l.contains('sf1') || + l.contains('sf-1') || + l.contains('sf 1')) { return 'SF9'; } if (l.contains('sf10') || l.contains('sf-10') || l.contains('sf 10') || l.contains('permanent record') || + l.contains('permanentrecord') || + l.contains('student permanent record') || l.contains('school form 10') || - l.contains('school-form-10')) { + l.contains('school-form-10') || + l.contains('form 137') || + l.contains('form-137') || + l.contains('form137') || + l.contains('form 137-a') || + l.contains('form 137a') || + l.contains('form 10') || + l.contains('form-10')) { return 'SF10'; } return null; @@ -552,10 +579,22 @@ class _BulkOcrImportDialogState extends ConsumerState { if (isWindows) { dropZone = DropTarget( - onDragEntered: (_) => setState(() => _isDragOver = true), - onDragExited: (_) => setState(() => _isDragOver = false), + onDragEntered: (_) { + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = true); + _dragResetTimer = Timer(const Duration(seconds: 3), () { + if (mounted && _isDragOver) { + setState(() => _isDragOver = false); + } + }); + }, + onDragExited: (_) { + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = false); + }, onDragDone: (detail) { - setState(() => _isDragOver = false); + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = false); if (detail.files.isNotEmpty) { final valid = detail.files .where((f) => _allowedExtensions diff --git a/frontend/lib/ui/shared/inputs/document_source_picker.dart b/frontend/lib/ui/shared/inputs/document_source_picker.dart index 30190d1..d92831f 100644 --- a/frontend/lib/ui/shared/inputs/document_source_picker.dart +++ b/frontend/lib/ui/shared/inputs/document_source_picker.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -28,6 +29,13 @@ class DocumentSourcePicker extends StatefulWidget { class _DocumentSourcePickerState extends State { final ImagePicker _imagePicker = ImagePicker(); bool _isDragOver = false; + Timer? _dragResetTimer; + + @override + void dispose() { + _dragResetTimer?.cancel(); + super.dispose(); + } Future _pickFile() async { try { @@ -213,10 +221,22 @@ class _DocumentSourcePickerState extends State { if (isWindows) { content = DropTarget( - onDragEntered: (details) => setState(() => _isDragOver = true), - onDragExited: (details) => setState(() => _isDragOver = false), + onDragEntered: (details) { + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = true); + _dragResetTimer = Timer(const Duration(seconds: 3), () { + if (mounted && _isDragOver) { + setState(() => _isDragOver = false); + } + }); + }, + onDragExited: (details) { + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = false); + }, onDragDone: (details) { - setState(() => _isDragOver = false); + _dragResetTimer?.cancel(); + if (mounted) setState(() => _isDragOver = false); if (details.files.isNotEmpty) { final xfile = details.files.first; final ext = xfile.path.split('.').last.toLowerCase(); diff --git a/frontend/test.dart b/frontend/test.dart deleted file mode 100644 index d2ec049..0000000 Binary files a/frontend/test.dart and /dev/null differ diff --git a/frontend_installer.iss b/frontend_installer.iss deleted file mode 100644 index 5eca737..0000000 --- a/frontend_installer.iss +++ /dev/null @@ -1,25 +0,0 @@ -[Setup] -AppName=TIS RMS Client -AppVersion=1.0 -DefaultDirName={pf}\TIS RMS Client -DefaultGroupName=TIS RMS Client -OutputDir=f:\SumbrerongBato\tis_rms_server\installers -OutputBaseFilename=TIS_RMS_Client_Setup -Compression=lzma -SolidCompression=yes -ArchitecturesInstallIn64BitMode=x64 - -[Tasks] -Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"; Flags: unchecked - -[Files] -Source: "f:\SumbrerongBato\tis_rms_server\frontend\build\windows\x64\runner\Release\frontend.exe"; DestDir: "{app}"; Flags: ignoreversion -Source: "f:\SumbrerongBato\tis_rms_server\frontend\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs - -[Icons] -Name: "{group}\TIS RMS Client"; Filename: "{app}\frontend.exe" -Name: "{group}\Uninstall TIS RMS Client"; Filename: "{uninstallexe}" -Name: "{commondesktop}\TIS RMS Client"; Filename: "{app}\frontend.exe"; Tasks: desktopicon - -[Run] -Filename: "{app}\frontend.exe"; Description: "Launch TIS RMS Client"; Flags: nowait postinstall skipifsilent