feat(reports,settings,docs): server-side PDF export, cutoff date picker, and UI refinements

- Reports: Migrate DepEd Transparency Board PDF export to backend Puppeteer service with enlarged layout and per-section downloads

- Settings: Replace dynamic grace period days input with cutoff date picker and calculated days preview; separate schedule date pickers

- Documents: Default requirements section to collapsed/closed state in upload OCR modal with pulsing border container

- Fix contrast on Auto-Graduation trigger button and ensure desktop responsiveness across report buttons
This commit is contained in:
ssbg04 committed 2026-09-26 18:32:44 +08:00
1 parent 2376e99217
commit 7faade70a9
28 files changed
+2500 -1800

No files matched your search

@@ -748,6 +748,25 @@ class DocumentRepository {
}
}
Future<Uint8List> mergePrintQueuePdf({List<int>? documentIds}) async {
try {
final options = await _getAuthOptions();
options.responseType = ResponseType.bytes;
final res = await _dio.post<List<int>>(
'/documents/print-queue/merge-pdf',
data: documentIds != null && documentIds.isNotEmpty
? {'documentIds': documentIds}
: null,
options: options,
);
return Uint8List.fromList(res.data!);
} on DioException catch (e) {
final msg =
e.response?.data?['message'] ?? 'Failed to merge print queue PDF.';
throw Exception(msg);
}
}
// Recycle Bin / Trash
Future<List<TrashDocumentModel>> getTrashDocuments() async {
try {
@@ -1,3 +1,4 @@
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../../core/network/api_constants.dart';
@@ -122,5 +123,43 @@ class ReportRepository {
);
}
}
Future<Uint8List> downloadTransparencyBoardPdf({
int? academicYearId,
List<int>? yearIds,
String? schoolName,
String? divisionName,
String? regionName,
String? category,
}) async {
try {
final queryParams = <String, dynamic>{};
if (academicYearId != null) queryParams['academicYearId'] = academicYearId;
if (yearIds != null && yearIds.isNotEmpty) {
queryParams['yearIds'] = yearIds.join(',');
}
if (schoolName != null) queryParams['schoolName'] = schoolName;
if (divisionName != null) queryParams['divisionName'] = divisionName;
if (regionName != null) queryParams['regionName'] = regionName;
if (category != null && category.isNotEmpty && category != 'all') {
queryParams['category'] = category;
}
final options = await _authOptions();
options.responseType = ResponseType.bytes;
final res = await _dio.get<List<int>>(
'/reports/transparency-board/pdf',
queryParameters: queryParams.isNotEmpty ? queryParams : null,
options: options,
);
return Uint8List.fromList(res.data!);
} on DioException catch (e) {
throw Exception(
e.response?.data?['message'] ??
'Failed to generate transparency board PDF.',
);
}
}
}
@@ -100,6 +100,21 @@ class SetupRepository {
}
}
Future<Map<String, dynamic>> checkAutoArchive() async {
try {
final options = await _getAuthOptions();
final res = await _dio.post(
'/setup/academic-years/check-auto-archive',
options: options,
);
return res.data as Map<String, dynamic>;
} on DioException catch (e) {
throw Exception(
e.response?.data?['message'] ?? 'Failed to check auto-archive.',
);
}
}
Future<void> deleteAcademicYear(int id) async {
try {
final options = await _getAuthOptions();