From 15d883f0e313897e42c7d97852582a7c6809d804 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Mon, 24 Nov 2025 17:43:11 +0100 Subject: [PATCH 01/52] server: missing pupil identity field, generated models, matrix notifications --- .../models/pupil_identity/pupil_identity.dart | 18 +++++++++++++++++ .../pupil_identity/pupil_identity.spy.yaml | 1 + .../models/pupil_identity/pupil_identity.dart | 20 +++++++++++++++++++ .../matrix_notifications.dart | 1 + 4 files changed, 40 insertions(+) diff --git a/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity.dart b/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity.dart index 5525a019..59654434 100644 --- a/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity.dart +++ b/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity.dart @@ -31,6 +31,7 @@ abstract class PupilIdentity implements _i1.SerializableModel { required this.afterSchoolCare, this.religion, this.religionLessonsSince, + this.religionLessonsCancelledAt, this.familyLanguageLessonsSince, this.leavingDate, }); @@ -52,6 +53,7 @@ abstract class PupilIdentity implements _i1.SerializableModel { required bool afterSchoolCare, String? religion, DateTime? religionLessonsSince, + DateTime? religionLessonsCancelledAt, DateTime? familyLanguageLessonsSince, DateTime? leavingDate, }) = _PupilIdentityImpl; @@ -83,6 +85,11 @@ abstract class PupilIdentity implements _i1.SerializableModel { ? null : _i1.DateTimeJsonExtension.fromJson( jsonSerialization['religionLessonsSince']), + religionLessonsCancelledAt: + jsonSerialization['religionLessonsCancelledAt'] == null + ? null + : _i1.DateTimeJsonExtension.fromJson( + jsonSerialization['religionLessonsCancelledAt']), familyLanguageLessonsSince: jsonSerialization['familyLanguageLessonsSince'] == null ? null @@ -127,6 +134,8 @@ abstract class PupilIdentity implements _i1.SerializableModel { DateTime? religionLessonsSince; + DateTime? religionLessonsCancelledAt; + DateTime? familyLanguageLessonsSince; DateTime? leavingDate; @@ -151,6 +160,7 @@ abstract class PupilIdentity implements _i1.SerializableModel { bool? afterSchoolCare, String? religion, DateTime? religionLessonsSince, + DateTime? religionLessonsCancelledAt, DateTime? familyLanguageLessonsSince, DateTime? leavingDate, }); @@ -175,6 +185,8 @@ abstract class PupilIdentity implements _i1.SerializableModel { if (religion != null) 'religion': religion, if (religionLessonsSince != null) 'religionLessonsSince': religionLessonsSince?.toJson(), + if (religionLessonsCancelledAt != null) + 'religionLessonsCancelledAt': religionLessonsCancelledAt?.toJson(), if (familyLanguageLessonsSince != null) 'familyLanguageLessonsSince': familyLanguageLessonsSince?.toJson(), if (leavingDate != null) 'leavingDate': leavingDate?.toJson(), @@ -207,6 +219,7 @@ class _PupilIdentityImpl extends PupilIdentity { required bool afterSchoolCare, String? religion, DateTime? religionLessonsSince, + DateTime? religionLessonsCancelledAt, DateTime? familyLanguageLessonsSince, DateTime? leavingDate, }) : super._( @@ -226,6 +239,7 @@ class _PupilIdentityImpl extends PupilIdentity { afterSchoolCare: afterSchoolCare, religion: religion, religionLessonsSince: religionLessonsSince, + religionLessonsCancelledAt: religionLessonsCancelledAt, familyLanguageLessonsSince: familyLanguageLessonsSince, leavingDate: leavingDate, ); @@ -251,6 +265,7 @@ class _PupilIdentityImpl extends PupilIdentity { bool? afterSchoolCare, Object? religion = _Undefined, Object? religionLessonsSince = _Undefined, + Object? religionLessonsCancelledAt = _Undefined, Object? familyLanguageLessonsSince = _Undefined, Object? leavingDate = _Undefined, }) { @@ -275,6 +290,9 @@ class _PupilIdentityImpl extends PupilIdentity { religionLessonsSince: religionLessonsSince is DateTime? ? religionLessonsSince : this.religionLessonsSince, + religionLessonsCancelledAt: religionLessonsCancelledAt is DateTime? + ? religionLessonsCancelledAt + : this.religionLessonsCancelledAt, familyLanguageLessonsSince: familyLanguageLessonsSince is DateTime? ? familyLanguageLessonsSince : this.familyLanguageLessonsSince, diff --git a/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity.spy.yaml b/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity.spy.yaml index 4b429057..4ec557c1 100644 --- a/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity.spy.yaml +++ b/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity.spy.yaml @@ -16,5 +16,6 @@ fields: afterSchoolCare: bool religion: String? religionLessonsSince: DateTime? + religionLessonsCancelledAt: DateTime? familyLanguageLessonsSince: DateTime? leavingDate: DateTime? diff --git a/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity.dart b/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity.dart index bc3ef5a3..3885264c 100644 --- a/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity.dart +++ b/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity.dart @@ -32,6 +32,7 @@ abstract class PupilIdentity required this.afterSchoolCare, this.religion, this.religionLessonsSince, + this.religionLessonsCancelledAt, this.familyLanguageLessonsSince, this.leavingDate, }); @@ -53,6 +54,7 @@ abstract class PupilIdentity required bool afterSchoolCare, String? religion, DateTime? religionLessonsSince, + DateTime? religionLessonsCancelledAt, DateTime? familyLanguageLessonsSince, DateTime? leavingDate, }) = _PupilIdentityImpl; @@ -84,6 +86,11 @@ abstract class PupilIdentity ? null : _i1.DateTimeJsonExtension.fromJson( jsonSerialization['religionLessonsSince']), + religionLessonsCancelledAt: + jsonSerialization['religionLessonsCancelledAt'] == null + ? null + : _i1.DateTimeJsonExtension.fromJson( + jsonSerialization['religionLessonsCancelledAt']), familyLanguageLessonsSince: jsonSerialization['familyLanguageLessonsSince'] == null ? null @@ -128,6 +135,8 @@ abstract class PupilIdentity DateTime? religionLessonsSince; + DateTime? religionLessonsCancelledAt; + DateTime? familyLanguageLessonsSince; DateTime? leavingDate; @@ -152,6 +161,7 @@ abstract class PupilIdentity bool? afterSchoolCare, String? religion, DateTime? religionLessonsSince, + DateTime? religionLessonsCancelledAt, DateTime? familyLanguageLessonsSince, DateTime? leavingDate, }); @@ -176,6 +186,8 @@ abstract class PupilIdentity if (religion != null) 'religion': religion, if (religionLessonsSince != null) 'religionLessonsSince': religionLessonsSince?.toJson(), + if (religionLessonsCancelledAt != null) + 'religionLessonsCancelledAt': religionLessonsCancelledAt?.toJson(), if (familyLanguageLessonsSince != null) 'familyLanguageLessonsSince': familyLanguageLessonsSince?.toJson(), if (leavingDate != null) 'leavingDate': leavingDate?.toJson(), @@ -203,6 +215,8 @@ abstract class PupilIdentity if (religion != null) 'religion': religion, if (religionLessonsSince != null) 'religionLessonsSince': religionLessonsSince?.toJson(), + if (religionLessonsCancelledAt != null) + 'religionLessonsCancelledAt': religionLessonsCancelledAt?.toJson(), if (familyLanguageLessonsSince != null) 'familyLanguageLessonsSince': familyLanguageLessonsSince?.toJson(), if (leavingDate != null) 'leavingDate': leavingDate?.toJson(), @@ -235,6 +249,7 @@ class _PupilIdentityImpl extends PupilIdentity { required bool afterSchoolCare, String? religion, DateTime? religionLessonsSince, + DateTime? religionLessonsCancelledAt, DateTime? familyLanguageLessonsSince, DateTime? leavingDate, }) : super._( @@ -254,6 +269,7 @@ class _PupilIdentityImpl extends PupilIdentity { afterSchoolCare: afterSchoolCare, religion: religion, religionLessonsSince: religionLessonsSince, + religionLessonsCancelledAt: religionLessonsCancelledAt, familyLanguageLessonsSince: familyLanguageLessonsSince, leavingDate: leavingDate, ); @@ -279,6 +295,7 @@ class _PupilIdentityImpl extends PupilIdentity { bool? afterSchoolCare, Object? religion = _Undefined, Object? religionLessonsSince = _Undefined, + Object? religionLessonsCancelledAt = _Undefined, Object? familyLanguageLessonsSince = _Undefined, Object? leavingDate = _Undefined, }) { @@ -303,6 +320,9 @@ class _PupilIdentityImpl extends PupilIdentity { religionLessonsSince: religionLessonsSince is DateTime? ? religionLessonsSince : this.religionLessonsSince, + religionLessonsCancelledAt: religionLessonsCancelledAt is DateTime? + ? religionLessonsCancelledAt + : this.religionLessonsCancelledAt, familyLanguageLessonsSince: familyLanguageLessonsSince is DateTime? ? familyLanguageLessonsSince : this.familyLanguageLessonsSince, diff --git a/school_data_hub_server/lib/src/utils/matrix_notifications/matrix_notifications.dart b/school_data_hub_server/lib/src/utils/matrix_notifications/matrix_notifications.dart index df68508a..cef4fce6 100644 --- a/school_data_hub_server/lib/src/utils/matrix_notifications/matrix_notifications.dart +++ b/school_data_hub_server/lib/src/utils/matrix_notifications/matrix_notifications.dart @@ -17,6 +17,7 @@ class MatrixNotifications { MatrixNotifications._internal(); +// TODO: This is hardcoded, put it in the secrets final _currentUserId = '@schuldaten-hub:hermannschule.de'; final MatrixClient _matrixClient = MatrixClient(); final _log = Logger('MatrixNotifications'); From 88329dd165fed3d062a89616ee6beb5c6fc148e6 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Mon, 24 Nov 2025 17:43:50 +0100 Subject: [PATCH 02/52] app: fix datetime extensions (?) WIP --- .../lib/app_utils/extensions/datetime_extensions.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart b/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart index e2702386..186cf13e 100644 --- a/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart +++ b/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart @@ -23,7 +23,7 @@ extension DateHubExtension on DateTime { } String formatDateForUser() { - final date = this.isUtc ? this.toLocal() : this; + final date = this.toLocal().add(const Duration(hours: 2)); final DateFormat dateFormat = DateFormat("dd.MM.yyyy"); return dateFormat.format(date).toString(); } From ee17764ef0d365ce1bd126271a25de19a7edbe10 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 26 Nov 2025 20:41:38 +0100 Subject: [PATCH 03/52] app, server: schoolday event notifications WIP --- .../data/schoolday_event_api_service.dart | 2 +- .../endpoints/schoolday_event_endpoint.dart | 93 +++-------- .../schoolday_event_notification_helper.dart | 144 ++++++++++++++++++ .../schoolday_event_notification_text.dart | 95 ------------ 4 files changed, 164 insertions(+), 170 deletions(-) create mode 100644 school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart b/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart index cceae58d..7d9620d7 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart @@ -33,7 +33,7 @@ class SchooldayEventApiService { try { final event = await _client.schooldayEvent.createSchooldayEvent( pupilNameAndGroup: pupilName, - dateTimeAsString: dateTime.formatDateAndTimeForUser(), + dateTimeAsString: dateTime.formatDateForUser(), pupilId: pupilId, schooldayId: schooldayId, type: type, diff --git a/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart b/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart index 8f4fbb49..35c55fb8 100644 --- a/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart +++ b/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart @@ -1,9 +1,8 @@ import 'dart:async'; import 'package:logging/logging.dart'; +import 'package:school_data_hub_server/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart'; import 'package:school_data_hub_server/src/generated/protocol.dart'; -import 'package:school_data_hub_server/src/utils/matrix_notifications/matrix_notifications.dart'; -import 'package:school_data_hub_server/src/utils/matrix_notifications/schoolday_event_notification_text.dart'; import 'package:serverpod/serverpod.dart'; final _log = Logger('SchooldayEventEndpoint'); @@ -52,56 +51,16 @@ class SchooldayEventEndpoint extends Endpoint { processedDocument: HubDocument.include(), ), ); - - try { - final pupil = await PupilData.db.findFirstRow(session, - where: (t) => t.id.equals(eventWithSchoolday!.pupilId), - include: - PupilData.include(schooldayEvents: SchooldayEvent.includeList())); - final numberOfEventsOfTheSameType = pupil?.schooldayEvents - ?.where((event) => event.eventType == type) - .length ?? - 0; - final recipients = - await MatrixNotifications.instance.findNotificationRecipients( + unawaited( + SchooldayEventNotificationHelper.sendNotification( session: session, pupilNameAndGroup: pupilNameAndGroup, tutor: tutor, - ); - - // Send notification to all recipients - if (recipients.isEmpty) { - // Fallback to default recipient if no matches found - _log.warning('No recipients found for schoolday event $eventId'); - return eventWithSchoolday!; - } - - unawaited(MatrixNotifications.instance.sendDirectTextMessage( - session: session, - recipients: recipients.toList(), - text: getSchooldayEventNotificationText( - eventcreator: createdBy, - pupilName: pupilNameAndGroup, - dateTimeAsString: dateTimeAsString, - schooldayEvent: eventWithSchoolday!, - numberOfEvents: numberOfEventsOfTheSameType), - html: getSchooldayEventNotificationHtml( - eventcreator: createdBy, - pupilName: pupilNameAndGroup, - dateTimeAsString: dateTimeAsString, - schooldayEvent: eventWithSchoolday, - numberOfEvents: numberOfEventsOfTheSameType), - )); - // final success = await MailerService.instance.sendNotification( - // recipient: recipient?.userInfo?.email ?? '', - // subject: 'Neues Schulereignis', - // message: 'Es wurde ein neues Schulereignis erstellt.\n\n' - // 'Es ist das Schulereignis Nummer ${pupil?.schooldayEvents?.length}'); - } catch (e) { - _log.severe('Error sending matrix notification: $e'); - } - - return eventWithSchoolday!; + eventWithSchoolday: eventWithSchoolday!, + dateTimeAsString: dateTimeAsString, + ), + ); + return eventWithSchoolday; } Future updateSchooldayEvent( @@ -113,31 +72,6 @@ class SchooldayEventEndpoint extends Endpoint { String modifiedBy, String dateTimeAsString, ) async { - if (changedProcessedStatus) { - final recipients = - await MatrixNotifications.instance.findNotificationRecipients( - session: session, - pupilNameAndGroup: pupilNameAndGroup, - tutor: tutor, - ); - unawaited(MatrixNotifications.instance.sendDirectTextMessage( - session: session, - recipients: recipients.toList(), - text: getSchooldayEventNotificationText( - eventcreator: modifiedBy, - pupilName: pupilNameAndGroup, - dateTimeAsString: dateTimeAsString, - schooldayEvent: schooldayEvent, - processedStatusChange: changedProcessedStatus, - ), - html: getSchooldayEventNotificationHtml( - eventcreator: modifiedBy, - pupilName: pupilNameAndGroup, - dateTimeAsString: dateTimeAsString, - schooldayEvent: schooldayEvent, - processedStatusChange: changedProcessedStatus), - )); - } // If processed is false We need to detach and delete the processed document if it exists if (changedProcessedStatus && schooldayEvent.processed == false) { if (schooldayEvent.processedDocumentId != null) { @@ -167,6 +101,17 @@ class SchooldayEventEndpoint extends Endpoint { document: HubDocument.include(), processedDocument: HubDocument.include(), )); + + if (changedProcessedStatus) { + unawaited(SchooldayEventNotificationHelper.sendNotification( + session: session, + pupilNameAndGroup: pupilNameAndGroup, + tutor: tutor, + eventWithSchoolday: updatedSchooldayEvent!, + dateTimeAsString: dateTimeAsString, + changedProcessedStatus: changedProcessedStatus, + )); + } return updatedSchooldayEvent!; } diff --git a/school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart b/school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart new file mode 100644 index 00000000..1d7c5f0e --- /dev/null +++ b/school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart @@ -0,0 +1,144 @@ +import 'dart:async'; + +import 'package:logging/logging.dart'; +import 'package:school_data_hub_server/src/generated/protocol.dart'; +import 'package:school_data_hub_server/src/utils/matrix_notifications/matrix_notifications.dart'; +import 'package:serverpod/serverpod.dart'; + +final Logger _log = Logger('SchooldayEventNotificationHelper'); + +class SchooldayEventNotificationHelper { + /// Returns plain text notification message + + static Future sendNotification({ + required Session session, + required String pupilNameAndGroup, + required String tutor, + required SchooldayEvent eventWithSchoolday, + required String dateTimeAsString, + bool? changedProcessedStatus, + }) async { + try { + final pupil = await PupilData.db.findFirstRow(session, + where: (t) => t.id.equals(eventWithSchoolday.pupilId), + include: + PupilData.include(schooldayEvents: SchooldayEvent.includeList())); + final numberOfEventsOfTheSameType = pupil?.schooldayEvents + ?.where( + (event) => event.eventType == eventWithSchoolday.eventType) + .length ?? + 0; + final recipients = + await MatrixNotifications.instance.findNotificationRecipients( + session: session, + pupilNameAndGroup: pupilNameAndGroup, + tutor: tutor, + ); + + // Send notification to all recipients + if (recipients.isEmpty) { + // Fallback to default recipient if no matches found + _log.warning( + 'No recipients found for schoolday event ${eventWithSchoolday.id}'); + return; + } + + unawaited(MatrixNotifications.instance.sendDirectTextMessage( + session: session, + recipients: recipients.toList(), + text: _getSchooldayEventNotificationText( + eventcreator: eventWithSchoolday.createdBy, + pupilName: pupilNameAndGroup, + dateTimeAsString: dateTimeAsString, + schooldayEvent: eventWithSchoolday, + numberOfEvents: numberOfEventsOfTheSameType), + html: _getSchooldayEventNotificationHtml( + eventcreator: eventWithSchoolday.createdBy, + pupilName: pupilNameAndGroup, + dateTimeAsString: dateTimeAsString, + schooldayEvent: eventWithSchoolday, + numberOfEvents: numberOfEventsOfTheSameType), + )); + } catch (e) { + _log.severe('Error sending matrix notification: $e'); + } + } +} + +String _getEventTypeText(SchooldayEventType type) => switch (type) { + SchooldayEventType.admonition => 'Rote Karte 🚫', + SchooldayEventType.admonitionAndBanned => 'Rote Karte und Abholen 🚫🏠️', + SchooldayEventType.afternoonCareAdmonition => 'Rote Karte OGS ⚠️🍽️', + SchooldayEventType.parentsMeeting => 'Elterngespräch 👪💬', + SchooldayEventType.otherEvent => 'Sonstiges 🗒️', + // TODO: Handle this case. + SchooldayEventType.notSet => '❓️', + }; + +String _getEventReasonText(String reason) => reason + .replaceAll('gm', '🤜🤕') + .replaceAll('gl', '🤜🎓️') + .replaceAll('gs', '🤜🏫') + .replaceAll('ab', '🤬💔') + .replaceAll('gv', '🚨😱') + .replaceAll('äa', '😈😖') + .replaceAll('il', '🎓️🙉') + .replaceAll('us', '🛑🎓️') + .replaceAll('ss', '📝') + .replaceAll('le', '💡🧠') + .replaceAll('fi', '🛟🧠') + .replaceAll('ki', '⚠️ℹ️'); + +String _getSchooldayEventNotificationText( + {required String eventcreator, + required String pupilName, + required String dateTimeAsString, + required SchooldayEvent schooldayEvent, + bool? processedStatusChange, + int? numberOfEvents}) { + final eventType = _getEventTypeText(schooldayEvent.eventType); + final eventReason = _getEventReasonText(schooldayEvent.eventReason); + + return ''' +$eventType +für $pupilName +$eventReason + +von $eventcreator am $dateTimeAsString + +${numberOfEvents != null ? 'Das ist die $numberOfEvents. Schulereignis für $pupilName.' : ''} +'''; +} + +/// Returns HTML formatted notification message for Matrix +String _getSchooldayEventNotificationHtml({ + required String eventcreator, + required String pupilName, + required String dateTimeAsString, + required SchooldayEvent schooldayEvent, + int? numberOfEvents, + bool? processedStatusChange, +}) { + final eventType = _getEventTypeText(schooldayEvent.eventType); + final eventReason = _getEventReasonText(schooldayEvent.eventReason); + + // Escape HTML entities + String escapeHtml(String text) { + return text + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", '''); + } + + return ''' +

${escapeHtml(eventType)}

für

+

${escapeHtml(pupilName)}

+

Grund:

+

${escapeHtml(eventReason).replaceAll('*', '
')}

+ ${processedStatusChange != null ? schooldayEvent.processed == true ? '

Status: Bearbeitet von ${escapeHtml(eventcreator)} am ${escapeHtml(dateTimeAsString)}' : '

Status: Nicht bearbeitet' : '

Eingetragen von ${escapeHtml(eventcreator)} am ${escapeHtml(dateTimeAsString)}

'} + +${numberOfEvents != null ? '

Das ist das $numberOfEvents. Schulereignis dieser Art für ${escapeHtml(pupilName)}.

' : ''} +'''; +} diff --git a/school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart b/school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart index 3fe6769e..baf431d4 100644 --- a/school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart +++ b/school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart @@ -1,96 +1 @@ import 'package:school_data_hub_server/src/generated/protocol.dart'; - -/// Returns plain text notification message -String getSchooldayEventNotificationText( - {required String eventcreator, - required String pupilName, - required String dateTimeAsString, - required SchooldayEvent schooldayEvent, - bool? processedStatusChange, - int? numberOfEvents}) { - final String eventType = switch (schooldayEvent.eventType) { - SchooldayEventType.admonition => 'Rote Karte 🚫', - SchooldayEventType.admonitionAndBanned => 'Rote Karte und Abholen 🚫🏠️', - SchooldayEventType.afternoonCareAdmonition => 'Rote Karte OGS ⚠️🍽️', - SchooldayEventType.parentsMeeting => 'Elterngespräch 👪💬', - SchooldayEventType.otherEvent => 'Sonstiges 🗒️', - // TODO: Handle this case. - SchooldayEventType.notSet => '❓️', - }; - final String eventReason = schooldayEvent.eventReason - .replaceFirst('gm', '🤜🤕') - .replaceFirst('gl', '🤜🎓️') - .replaceFirst('gs', '🤜🏫') - .replaceFirst('ab', '🤬💔') - .replaceFirst('gv', '🚨😱') - .replaceFirst('äa', '😈😖') - .replaceFirst('il', '🎓️🙉') - .replaceFirst('us', '🛑🎓️') - .replaceFirst('ss', '📝') - .replaceFirst('le', '💡🧠') - .replaceFirst('fi', '🛟🧠') - .replaceFirst('ki', '⚠️ℹ️'); - - return ''' -$eventType -für $pupilName -$eventReason - -von $eventcreator am $dateTimeAsString - -${numberOfEvents != null ? 'Das ist die $numberOfEvents. Schulereignis für $pupilName.' : ''} -'''; -} - -/// Returns HTML formatted notification message for Matrix -String getSchooldayEventNotificationHtml({ - required String eventcreator, - required String pupilName, - required String dateTimeAsString, - required SchooldayEvent schooldayEvent, - int? numberOfEvents, - bool? processedStatusChange, -}) { - final String eventType = switch (schooldayEvent.eventType) { - SchooldayEventType.admonition => 'Rote Karte 🚫', - SchooldayEventType.admonitionAndBanned => 'Rote Karte und Abholen 🚫🏠️', - SchooldayEventType.afternoonCareAdmonition => 'Rote Karte OGS ⚠️🍽️', - SchooldayEventType.parentsMeeting => 'Elterngespräch 👪💬', - SchooldayEventType.otherEvent => 'Sonstiges 🗒️', - SchooldayEventType.notSet => '❓️', - }; - - final String eventReason = schooldayEvent.eventReason - .replaceFirst('gm', '🤜🤕 Gewalt gegen Kinder') - .replaceFirst('gl', '🤜🎓️ Gewalt gegen Erwachsene') - .replaceFirst('gs', '🤜🏫 Gewalt gegen Sachen') - .replaceFirst('ab', '🤬💔 Beleidigen') - .replaceFirst('gv', '🚨😱 Gefahr für sich/andere') - .replaceFirst('äa', '😈😖 Ärgern') - .replaceFirst('il', '🎓️🙉 Anweisungen ignorieren') - .replaceFirst('us', '🛑🎓️ Unterricht stören') - .replaceFirst('ss', '📝 Sonstiges') - .replaceFirst('le', '💡🧠 Lernentwicklung') - .replaceFirst('fi', '🛟🧠 Förderung') - .replaceFirst('ki', '⚠️ℹ️ Regelverstoß'); - - // Escape HTML entities - String escapeHtml(String text) { - return text - .replaceAll('&', '&') - .replaceAll('<', '<') - .replaceAll('>', '>') - .replaceAll('"', '"') - .replaceAll("'", '''); - } - - return ''' -

${escapeHtml(eventType)}

für

-

${escapeHtml(pupilName)}

-

Grund:

-

${escapeHtml(eventReason).replaceAll('*', '
')}

- ${processedStatusChange != null ? schooldayEvent.processed == true ? '

Status: Bearbeitet von ${escapeHtml(eventcreator)} am ${escapeHtml(dateTimeAsString)}' : '

Status: Nicht bearbeitet' : '

Eingetragen von ${escapeHtml(eventcreator)} am ${escapeHtml(dateTimeAsString)}

'} - -${numberOfEvents != null ? '

Das ist das $numberOfEvents. Schulereignis dieser Art für ${escapeHtml(pupilName)}.

' : ''} -'''; -} From e2b3f2e9bb64563373b75f340093afcf25971a7e Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 26 Nov 2025 20:42:30 +0100 Subject: [PATCH 04/52] app: pupil isentities stream - add option to accept requests automatically --- .../controllers/stream_controller.dart | 34 ++- .../models/stream_state.dart | 5 +- .../pupil_identity_stream_page.dart | 234 ++++++++++-------- 3 files changed, 156 insertions(+), 117 deletions(-) diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart index 4f93e2c3..125546cf 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart @@ -63,6 +63,7 @@ class PupilIdentityStreamController { if (role == PupilIdentityStreamRole.sender && state.streamState.receiverUserName.value.isNotEmpty) { state.transferState.transferCounter.value += 1; + // TODO: here we must use a datetime sent by the sender final now = DateTime.now(); final dateStr = '${now.day.toString().padLeft(2, '0')}.${now.month.toString().padLeft(2, '0')}.${now.year} ${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}'; @@ -81,8 +82,8 @@ class PupilIdentityStreamController { state.streamState.statusMessage.value = role == PupilIdentityStreamRole.sender - ? 'Datenübertragung abgeschlossen!' - : 'Schülerdaten wurden erfolgreich empfangen!'; + ? 'Datenübertragung abgeschlossen!' + : 'Schülerdaten wurden erfolgreich empfangen!'; // Only cancel subscription for receiver, sender should stay connected if (role == PupilIdentityStreamRole.receiver) { @@ -198,8 +199,8 @@ class PupilIdentityStreamController { state.streamState.isProcessing.value = true; state.streamState.statusMessage.value = role == PupilIdentityStreamRole.sender - ? 'Warte auf Verbindung des Empfängers...' - : 'Verbindung zum Sender herstellen...'; + ? 'Warte auf Verbindung des Empfängers...' + : 'Verbindung zum Sender herstellen...'; try { // If we're a sender, we need to generate encrypted data if not provided @@ -227,11 +228,10 @@ class PupilIdentityStreamController { onReceiverLeft: (userName) => _handleReceiverLeft(userName), onRequestReceived: (userName) => _handleRequestReceived(userName), onRequestConfirmed: () => _handleRequestConfirmed(), - onRequestRejected: - (wasAutoRejected) => _handleRequestRejected(wasAutoRejected), - onDataReceived: - (newCount, totalCount) => - _handleDataReceived(newCount, totalCount), + onRequestRejected: (wasAutoRejected) => + _handleRequestRejected(wasAutoRejected), + onDataReceived: (newCount, totalCount) => + _handleDataReceived(newCount, totalCount), onShouldPopPage: () => _handleShouldPopPage(), onSenderShutdown: (message) => _handleSenderShutdown(message), ); @@ -439,7 +439,16 @@ class PupilIdentityStreamController { state.receiverState.pendingRequests.value = updatedPendingRequests; state.streamState.requestReceived.value = true; - onConfirmationRequired.call(userName); + + // Auto-confirm if enabled + if (state.streamState.autoConfirmEnabled.value) { + _log.info( + 'Auto-confirm enabled, automatically confirming transfer for $userName', + ); + confirmUserRequest(userName); + } else { + onConfirmationRequired.call(userName); + } } /// Handle request confirmed @@ -573,4 +582,9 @@ class PupilIdentityStreamController { // Use the existing rejectUserRequest method rejectUserRequest(receiverName); } + + /// Set auto-confirm enabled state + void setAutoConfirmEnabled(bool enabled) { + state.streamState.autoConfirmEnabled.value = enabled; + } } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/models/stream_state.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/models/stream_state.dart index 004a1ee1..fb8794ac 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/models/stream_state.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/models/stream_state.dart @@ -11,6 +11,7 @@ class StreamState { final ValueNotifier requestReceived; final ValueNotifier requestSent; final ValueNotifier isTransmitting; + final ValueNotifier autoConfirmEnabled; StreamState() : isConnected = ValueNotifier(false), @@ -21,7 +22,8 @@ class StreamState { receiverUserName = ValueNotifier(''), requestReceived = ValueNotifier(false), requestSent = ValueNotifier(false), - isTransmitting = ValueNotifier(false); + isTransmitting = ValueNotifier(false), + autoConfirmEnabled = ValueNotifier(false); void dispose() { isConnected.dispose(); @@ -33,6 +35,7 @@ class StreamState { requestReceived.dispose(); requestSent.dispose(); isTransmitting.dispose(); + autoConfirmEnabled.dispose(); } } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart index 9651df3f..6ecb7be9 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart @@ -76,6 +76,9 @@ class PupilIdentityStreamPage extends WatchingWidget { final receiverJoined = watch(state.streamState.receiverJoined).value; final requestSent = watch(state.streamState.requestSent).value; final isTransmitting = watch(state.streamState.isTransmitting).value; + final autoConfirmEnabled = watch( + state.streamState.autoConfirmEnabled, + ).value; // Register handlers for receiver status changes that require overlay management if (role == PupilIdentityStreamRole.receiver) { @@ -155,6 +158,7 @@ class PupilIdentityStreamPage extends WatchingWidget { state, controller, isConnected, + autoConfirmEnabled, ), const Gap(16), Expanded( @@ -210,13 +214,40 @@ class PupilIdentityStreamPage extends WatchingWidget { PupilIdentityStreamState state, PupilIdentityStreamController controller, bool isConnected, + bool autoConfirmEnabled, ) { if (!isConnected) { return const SizedBox.shrink(); } - return ConnectionCodeDisplay( - channelName: controller.channelName, - description: 'Teilen Sie diesen Code mit dem Empfänger', + return Column( + children: [ + ConnectionCodeDisplay( + channelName: controller.channelName, + description: 'Teilen Sie diesen Code mit dem Empfänger', + ), + const Gap(16), + Card( + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + children: [ + Checkbox( + value: autoConfirmEnabled, + onChanged: (value) { + controller.setAutoConfirmEnabled(value ?? false); + }, + ), + const Expanded( + child: Text( + 'Übertragungen automatisch bestätigen', + style: TextStyle(fontSize: 14), + ), + ), + ], + ), + ), + ), + ], ); } @@ -254,10 +285,9 @@ class PupilIdentityStreamPage extends WatchingWidget { // Rejected Requests RejectedRequestsWidget( rejectedUsers: rejectedUsers, - onClearRejected: - rejectedUsers.isNotEmpty - ? () => controller.clearRejectedUsers() - : null, + onClearRejected: rejectedUsers.isNotEmpty + ? () => controller.clearRejectedUsers() + : null, ), ], ), @@ -294,10 +324,9 @@ class PupilIdentityStreamPage extends WatchingWidget { isConnected: isConnected, hasActiveTransfers: activeTransfers.isNotEmpty, isProcessing: isProcessing, - onStartStream: - role == PupilIdentityStreamRole.sender - ? () => controller.startStream() - : () => _showConnectionDialog(context, controller), + onStartStream: role == PupilIdentityStreamRole.sender + ? () => controller.startStream() + : () => _showConnectionDialog(context, controller), onStopStream: () { controller.stopStream(); Navigator.of(context).pop(); @@ -350,14 +379,12 @@ class PupilIdentityStreamPage extends WatchingWidget { receiverName: receiverName, status: status, isAnyTransferInProgress: activeTransfers.isNotEmpty, - onConfirm: - status == ReceiverStatus.requested - ? () => controller.confirmTransfer(receiverName) - : null, - onReject: - status == ReceiverStatus.requested - ? () => controller.rejectTransfer(receiverName) - : null, + onConfirm: status == ReceiverStatus.requested + ? () => controller.confirmTransfer(receiverName) + : null, + onReject: status == ReceiverStatus.requested + ? () => controller.rejectTransfer(receiverName) + : null, ), ); }).toList(), @@ -397,101 +424,96 @@ class PupilIdentityStreamPage extends WatchingWidget { } _receiverStatusOverlay = OverlayEntry( - builder: - (context) => Material( - color: Colors.black54, - child: Center( - child: Card( - margin: const EdgeInsets.all(20), - child: Container( - width: 350, - padding: const EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (isTransmitting) - const Column( - children: [ - CircularProgressIndicator(), - Gap(16), - Text( - 'Daten werden übertragen...', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - Gap(8), - Text( - 'Bitte warten Sie, bis die Übertragung abgeschlossen ist.', - textAlign: TextAlign.center, - ), - ], - ) - else if (joined) - const Column( - children: [ - Icon( - Icons.check_circle, - color: Colors.green, - size: 48, - ), - Gap(16), - Text( - 'Mit Sender verbunden', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - Gap(8), - Text( - 'Warten auf Datenübertragung vom Sender.', - textAlign: TextAlign.center, - ), - ], - ) - else if (requestSent) - const Column( - children: [ - CircularProgressIndicator(), - Gap(16), - Text( - 'Anfrage gesendet', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - Gap(8), - Text( - 'Warten auf Bestätigung vom Sender.', - textAlign: TextAlign.center, - ), - ], + builder: (context) => Material( + color: Colors.black54, + child: Center( + child: Card( + margin: const EdgeInsets.all(20), + child: Container( + width: 350, + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (isTransmitting) + const Column( + children: [ + CircularProgressIndicator(), + Gap(16), + Text( + 'Daten werden übertragen...', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + Gap(8), + Text( + 'Bitte warten Sie, bis die Übertragung abgeschlossen ist.', + textAlign: TextAlign.center, + ), + ], + ) + else if (joined) + const Column( + children: [ + Icon(Icons.check_circle, color: Colors.green, size: 48), + Gap(16), + Text( + 'Mit Sender verbunden', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + Gap(8), + Text( + 'Warten auf Datenübertragung vom Sender.', + textAlign: TextAlign.center, ), - const Gap(20), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: () { - controller.stopStream(); - _hideReceiverStatusOverlay(); - Navigator.of(context).pop(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - foregroundColor: Colors.white, + ], + ) + else if (requestSent) + const Column( + children: [ + CircularProgressIndicator(), + Gap(16), + Text( + 'Anfrage gesendet', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, ), - child: const Text('Stream beenden'), ), + Gap(8), + Text( + 'Warten auf Bestätigung vom Sender.', + textAlign: TextAlign.center, + ), + ], + ), + const Gap(20), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + controller.stopStream(); + _hideReceiverStatusOverlay(); + Navigator.of(context).pop(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + foregroundColor: Colors.white, ), - ], + child: const Text('Stream beenden'), + ), ), - ), + ], ), ), ), + ), + ), ); Overlay.of(context).insert(_receiverStatusOverlay!); From 2a5d870b8fb30b94ba3c240a1b78745e1ce04f20 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 26 Nov 2025 20:43:27 +0100 Subject: [PATCH 05/52] fix pupil profile content if empty --- .../pupil_profile_authorization_content_list.dart | 13 +++++++++++-- .../widgets/pupil_school_lists_content_list.dart | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content_list.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content_list.dart index 21a6ba0a..e8da71cb 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content_list.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content_list.dart @@ -11,8 +11,9 @@ class PupilProfileAuthorizationContentList extends WatchingWidget { @override Widget build(BuildContext context) { - final authorizations = - watchValue((AuthorizationManager x) => x.authorizations); + final authorizations = watchValue( + (AuthorizationManager x) => x.authorizations, + ); List pupilAuthorizations = []; for (final authorization in authorizations) { for (final pupilAuthorization in authorization.authorizedPupils!) { @@ -21,6 +22,14 @@ class PupilProfileAuthorizationContentList extends WatchingWidget { } } } + if (pupilAuthorizations.isEmpty) { + return const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 20.0), + child: Text('Keine Einwilligungen gefunden'), + ), + ); + } return ListView.builder( padding: const EdgeInsets.all(0), shrinkWrap: true, diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_school_lists_content_list.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_school_lists_content_list.dart index dd83149a..640faf3b 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_school_lists_content_list.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_school_lists_content_list.dart @@ -15,13 +15,22 @@ class PupilSchoolListContentList extends WatchingWidget { (SchoolListManager m) => m.allPupilEntries, ); final pupilListEntries = [ - ...pupilListEntriesProxyMaps - .expand((element) => element.pupilEntries.values) + ...pupilListEntriesProxyMaps.expand( + (element) => element.pupilEntries.values, + ), ]; final filteredPupilListEntries = pupilListEntries .where((element) => element.pupilEntry.pupilId == pupil.pupilId) .toList(); + if (filteredPupilListEntries.isEmpty) { + return const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 20.0), + child: Text('Keine Listen gefunden'), + ), + ); + } return Column( children: [ ListView.builder( @@ -35,7 +44,7 @@ class PupilSchoolListContentList extends WatchingWidget { ); }, ), - const Gap(10) + const Gap(10), ], ); } From 505f29743fbc028d7317fe6096e6cf2571f7a8df Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 26 Nov 2025 20:44:15 +0100 Subject: [PATCH 06/52] app: fix filter bottom sheets issues --- .../schoolday_event_filter_bottom_sheet.dart | 709 +++++++++--------- .../ogs/widgets/ogs_filter_bottom_sheet.dart | 167 +++-- .../widgets/common_pupil_filters.dart | 42 +- 3 files changed, 465 insertions(+), 453 deletions(-) diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart index fbc47595..306cf983 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; import 'package:school_data_hub_flutter/common/widgets/themed_filter_chip.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/domain/filters/schoolday_event_filter_manager.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/domain/models/schoolday_event_enums.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/enums.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/common_pupil_filters.dart'; -import 'package:school_data_hub_flutter/features/_schoolday_events/domain/filters/schoolday_event_filter_manager.dart'; -import 'package:school_data_hub_flutter/features/_schoolday_events/domain/models/schoolday_event_enums.dart'; import 'package:watch_it/watch_it.dart'; final _schooldayEventFilterManager = di(); @@ -20,7 +20,8 @@ class SchooldayEventFilterBottomSheet extends WatchingWidget { PupilSortMode sortMode = watchValue((PupilsFilter x) => x.sortMode); final Map activeSchooldayEventFilters = watchValue( - (SchooldayEventFilterManager x) => x.schooldayEventsFilterState); + (SchooldayEventFilterManager x) => x.schooldayEventsFilterState, + ); bool valueLastSevenDays = activeSchooldayEventFilters[SchooldayEventFilter.sevenDays]!; // event type @@ -28,20 +29,24 @@ class SchooldayEventFilterBottomSheet extends WatchingWidget { activeSchooldayEventFilters[SchooldayEventFilter.processed]!; bool valueRedCard = activeSchooldayEventFilters[SchooldayEventFilter.admonition]!; - bool valueRedCardOgs = activeSchooldayEventFilters[ - SchooldayEventFilter.afternoonCareAdmonition]!; + bool valueRedCardOgs = + activeSchooldayEventFilters[SchooldayEventFilter + .afternoonCareAdmonition]!; bool valueRedCardSentHome = activeSchooldayEventFilters[SchooldayEventFilter.admonitionAndBanned]!; bool valueParentsMeeting = activeSchooldayEventFilters[SchooldayEventFilter.parentsMeeting]!; bool valueOtherEvents = activeSchooldayEventFilters[SchooldayEventFilter.otherEvent]!; - bool valueViolenceAgainstPupils = activeSchooldayEventFilters[ - SchooldayEventFilter.violenceAgainstPupils]!; - bool valueViolenceAgainstAdults = activeSchooldayEventFilters[ - SchooldayEventFilter.violenceAgainstAdults]!; - bool valueViolenceAgainstThings = activeSchooldayEventFilters[ - SchooldayEventFilter.violenceAgainstThings]!; + bool valueViolenceAgainstPupils = + activeSchooldayEventFilters[SchooldayEventFilter + .violenceAgainstPupils]!; + bool valueViolenceAgainstAdults = + activeSchooldayEventFilters[SchooldayEventFilter + .violenceAgainstAdults]!; + bool valueViolenceAgainstThings = + activeSchooldayEventFilters[SchooldayEventFilter + .violenceAgainstThings]!; bool valueInsultOthers = activeSchooldayEventFilters[SchooldayEventFilter.insultOthers]!; bool valueAnnoy = activeSchooldayEventFilters[SchooldayEventFilter.annoy]!; @@ -53,8 +58,9 @@ class SchooldayEventFilterBottomSheet extends WatchingWidget { activeSchooldayEventFilters[SchooldayEventFilter.disturbLesson]!; bool valueOtherReasons = activeSchooldayEventFilters[SchooldayEventFilter.other]!; - bool valueLearningDevelopmentInfo = activeSchooldayEventFilters[ - SchooldayEventFilter.learningDevelopmentInfo]!; + bool valueLearningDevelopmentInfo = + activeSchooldayEventFilters[SchooldayEventFilter + .learningDevelopmentInfo]!; bool valueLearningSupportInfo = activeSchooldayEventFilters[SchooldayEventFilter.learningSupportInfo]!; bool valueAdmonitionInfo = @@ -62,349 +68,350 @@ class SchooldayEventFilterBottomSheet extends WatchingWidget { return Padding( padding: const EdgeInsets.only(left: 15.0, right: 15, top: 5), - child: Center( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 800), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const FilterHeading(), - Expanded( - child: SingleChildScrollView( - child: Column(children: [ - const CommonPupilFiltersWidget(), - const Row( - children: [ - Text( - 'Ereignisse', - style: AppStyles.subtitle, - ) - ], - ), - const Gap(5), - Wrap( - spacing: 5, - crossAxisAlignment: WrapCrossAlignment.center, - alignment: WrapAlignment.center, - children: [ - ThemedFilterChip( - label: '7 Tage', - selected: valueLastSevenDays, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.sevenDays, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: 'nicht bearbeitet', - selected: valueProcessed, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.processed, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🟥', - selected: valueRedCard, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.admonition, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🟥 OGS', - selected: valueRedCardOgs, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter - .afternoonCareAdmonition, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🟥🏠', - selected: valueRedCardSentHome, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const FilterHeading(), + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + const CommonPupilFiltersWidget(), + const Row( + children: [Text('Ereignisse', style: AppStyles.subtitle)], + ), + const Gap(5), + Wrap( + spacing: 5, + crossAxisAlignment: WrapCrossAlignment.center, + alignment: WrapAlignment.center, + children: [ + ThemedFilterChip( + label: '7 Tage', + selected: valueLastSevenDays, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.sevenDays, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: 'nicht bearbeitet', + selected: valueProcessed, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.processed, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🟥', + selected: valueRedCard, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.admonition, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🟥 OGS', + selected: valueRedCardOgs, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter + .afternoonCareAdmonition, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🟥🏠', + selected: valueRedCardSentHome, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ ( filter: SchooldayEventFilter.admonitionAndBanned, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '👪️', - selected: valueParentsMeeting, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.parentsMeeting, - value: val - ) - ], - ); - }, - ), - ThemedFilterChip( - label: '📝', - selected: valueOtherEvents, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.otherEvent, - value: val - ) - ]); - }, - ), - ], - ), - const Gap(10), - const Row( - children: [ - Text( - 'Grund', - style: AppStyles.subtitle, - ) - ], - ), - const Gap(5), - Wrap( - spacing: 5, - crossAxisAlignment: WrapCrossAlignment.center, - alignment: WrapAlignment.center, - children: [ - ThemedFilterChip( - label: '🤜🤕', - selected: valueViolenceAgainstPupils, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter - .violenceAgainstPupils, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🤜🎓️', - selected: valueViolenceAgainstAdults, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter - .violenceAgainstAdults, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🤜🏠', - selected: valueViolenceAgainstThings, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter - .violenceAgainstThings, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🤬💔', - selected: valueInsultOthers, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.insultOthers, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '😈😖', - selected: valueAnnoy, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ - (filter: SchooldayEventFilter.annoy, value: val) - ]); - }, - ), - ThemedFilterChip( - label: '🚨😱', - selected: valueDangerousBehaviour, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ - ( - filter: - SchooldayEventFilter.dangerousBehaviour, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🛑🎓️', - selected: valueDisturbLesson, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.disturbLesson, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🎓️🙉', - selected: valueIgnoreInstructions, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ - ( - filter: - SchooldayEventFilter.ignoreInstructions, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '💡🧠', - selected: valueLearningDevelopmentInfo, - onSelected: (val) { - _schooldayEventFilterManager.setFilter( - schooldayEventFilters: [ - ( - filter: SchooldayEventFilter - .learningDevelopmentInfo, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '🛟🧠', - selected: valueLearningSupportInfo, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ - ( - filter: - SchooldayEventFilter.learningSupportInfo, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '⚠️ℹ️', - selected: valueAdmonitionInfo, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ - ( - filter: SchooldayEventFilter.admonitionInfo, - value: val - ) - ]); - }, - ), - ThemedFilterChip( - label: '📝', - selected: valueOtherReasons, - onSelected: (val) { - _schooldayEventFilterManager - .setFilter(schooldayEventFilters: [ - (filter: SchooldayEventFilter.other, value: val) - ]); - }, - ), - ]), - const Gap(10), - const Row( - children: [ - Text( - 'Sortieren', - style: AppStyles.subtitle, - ) - ], - ), - const Gap(5), - Wrap( - spacing: 5, - crossAxisAlignment: WrapCrossAlignment.center, - alignment: WrapAlignment.center, - children: [ - ThemedFilterChip( - label: 'A-Z', - selected: sortMode == PupilSortMode.sortByName, - onSelected: (val) { - _pupilsFilter.setSortMode(PupilSortMode.sortByName); - }, - ), - ThemedFilterChip( - label: 'Anzahl', - selected: - sortMode == PupilSortMode.sortBySchooldayEvents, - onSelected: (val) { - _pupilsFilter.setSortMode( - PupilSortMode.sortBySchooldayEvents); - }, - ), - ThemedFilterChip( - label: 'zuletzt', - selected: sortMode == - PupilSortMode.sortByLastSchooldayEvent, - onSelected: (val) { - _pupilsFilter.setSortMode( - PupilSortMode.sortByLastSchooldayEvent); - }, - ), - ], - ), - const Gap(20), - ]), - ), + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '👪️', + selected: valueParentsMeeting, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.parentsMeeting, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '📝', + selected: valueOtherEvents, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.otherEvent, + value: val, + ), + ], + ); + }, + ), + ], + ), + const Gap(10), + const Row( + children: [Text('Grund', style: AppStyles.subtitle)], + ), + const Gap(5), + Wrap( + spacing: 5, + crossAxisAlignment: WrapCrossAlignment.center, + alignment: WrapAlignment.center, + children: [ + ThemedFilterChip( + label: '🤜🤕', + selected: valueViolenceAgainstPupils, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: + SchooldayEventFilter.violenceAgainstPupils, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🤜🎓️', + selected: valueViolenceAgainstAdults, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: + SchooldayEventFilter.violenceAgainstAdults, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🤜🏠', + selected: valueViolenceAgainstThings, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: + SchooldayEventFilter.violenceAgainstThings, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🤬💔', + selected: valueInsultOthers, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.insultOthers, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '😈😖', + selected: valueAnnoy, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + (filter: SchooldayEventFilter.annoy, value: val), + ], + ); + }, + ), + ThemedFilterChip( + label: '🚨😱', + selected: valueDangerousBehaviour, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.dangerousBehaviour, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🛑🎓️', + selected: valueDisturbLesson, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.disturbLesson, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🎓️🙉', + selected: valueIgnoreInstructions, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.ignoreInstructions, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '💡🧠', + selected: valueLearningDevelopmentInfo, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter + .learningDevelopmentInfo, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '🛟🧠', + selected: valueLearningSupportInfo, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: + SchooldayEventFilter.learningSupportInfo, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '⚠️ℹ️', + selected: valueAdmonitionInfo, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + ( + filter: SchooldayEventFilter.admonitionInfo, + value: val, + ), + ], + ); + }, + ), + ThemedFilterChip( + label: '📝', + selected: valueOtherReasons, + onSelected: (val) { + _schooldayEventFilterManager.setFilter( + schooldayEventFilters: [ + (filter: SchooldayEventFilter.other, value: val), + ], + ); + }, + ), + ], + ), + const Gap(10), + const Row( + children: [Text('Sortieren', style: AppStyles.subtitle)], + ), + const Gap(5), + Wrap( + spacing: 5, + crossAxisAlignment: WrapCrossAlignment.center, + alignment: WrapAlignment.center, + children: [ + ThemedFilterChip( + label: 'A-Z', + selected: sortMode == PupilSortMode.sortByName, + onSelected: (val) { + _pupilsFilter.setSortMode(PupilSortMode.sortByName); + }, + ), + ThemedFilterChip( + label: 'Anzahl', + selected: + sortMode == PupilSortMode.sortBySchooldayEvents, + onSelected: (val) { + _pupilsFilter.setSortMode( + PupilSortMode.sortBySchooldayEvents, + ); + }, + ), + ThemedFilterChip( + label: 'zuletzt', + selected: + sortMode == PupilSortMode.sortByLastSchooldayEvent, + onSelected: (val) { + _pupilsFilter.setSortMode( + PupilSortMode.sortByLastSchooldayEvent, + ); + }, + ), + ], + ), + const Gap(20), + ], ), - ], + ), ), - ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/ogs/widgets/ogs_filter_bottom_sheet.dart b/school_data_hub_flutter/lib/features/ogs/widgets/ogs_filter_bottom_sheet.dart index 096bf673..67677be8 100644 --- a/school_data_hub_flutter/lib/features/ogs/widgets/ogs_filter_bottom_sheet.dart +++ b/school_data_hub_flutter/lib/features/ogs/widgets/ogs_filter_bottom_sheet.dart @@ -21,90 +21,107 @@ class OgsFilterBottomSheet extends WatchingWidget { bool valueNotPresent = activeAttendanceFilters[AttendancePupilFilter.notPresent]!; return Padding( - padding: const EdgeInsets.only(left: 20.0, right: 20, top: 8), + padding: const EdgeInsets.only(left: 15.0, right: 15, top: 5), child: Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ const FilterHeading(), - const CommonPupilFiltersWidget(), - const Row(children: [Text('OGS-Filter', style: AppStyles.subtitle)]), - Wrap( - children: [ - ThemedFilterChip( - label: 'anwesend', - selected: valuePresent, - onSelected: (val) { - // in case present is selected, not present and unexcused should be deselected + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const CommonPupilFiltersWidget(), + const Row( + children: [Text('OGS-Filter', style: AppStyles.subtitle)], + ), + Wrap( + children: [ + ThemedFilterChip( + label: 'anwesend', + selected: valuePresent, + onSelected: (val) { + // in case present is selected, not present and unexcused should be deselected - if (val) { - _attendanceFilterLocator.setAttendancePupilFilter( - attendancePupilFilterRecords: [ - ( - attendancePupilFilter: - AttendancePupilFilter.notPresent, - value: false, - ), - ( - attendancePupilFilter: - AttendancePupilFilter.unexcused, - value: false, - ), - ( - attendancePupilFilter: AttendancePupilFilter.present, - value: val, - ), - ], - ); - return; - } - _attendanceFilterLocator.setAttendancePupilFilter( - attendancePupilFilterRecords: [ - ( - attendancePupilFilter: AttendancePupilFilter.present, - value: val, + if (val) { + _attendanceFilterLocator.setAttendancePupilFilter( + attendancePupilFilterRecords: [ + ( + attendancePupilFilter: + AttendancePupilFilter.notPresent, + value: false, + ), + ( + attendancePupilFilter: + AttendancePupilFilter.unexcused, + value: false, + ), + ( + attendancePupilFilter: + AttendancePupilFilter.present, + value: val, + ), + ], + ); + return; + } + _attendanceFilterLocator.setAttendancePupilFilter( + attendancePupilFilterRecords: [ + ( + attendancePupilFilter: + AttendancePupilFilter.present, + value: val, + ), + ], + ); + }, ), - ], - ); - }, - ), - ThemedFilterChip( - label: 'nicht da', - selected: valueNotPresent, - onSelected: (val) { - // in case not present is selected, present should be deselected - if (val) { - //_valuePresent = false; - _attendanceFilterLocator.setAttendancePupilFilter( - attendancePupilFilterRecords: [ - ( - attendancePupilFilter: - AttendancePupilFilter.notPresent, - value: val, - ), - ( - attendancePupilFilter: AttendancePupilFilter.present, - value: false, - ), - ( - attendancePupilFilter: - AttendancePupilFilter.unexcused, - value: false, - ), - ], - ); - return; - } + ThemedFilterChip( + label: 'nicht da', + selected: valueNotPresent, + onSelected: (val) { + // in case not present is selected, present should be deselected + if (val) { + //_valuePresent = false; + _attendanceFilterLocator.setAttendancePupilFilter( + attendancePupilFilterRecords: [ + ( + attendancePupilFilter: + AttendancePupilFilter.notPresent, + value: val, + ), + ( + attendancePupilFilter: + AttendancePupilFilter.present, + value: false, + ), + ( + attendancePupilFilter: + AttendancePupilFilter.unexcused, + value: false, + ), + ], + ); + return; + } - _attendanceFilterLocator.setAttendancePupilFilter( - attendancePupilFilterRecords: [ - ( - attendancePupilFilter: AttendancePupilFilter.notPresent, - value: val, + _attendanceFilterLocator.setAttendancePupilFilter( + attendancePupilFilterRecords: [ + ( + attendancePupilFilter: + AttendancePupilFilter.notPresent, + value: val, + ), + ], + ); + }, ), ], - ); - }, + ), + ], ), - ], + ), ), ], ), diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/common_pupil_filters.dart b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/common_pupil_filters.dart index 767daf3e..04f64b8b 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/common_pupil_filters.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/common_pupil_filters.dart @@ -14,19 +14,21 @@ class FilterHeading extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - const Text( - 'Filter', - style: AppStyles.title, - ), + const Text('Filter', style: AppStyles.title), const Spacer(), IconButton( - iconSize: 35, - color: AppColors.interactiveColor, - onPressed: () { - di().resetFilters(); - //Navigator.pop(context); - }, - icon: const Icon(Icons.restart_alt_rounded)), + iconSize: 35, + color: AppColors.interactiveColor, + onPressed: () { + di().resetFilters(); + //Navigator.pop(context); + }, + icon: const Icon( + Icons.delete_forever_rounded, + size: 35, + color: AppColors.accentColor, + ), + ), ], ); } @@ -44,14 +46,7 @@ class CommonPupilFiltersWidget extends WatchingWidget { return Column( children: [ - const Row( - children: [ - Text( - 'Jahrgang', - style: AppStyles.subtitle, - ) - ], - ), + const Row(children: [Text('Jahrgang', style: AppStyles.subtitle)]), const Gap(5), Wrap( spacing: 5, @@ -68,14 +63,7 @@ class CommonPupilFiltersWidget extends WatchingWidget { ), ], ), - const Row( - children: [ - Text( - 'Klasse', - style: AppStyles.subtitle, - ) - ], - ), + const Row(children: [Text('Klasse', style: AppStyles.subtitle)]), const Gap(5), Wrap( spacing: 5, From 788bb5ed0e0b27fc1d66bfcd71a07af3bffb41cb Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 26 Nov 2025 20:44:37 +0100 Subject: [PATCH 07/52] app: charts WIP --- .../statistics/chart_page/chart_page.dart | 514 +++++++++++++----- .../chart_page/chart_page_controller.dart | 100 +++- 2 files changed, 472 insertions(+), 142 deletions(-) diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart index 43c1d0a6..effc3fb3 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart @@ -1,4 +1,5 @@ -import 'package:community_charts_flutter/community_charts_flutter.dart' as charts; +import 'package:community_charts_flutter/community_charts_flutter.dart' + as charts; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:intl/intl.dart'; @@ -7,12 +8,34 @@ import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; class ChartPage extends StatelessWidget { - final Map chartData; + final Map< + DateTime, + ({ + int specialNeeds, + int migrationSupport, + int supportLevel3, + int regularPupils, + int newPupils, + }) + > + chartData; + final Map< + DateTime, + ({ + int parentsMeeting, + int admonition, + int afternoonCareAdmonition, + int admonitionAndBanned, + int otherEvent, + }) + > + eventChartData; final List schooldays; const ChartPage({ super.key, required this.chartData, + required this.eventChartData, required this.schooldays, }); @@ -30,7 +53,7 @@ class ChartPage extends StatelessWidget { for (final schoolday in sortedSchooldays) { final date = schoolday.schoolday.toLocal(); final monthKey = '${date.year}-${date.month}'; - + // If this is a new month, add the first schoolday of that month if (monthKey != currentMonth) { currentMonth = monthKey; @@ -103,27 +126,24 @@ class ChartPage extends StatelessWidget { return [ charts.Series( id: 'Special Needs', - colorFn: (_, __) => charts.ColorUtil.fromDartColor( - AppColors.accentColor, - ), + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.accentColor), domainFn: (ChartData data, _) => data.dateString, measureFn: (ChartData data, _) => data.count, data: specialNeedsData, ), charts.Series( id: 'Migration Support', - colorFn: (_, __) => charts.ColorUtil.fromDartColor( - AppColors.backgroundColor, - ), + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.backgroundColor), domainFn: (ChartData data, _) => data.dateString, measureFn: (ChartData data, _) => data.count, data: migrationSupportData, ), charts.Series( id: 'Support Level 3', - colorFn: (_, __) => charts.ColorUtil.fromDartColor( - AppColors.warningButtonColor, - ), + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.warningButtonColor), domainFn: (ChartData data, _) => data.dateString, measureFn: (ChartData data, _) => data.count, data: supportLevel3Data, @@ -131,7 +151,12 @@ class ChartPage extends StatelessWidget { charts.Series( id: 'Regular Pupils', colorFn: (_, __) => charts.ColorUtil.fromDartColor( - const Color.fromARGB(255, 200, 200, 200), // Light gray for regular pupils + const Color.fromARGB( + 255, + 200, + 200, + 200, + ), // Light gray for regular pupils ), domainFn: (ChartData data, _) => data.dateString, measureFn: (ChartData data, _) => data.count, @@ -139,9 +164,8 @@ class ChartPage extends StatelessWidget { ), charts.Series( id: 'New Pupils', - colorFn: (_, __) => charts.ColorUtil.fromDartColor( - AppColors.successButtonColor, - ), + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.successButtonColor), domainFn: (ChartData data, _) => data.dateString, measureFn: (ChartData data, _) => data.count, data: newPupilsData, @@ -154,6 +178,103 @@ class ChartPage extends StatelessWidget { return DateFormat('dd.MM').format(localDate); } + List> _createEventSeries() { + // Sort schooldays by date + final sorted = sortedSchooldays; + + final parentsMeetingData = sorted.map((schoolday) { + final data = eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.parentsMeeting ?? 0, + seriesId: 'parentsMeeting', + ); + }).toList(); + + final admonitionData = sorted.map((schoolday) { + final data = eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.admonition ?? 0, + seriesId: 'admonition', + ); + }).toList(); + + final afternoonCareAdmonitionData = sorted.map((schoolday) { + final data = eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.afternoonCareAdmonition ?? 0, + seriesId: 'afternoonCareAdmonition', + ); + }).toList(); + + final admonitionAndBannedData = sorted.map((schoolday) { + final data = eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.admonitionAndBanned ?? 0, + seriesId: 'admonitionAndBanned', + ); + }).toList(); + + final otherEventData = sorted.map((schoolday) { + final data = eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.otherEvent ?? 0, + seriesId: 'otherEvent', + ); + }).toList(); + + return [ + charts.Series( + id: 'Elterngespräch', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.blue), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: parentsMeetingData, + ), + charts.Series( + id: 'Rote Karte', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: admonitionData, + ), + charts.Series( + id: 'Rote Karte - OGS', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: afternoonCareAdmonitionData, + ), + charts.Series( + id: 'Rote Karte + Abholen', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.purple), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: admonitionAndBannedData, + ), + charts.Series( + id: 'Sonstiges', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.grey), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: otherEventData, + ), + ]; + } @override Widget build(BuildContext context) { @@ -173,9 +294,7 @@ class ChartPage extends StatelessWidget { ], ), ), - body: const Center( - child: Text('Keine Daten verfügbar'), - ), + body: const Center(child: Text('Keine Daten verfügbar')), ); } @@ -199,135 +318,252 @@ class ChartPage extends StatelessWidget { body: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 800), - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Gap(15), - const Text( - 'Schülerzahlen nach Schultag', - style: AppStyles.title, - ), - const Gap(10), - Expanded( - child: LayoutBuilder( - builder: (context, constraints) { - if (constraints.maxHeight <= 0 || constraints.maxWidth <= 0) { - return const Center( - child: Text('Chart wird geladen...'), - ); - } - return charts.OrdinalComboChart( - series, - animate: true, - animationDuration: const Duration(milliseconds: 500), - defaultRenderer: charts.LineRendererConfig( - includeArea: true, - stacked: true, - ), - customSeriesRenderers: [ - charts.LineRendererConfig( - customRendererId: 'lineSeries', - strokeWidthPx: 3.0, // Make the line thicker - includeArea: false, - stacked: false, + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(15), + const Text( + 'Schülerzahlen nach Schultag', + style: AppStyles.title, + ), + const Gap(10), + SizedBox( + height: 300, + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxHeight <= 0 || + constraints.maxWidth <= 0) { + return const Center( + child: Text('Chart wird geladen...'), + ); + } + return charts.OrdinalComboChart( + series, + animate: true, + animationDuration: const Duration(milliseconds: 500), + defaultRenderer: charts.LineRendererConfig( + includeArea: true, + stacked: true, + ), + customSeriesRenderers: [ + charts.LineRendererConfig( + customRendererId: 'lineSeries', + strokeWidthPx: 3.0, // Make the line thicker + includeArea: false, + stacked: false, + ), + ], + layoutConfig: charts.LayoutConfig( + leftMarginSpec: charts.MarginSpec.fixedPixel(60), + topMarginSpec: charts.MarginSpec.fixedPixel(20), + rightMarginSpec: charts.MarginSpec.fixedPixel(40), + bottomMarginSpec: charts.MarginSpec.fixedPixel(60), ), - ], - layoutConfig: charts.LayoutConfig( - leftMarginSpec: charts.MarginSpec.fixedPixel(60), - topMarginSpec: charts.MarginSpec.fixedPixel(20), - rightMarginSpec: charts.MarginSpec.fixedPixel(40), - bottomMarginSpec: charts.MarginSpec.fixedPixel(60), - ), - primaryMeasureAxis: const charts.NumericAxisSpec( - tickProviderSpec: charts.BasicNumericTickProviderSpec( - zeroBound: true, + primaryMeasureAxis: const charts.NumericAxisSpec( + tickProviderSpec: + charts.BasicNumericTickProviderSpec( + zeroBound: true, + ), ), - ), - domainAxis: charts.OrdinalAxisSpec( - tickProviderSpec: charts.StaticOrdinalTickProviderSpec( - sortedSchooldays.map((schoolday) { - final dateStr = _formatDateForChart(schoolday.schoolday); - final firstOfMonthDates = _getFirstOfMonthDates(); - - // Only show label if it's the first of a month - if (firstOfMonthDates.contains(dateStr)) { - try { - final date = DateFormat('dd.MM').parse(dateStr); + domainAxis: charts.OrdinalAxisSpec( + tickProviderSpec: charts.StaticOrdinalTickProviderSpec( + sortedSchooldays.map((schoolday) { + final dateStr = _formatDateForChart( + schoolday.schoolday, + ); + final firstOfMonthDates = + _getFirstOfMonthDates(); + + // Only show label if it's the first of a month + if (firstOfMonthDates.contains(dateStr)) { + try { + final date = DateFormat( + 'dd.MM', + ).parse(dateStr); + return charts.TickSpec( + dateStr, + label: DateFormat('MMM').format(date), + ); + } catch (e) { + return charts.TickSpec(dateStr); + } + } else { + // Return tick with empty label for other dates return charts.TickSpec( dateStr, - label: DateFormat('MMM').format(date), + label: '', ); - } catch (e) { - return charts.TickSpec(dateStr); } - } else { - // Return tick with empty label for other dates - return charts.TickSpec( - dateStr, - label: '', - ); - } - }).toList(), + }).toList(), + ), ), - ), - behaviors: [ - charts.SeriesLegend( - position: charts.BehaviorPosition.bottom, - desiredMaxRows: 4, - cellPadding: const EdgeInsets.only( - right: 4.0, - bottom: 4.0, + behaviors: [ + charts.SeriesLegend( + position: charts.BehaviorPosition.bottom, + desiredMaxRows: 4, + cellPadding: const EdgeInsets.only( + right: 4.0, + bottom: 4.0, + ), ), + charts.ChartTitle( + 'Schultag', + behaviorPosition: charts.BehaviorPosition.bottom, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + charts.ChartTitle( + 'Anzahl Schüler', + behaviorPosition: charts.BehaviorPosition.start, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + ], + ); + }, + ), + ), + const Gap(20), + Wrap( + alignment: WrapAlignment.center, + spacing: 20, + runSpacing: 10, + children: [ + _buildLegendItem( + 'Besonderer Förderbedarf', + AppColors.accentColor, + ), + _buildLegendItem( + 'Migrationsunterstützung', + AppColors.backgroundColor, + ), + _buildLegendItem( + 'Förderstufe 3', + AppColors.warningButtonColor, + ), + _buildLegendItem( + 'Reguläre Schüler', + const Color.fromARGB(255, 200, 200, 200), + ), + _buildLegendItem( + 'Neue Schüler', + AppColors.successButtonColor, + ), + ], + ), + const Gap(30), + const Text( + 'Schultagevents nach Schultag', + style: AppStyles.title, + ), + const Gap(10), + SizedBox( + height: 300, + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxHeight <= 0 || + constraints.maxWidth <= 0) { + return const Center( + child: Text('Chart wird geladen...'), + ); + } + final eventSeries = _createEventSeries(); + return charts.OrdinalComboChart( + eventSeries, + animate: true, + animationDuration: const Duration(milliseconds: 500), + defaultRenderer: charts.BarRendererConfig( + groupingType: charts.BarGroupingType.stacked, ), - charts.ChartTitle( - 'Schultag', - behaviorPosition: charts.BehaviorPosition.bottom, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, + layoutConfig: charts.LayoutConfig( + leftMarginSpec: charts.MarginSpec.fixedPixel(60), + topMarginSpec: charts.MarginSpec.fixedPixel(20), + rightMarginSpec: charts.MarginSpec.fixedPixel(40), + bottomMarginSpec: charts.MarginSpec.fixedPixel(60), ), - charts.ChartTitle( - 'Anzahl Schüler', - behaviorPosition: charts.BehaviorPosition.start, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, + primaryMeasureAxis: const charts.NumericAxisSpec( + tickProviderSpec: + charts.BasicNumericTickProviderSpec( + zeroBound: true, + ), ), - ], - ); - }, - ), - ), - const Gap(20), - Wrap( - alignment: WrapAlignment.center, - spacing: 20, - runSpacing: 10, - children: [ - _buildLegendItem( - 'Besonderer Förderbedarf', - AppColors.accentColor, - ), - _buildLegendItem( - 'Migrationsunterstützung', - AppColors.backgroundColor, - ), - _buildLegendItem( - 'Förderstufe 3', - AppColors.warningButtonColor, - ), - _buildLegendItem( - 'Reguläre Schüler', - const Color.fromARGB(255, 200, 200, 200), - ), - _buildLegendItem( - 'Neue Schüler', - AppColors.successButtonColor, + domainAxis: charts.OrdinalAxisSpec( + tickProviderSpec: charts.StaticOrdinalTickProviderSpec( + sortedSchooldays.map((schoolday) { + final dateStr = _formatDateForChart( + schoolday.schoolday, + ); + final firstOfMonthDates = + _getFirstOfMonthDates(); + + // Only show label if it's the first of a month + if (firstOfMonthDates.contains(dateStr)) { + try { + final date = DateFormat( + 'dd.MM', + ).parse(dateStr); + return charts.TickSpec( + dateStr, + label: DateFormat('MMM').format(date), + ); + } catch (e) { + return charts.TickSpec(dateStr); + } + } else { + // Return tick with empty label for other dates + return charts.TickSpec( + dateStr, + label: '', + ); + } + }).toList(), + ), + ), + behaviors: [ + charts.SeriesLegend( + position: charts.BehaviorPosition.bottom, + desiredMaxRows: 3, + cellPadding: const EdgeInsets.only( + right: 4.0, + bottom: 4.0, + ), + ), + charts.ChartTitle( + 'Schultag', + behaviorPosition: charts.BehaviorPosition.bottom, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + charts.ChartTitle( + 'Anzahl Events', + behaviorPosition: charts.BehaviorPosition.start, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + ], + ); + }, ), - ], - ), - const Gap(20), - ], + ), + const Gap(20), + Wrap( + alignment: WrapAlignment.center, + spacing: 20, + runSpacing: 10, + children: [ + _buildLegendItem('Elterngespräch', Colors.blue), + _buildLegendItem('Rote Karte', Colors.red), + _buildLegendItem('Rote Karte - OGS', Colors.orange), + _buildLegendItem('Rote Karte + Abholen', Colors.purple), + _buildLegendItem('Sonstiges', Colors.grey), + ], + ), + const Gap(20), + ], + ), ), ), ), @@ -370,10 +606,7 @@ class ChartPage extends StatelessWidget { ), ), const Gap(8), - Text( - label, - style: const TextStyle(fontSize: 14), - ), + Text(label, style: const TextStyle(fontSize: 14)), ], ); } @@ -392,4 +625,3 @@ class ChartData { required this.seriesId, }); } - diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart index a613e009..7d5d3ad9 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page.dart'; @@ -15,12 +16,14 @@ class ChartPageController extends StatefulWidget { class _ChartPageControllerState extends State { late final PupilManager _pupilManager; late final SchoolCalendarManager _schoolCalendarManager; + late final SchooldayEventManager _schooldayEventManager; @override void initState() { super.initState(); _pupilManager = di(); _schoolCalendarManager = di(); + _schooldayEventManager = di(); } /// Gets schooldays for the current semester @@ -294,6 +297,42 @@ class _ChartPageControllerState extends State { }).length; } + /// Counts events by type for a given schoolday + int getEventCountForSchoolday( + Schoolday schoolday, + SchooldayEventType eventType, + ) { + final allEvents = _schooldayEventManager.schooldayEvents; + final schooldayDate = schoolday.schoolday.toLocal(); + final dayDate = DateTime( + schooldayDate.year, + schooldayDate.month, + schooldayDate.day, + ); + + return allEvents.where((event) { + // Match by schooldayId if available, otherwise by date + if (event.schooldayId == schoolday.id) { + return event.eventType == eventType; + } + + // Fallback: match by date if schoolday object is available + if (event.schoolday != null) { + final eventDate = event.schoolday!.schoolday.toLocal(); + final eventDayDate = DateTime( + eventDate.year, + eventDate.month, + eventDate.day, + ); + if (eventDayDate == dayDate && event.eventType == eventType) { + return true; + } + } + + return false; + }).length; + } + /// Gets data for the chart: schooldays with their counts Map< DateTime, @@ -332,6 +371,59 @@ class _ChartPageControllerState extends State { return data; } + /// Gets event data for the chart: schooldays with event counts by type + Map< + DateTime, + ({ + int parentsMeeting, + int admonition, + int afternoonCareAdmonition, + int admonitionAndBanned, + int otherEvent, + }) + > + getEventChartData() { + final schooldays = getSchooldaysForCurrentSemester(); + final Map< + DateTime, + ({ + int parentsMeeting, + int admonition, + int afternoonCareAdmonition, + int admonitionAndBanned, + int otherEvent, + }) + > + data = {}; + + for (final schoolday in schooldays) { + data[schoolday.schoolday] = ( + parentsMeeting: getEventCountForSchoolday( + schoolday, + SchooldayEventType.parentsMeeting, + ), + admonition: getEventCountForSchoolday( + schoolday, + SchooldayEventType.admonition, + ), + afternoonCareAdmonition: getEventCountForSchoolday( + schoolday, + SchooldayEventType.afternoonCareAdmonition, + ), + admonitionAndBanned: getEventCountForSchoolday( + schoolday, + SchooldayEventType.admonitionAndBanned, + ), + otherEvent: getEventCountForSchoolday( + schoolday, + SchooldayEventType.otherEvent, + ), + ); + } + + return data; + } + @override Widget build(BuildContext context) { final chartData = getChartData(); @@ -360,6 +452,12 @@ class _ChartPageControllerState extends State { ); } - return ChartPage(chartData: chartData, schooldays: schooldays); + final eventChartData = getEventChartData(); + + return ChartPage( + chartData: chartData, + eventChartData: eventChartData, + schooldays: schooldays, + ); } } From 734a76a7d310ceb9d905dca4b8ae15294de37407 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 26 Nov 2025 20:44:59 +0100 Subject: [PATCH 08/52] app: fixes --- .../edit_book_page/edit_book_page.dart | 60 +++++---- .../widgets/pupil_rooms_list.dart | 120 ++++++++++++------ .../user_list/widgets/user_list_card.dart | 68 ++++++---- 3 files changed, 149 insertions(+), 99 deletions(-) diff --git a/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart b/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart index 10788e07..6fc8f94e 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart @@ -3,6 +3,7 @@ import 'package:gap/gap.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; import 'package:school_data_hub_flutter/common/widgets/themed_filter_chip.dart'; import 'package:school_data_hub_flutter/common/widgets/unencrypted_image_in_card.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; @@ -20,11 +21,9 @@ class EditBookPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - backgroundColor: AppColors.backgroundColor, - title: const Center( - child: Text('Buch bearbeiten', style: AppStyles.appBarTextStyle), - ), + appBar: const GenericAppBar( + iconData: Icons.book, + title: 'Buch bearbeiten', ), body: Center( heightFactor: 1, @@ -49,8 +48,8 @@ class EditBookPage extends StatelessWidget { borderRadius: BorderRadius.circular(10.0), child: UnencryptedImageInCard( path: controller.imagePath!, - cacheKey: - controller.libraryBook.isbn.toString(), + cacheKey: controller.libraryBook.isbn + .toString(), size: 220, ), ), @@ -94,26 +93,26 @@ class EditBookPage extends StatelessWidget { value: ReadingLevel.fromString( controller.readingLevel, ), - onChanged: - (value) => controller - .onChangedReadingLevelDropDown(value), + onChanged: (value) => controller + .onChangedReadingLevelDropDown(value), ), const Gap(20), Row( children: [ Expanded( - child: DropdownButtonFormField< - LibraryBookLocation - >( - decoration: AppStyles.textFieldDecoration( - labelText: 'Ablageort', - ), - items: controller.locationDropdownItems, - value: controller.selectedLocation, - onChanged: - (value) => controller + child: + DropdownButtonFormField< + LibraryBookLocation + >( + decoration: + AppStyles.textFieldDecoration( + labelText: 'Ablageort', + ), + items: controller.locationDropdownItems, + value: controller.selectedLocation, + onChanged: (value) => controller .onChangedLocationDropDown(value!), - ), + ), ), if (_hubSessionManager.isAdmin) ...[ const Gap(10), @@ -204,16 +203,15 @@ class EditBookPage extends StatelessWidget { Wrap( spacing: 5, runSpacing: 5, - children: - controller.bookTagSelection.entries.map((entry) { - return ThemedFilterChip( - label: entry.key.name, - selected: entry.value, - onSelected: (bool selected) { - controller.switchBookTagSelection(entry.key); - }, - ); - }).toList(), + children: controller.bookTagSelection.entries.map((entry) { + return ThemedFilterChip( + label: entry.key.name, + selected: entry.value, + onSelected: (bool selected) { + controller.switchBookTagSelection(entry.key); + }, + ); + }).toList(), ), const Gap(30), ElevatedButton( diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart index d99cd986..1f0b5d6b 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart @@ -14,8 +14,11 @@ final _matrixPolicyManager = di(); class MatrixUserRoomsList extends WatchingWidget { final MatrixUser matrixUser; final List matrixRooms; - const MatrixUserRoomsList( - {required this.matrixUser, required this.matrixRooms, super.key}); + const MatrixUserRoomsList({ + required this.matrixUser, + required this.matrixRooms, + super.key, + }); @override Widget build(BuildContext context) { @@ -30,10 +33,7 @@ class MatrixUserRoomsList extends WatchingWidget { padding: EdgeInsets.only(left: 22), child: Text( 'Räume:', - style: TextStyle( - color: Colors.black, - fontSize: 18, - ), + style: TextStyle(color: Colors.black, fontSize: 18), ), ), ], @@ -48,10 +48,12 @@ class MatrixUserRoomsList extends WatchingWidget { int powerLevel = 0; final List matrixRoomList = List.from(matrixRooms); MatrixRoom matrixRoom = namedMatrixRooms.firstWhere( - (element) => element.id == matrixRoomList[index].id); + (element) => element.id == matrixRoomList[index].id, + ); if (matrixRoom.roomAdmins != null) { - RoomAdmin? admin = matrixRoom.roomAdmins! - .firstWhereOrNull((element) => element.id == user.id); + RoomAdmin? admin = matrixRoom.roomAdmins!.firstWhereOrNull( + (element) => element.id == user.id, + ); if (admin != null) { powerLevel = admin.powerLevel; } @@ -72,10 +74,11 @@ class MatrixUserRoomsList extends WatchingWidget { InkWell( onLongPress: () async { final bool? confirm = await confirmationDialog( - context: context, - title: 'Raum verlassen?', - message: - 'Diese/n Nutzer/in wirklich aus dem Raum entfernen?'); + context: context, + title: 'Raum verlassen?', + message: + 'Diese/n Nutzer/in wirklich aus dem Raum entfernen?', + ); if (confirm == true) { user.leaveRoom(matrixRoom); } @@ -90,18 +93,50 @@ class MatrixUserRoomsList extends WatchingWidget { ), ), const Gap(5), - Text(matrixRoom.id), + InkWell( + onLongPress: () async { + final bool? result = await confirmationDialog( + context: context, + message: + 'Moderationsrechte für ${matrixUser.displayName} vergeben?', + title: 'Moderationsrechte vergeben', + ); + if (result == true) { + matrixUser.setPowerLevel( + matrixRooms[index].id, + 50, + ); + // _matrixPolicyManager.rooms.changeRoomPowerLevels( + // roomId: roomId, + // roomAdmin: RoomAdmin( + // id: matrixUser.id!, + // powerLevel: 50, + // ), + // ); + } + }, + child: Text(matrixRoom.id), + ), const Gap(5), powerLevel > 99 - ? const Icon(Icons.star, - color: Colors.yellow, size: 20) + ? const Icon( + Icons.star, + color: Colors.yellow, + size: 20, + ) : powerLevel > 49 - ? const Icon(Icons.chat, - color: Colors.orange, size: 20) - : powerLevel > 29 - ? const Icon(Icons.remove_red_eye_outlined, - color: AppColors.groupColor, size: 20) - : const SizedBox.shrink(), + ? const Icon( + Icons.chat, + color: Colors.orange, + size: 20, + ) + : powerLevel > 29 + ? const Icon( + Icons.remove_red_eye_outlined, + color: AppColors.groupColor, + size: 20, + ) + : const SizedBox.shrink(), ], ), ), @@ -109,14 +144,17 @@ class MatrixUserRoomsList extends WatchingWidget { ), ); }, - ) + ), ], ); } } List roomsList( - MatrixUser matrixUser, List matrixRooms, BuildContext context) { + MatrixUser matrixUser, + List matrixRooms, + BuildContext context, +) { List namedMatrixRooms = _matrixPolicyManager.matrixRooms.value; return [ @@ -131,10 +169,7 @@ List roomsList( onPressed: () async { // changeCreditDialog(context, pupil); }, - child: const Text( - "RÄUME ÄNDERN", - style: AppStyles.buttonTextStyle, - ), + child: const Text("RÄUME ÄNDERN", style: AppStyles.buttonTextStyle), ), ), ), @@ -144,10 +179,7 @@ List roomsList( padding: EdgeInsets.only(left: 22), child: Text( 'Räume:', - style: TextStyle( - color: Colors.black, - fontSize: 18, - ), + style: TextStyle(color: Colors.black, fontSize: 18), ), ), ], @@ -161,10 +193,12 @@ List roomsList( itemBuilder: (BuildContext context, int index) { int powerLevel = 0; final List matrixRoomList = List.from(matrixRooms); - MatrixRoom matrixRoom = namedMatrixRooms - .firstWhere((element) => element.id == matrixRoomList[index].id); - RoomAdmin? admin = matrixRoom.roomAdmins! - .firstWhereOrNull((element) => element.id == matrixUser.id); + MatrixRoom matrixRoom = namedMatrixRooms.firstWhere( + (element) => element.id == matrixRoomList[index].id, + ); + RoomAdmin? admin = matrixRoom.roomAdmins!.firstWhereOrNull( + (element) => element.id == matrixUser.id, + ); if (admin != null) { powerLevel = admin.powerLevel; } @@ -197,12 +231,14 @@ List roomsList( powerLevel > 99 ? const Icon(Icons.star, color: Colors.yellow, size: 20) : powerLevel > 49 - ? const Icon(Icons.chat, - color: Colors.orange, size: 20) - : powerLevel > 29 - ? const Icon(Icons.remove_red_eye_outlined, - color: AppColors.groupColor, size: 20) - : const SizedBox.shrink(), + ? const Icon(Icons.chat, color: Colors.orange, size: 20) + : powerLevel > 29 + ? const Icon( + Icons.remove_red_eye_outlined, + color: AppColors.groupColor, + size: 20, + ) + : const SizedBox.shrink(), ], ), ), diff --git a/school_data_hub_flutter/lib/features/user/presentation/user_list/widgets/user_list_card.dart b/school_data_hub_flutter/lib/features/user/presentation/user_list/widgets/user_list_card.dart index ac8fa469..eac96365 100644 --- a/school_data_hub_flutter/lib/features/user/presentation/user_list/widgets/user_list_card.dart +++ b/school_data_hub_flutter/lib/features/user/presentation/user_list/widgets/user_list_card.dart @@ -103,6 +103,18 @@ class UserListCard extends WatchingWidget { ), ], ), + Row( + children: [ + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [Text('Rolle: ${user.role.name}')], + ), + ), + ), + ], + ), Row( children: [ Expanded( @@ -110,16 +122,20 @@ class UserListCard extends WatchingWidget { scrollDirection: Axis.horizontal, child: Row( children: [ - Text('Rolle: ${user.role.name}'), + const Text('Email:'), const Gap(10), - Text('Email: ${user.userInfo?.email ?? 'N/A'}'), + Text( + user.userInfo?.email ?? 'N/A', + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), ], ), ), ), ], ), - const Gap(5), Row( children: [ Expanded( @@ -127,10 +143,10 @@ class UserListCard extends WatchingWidget { scrollDirection: Axis.horizontal, child: Row( children: [ - const Text('Zeiteinheiten:'), - const Gap(5), + const Text('Matrix-Id:'), + const Gap(10), Text( - user.timeUnits.toString(), + user.matrixUserId ?? 'N/A', style: const TextStyle( fontWeight: FontWeight.bold, ), @@ -148,9 +164,18 @@ class UserListCard extends WatchingWidget { scrollDirection: Axis.horizontal, child: Row( children: [ - const Text('Entlastungseinheiten:'), + const Text('Stunden:'), const Gap(5), - //- TODO: Implement update user properties + Text( + user.timeUnits.toString(), + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + const Gap(10), + const Text('Entlastung:'), + const Gap(5), + Text( user.reliefTimeUnits.toString(), style: const TextStyle( @@ -163,6 +188,7 @@ class UserListCard extends WatchingWidget { ), ], ), + const Gap(5), ], ), ), @@ -197,25 +223,15 @@ class UserListCard extends WatchingWidget { title: null, tileController: tileController, widgetList: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('User ID: ${user.id ?? 'N/A'}'), - const Gap(5), - Text('User Info ID: ${user.userInfoId}'), - const Gap(5), - Text( - 'Erstellt: ${user.userInfo?.created != null ? user.userInfo!.created.formatDateForUser() : 'N/A'}', - ), - const Gap(5), - Text( - 'Autorisierte Schüler: ${user.pupilsAuth?.length ?? 0}', - ), - ], - ), + Row(children: [Text('User ID: ${user.id ?? 'N/A'}')]), + const Gap(5), + Text('User Info ID: ${user.userInfoId}'), + const Gap(5), + Text( + 'Erstellt: ${user.userInfo?.created != null ? user.userInfo!.created.formatDateForUser() : 'N/A'}', ), + const Gap(5), + Text('Autorisierte Schüler: ${user.pupilsAuth?.length ?? 0}'), ], ), ], From 1b48028a8f569a8e876dfc8208aa9f11584f9c41 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 26 Nov 2025 21:20:48 +0100 Subject: [PATCH 09/52] app: pupil_profile_content changes --- .../widgets/landing_bottom_nav_bar.dart | 211 ++++++------ .../pupil_ogs_content.dart | 42 +-- .../pupil_profile_attendance_content.dart | 135 ++++---- .../pupil_profile_authorization_content.dart | 52 ++- .../pupil_profile_communication_content.dart | 283 +++------------- .../credit/pupil_profile_credit_content.dart | 302 +++++++++--------- .../pupil_profile_infos_content.dart | 234 ++------------ .../pupil_profile_learning_content.dart | 222 ++++++------- ...upil_profile_learning_support_content.dart | 50 ++- .../pupil_school_lists_content_card.dart | 50 +-- ...upil_profile_schoolday_events_content.dart | 52 ++- .../pupil_profile_content_widgets.dart | 204 ++++++++++++ .../widgets/pupil_search_text_field.dart | 25 +- 13 files changed, 790 insertions(+), 1072 deletions(-) create mode 100644 school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart index d424cb2b..38435eae 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart @@ -20,9 +20,15 @@ import 'package:watch_it/watch_it.dart'; final _bottomNavmanager = di(); final _envManager = di(); -class MainMenuBottomNavigation extends WatchingWidget { - MainMenuBottomNavigation({super.key}); +class MainMenuBottomNavigation extends WatchingStatefulWidget { + const MainMenuBottomNavigation({super.key}); + @override + State createState() => + _MainMenuBottomNavigationState(); +} + +class _MainMenuBottomNavigationState extends State { final List pages = [ const PupilListsMenuPage(), const SchoolListsMenuPage(), @@ -36,44 +42,41 @@ class MainMenuBottomNavigation extends WatchingWidget { void showHeavyLoadingOverlay(BuildContext context) { overlayEntry = OverlayEntry( - builder: - (context) => const Stack( - fit: StackFit.expand, - children: [ - ModalBarrier( - dismissible: false, - color: Color.fromARGB( - 108, - 0, - 0, - 0, - ), // Colors.black.withOpacity(0.3) - ), // Background color - Material( - color: Colors.transparent, - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - 'Bitte warten...', // Your text here - style: TextStyle( - color: Colors.white, // Text color - fontSize: 20, // Text size - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 16), - CircularProgressIndicator( - color: AppColors.interactiveColor, - ), - ], + builder: (context) => const Stack( + fit: StackFit.expand, + children: [ + ModalBarrier( + dismissible: false, + color: Color.fromARGB( + 108, + 0, + 0, + 0, + ), // Colors.black.withOpacity(0.3) + ), // Background color + Material( + color: Colors.transparent, + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Bitte warten...', // Your text here + style: TextStyle( + color: Colors.white, // Text color + fontSize: 20, // Text size + fontWeight: FontWeight.bold, + ), ), - ), + SizedBox(height: 16), + CircularProgressIndicator(color: AppColors.interactiveColor), + ], ), - ], + ), ), + ], + ), ); Overlay.of(context).insert(overlayEntry!); @@ -98,16 +101,15 @@ class MainMenuBottomNavigation extends WatchingWidget { } else { // Create new banner _notificationOverlayEntry = OverlayEntry( - builder: - (context) => Positioned( - top: 0, - left: 0, - right: 0, - child: NotificationBanner( - notifications: List.from(_notifications), - onDismiss: hideNotificationBanner, - ), - ), + builder: (context) => Positioned( + top: 0, + left: 0, + right: 0, + child: NotificationBanner( + notifications: List.from(_notifications), + onDismiss: hideNotificationBanner, + ), + ), ); Overlay.of(context).insert(_notificationOverlayEntry!); @@ -169,8 +171,8 @@ class MainMenuBottomNavigation extends WatchingWidget { }, ); callOnce((context) async { - final envDataIncomplete = - di().isAnyImportantEnvDataNotPopulatedInServer(); + final envDataIncomplete = di() + .isAnyImportantEnvDataNotPopulatedInServer(); if (envDataIncomplete) { final serverDataStatus = _envManager.populatedEnvServerData; final List missingFields = []; @@ -261,67 +263,64 @@ OverlayEntry? overlayEntry; void showInstanceLoadingOverlay(BuildContext context) { final locale = AppLocalizations.of(context)!; overlayEntry = OverlayEntry( - builder: - (context) => Stack( - fit: StackFit.expand, - children: [ - const ModalBarrier( - dismissible: false, - color: AppColors.backgroundColor, // Colors.black.withOpacity(0.3) - ), // Background color - Material( - color: Colors.transparent, - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: 300, - width: 300, - child: Image(image: AssetImage('assets/foreground.png')), - ), - Text( - locale.schoolDataHub, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 30, - ), - ), - const Gap(15), - if (di().activeEnv != null) - Text( - di().activeEnv!.serverName, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 22, - ), - ), - const Gap(10), - const Text( - 'Instanzdaten werden geladen!', - style: TextStyle(color: Colors.white, fontSize: 20), - ), - const Gap(5), - const Text( - 'Bitte warten...', // Your text here - style: TextStyle( - color: Colors.white, // Text color - fontSize: 20, - fontWeight: FontWeight.bold, // Text size - ), - ), - const SizedBox(height: 16), - const CircularProgressIndicator( - color: AppColors.accentColor, + builder: (context) => Stack( + fit: StackFit.expand, + children: [ + const ModalBarrier( + dismissible: false, + color: AppColors.backgroundColor, // Colors.black.withOpacity(0.3) + ), // Background color + Material( + color: Colors.transparent, + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 300, + width: 300, + child: Image(image: AssetImage('assets/foreground.png')), + ), + Text( + locale.schoolDataHub, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 30, + ), + ), + const Gap(15), + if (di().activeEnv != null) + Text( + di().activeEnv!.serverName, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 22, ), - ], + ), + const Gap(10), + const Text( + 'Instanzdaten werden geladen!', + style: TextStyle(color: Colors.white, fontSize: 20), ), - ), + const Gap(5), + const Text( + 'Bitte warten...', // Your text here + style: TextStyle( + color: Colors.white, // Text color + fontSize: 20, + fontWeight: FontWeight.bold, // Text size + ), + ), + const SizedBox(height: 16), + const CircularProgressIndicator(color: AppColors.accentColor), + ], ), - ], + ), ), + ], + ), ); Overlay.of(context).insert(overlayEntry!); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/after_school_care_content/pupil_ogs_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/after_school_care_content/pupil_ogs_content.dart index 20d0afa4..c5578dfa 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/after_school_care_content/pupil_ogs_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/after_school_care_content/pupil_ogs_content.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; import 'package:school_data_hub_flutter/features/ogs/widgets/ogs_details.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; class PupilOgsContent extends StatelessWidget { final PupilProxy pupil; @@ -11,33 +10,18 @@ class PupilOgsContent extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(Icons.lightbulb, color: AppColors.accentColor, size: 24), - Gap(5), - Text( - 'OGS-Informationen', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - ), - ), - ], - ), - const Gap(15), - OgsDetails(pupil: pupil), - ], - ), + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, + ), + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.access_time, + title: 'OGS-Informationen', + child: OgsDetails(pupil: pupil), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart index 7c972c29..eed11568 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; import 'package:school_data_hub_flutter/features/_attendance/domain/attendance_helper_functions.dart'; import 'package:school_data_hub_flutter/features/_attendance/domain/attendance_manager.dart'; import 'package:school_data_hub_flutter/features/_attendance/presentation/missed_classes_pupil_list_page/missed_classes_pupil_list_page.dart'; import 'package:school_data_hub_flutter/features/_attendance/presentation/widgets/attendance_stats_pupil.dart'; import 'package:school_data_hub_flutter/features/_attendance/presentation/widgets/missed_class_card.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:watch_it/watch_it.dart'; final _attendanceManager = di(); @@ -29,88 +29,71 @@ class PupilProfileAttendanceContent extends StatelessWidget { missedSchooldays.sort( (b, a) => a.schoolday!.schoolday.compareTo(b.schoolday!.schoolday), ); - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon( - Icons.calendar_month_rounded, - color: Colors.grey[700], - size: 24, + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, + ), + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.calendar_month_rounded, + title: 'Fehlzeiten', + onTitleTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => const MissedSchooldayesPupilListPage(), ), - const Gap(5), - InkWell( - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => - const MissedSchooldayesPupilListPage(), + ); + }, + child: Column( + children: [ + attendanceStats(pupil), + const Gap(10), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Text('Fehlstunden:', style: TextStyle(fontSize: 14)), + Text( + ' ${missedHoursForActualReport.missed.toString()}', + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 16, ), - ); - }, - child: const Text( - 'Fehlzeiten', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, ), - ), - ), - ], - ), - const Gap(15), - attendanceStats(pupil), - const Gap(10), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('Fehlstunden:', style: TextStyle(fontSize: 14)), - Text( - ' ${missedHoursForActualReport.missed.toString()}', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), + const Gap(5), + const Text('davon unent:', style: TextStyle(fontSize: 14)), + Text( + ' ${missedHoursForActualReport.unexcused.toString()}', + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const Gap(15), + ], ), - const Gap(5), - const Text('davon unent:', style: TextStyle(fontSize: 14)), - Text( - ' ${missedHoursForActualReport.unexcused.toString()}', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), + const Gap(10), + ListView.builder( + padding: const EdgeInsets.only(top: 5, bottom: 15), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: missedSchooldays.length, + itemBuilder: (BuildContext context, int index) { + // pupil.pupilMissedSchooldayes.sort( + // (a, b) => a.missedDay.compareTo(b.missedDay)); + + return MissedSchooldayCard( + pupil: pupil, + missedSchoolday: missedSchooldays[index], + ); + }, ), - const Gap(15), ], ), - const Gap(10), - ListView.builder( - padding: const EdgeInsets.only(top: 5, bottom: 15), - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: missedSchooldays.length, - itemBuilder: (BuildContext context, int index) { - // pupil.pupilMissedSchooldayes.sort( - // (a, b) => a.missedDay.compareTo(b.missedDay)); - - return MissedSchooldayCard( - pupil: pupil, - missedSchoolday: missedSchooldays[index], - ); - }, - ), - ], - ), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content.dart index 9a168a92..2c272ff1 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content_list.dart'; import 'package:school_data_hub_flutter/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_profile_authorization_content_list.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; class PupilProfileAuthorizationContent extends StatelessWidget { final PupilProxy pupil; @@ -12,38 +11,25 @@ class PupilProfileAuthorizationContent extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, ), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column(children: [ - Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.fact_check_rounded, - color: AppColors.backgroundColor, - size: 24, - ), - const Gap(5), - InkWell( - onTap: () { - Navigator.of(context).push(MaterialPageRoute( + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.fact_check_rounded, + title: 'Einwilligungen', + onTitleTap: () { + Navigator.of(context).push( + MaterialPageRoute( builder: (ctx) => const AuthorizationsListPage(), - )); - }, - child: const Text('Einwilligungen', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - )), - ) - ]), - const Gap(15), - PupilProfileAuthorizationContentList(pupil: pupil), - ]), + ), + ); + }, + child: PupilProfileAuthorizationContentList(pupil: pupil), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart index 5fcbc4c1..dc3d1ca3 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart @@ -12,6 +12,7 @@ import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/communication_values.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:watch_it/watch_it.dart'; final _pupilManager = di(); @@ -36,50 +37,20 @@ class PupilProfileCommunicationContent extends WatchingWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Header Section - Container( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - decoration: BoxDecoration( - color: AppColors.canvasColor, - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: AppColors.backgroundColor.withValues(alpha: 0.2), - width: 1, - ), - ), - child: const Row( - children: [ - Icon( - Icons.language_rounded, - color: AppColors.groupColor, - size: 28, - ), - Gap(12), - Text( - 'Sprachen', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - ), - ), - ], - ), - ), - const Gap(8), // Language Information Section - _buildInfoSection( + // Language Information Section + PupilProfileContentSection( icon: Icons.translate_rounded, title: 'Sprachinformationen', child: Column( children: [ - _buildInfoRow( + PupilProfileContentRow( icon: Icons.home_outlined, label: 'Familiensprache', value: pupil.language, ), const Gap(8), - _buildInfoRow( + PupilProfileContentRow( icon: Icons.support_outlined, label: 'Erstförderung', value: pupil.migrationSupportEnds != null @@ -87,7 +58,7 @@ class PupilProfileCommunicationContent extends WatchingWidget { : 'keine', ), const Gap(8), - _buildInfoRow( + PupilProfileContentRow( icon: Icons.support_outlined, label: 'HKU', value: pupil.familyLanguageLessonsSince != null @@ -99,14 +70,27 @@ class PupilProfileCommunicationContent extends WatchingWidget { ), const Gap(8), // German Language Competence Section - _buildInfoSection( + // German Language Competence Section + PupilProfileContentSection( icon: Icons.record_voice_over_outlined, title: 'Deutsch - Sprachkompetenz', child: Column( children: [ - _buildCommunicationRow( + PupilProfileContentRow( + icon: Icons.person_outline, label: 'Kind', - communicationSkills: communicationPupil, + valueWidget: communicationPupil == null + ? const Text( + 'kein Eintrag - tippen zum Hinzufügen', + style: TextStyle( + fontSize: 14, + fontStyle: FontStyle.italic, + color: AppColors.interactiveColor, + ), + ) + : CommunicationValues( + communicationSkills: communicationPupil, + ), onTap: () => languageDialog( context, pupil, @@ -135,9 +119,21 @@ class PupilProfileCommunicationContent extends WatchingWidget { }, ), const Gap(10), - _buildCommunicationRow( + PupilProfileContentRow( + icon: Icons.person_outline, label: 'Mutter / TutorIn 1', - communicationSkills: tutorInfo?.communicationTutor1, + valueWidget: tutorInfo?.communicationTutor1 == null + ? const Text( + 'kein Eintrag - tippen zum Hinzufügen', + style: TextStyle( + fontSize: 14, + fontStyle: FontStyle.italic, + color: AppColors.interactiveColor, + ), + ) + : CommunicationValues( + communicationSkills: tutorInfo!.communicationTutor1, + ), onTap: () => languageDialog( context, pupil, @@ -169,9 +165,21 @@ class PupilProfileCommunicationContent extends WatchingWidget { }, ), const Gap(10), - _buildCommunicationRow( + PupilProfileContentRow( + icon: Icons.person_outline, label: 'Vater / TutorIn 2', - communicationSkills: tutorInfo?.communicationTutor2, + valueWidget: tutorInfo?.communicationTutor2 == null + ? const Text( + 'kein Eintrag - tippen zum Hinzufügen', + style: TextStyle( + fontSize: 14, + fontStyle: FontStyle.italic, + color: AppColors.interactiveColor, + ), + ) + : CommunicationValues( + communicationSkills: tutorInfo!.communicationTutor2, + ), onTap: () => languageDialog( context, pupil, @@ -212,195 +220,4 @@ class PupilProfileCommunicationContent extends WatchingWidget { ), ); } - - // Helper method to build section containers - Widget _buildInfoSection({ - required IconData icon, - required String title, - required Widget child, - }) { - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: AppColors.backgroundColor.withValues(alpha: 0.2), - width: 1.5, - ), - color: AppColors.cardInCardColor, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(icon, color: AppColors.backgroundColor, size: 25), - const Gap(10), - Text( - title, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - ), - ), - ], - ), - const Gap(12), - child, - ], - ), - ); - } - - // Helper method to build information rows - Widget _buildInfoRow({ - required IconData icon, - required String label, - required String value, - VoidCallback? onTap, - VoidCallback? onLongPress, - Widget? actionButton, - }) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 12), - decoration: BoxDecoration( - color: AppColors.pupilProfileCardColor.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: AppColors.backgroundColor.withValues(alpha: 0.1), - width: 1, - ), - ), - child: Row( - children: [ - Icon( - icon, - color: AppColors.backgroundColor.withValues(alpha: 0.7), - size: 18, - ), - const Gap(8), - Text( - '$label:', - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: AppColors.backgroundColor, - ), - ), - const Gap(6), - Expanded( - child: InkWell( - onTap: onTap, - onLongPress: onLongPress, - borderRadius: BorderRadius.circular(8), - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 12, - ), - decoration: BoxDecoration( - color: onTap != null - ? AppColors.interactiveColor.withValues(alpha: 0.1) - : Colors.transparent, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - value, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: onTap != null - ? AppColors.interactiveColor - : Colors.black87, - ), - ), - ), - ), - ), - if (actionButton != null) ...[const Gap(8), actionButton], - ], - ), - ); - } - - // Helper method to build communication rows - Widget _buildCommunicationRow({ - required String label, - required CommunicationSkills? communicationSkills, - VoidCallback? onTap, - VoidCallback? onLongPress, - }) { - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: AppColors.pupilProfileCardColor.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: AppColors.backgroundColor.withValues(alpha: 0.1), - width: 1, - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: AppColors.backgroundColor.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(6), - ), - child: Icon( - Icons.person_outline, - color: AppColors.backgroundColor, - size: 18, - ), - ), - const Gap(3), - Text( - '$label:', - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - color: AppColors.backgroundColor, - ), - ), - ], - ), - const Gap(8), - InkWell( - onTap: onTap, - onLongPress: onLongPress, - borderRadius: BorderRadius.circular(8), - child: Container( - width: double.infinity, - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: AppColors.interactiveColor.withValues(alpha: 0.05), - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: AppColors.interactiveColor.withValues(alpha: 0.2), - width: 1, - ), - ), - child: communicationSkills == null - ? const Text( - 'kein Eintrag - tippen zum Hinzufügen', - style: TextStyle( - fontSize: 14, - fontStyle: FontStyle.italic, - color: AppColors.interactiveColor, - ), - ) - : CommunicationValues( - communicationSkills: communicationSkills, - ), - ), - ), - ], - ), - ); - } } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/credit/pupil_profile_credit_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/credit/pupil_profile_credit_content.dart index 22c84980..a6ea8ca1 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/credit/pupil_profile_credit_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/credit/pupil_profile_credit_content.dart @@ -3,11 +3,11 @@ import 'package:gap/gap.dart'; import 'package:intl/intl.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/_credit/credit_list_page/credit_list_page.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/_credit/credit_list_page/widgets/dialogues/change_credit_dialog.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:watch_it/watch_it.dart'; class PupilProfileCreditContent extends WatchingWidget { @@ -17,167 +17,167 @@ class PupilProfileCreditContent extends WatchingWidget { @override Widget build(BuildContext context) { final credit = watchPropertyValue((x) => x.credit, target: pupil); - final creditTransactions = - watchPropertyValue((x) => x.creditTransactions, target: pupil); - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + final creditTransactions = watchPropertyValue( + (x) => x.creditTransactions, + target: pupil, + ); + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, ), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Icon( - Icons.attach_money_rounded, - color: AppColors.accentColor, - size: 24, - ), - const Gap(5), - InkWell( - onTap: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (ctx) => const CreditListPage(), - )); - }, - child: const Text('Guthaben', - style: TextStyle( - fontSize: 24, + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.attach_money_rounded, + title: 'Guthaben', + onTitleTap: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (ctx) => const CreditListPage()), + ); + }, + headerTrailing: Row( + children: [ + Text( + credit.toString(), + style: const TextStyle( + color: AppColors.groupColor, + fontSize: 60, fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - )), - ), - const Spacer(), - Text( - credit.toString(), - style: const TextStyle( - color: AppColors.groupColor, - fontSize: 60, - fontWeight: FontWeight.bold), - ), - const Gap(20), - ]), - //const Gap(15), - Padding( - padding: const EdgeInsets.all(10.0), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12), - //margin: const EdgeInsets.only(bottom: 16), - width: double.infinity, - child: ElevatedButton( - style: AppStyles.successButtonStyle, - onPressed: () async { - changeCreditDialog(context, pupil); - }, - child: const Text( - "GUTHABEN ÄNDERN", - style: AppStyles.buttonTextStyle, + ), ), - ), + const Gap(20), + ], ), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Insgesamt verdient:', - style: TextStyle(fontSize: 18.0), - textAlign: TextAlign.left, - ), - const Gap(5), - Text(pupil.creditEarned.toString(), - style: const TextStyle( - fontSize: 18.0, fontWeight: FontWeight.bold)), - ], - ), - const Gap(10), - const Row( - children: [ - Padding( - padding: EdgeInsets.only(left: 22), - child: Text( - 'Verlauf', - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 20, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + //margin: const EdgeInsets.only(bottom: 16), + width: double.infinity, + child: ElevatedButton( + style: AppStyles.successButtonStyle, + onPressed: () async { + changeCreditDialog(context, pupil); + }, + child: const Text( + "GUTHABEN ÄNDERN", + style: AppStyles.buttonTextStyle, + ), + ), ), ), - ), - ], - ), - const Gap(10), - if (creditTransactions != null) - ListView.builder( - padding: const EdgeInsets.only(left: 20, bottom: 15), - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: creditTransactions.length, - itemBuilder: (BuildContext context, int index) { - final List pupilCreditHistoryLogs = - List.from(pupil.creditTransactions!); - // order by date latest first - // pupilCreditHistoryLogs - // .sort((a, b) => b.createdAt.compareTo(a.createdAt)); - CreditTransaction creditHistoryLog = - pupilCreditHistoryLogs[index]; + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'Insgesamt verdient:', + style: TextStyle(fontSize: 18.0), + textAlign: TextAlign.left, + ), + const Gap(5), + Text( + pupil.creditEarned.toString(), + style: const TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const Gap(10), + const Row( + children: [ + Padding( + padding: EdgeInsets.only(left: 22), + child: Text( + 'Verlauf', + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + ), + ], + ), + const Gap(10), + if (creditTransactions != null) + ListView.builder( + padding: const EdgeInsets.only(left: 20, bottom: 15), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: creditTransactions.length, + itemBuilder: (BuildContext context, int index) { + final List pupilCreditHistoryLogs = + List.from(pupil.creditTransactions!); + // order by date latest first + // pupilCreditHistoryLogs + // .sort((a, b) => b.createdAt.compareTo(a.createdAt)); + CreditTransaction creditHistoryLog = + pupilCreditHistoryLogs[index]; - return Padding( - padding: const EdgeInsets.all(5.0), - child: GestureDetector( - onTap: () { - //- TO-DO: change missed class function - //- like _changeMissedSchooldayHermannpupilPage - }, - onLongPress: () async {}, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - DateFormat('dd.MM.yyyy') - .format(creditHistoryLog.dateTime) - .toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 20, + return Padding( + padding: const EdgeInsets.all(5.0), + child: GestureDetector( + onTap: () { + //- TO-DO: change missed class function + //- like _changeMissedSchooldayHermannpupilPage + }, + onLongPress: () async {}, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + DateFormat('dd.MM.yyyy') + .format(creditHistoryLog.dateTime) + .toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + const Gap(10), + const Text('Betrag:'), + const Gap(10), + Text( + creditHistoryLog.amount.toString(), + style: TextStyle( + color: creditHistoryLog.amount < 0 + ? Colors.red + : AppColors.groupColor, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + const Gap(10), + const Text('von:'), + const Gap(10), + Text( + creditHistoryLog.sender, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + ], ), ), - const Gap(10), - const Text('Betrag:'), - const Gap(10), - Text( - creditHistoryLog.amount.toString(), - style: TextStyle( - color: creditHistoryLog.amount < 0 - ? Colors.red - : AppColors.groupColor, - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), - const Gap(10), - const Text('von:'), - const Gap(10), - Text( - creditHistoryLog.sender, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ) - ], - ), - ), + ), + ); + }, ), - ); - }, + ], ), - ]), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart index 276bdc29..7c3f0fc6 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart @@ -19,6 +19,7 @@ import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avatar.dart'; import 'package:watch_it/watch_it.dart'; @@ -47,32 +48,10 @@ class PupilProfileInfosContent extends WatchingWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Section - const Card( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - child: Row( - children: [ - Icon( - Icons.info_rounded, - color: AppColors.backgroundColor, - size: 28, - ), - const Gap(12), - Text( - 'Persönliche Informationen', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - ), - ), - ], - ), - ), - ), - const Gap(8), // Reduced from 24 to 16 + + // Special Information Section // Special Information Section - _buildInfoSection( + PupilProfileContentSection( icon: Icons.priority_high_rounded, title: 'Besondere Infos', child: InkWell( @@ -140,24 +119,25 @@ class PupilProfileInfosContent extends WatchingWidget { ), const Gap(8), // Basic Information Section - _buildInfoSection( + // Basic Information Section + PupilProfileContentSection( icon: Icons.person_outline, title: 'Grunddaten', child: Column( children: [ - _buildInfoRow( + PupilProfileContentRow( icon: Icons.wc, label: 'Geschlecht', value: pupil.gender == 'm' ? 'männlich' : 'weiblich', ), - const Gap(8), // Reduced from 12 to 8 - _buildInfoRow( + const Gap(8), + PupilProfileContentRow( icon: Icons.cake_outlined, label: 'Geburtsdatum', value: pupil.birthday.formatDateForUser(), ), - const Gap(8), // Reduced from 12 to 8 - _buildInfoRow( + const Gap(8), + PupilProfileContentRow( icon: Icons.school_outlined, label: 'Aufnahmedatum', value: pupil.pupilSince.formatDateForUser(), @@ -167,12 +147,13 @@ class PupilProfileInfosContent extends WatchingWidget { ), const Gap(8), // Contact Information Section - _buildInfoSection( + // Contact Information Section + PupilProfileContentSection( icon: Icons.contact_phone_outlined, title: 'Kontaktinformationen', child: Column( children: [ - _buildContactRow( + PupilProfileContentRow( icon: Icons.person_outline, label: 'Schüler/in Kontakt', value: pupil.contact?.isNotEmpty == true @@ -262,8 +243,8 @@ class PupilProfileInfosContent extends WatchingWidget { ), ), ), - const Gap(10), // Reduced from 16 to 10 - _buildContactRow( + const Gap(10), + PupilProfileContentRow( icon: Icons.family_restroom_outlined, label: 'Familie Kontakt', value: pupil.tutorInfo?.parentsContact ?? 'keine Angabe', @@ -374,7 +355,8 @@ class PupilProfileInfosContent extends WatchingWidget { ), const Gap(8), // Authorizations Section - _buildInfoSection( + // Authorizations Section + PupilProfileContentSection( icon: Icons.verified_user_outlined, title: 'Einwilligungen', child: Column( @@ -387,7 +369,8 @@ class PupilProfileInfosContent extends WatchingWidget { ), const Gap(8), // Siblings Section - _buildInfoSection( + // Siblings Section + PupilProfileContentSection( icon: Icons.family_restroom_outlined, title: 'Geschwister', child: pupilSiblings.isNotEmpty @@ -496,181 +479,4 @@ class PupilProfileInfosContent extends WatchingWidget { ), ); } - - // Helper method to build section containers - Widget _buildInfoSection({ - required IconData icon, - required String title, - required Widget child, - }) { - return Card( - child: Padding( - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(icon, color: AppColors.backgroundColor, size: 25), - const Gap(10), // Reduced from 12 to 10 - Text( - title, - style: const TextStyle( - fontSize: 20, // Reduced from 20 to 18 - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - ), - ), - ], - ), - const Gap(12), // Reduced from 16 to 12 - child, - ], - ), - ), - ); - } - - // Helper method to build information rows - Widget _buildInfoRow({ - required IconData icon, - required String label, - required String value, - VoidCallback? onTap, - VoidCallback? onLongPress, - Widget? actionButton, - }) { - return Card( - child: Row( - children: [ - Icon( - icon, - color: AppColors.backgroundColor.withValues(alpha: 0.7), - size: 18, // Reduced from 20 to 18 - ), - const Gap(8), // Reduced from 12 to 8 - Text( - '$label:', - style: const TextStyle( - fontSize: 14, // Reduced from 16 to 14 - fontWeight: FontWeight.w500, - color: AppColors.backgroundColor, - ), - ), - const Gap(6), // Reduced from 8 to 6 - Expanded( - child: InkWell( - onTap: onTap, - onLongPress: onLongPress, - borderRadius: BorderRadius.circular(8), - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 12, - ), - decoration: BoxDecoration( - color: onTap != null - ? AppColors.interactiveColor.withValues(alpha: 0.1) - : Colors.transparent, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - value, - style: TextStyle( - fontSize: 14, // Reduced from 16 to 14 - fontWeight: FontWeight.w600, - color: onTap != null - ? AppColors - .interactiveColor // Keep blue for interactive elements - : Colors - .black87, // Changed from AppColors.backgroundColor to black for non-interactive - ), - ), - ), - ), - ), - if (actionButton != null) ...[const Gap(8), actionButton], - ], - ), - ); - } - - // Helper method to build contact rows - Widget _buildContactRow({ - required IconData icon, - required String label, - required String value, - VoidCallback? onTap, - VoidCallback? onLongPress, - Widget? actionButton, - }) { - return Card( - child: Padding( - padding: const EdgeInsets.all(4), // Reduced from 16 to 12 - child: Row( - children: [ - Container( - padding: const EdgeInsets.all(6), // Reduced from 8 to 6 - decoration: BoxDecoration( - color: AppColors.backgroundColor.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(6), // Reduced from 8 to 6 - ), - child: Icon( - icon, - color: AppColors.backgroundColor, - size: 18, - ), // Reduced from 20 to 18 - ), - const Gap(10), // Reduced from 12 to 10 - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: TextStyle( - fontSize: 12, // Reduced from 14 to 12 - fontWeight: FontWeight.w500, - color: Colors.grey.withValues(alpha: 0.8), - ), - ), - const Gap(3), // Reduced from 4 to 3 - InkWell( - onTap: onTap, - onLongPress: onLongPress, - borderRadius: BorderRadius.circular(8), - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 12, - ), - decoration: BoxDecoration( - color: onTap != null - ? AppColors.interactiveColor.withValues(alpha: 0.1) - : Colors.transparent, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - value, - style: TextStyle( - fontSize: 14, // Reduced from 16 to 14 - fontWeight: FontWeight.w600, - color: onTap != null - ? AppColors - .interactiveColor // Keep blue for interactive elements - : Colors - .black87, // Changed from AppColors.backgroundColor to black for non-interactive - ), - ), - ), - ), - ], - ), - ), - if (actionButton != null) ...[const Gap(8), actionButton], - ], - ), - ), - ); - } } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart index 8035f78e..dd548905 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart @@ -3,12 +3,12 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/competence_checks_badges.dart'; import 'package:school_data_hub_flutter/features/learning/presentation/widgets/pupil_learning_content_expansion_tile_nav_bar.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:watch_it/watch_it.dart'; class PupilLearningContent extends WatchingWidget { @@ -18,136 +18,126 @@ class PupilLearningContent extends WatchingWidget { @override Widget build(BuildContext context) { watch(pupil); - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(Icons.lightbulb, color: AppColors.accentColor, size: 24), - Gap(5), - Text( - 'Lernen', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - ), - ), - ], - ), - const Gap(10), - Row( + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, + ), + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.lightbulb, + title: 'Lernen', + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Gap(5), - const Text('3 Jahre Eingangsphase?'), - const Gap(5), - InkWell( - onTap: () async { - final date = await showCalendarDatePicker2Dialog( - context: context, - config: CalendarDatePicker2WithActionButtonsConfig( - // selectableDayPredicate: (day) => - // !schooldayDates.any((element) => element.isSameDate(day)), - calendarType: CalendarDatePicker2Type.single, - ), - dialogSize: const Size(325, 400), - value: [], //schooldayDates, - borderRadius: BorderRadius.circular(15), - ); + Row( + children: [ + const Gap(5), + const Text('3 Jahre Eingangsphase?'), + const Gap(5), + InkWell( + onTap: () async { + final date = await showCalendarDatePicker2Dialog( + context: context, + config: CalendarDatePicker2WithActionButtonsConfig( + // selectableDayPredicate: (day) => + // !schooldayDates.any((element) => element.isSameDate(day)), + calendarType: CalendarDatePicker2Type.single, + ), + dialogSize: const Size(325, 400), + value: [], //schooldayDates, + borderRadius: BorderRadius.circular(15), + ); - if (date != null && date.isNotEmpty) { - di().updateSchoolyearHeldBackDate( - pupilId: pupil.pupilId, - date: (value: date.first!.toUtc()), - ); - } - }, - onLongPress: () async { - if (pupil.schoolyearHeldBackAt == null) return; - final confirmation = await confirmationDialog( - context: context, - title: 'Eintrag löschen', - message: 'Eintrag wirklich löschen?', - ); - if (confirmation != true) return; - di().updateSchoolyearHeldBackDate( - pupilId: pupil.internalId, - date: (value: null), - ); - }, - child: Text( - pupil.schoolyearHeldBackAt != null - ? 'Entscheidung vom ${pupil.schoolyearHeldBackAt!.formatDateForUser()}' - : 'nein', - style: const TextStyle( - color: AppColors.interactiveColor, - fontWeight: FontWeight.bold, + if (date != null && date.isNotEmpty) { + di().updateSchoolyearHeldBackDate( + pupilId: pupil.pupilId, + date: (value: date.first!.toUtc()), + ); + } + }, + onLongPress: () async { + if (pupil.schoolyearHeldBackAt == null) return; + final confirmation = await confirmationDialog( + context: context, + title: 'Eintrag löschen', + message: 'Eintrag wirklich löschen?', + ); + if (confirmation != true) return; + di().updateSchoolyearHeldBackDate( + pupilId: pupil.internalId, + date: (value: null), + ); + }, + child: Text( + pupil.schoolyearHeldBackAt != null + ? 'Entscheidung vom ${pupil.schoolyearHeldBackAt!.formatDateForUser()}' + : 'nein', + style: const TextStyle( + color: AppColors.interactiveColor, + fontWeight: FontWeight.bold, + ), + ), ), - ), + ], ), - ], - ), - const Gap(5), - Row( - children: [ - const Gap(5), - const Text('Klassenleitung:'), const Gap(5), - Text( - pupil.groupTutor ?? 'Kein Eintrag', - style: const TextStyle(fontWeight: FontWeight.bold), + Row( + children: [ + const Gap(5), + const Text('Klassenleitung:'), + const Gap(5), + Text( + pupil.groupTutor ?? 'Kein Eintrag', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], ), - ], - ), - if (pupil.familyLanguageLessonsSince != null) ...[ - const Gap(5), - Row( - children: [ - const Gap(5), - const Text('HSU seit:'), + if (pupil.familyLanguageLessonsSince != null) ...[ const Gap(5), - Text( - pupil.familyLanguageLessonsSince?.formatDateForUser() ?? - 'Kein Eintrag', - style: const TextStyle(fontWeight: FontWeight.bold), + Row( + children: [ + const Gap(5), + const Text('HSU seit:'), + const Gap(5), + Text( + pupil.familyLanguageLessonsSince?.formatDateForUser() ?? + 'Kein Eintrag', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Text(pupil.language), + ], ), - Text(pupil.language), ], - ), - ], - if (pupil.religionLessonsSince != null) ...[ - const Gap(5), - Row( - children: [ - const Gap(5), - const Text('Religionsunterricht seit:'), + if (pupil.religionLessonsSince != null) ...[ const Gap(5), - Text( - pupil.religionLessonsSince?.formatDateForUser() ?? - 'Kein Eintrag', - style: const TextStyle(fontWeight: FontWeight.bold), + Row( + children: [ + const Gap(5), + const Text('Religionsunterricht seit:'), + const Gap(5), + Text( + pupil.religionLessonsSince?.formatDateForUser() ?? + 'Kein Eintrag', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + const Gap(5), + Text(pupil.religion ?? 'Kein Eintrag'), + ], ), - const Gap(5), - Text(pupil.religion ?? 'Kein Eintrag'), ], - ), - ], - const Gap(10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [CompetenceChecksBadges(pupil: pupil)], + const Gap(10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [CompetenceChecksBadges(pupil: pupil)], + ), + PupilLearningContentExpansionTileNavBar(pupil: pupil), + ], ), - PupilLearningContentExpansionTileNavBar(pupil: pupil), - ], - ), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content.dart index 7415ea58..d54fe4fd 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; import 'package:school_data_hub_flutter/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; class PupilProfileLearningSupportContent extends StatelessWidget { final PupilProxy pupil; @@ -12,38 +11,25 @@ class PupilProfileLearningSupportContent extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, ), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.support_rounded, - color: Colors.red, - size: 24, - ), - const Gap(5), - InkWell( - onTap: () { - Navigator.of(context).push(MaterialPageRoute( + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.support_rounded, + title: 'Förderung', + onTitleTap: () { + Navigator.of(context).push( + MaterialPageRoute( builder: (ctx) => const LearningSupportListPage(), - )); - }, - child: const Text('Förderung', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - )), - ), - ]), - const Gap(15), - PupilProfileLearningSupportContentList(pupil: pupil), - ]), + ), + ); + }, + child: PupilProfileLearningSupportContentList(pupil: pupil), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/pupil_school_lists_content_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/pupil_school_lists_content_card.dart index 36ffb1c0..fae9cda4 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/pupil_school_lists_content_card.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/pupil_school_lists_content_card.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_school_lists_content_list.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:school_data_hub_flutter/features/school_lists/presentation/school_lists_page/school_lists_page.dart'; class PupilSchoolListsContentCard extends StatelessWidget { @@ -12,38 +11,23 @@ class PupilSchoolListsContentCard extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, ), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column(children: [ - Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.rule, - color: AppColors.accentColor, - size: 24, - ), - const Gap(5), - InkWell( - onTap: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (ctx) => const SchoolListsPage(), - )); - }, - child: const Text('Listen', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - )), - ) - ]), - const Gap(15), - PupilSchoolListContentList(pupil: pupil), - ]), + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.rule, + title: 'Listen', + onTitleTap: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (ctx) => const SchoolListsPage()), + ); + }, + child: PupilSchoolListContentList(pupil: pupil), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/schoolday_events_content/pupil_profile_schoolday_events_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/schoolday_events_content/pupil_profile_schoolday_events_content.dart index 82dd23c7..9beecb58 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/schoolday_events_content/pupil_profile_schoolday_events_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/schoolday_events_content/pupil_profile_schoolday_events_content.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/paddings.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_events_list.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; class PupilProfileSchooldayEventsContent extends StatelessWidget { final PupilProxy pupil; @@ -12,38 +11,25 @@ class PupilProfileSchooldayEventsContent extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - color: AppColors.pupilProfileCardColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + return Container( + decoration: const BoxDecoration( + color: AppColors.pupilProfileBackgroundColor, ), - child: Padding( - padding: AppPaddings.pupilProfileCardPadding, - child: Column(children: [ - Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.warning_amber_rounded, - color: Color.fromARGB(255, 239, 66, 66), - size: 24, - ), - const Gap(5), - InkWell( - onTap: () { - Navigator.of(context).push(MaterialPageRoute( + child: Column( + children: [ + PupilProfileContentSection( + icon: Icons.warning_amber_rounded, + title: 'Ereignisse', + onTitleTap: () { + Navigator.of(context).push( + MaterialPageRoute( builder: (ctx) => const SchooldayEventListPage(), - )); - }, - child: const Text('Ereignisse', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - )), - ) - ]), - const Gap(15), - PupilSchooldayEventsList(pupil: pupil), - ]), + ), + ); + }, + child: PupilSchooldayEventsList(pupil: pupil), + ), + ], ), ); } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart new file mode 100644 index 00000000..7e53d444 --- /dev/null +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart @@ -0,0 +1,204 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; + +class PupilProfileContentHeader extends StatelessWidget { + final IconData icon; + final String title; + + const PupilProfileContentHeader({ + required this.icon, + required this.title, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + decoration: BoxDecoration( + color: AppColors.canvasColor, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: AppColors.backgroundColor.withValues(alpha: 0.2), + width: 1, + ), + ), + child: Row( + children: [ + Icon( + icon, + color: AppColors.groupColor, + size: 28, + ), + const Gap(12), + Text( + title, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: AppColors.backgroundColor, + ), + ), + ], + ), + ); + } +} + +class PupilProfileContentSection extends StatelessWidget { + final IconData icon; + final String title; + final Widget child; + final VoidCallback? onTitleTap; + final Widget? headerTrailing; + + const PupilProfileContentSection({ + required this.icon, + required this.title, + required this.child, + this.onTitleTap, + this.headerTrailing, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: AppColors.backgroundColor.withValues(alpha: 0.2), + width: 1.5, + ), + color: AppColors.cardInCardColor, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + InkWell( + onTap: onTitleTap, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 4.0, horizontal: 4.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, color: AppColors.backgroundColor, size: 25), + const Gap(10), + Text( + title, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: AppColors.backgroundColor, + ), + ), + ], + ), + ), + ), + if (headerTrailing != null) ...[ + const Spacer(), + headerTrailing!, + ], + ], + ), + const Gap(12), + child, + ], + ), + ); + } +} + +class PupilProfileContentRow extends StatelessWidget { + final IconData icon; + final String label; + final String? value; + final Widget? valueWidget; + final VoidCallback? onTap; + final VoidCallback? onLongPress; + final Widget? actionButton; + + const PupilProfileContentRow({ + required this.icon, + required this.label, + this.value, + this.valueWidget, + this.onTap, + this.onLongPress, + this.actionButton, + super.key, + }) : assert(value != null || valueWidget != null, + 'Either value or valueWidget must be provided'); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 12), + decoration: BoxDecoration( + color: AppColors.pupilProfileCardColor.withValues(alpha: 0.5), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: AppColors.backgroundColor.withValues(alpha: 0.1), + width: 1, + ), + ), + child: Row( + children: [ + Icon( + icon, + color: AppColors.backgroundColor.withValues(alpha: 0.7), + size: 18, + ), + const Gap(8), + Text( + '$label:', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.backgroundColor, + ), + ), + const Gap(6), + Expanded( + child: InkWell( + onTap: onTap, + onLongPress: onLongPress, + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 12, + ), + decoration: BoxDecoration( + color: onTap != null + ? AppColors.interactiveColor.withValues(alpha: 0.1) + : Colors.transparent, + borderRadius: BorderRadius.circular(8), + ), + child: valueWidget ?? + Text( + value!, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: onTap != null + ? AppColors.interactiveColor + : Colors.black87, + ), + ), + ), + ), + ), + if (actionButton != null) ...[const Gap(8), actionButton!], + ], + ), + ); + } +} diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_search_text_field.dart b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_search_text_field.dart index fb352788..3090eded 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_search_text_field.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_search_text_field.dart @@ -8,11 +8,12 @@ class PupilSearchTextField extends WatchingStatefulWidget { final SearchType searchType; final String hintText; final Function refreshFunction; - const PupilSearchTextField( - {required this.searchType, - required this.hintText, - required this.refreshFunction, - super.key}); + const PupilSearchTextField({ + required this.searchType, + required this.hintText, + required this.refreshFunction, + super.key, + }); @override State createState() => _SearchTextFieldState(); @@ -26,7 +27,6 @@ class _SearchTextFieldState extends State { final textEditingController = TextEditingController(); @override Widget build(BuildContext context) { - final textFilter = watch(di().textFilter); final filtersOn = watchValue((FiltersStateManager x) => x.filtersActive); if (di().textFilter.text == '') { @@ -42,26 +42,19 @@ class _SearchTextFieldState extends State { filled: true, border: UnderlineInputBorder( borderSide: BorderSide.none, - borderRadius: BorderRadius.circular( - 12, - ), + borderRadius: BorderRadius.circular(12), ), hintText: widget.hintText, floatingLabelBehavior: FloatingLabelBehavior.never, prefixIcon: filtersOn ? IconButton( - icon: const Icon( - Icons.close_outlined, - ), + icon: const Icon(Icons.close_outlined), onPressed: () => di().resetFilters(), color: Colors.black45, ) : IconButton( onPressed: () => widget.refreshFunction, - icon: const Icon( - Icons.search_outlined, - color: Colors.black45, - ), + icon: const Icon(Icons.search_outlined, color: Colors.black45), ), suffixIcon: const SizedBox.shrink(), ), From e78d885755d032d99b1c5f5a3286a6608cb707f8 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Thu, 27 Nov 2025 08:22:41 +0100 Subject: [PATCH 10/52] Remove print statements --- .../edit_book_page/edit_book_controller.dart | 26 +-- .../users/data/matrix_user_api_service.dart | 45 +++-- .../select_matrix_users_list_controller.dart | 4 +- .../utils/stream_utils.dart | 5 +- .../managers/school_data_crud_manager.dart | 24 +-- .../domain/managers/school_data_manager.dart | 25 +-- .../managers/school_data_ui_manager.dart | 17 +- .../school/domain/school_data_manager.dart | 25 +-- .../domain/models/pupil_list_entry_proxy.dart | 4 +- .../timetable/data/timetable_api_service.dart | 75 ++++---- .../managers/timetable_crud_manager.dart | 87 +++++----- .../managers/timetable_data_manager.dart | 111 ++++++------ .../new_timetable_page.dart | 19 ++- .../new_timetable_slot_page.dart | 70 ++++---- .../timetable_page/timetable_page.dart | 25 ++- .../presentation/widgets/timetable_grid.dart | 161 +++++++++--------- 16 files changed, 386 insertions(+), 337 deletions(-) diff --git a/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_controller.dart b/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_controller.dart index a161da4b..8c0ca821 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_controller.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; @@ -9,6 +10,8 @@ import 'package:school_data_hub_flutter/features/books/presentation/book_tag_man import 'package:school_data_hub_flutter/features/books/presentation/edit_book_page/edit_book_page.dart'; import 'package:watch_it/watch_it.dart'; +final _log = Logger('EditBookController'); + class EditBook extends WatchingStatefulWidget { final LibraryBookProxy libraryBook; @@ -93,10 +96,10 @@ class EditBookController extends State { final allBookTags = di().bookTags.value; // Debug logging - print( + _log.info( 'DEBUG: Current book tags: ${currentBookTags.map((t) => '${t.name} (id: ${t.id})').toList()}', ); - print( + _log.info( 'DEBUG: All available tags: ${allBookTags.map((t) => '${t.name} (id: ${t.id})').toList()}', ); @@ -109,10 +112,12 @@ class EditBookController extends State { (currentTag) => currentTag.id == tag.id, ); bookTagSelection[tag] = isSelected; - print('DEBUG: Tag ${tag.name} (id: ${tag.id}) -> selected: $isSelected'); + _log.info( + 'DEBUG: Tag ${tag.name} (id: ${tag.id}) -> selected: $isSelected', + ); } - print( + _log.info( 'DEBUG: Final bookTagSelection: ${bookTagSelection.map((k, v) => MapEntry('${k.name} (id: ${k.id})', v))}', ); } @@ -124,8 +129,8 @@ class EditBookController extends State { // Find the matching location in the dropdown items selectedLocation = allLocations.firstWhere( (location) => location.id == currentLocation.id, - orElse: - () => currentLocation, // Fallback to current location if not found + orElse: () => + currentLocation, // Fallback to current location if not found ); } @@ -239,11 +244,10 @@ class EditBookController extends State { } // Get selected tags - final selectedTags = - bookTagSelection.entries - .where((entry) => entry.value) - .map((entry) => entry.key) - .toList(); + final selectedTags = bookTagSelection.entries + .where((entry) => entry.value) + .map((entry) => entry.key) + .toList(); // Update the book properties await di().updateLibraryBookAndBookProperties( diff --git a/school_data_hub_flutter/lib/features/matrix/users/data/matrix_user_api_service.dart b/school_data_hub_flutter/lib/features/matrix/users/data/matrix_user_api_service.dart index fe9c1c9a..f3c043bc 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/data/matrix_user_api_service.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/data/matrix_user_api_service.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:dio/dio.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_user.dart'; import 'package:school_data_hub_flutter/features/matrix/services/api/api_client.dart'; @@ -18,9 +19,9 @@ class MatrixUserApiService { required String matrixUrl, required String matrixToken, // Keep for potential future use required String corporalToken, // Keep for potential future use - }) : _apiClient = apiClient, - _matrixUrl = matrixUrl; - + }) : _apiClient = apiClient, + _matrixUrl = matrixUrl; + final _log = Logger('MatrixUserApiService'); void setMatrixEnvironmentValues({ required String url, required String matrixToken, @@ -46,14 +47,14 @@ class MatrixUserApiService { "admin": false, "displayname": displayName, "threepids": [], - "avatar_url": "" + "avatar_url": "", }); // Add before your PUT request - print('Matrix API Request:'); - print('URL: $_matrixUrl${_createMatrixUser(matrixId)}'); - print('Data: $data'); - print('Headers: ${_apiClient.matrixOptions.headers}'); + _log.info('Matrix API Request:'); + _log.info('URL: $_matrixUrl${_createMatrixUser(matrixId)}'); + _log.info('Data: $data'); + _log.info('Headers: ${_apiClient.matrixOptions.headers}'); final Response response = await _apiClient.put( '$_matrixUrl${_createMatrixUser(matrixId)}', @@ -63,9 +64,13 @@ class MatrixUserApiService { // statuscode 201 means: User created if (!(response.statusCode == 201 || response.statusCode == 200)) { _notificationService.showSnackBar( - NotificationType.error, 'Fehler: status code ${response.statusCode}'); + NotificationType.error, + 'Fehler: status code ${response.statusCode}', + ); throw ApiException( - 'Fehler beim Erstellen des Benutzers', response.statusCode); + 'Fehler beim Erstellen des Benutzers', + response.statusCode, + ); } final MatrixUser newUser = MatrixUser( id: matrixId, @@ -76,11 +81,15 @@ class MatrixUserApiService { ); if (response.statusCode == 201) { _notificationService.showSnackBar( - NotificationType.success, 'Benutzer erstellt'); + NotificationType.success, + 'Benutzer erstellt', + ); } if (response.statusCode == 200) { _notificationService.showSnackBar( - NotificationType.success, 'Deaktivierter Benutzer reaktiviert'); + NotificationType.success, + 'Deaktivierter Benutzer reaktiviert', + ); } return newUser; @@ -93,9 +102,7 @@ class MatrixUserApiService { } Future deleteMatrixUser(String userId) async { - final data = jsonEncode({ - "erase": true, - }); + final data = jsonEncode({"erase": true}); final Response response = await _apiClient.post( '$_matrixUrl${_deleteMatrixUser(userId)}', data: data, @@ -104,7 +111,9 @@ class MatrixUserApiService { if (response.statusCode != 200) { _notificationService.showSnackBar( - NotificationType.error, 'Fehler: status code ${response.statusCode}'); + NotificationType.error, + 'Fehler: status code ${response.statusCode}', + ); return false; } @@ -135,7 +144,9 @@ class MatrixUserApiService { if (response.statusCode != 200) { _notificationService.showSnackBar( - NotificationType.error, 'Fehler: status code ${response.statusCode}'); + NotificationType.error, + 'Fehler: status code ${response.statusCode}', + ); return false; } diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/select_matrix_users_list_page/controller/select_matrix_users_list_controller.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/select_matrix_users_list_page/controller/select_matrix_users_list_controller.dart index 378e8075..070748d9 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/select_matrix_users_list_page/controller/select_matrix_users_list_controller.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/select_matrix_users_list_page/controller/select_matrix_users_list_controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/pdf_viewer_page.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/filters/matrix_policy_filter_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_manager.dart'; @@ -19,6 +20,7 @@ class SelectMatrixUsersList extends WatchingStatefulWidget { } class SelectMatrixUsersListController extends State { + final _log = Logger('SelectMatrixUsersListController'); List? users; List? filteredUsers; //TODO: This needs to be changed to specific filters! @@ -154,7 +156,7 @@ class SelectMatrixUsersListController extends State { if (pdfFile != null) { // Show PDF preview if (context.mounted) { - print('Navigating to BulkPdfViewPage with file: ${pdfFile.path}'); + _log.info('Navigating to BulkPdfViewPage with file: ${pdfFile.path}'); Navigator.of(context).push( MaterialPageRoute( builder: (context) => PdfViewerPage(pdfFile: pdfFile), diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/utils/stream_utils.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/utils/stream_utils.dart index 9dd3183a..8aa71454 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/utils/stream_utils.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/utils/stream_utils.dart @@ -2,8 +2,11 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +final _log = Logger('StreamUtils'); + /// Utility functions for stream operations class StreamUtils { /// Generates a unique connection code @@ -84,7 +87,7 @@ class StreamUtils { /// Logs debug information in debug mode only static void debugLog(String message) { if (kDebugMode) { - print('[PupilIdentityStream] $message'); + _log.info('[PupilIdentityStream] $message'); } } diff --git a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_crud_manager.dart b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_crud_manager.dart index 96eefbb1..fa8e63b6 100644 --- a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_crud_manager.dart +++ b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_crud_manager.dart @@ -1,23 +1,25 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/features/school/data/school_data_api_service.dart'; /// Manages CRUD operations for school data -class SchoolDataCrudManager extends ChangeNotifier { +class SchoolInfoDataManager extends ChangeNotifier { final _apiService = SchoolDataApiService(); + final _log = Logger('SchoolInfoDataManager'); /// Fetch school data from the server Future fetchSchoolData() async { try { final schoolData = await _apiService.fetchSchoolData(); if (schoolData != null) { - print('School data fetched successfully: ${schoolData.name}'); + _log.info('School data fetched successfully: ${schoolData.name}'); } return schoolData; } catch (e) { - print('Error fetching school data: $e'); + _log.info('Error fetching school data: $e'); rethrow; } } @@ -27,11 +29,11 @@ class SchoolDataCrudManager extends ChangeNotifier { try { final createdSchoolData = await _apiService.postSchoolData(schoolData); if (createdSchoolData != null) { - print('School data saved successfully: ${createdSchoolData.name}'); + _log.info('School data saved successfully: ${createdSchoolData.name}'); } return createdSchoolData; } catch (e) { - print('Error saving school data: $e'); + _log.info('Error saving school data: $e'); rethrow; } } @@ -41,11 +43,11 @@ class SchoolDataCrudManager extends ChangeNotifier { try { final logoPath = await _apiService.uploadLogo(imageFile); if (logoPath != null) { - print('Logo uploaded successfully: $logoPath'); + _log.info('Logo uploaded successfully: $logoPath'); } return logoPath; } catch (e) { - print('Error uploading logo: $e'); + _log.info('Error uploading logo: $e'); rethrow; } } @@ -55,11 +57,11 @@ class SchoolDataCrudManager extends ChangeNotifier { try { final sealPath = await _apiService.uploadOfficialSeal(imageFile); if (sealPath != null) { - print('Official seal uploaded successfully: $sealPath'); + _log.info('Official seal uploaded successfully: $sealPath'); } return sealPath; } catch (e) { - print('Error uploading official seal: $e'); + _log.info('Error uploading official seal: $e'); rethrow; } } @@ -69,7 +71,7 @@ class SchoolDataCrudManager extends ChangeNotifier { try { return await _apiService.getLogoImage(documentId); } catch (e) { - print('Error getting logo image: $e'); + _log.info('Error getting logo image: $e'); rethrow; } } @@ -79,7 +81,7 @@ class SchoolDataCrudManager extends ChangeNotifier { try { return await _apiService.getOfficialSealImage(documentId); } catch (e) { - print('Error getting official seal image: $e'); + _log.info('Error getting official seal image: $e'); rethrow; } } diff --git a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart index 593b1a24..b380e1f7 100644 --- a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart +++ b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart @@ -1,6 +1,9 @@ import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +final _log = Logger('SchoolDataManager'); + /// Manages local school data storage and state class SchoolDataManager extends ChangeNotifier { // Current school data @@ -42,21 +45,21 @@ class SchoolDataManager extends ChangeNotifier { void setSchoolData(SchoolData schoolData) { _schoolData.value = schoolData; notifyListeners(); - print('School data updated: ${schoolData.name}'); + _log.info('School data updated: ${schoolData.name}'); } /// Set logo image void setLogoImage(ByteData? imageData) { _logoImage.value = imageData; notifyListeners(); - print('Logo image updated'); + _log.info('Logo image updated'); } /// Set official seal image void setOfficialSealImage(ByteData? imageData) { _officialSealImage.value = imageData; notifyListeners(); - print('Official seal image updated'); + _log.info('Official seal image updated'); } /// Set loading state @@ -73,16 +76,18 @@ class SchoolDataManager extends ChangeNotifier { /// Debug method to print current state void debugPrintState() { - print('=== SchoolDataManager Debug State ==='); - print( + _log.info('=== SchoolDataManager Debug State ==='); + _log.info( 'School Data: ${_schoolData.value?.name ?? 'null'} (ID: ${_schoolData.value?.id ?? 'null'})', ); - print('Logo Image: ${_logoImage.value != null ? 'loaded' : 'not loaded'}'); - print( + _log.info( + 'Logo Image: ${_logoImage.value != null ? 'loaded' : 'not loaded'}', + ); + _log.info( 'Official Seal Image: ${_officialSealImage.value != null ? 'loaded' : 'not loaded'}', ); - print('Is Loading: ${_isLoading.value}'); - print('Is Saving: ${_isSaving.value}'); - print('===================================='); + _log.info('Is Loading: ${_isLoading.value}'); + _log.info('Is Saving: ${_isSaving.value}'); + _log.info('===================================='); } } diff --git a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart index 956a70e1..86c2fc76 100644 --- a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart +++ b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart @@ -1,6 +1,9 @@ import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +final _log = Logger('SchoolDataUiManager'); + /// Manages UI state and form data for school data class SchoolDataUiManager extends ChangeNotifier { // Form data @@ -140,12 +143,12 @@ class SchoolDataUiManager extends ChangeNotifier { /// Debug method to print current state void debugPrintState() { - print('=== SchoolDataUiManager Debug State ==='); - print('Form Data: ${_formData.value?.name ?? 'null'}'); - print('Is Form Valid: ${_isFormValid.value}'); - print('Is Form Dirty: ${_isFormDirty.value}'); - print('Selected Logo File: ${_selectedLogoFile.value ?? 'none'}'); - print('Selected Seal File: ${_selectedSealFile.value ?? 'none'}'); - print('===================================='); + _log.info('=== SchoolDataUiManager Debug State ==='); + _log.info('Form Data: ${_formData.value?.name ?? 'null'}'); + _log.info('Is Form Valid: ${_isFormValid.value}'); + _log.info('Is Form Dirty: ${_isFormDirty.value}'); + _log.info('Selected Logo File: ${_selectedLogoFile.value ?? 'none'}'); + _log.info('Selected Seal File: ${_selectedSealFile.value ?? 'none'}'); + _log.info('===================================='); } } diff --git a/school_data_hub_flutter/lib/features/school/domain/school_data_manager.dart b/school_data_hub_flutter/lib/features/school/domain/school_data_manager.dart index 7164cfa5..8d803b35 100644 --- a/school_data_hub_flutter/lib/features/school/domain/school_data_manager.dart +++ b/school_data_hub_flutter/lib/features/school/domain/school_data_manager.dart @@ -1,23 +1,28 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/features/school/domain/managers/school_data_crud_manager.dart'; import 'package:school_data_hub_flutter/features/school/domain/managers/school_data_manager.dart' as data_manager; import 'package:school_data_hub_flutter/features/school/domain/managers/school_data_ui_manager.dart'; +final _log = Logger('SchoolDataMainManager'); + +// AI GENERATED CODE + /// Main school data manager that orchestrates all sub-managers /// This follows the established pattern from the timetable feature class SchoolDataMainManager extends ChangeNotifier { // Sub-managers final data_manager.SchoolDataManager _dataManager; - final SchoolDataCrudManager _crudManager; + final SchoolInfoDataManager _crudManager; final SchoolDataUiManager _uiManager; SchoolDataMainManager() : _dataManager = data_manager.SchoolDataManager(), - _crudManager = SchoolDataCrudManager(), + _crudManager = SchoolInfoDataManager(), _uiManager = SchoolDataUiManager(); // Expose data manager properties @@ -60,7 +65,7 @@ class SchoolDataMainManager extends ChangeNotifier { } } } catch (e) { - print('Error refreshing school data: $e'); + _log.severe('Error refreshing school data: $e'); rethrow; } finally { _dataManager.setLoading(false); @@ -73,7 +78,7 @@ class SchoolDataMainManager extends ChangeNotifier { final imageData = await _crudManager.getLogoImage(documentId); _dataManager.setLogoImage(imageData); } catch (e) { - print('Error loading logo image: $e'); + _log.severe('Error loading logo image: $e'); } } @@ -83,7 +88,7 @@ class SchoolDataMainManager extends ChangeNotifier { final imageData = await _crudManager.getOfficialSealImage(documentId); _dataManager.setOfficialSealImage(imageData); } catch (e) { - print('Error loading official seal image: $e'); + _log.severe('Error loading official seal image: $e'); } } @@ -103,7 +108,7 @@ class SchoolDataMainManager extends ChangeNotifier { notifyListeners(); } } catch (e) { - print('Error saving school data: $e'); + _log.severe('Error saving school data: $e'); rethrow; } finally { _dataManager.setSaving(false); @@ -120,11 +125,11 @@ class SchoolDataMainManager extends ChangeNotifier { final currentData = _uiManager.getCurrentFormData(); if (currentData != null) { // Note: This would need to be updated when the API supports logo path storage - print('Logo uploaded successfully: $logoPath'); + _log.info('Logo uploaded successfully: $logoPath'); } } } catch (e) { - print('Error uploading logo: $e'); + _log.severe('Error uploading logo: $e'); rethrow; } finally { _dataManager.setSaving(false); @@ -141,11 +146,11 @@ class SchoolDataMainManager extends ChangeNotifier { final currentData = _uiManager.getCurrentFormData(); if (currentData != null) { // Note: This would need to be updated when the API supports seal path storage - print('Official seal uploaded successfully: $sealPath'); + _log.info('Official seal uploaded successfully: $sealPath'); } } } catch (e) { - print('Error uploading official seal: $e'); + _log.severe('Error uploading official seal: $e'); rethrow; } finally { _dataManager.setSaving(false); diff --git a/school_data_hub_flutter/lib/features/school_lists/domain/models/pupil_list_entry_proxy.dart b/school_data_hub_flutter/lib/features/school_lists/domain/models/pupil_list_entry_proxy.dart index 684834b2..a5371392 100644 --- a/school_data_hub_flutter/lib/features/school_lists/domain/models/pupil_list_entry_proxy.dart +++ b/school_data_hub_flutter/lib/features/school_lists/domain/models/pupil_list_entry_proxy.dart @@ -10,11 +10,11 @@ class PupilListEntryProxy with ChangeNotifier { PupilListEntry pupilEntry; PupilListEntryProxy({required this.pupilEntry}) { notifyListeners(); - _log.info('PupilListEntryProxy created: ${pupilEntry.id}'); + // _log.info('PupilListEntryProxy created: ${pupilEntry.id}'); } void setPupilEntry(PupilListEntry entry) { pupilEntry = entry; notifyListeners(); - _log.info('PupilListEntryProxy updated: ${pupilEntry.id}'); + // _log.info('PupilListEntryProxy updated: ${pupilEntry.id}'); } } diff --git a/school_data_hub_flutter/lib/features/timetable/data/timetable_api_service.dart b/school_data_hub_flutter/lib/features/timetable/data/timetable_api_service.dart index b475d1bd..e5dc7020 100644 --- a/school_data_hub_flutter/lib/features/timetable/data/timetable_api_service.dart +++ b/school_data_hub_flutter/lib/features/timetable/data/timetable_api_service.dart @@ -1,3 +1,4 @@ +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:watch_it/watch_it.dart'; @@ -7,6 +8,7 @@ import 'package:watch_it/watch_it.dart'; /// This service uses the implemented server-side endpoints for timetable management. class TimetableApiService { final _client = di(); + final _log = Logger('TimetableApiService'); // ============================================================================ // TIMETABLE OPERATIONS @@ -14,10 +16,14 @@ class TimetableApiService { /// Fetch the main timetable with all related data Future fetchTimetable() async { + _log.info('Calling fetchTimetable...'); final timetable = await ClientHelper.apiCall( call: () => _client.timetable.fetchTimetable(), errorMessage: 'Fehler beim Laden des Stundenplans', ); + _log.info( + 'fetchTimetable returned: ${timetable?.name} (ID: ${timetable?.id})', + ); return timetable; } @@ -48,29 +54,31 @@ class TimetableApiService { /// Fetch complete timetable data (timetable + slots + lessons + related data) Future fetchCompleteTimetableData() async { - print('API: Calling fetchCompleteTimetableData...'); + _log.info('Calling fetchCompleteTimetableData...'); final timetable = await ClientHelper.apiCall( call: () => _client.timetable.fetchCompleteTimetableData(), errorMessage: 'Fehler beim Laden der vollständigen Stundenplandaten', ); - print( + _log.info( 'API: fetchCompleteTimetableData returned: ${timetable?.name} (ID: ${timetable?.id})', ); - print('API: Timetable is null: ${timetable == null}'); + _log.info('API: Timetable is null: ${timetable == null}'); return timetable; } /// Fetch all timetables Future?> fetchTimetables() async { - print('API: Calling fetchTimetables...'); + _log.info('API: Calling fetchTimetables...'); final timetables = await ClientHelper.apiCall( call: () => _client.timetable.fetchTimetables(), errorMessage: 'Fehler beim Laden der Stundenpläne', ); - print('API: fetchTimetables returned: ${timetables?.length} timetables'); + _log.info( + 'API: fetchTimetables returned: ${timetables?.length} timetables', + ); if (timetables != null) { for (final timetable in timetables) { - print('API: - Timetable: ${timetable.name} (ID: ${timetable.id})'); + _log.info('API: - Timetable: ${timetable.name} (ID: ${timetable.id})'); } } return timetables; @@ -93,10 +101,8 @@ class TimetableApiService { int timetableId, ) async { return await ClientHelper.apiCall( - call: - () => _client.timetableSlot.fetchTimetableSlotsByTimetableId( - timetableId, - ), + call: () => + _client.timetableSlot.fetchTimetableSlotsByTimetableId(timetableId), errorMessage: 'Fehler beim Laden der Zeitslots für den Stundenplan', ); } @@ -142,10 +148,8 @@ class TimetableApiService { int timetableId, ) async { return await ClientHelper.apiCall( - call: - () => _client.scheduledLesson.fetchScheduledLessonsByTimetable( - timetableId, - ), + call: () => + _client.scheduledLesson.fetchScheduledLessonsByTimetable(timetableId), errorMessage: 'Fehler beim Laden der geplanten Stunden für den Stundenplan', ); @@ -204,8 +208,8 @@ class TimetableApiService { int timetableId, ) async { return await ClientHelper.apiCall( - call: - () => _client.learningGroup.fetchLessonGroupsByTimetable(timetableId), + call: () => + _client.learningGroup.fetchLessonGroupsByTimetable(timetableId), errorMessage: 'Fehler beim Laden der Klassen für den Stundenplan', ); } @@ -341,10 +345,8 @@ class TimetableApiService { Future?> fetchScheduledLessonGroupMemberships() async { return await ClientHelper.apiCall( - call: - () => - _client.scheduledLessonGroupMembership - .fetchScheduledLessonGroupMemberships(), + call: () => _client.scheduledLessonGroupMembership + .fetchScheduledLessonGroupMemberships(), errorMessage: 'Fehler beim Laden der Klassenmitgliedschaften', ); } @@ -354,9 +356,8 @@ class TimetableApiService { int lessonGroupId, ) async { return await ClientHelper.apiCall( - call: - () => _client.scheduledLessonGroupMembership - .fetchMembershipsByLessonGroupId(lessonGroupId), + call: () => _client.scheduledLessonGroupMembership + .fetchMembershipsByLessonGroupId(lessonGroupId), errorMessage: 'Fehler beim Laden der Klassenmitgliedschaften', ); } @@ -366,9 +367,8 @@ class TimetableApiService { ScheduledLessonGroupMembership membership, ) async { return await ClientHelper.apiCall( - call: - () => _client.scheduledLessonGroupMembership - .createScheduledLessonGroupMembership(membership), + call: () => _client.scheduledLessonGroupMembership + .createScheduledLessonGroupMembership(membership), errorMessage: 'Fehler beim Erstellen der Klassenmitgliedschaft', ); } @@ -378,9 +378,8 @@ class TimetableApiService { ScheduledLessonGroupMembership membership, ) async { return await ClientHelper.apiCall( - call: - () => _client.scheduledLessonGroupMembership - .updateScheduledLessonGroupMembership(membership), + call: () => _client.scheduledLessonGroupMembership + .updateScheduledLessonGroupMembership(membership), errorMessage: 'Fehler beim Aktualisieren der Klassenmitgliedschaft', ); } @@ -388,9 +387,8 @@ class TimetableApiService { /// Delete a scheduled lesson group membership Future deleteScheduledLessonGroupMembership(int membershipId) async { return await ClientHelper.apiCall( - call: - () => _client.scheduledLessonGroupMembership - .deleteScheduledLessonGroupMembership(membershipId), + call: () => _client.scheduledLessonGroupMembership + .deleteScheduledLessonGroupMembership(membershipId), errorMessage: 'Fehler beim Löschen der Klassenmitgliedschaft', ); } @@ -401,9 +399,8 @@ class TimetableApiService { int pupilDataId, ) async { return await ClientHelper.apiCall( - call: - () => _client.scheduledLessonGroupMembership - .deletePupilFromLessonGroup(lessonGroupId, pupilDataId), + call: () => _client.scheduledLessonGroupMembership + .deletePupilFromLessonGroup(lessonGroupId, pupilDataId), errorMessage: 'Fehler beim Entfernen des Schülers aus der Klasse', ); } @@ -418,12 +415,8 @@ class TimetableApiService { List pupilDataIds, ) async { return await ClientHelper.apiCall( - call: - () => _client.scheduledLessonGroupMembership - .updatePupilMembershipsForLessonGroup( - lessonGroupId, - pupilDataIds, - ), + call: () => _client.scheduledLessonGroupMembership + .updatePupilMembershipsForLessonGroup(lessonGroupId, pupilDataIds), errorMessage: 'Fehler beim Aktualisieren der Klassenmitgliedschaften', ); } diff --git a/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_crud_manager.dart b/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_crud_manager.dart index 1d88ed2a..43a6d9fb 100644 --- a/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_crud_manager.dart +++ b/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_crud_manager.dart @@ -1,8 +1,11 @@ import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/features/timetable/data/timetable_api_service.dart'; import 'package:watch_it/watch_it.dart'; +final _log = Logger('TimetableCrudManager'); + /// Manages CRUD operations for timetable entities class TimetableCrudManager extends ChangeNotifier { final _apiService = di(); @@ -12,18 +15,18 @@ class TimetableCrudManager extends ChangeNotifier { // Scheduled Lesson CRUD operations Future addScheduledLesson(ScheduledLesson lesson) async { try { - print( + _log.info( 'Creating scheduled lesson with timetableId: ${lesson.timetableId}', ); final createdLesson = await _apiService.createScheduledLesson(lesson); if (createdLesson != null) { - print( + _log.info( 'Scheduled lesson created successfully: ${createdLesson.lessonId}', ); notifyListeners(); } } catch (e) { - print('Error creating scheduled lesson: $e'); + _log.info('Error creating scheduled lesson: $e'); rethrow; } } @@ -32,13 +35,13 @@ class TimetableCrudManager extends ChangeNotifier { try { final updatedLesson = await _apiService.updateScheduledLesson(lesson); if (updatedLesson != null) { - print( + _log.info( 'Scheduled lesson updated successfully: ${updatedLesson.lessonId}', ); notifyListeners(); } } catch (e) { - print('Error updating scheduled lesson: $e'); + _log.info('Error updating scheduled lesson: $e'); rethrow; } } @@ -47,11 +50,11 @@ class TimetableCrudManager extends ChangeNotifier { try { final success = await _apiService.deleteScheduledLesson(lessonId); if (success == true) { - print('Scheduled lesson deleted successfully: $lessonId'); + _log.info('Scheduled lesson deleted successfully: $lessonId'); notifyListeners(); } } catch (e) { - print('Error deleting scheduled lesson: $e'); + _log.info('Error deleting scheduled lesson: $e'); rethrow; } } @@ -61,12 +64,12 @@ class TimetableCrudManager extends ChangeNotifier { try { final createdSubject = await _apiService.createSubject(subject); if (createdSubject != null) { - print('Subject created successfully: ${createdSubject.name}'); + _log.info('Subject created successfully: ${createdSubject.name}'); notifyListeners(); } return createdSubject; } catch (e) { - print('Error creating subject: $e'); + _log.info('Error creating subject: $e'); rethrow; } } @@ -75,12 +78,12 @@ class TimetableCrudManager extends ChangeNotifier { try { final updatedSubject = await _apiService.updateSubject(subject); if (updatedSubject != null) { - print('Subject updated successfully: ${updatedSubject.name}'); + _log.info('Subject updated successfully: ${updatedSubject.name}'); notifyListeners(); } return updatedSubject; } catch (e) { - print('Error updating subject: $e'); + _log.info('Error updating subject: $e'); rethrow; } } @@ -89,11 +92,11 @@ class TimetableCrudManager extends ChangeNotifier { try { final success = await _apiService.deleteSubject(subjectId); if (success == true) { - print('Subject deleted successfully: $subjectId'); + _log.info('Subject deleted successfully: $subjectId'); notifyListeners(); } } catch (e) { - print('Error deleting subject: $e'); + _log.info('Error deleting subject: $e'); rethrow; } } @@ -103,12 +106,14 @@ class TimetableCrudManager extends ChangeNotifier { try { final createdClassroom = await _apiService.createClassroom(classroom); if (createdClassroom != null) { - print('Classroom created successfully: ${createdClassroom.roomName}'); + _log.info( + 'Classroom created successfully: ${createdClassroom.roomName}', + ); notifyListeners(); } return createdClassroom; } catch (e) { - print('Error creating classroom: $e'); + _log.info('Error creating classroom: $e'); rethrow; } } @@ -117,12 +122,14 @@ class TimetableCrudManager extends ChangeNotifier { try { final updatedClassroom = await _apiService.updateClassroom(classroom); if (updatedClassroom != null) { - print('Classroom updated successfully: ${updatedClassroom.roomName}'); + _log.info( + 'Classroom updated successfully: ${updatedClassroom.roomName}', + ); notifyListeners(); } return updatedClassroom; } catch (e) { - print('Error updating classroom: $e'); + _log.info('Error updating classroom: $e'); rethrow; } } @@ -131,11 +138,11 @@ class TimetableCrudManager extends ChangeNotifier { try { final success = await _apiService.deleteClassroom(classroomId); if (success == true) { - print('Classroom deleted successfully: $classroomId'); + _log.info('Classroom deleted successfully: $classroomId'); notifyListeners(); } } catch (e) { - print('Error deleting classroom: $e'); + _log.info('Error deleting classroom: $e'); rethrow; } } @@ -143,19 +150,19 @@ class TimetableCrudManager extends ChangeNotifier { // Lesson Group CRUD operations Future addLessonGroup(LessonGroup lessonGroup) async { try { - print('Creating lesson group: ${lessonGroup.name}'); + _log.info('Creating lesson group: ${lessonGroup.name}'); final createdLessonGroup = await _apiService.createLessonGroup( lessonGroup, ); if (createdLessonGroup != null) { - print( + _log.info( 'Lesson group created successfully: ${createdLessonGroup.name} (ID: ${createdLessonGroup.id})', ); notifyListeners(); } return createdLessonGroup; } catch (e) { - print('Error creating lesson group: $e'); + _log.info('Error creating lesson group: $e'); rethrow; } } @@ -166,12 +173,14 @@ class TimetableCrudManager extends ChangeNotifier { lessonGroup, ); if (updatedLessonGroup != null) { - print('Lesson group updated successfully: ${updatedLessonGroup.name}'); + _log.info( + 'Lesson group updated successfully: ${updatedLessonGroup.name}', + ); notifyListeners(); } return updatedLessonGroup; } catch (e) { - print('Error updating lesson group: $e'); + _log.info('Error updating lesson group: $e'); rethrow; } } @@ -180,11 +189,11 @@ class TimetableCrudManager extends ChangeNotifier { try { final success = await _apiService.deleteLessonGroup(lessonGroupId); if (success == true) { - print('Lesson group deleted successfully: $lessonGroupId'); + _log.info('Lesson group deleted successfully: $lessonGroupId'); notifyListeners(); } } catch (e) { - print('Error deleting lesson group: $e'); + _log.info('Error deleting lesson group: $e'); rethrow; } } @@ -192,16 +201,16 @@ class TimetableCrudManager extends ChangeNotifier { // Timetable CRUD operations Future createTimetable(Timetable timetable) async { try { - print('Creating timetable: ${timetable.name}'); + _log.info('Creating timetable: ${timetable.name}'); final createdTimetable = await _apiService.createTimetable(timetable); if (createdTimetable != null) { - print( + _log.info( 'Timetable created successfully: ${createdTimetable.name} (ID: ${createdTimetable.id})', ); notifyListeners(); } } catch (e) { - print('Error creating timetable: $e'); + _log.info('Error creating timetable: $e'); rethrow; } } @@ -210,11 +219,11 @@ class TimetableCrudManager extends ChangeNotifier { try { final updatedTimetable = await _apiService.updateTimetable(timetable); if (updatedTimetable != null) { - print('Timetable updated successfully: ${updatedTimetable.name}'); + _log.info('Timetable updated successfully: ${updatedTimetable.name}'); notifyListeners(); } } catch (e) { - print('Error updating timetable: $e'); + _log.info('Error updating timetable: $e'); rethrow; } } @@ -223,11 +232,11 @@ class TimetableCrudManager extends ChangeNotifier { try { final success = await _apiService.deleteTimetable(timetableId); if (success == true) { - print('Timetable deleted successfully: $timetableId'); + _log.info('Timetable deleted successfully: $timetableId'); notifyListeners(); } } catch (e) { - print('Error deleting timetable: $e'); + _log.info('Error deleting timetable: $e'); rethrow; } } @@ -237,13 +246,13 @@ class TimetableCrudManager extends ChangeNotifier { try { final createdSlot = await _apiService.createTimetableSlot(slot); if (createdSlot != null) { - print( + _log.info( 'Timetable slot created successfully: ${createdSlot.day} ${createdSlot.startTime}-${createdSlot.endTime}', ); notifyListeners(); } } catch (e) { - print('Error creating timetable slot: $e'); + _log.info('Error creating timetable slot: $e'); rethrow; } } @@ -252,13 +261,13 @@ class TimetableCrudManager extends ChangeNotifier { try { final updatedSlot = await _apiService.updateTimetableSlot(slot); if (updatedSlot != null) { - print( + _log.info( 'Timetable slot updated successfully: ${updatedSlot.day} ${updatedSlot.startTime}-${updatedSlot.endTime}', ); notifyListeners(); } } catch (e) { - print('Error updating timetable slot: $e'); + _log.info('Error updating timetable slot: $e'); rethrow; } } @@ -267,11 +276,11 @@ class TimetableCrudManager extends ChangeNotifier { try { final success = await _apiService.deleteTimetableSlot(slotId); if (success == true) { - print('Timetable slot deleted successfully: $slotId'); + _log.info('Timetable slot deleted successfully: $slotId'); notifyListeners(); } } catch (e) { - print('Error deleting timetable slot: $e'); + _log.info('Error deleting timetable slot: $e'); rethrow; } } diff --git a/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart b/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart index 2dd40714..7dc6e420 100644 --- a/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart +++ b/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/features/timetable/data/timetable_api_service.dart'; import 'package:school_data_hub_flutter/features/timetable/data/timetable_mock_data.dart'; @@ -7,6 +8,7 @@ import 'package:watch_it/watch_it.dart'; /// Manages timetable data loading, API calls, and state management class TimetableDataManager extends ChangeNotifier { final _apiService = di(); + final _log = Logger('TimetableDataManager'); // Main timetable container final _timetable = ValueNotifier(null); @@ -51,9 +53,9 @@ class TimetableDataManager extends ChangeNotifier { /// Refresh all data from API Future refreshData() async { - print('Refreshing timetable data...'); + _log.info('Refreshing timetable data...'); await _loadData(); - print( + _log.info( 'Data refresh completed. Lesson groups: ${_lessonGroups.value.length}', ); notifyListeners(); @@ -61,14 +63,16 @@ class TimetableDataManager extends ChangeNotifier { /// Debug method to print current state void debugPrintState() { - print('=== TimetableDataManager Debug State ==='); - print('Timetable: ${_timetable.value?.name} (ID: ${_timetable.value?.id})'); - print('TimetableSlots: ${_timetableSlots.value.length}'); - print('ScheduledLessons: ${_scheduledLessons.value.length}'); - print('Subjects: ${_subjects.value.length}'); - print('Classrooms: ${_classrooms.value.length}'); - print('LessonGroups: ${_lessonGroups.value.length}'); - print('===================================='); + _log.info('=== TimetableDataManager Debug State ==='); + _log.info( + 'Timetable: ${_timetable.value?.name} (ID: ${_timetable.value?.id})', + ); + _log.info('TimetableSlots: ${_timetableSlots.value.length}'); + _log.info('ScheduledLessons: ${_scheduledLessons.value.length}'); + _log.info('Subjects: ${_subjects.value.length}'); + _log.info('Classrooms: ${_classrooms.value.length}'); + _log.info('LessonGroups: ${_lessonGroups.value.length}'); + _log.info('===================================='); } void clearData() { @@ -88,13 +92,13 @@ class TimetableDataManager extends ChangeNotifier { // Load data from API with fallback to mock data Future _loadData() async { try { - print('Loading data from API...'); + _log.info('Loading data from API...'); // Try to load from API first await _loadFromApi(); - print('Data loaded from API successfully'); + _log.info('Data loaded from API successfully'); debugPrintState(); } catch (e) { - print('API failed, loading mock data: $e'); + _log.info('API failed, loading mock data: $e'); // Fallback to mock data if API fails await _loadMockData(); debugPrintState(); @@ -103,37 +107,39 @@ class TimetableDataManager extends ChangeNotifier { // Load data from API Future _loadFromApi() async { - print('Loading complete timetable data from API...'); + _log.info('Loading complete timetable data from API...'); // Load complete timetable data final timetable = await _apiService.fetchCompleteTimetableData(); - print('API returned timetable: ${timetable?.name} (ID: ${timetable?.id})'); + _log.info( + 'API returned timetable: ${timetable?.name} (ID: ${timetable?.id})', + ); if (timetable != null) { _timetable.value = timetable; _timetableSlots.value = timetable.timetableSlots ?? []; _scheduledLessons.value = timetable.scheduledLessons ?? []; - print( + _log.info( 'Set timetable data - slots: ${timetable.timetableSlots?.length ?? 0}, lessons: ${timetable.scheduledLessons?.length ?? 0}', ); } else { - print( + _log.info( 'No timetable data returned from fetchCompleteTimetableData, trying fetchTimetables...', ); // Try to fetch all timetables and use the first one final timetables = await _apiService.fetchTimetables(); if (timetables != null && timetables.isNotEmpty) { final firstTimetable = timetables.first; - print( + _log.info( 'Using first timetable from fetchTimetables: ${firstTimetable.name} (ID: ${firstTimetable.id})', ); _timetable.value = firstTimetable; _timetableSlots.value = firstTimetable.timetableSlots ?? []; _scheduledLessons.value = firstTimetable.scheduledLessons ?? []; - print( + _log.info( 'Set timetable data from fetchTimetables - slots: ${firstTimetable.timetableSlots?.length ?? 0}, lessons: ${firstTimetable.scheduledLessons?.length ?? 0}', ); } else { - print('No timetables found in database'); + _log.info('No timetables found in database'); } } @@ -171,7 +177,7 @@ class TimetableDataManager extends ChangeNotifier { _timetable.value!.id!, ); if (lessonGroups != null) { - print( + _log.info( 'Loaded ${lessonGroups.length} lesson groups from API for timetable ${_timetable.value!.id}', ); // Sort lesson groups alphabetically by name @@ -187,14 +193,14 @@ class TimetableDataManager extends ChangeNotifier { } } } else { - print('No timetable loaded, skipping lesson groups fetch'); + _log.info('No timetable loaded, skipping lesson groups fetch'); _lessonGroups.value = []; _lessonGroupIdMap.clear(); } // Load scheduled lesson group memberships - final memberships = - await _apiService.fetchScheduledLessonGroupMemberships(); + final memberships = await _apiService + .fetchScheduledLessonGroupMemberships(); if (memberships != null) { _scheduledLessonGroupMemberships.value = memberships; } @@ -334,10 +340,10 @@ class TimetableDataManager extends ChangeNotifier { // Timetable management methods Future createTimetable(Timetable timetable) async { try { - print('Creating timetable: ${timetable.name}'); + _log.info('Creating timetable: ${timetable.name}'); final createdTimetable = await _apiService.createTimetable(timetable); if (createdTimetable != null) { - print( + _log.info( 'Timetable created successfully: ${createdTimetable.name} (ID: ${createdTimetable.id})', ); @@ -361,12 +367,12 @@ class TimetableDataManager extends ChangeNotifier { await _loadAdditionalDataFromApi(); notifyListeners(); - print( + _log.info( 'Timetable set as active. Current timetable: ${_timetable.value?.name} (ID: ${_timetable.value?.id})', ); } } catch (e) { - print('Error creating timetable: $e'); + _log.info('Error creating timetable: $e'); // Fallback to local operation if API fails final newId = _timetable.value?.id ?? 1; final newTimetable = timetable.copyWith(id: newId); @@ -419,21 +425,21 @@ class TimetableDataManager extends ChangeNotifier { try { final createdSlot = await _apiService.createTimetableSlot(slot); if (createdSlot != null) { - print( + _log.info( 'Created slot: ${createdSlot.day} ${createdSlot.startTime}-${createdSlot.endTime}', ); } } catch (e) { - print( + _log.info( 'Error creating slot for ${weekday} ${time['start']}-${time['end']}: $e', ); } } } - print('Default timetable slots generation completed'); + _log.info('Default timetable slots generation completed'); } catch (e) { - print('Error generating default timetable slots: $e'); + _log.info('Error generating default timetable slots: $e'); } } @@ -454,7 +460,7 @@ class TimetableDataManager extends ChangeNotifier { _classrooms.value = updatedClassrooms; _buildLookupMaps(); notifyListeners(); - print( + _log.info( 'Added classroom to local data: ${classroom.roomName} (ID: ${classroom.id})', ); } @@ -472,7 +478,7 @@ class TimetableDataManager extends ChangeNotifier { _classrooms.value = updatedClassrooms; _buildLookupMaps(); notifyListeners(); - print( + _log.info( 'Updated classroom in local data: ${classroom.roomName} (ID: ${classroom.id})', ); } @@ -481,25 +487,26 @@ class TimetableDataManager extends ChangeNotifier { /// Remove a classroom from the local data void removeClassroom(int classroomId) { final currentClassrooms = _classrooms.value; - final updatedClassrooms = - currentClassrooms.where((c) => c.id != classroomId).toList(); + final updatedClassrooms = currentClassrooms + .where((c) => c.id != classroomId) + .toList(); if (updatedClassrooms.length != currentClassrooms.length) { _classrooms.value = updatedClassrooms; _buildLookupMaps(); notifyListeners(); - print('Removed classroom from local data: $classroomId'); + _log.info('Removed classroom from local data: $classroomId'); } } /// Add a new lesson group to the local data void addLessonGroup(LessonGroup lessonGroup) { - print( + _log.info( 'DEBUG: addLessonGroup called - timetableId: ${lessonGroup.timetableId}, current timetable: ${_timetable.value?.id}', ); // Only add lesson groups that belong to the current timetable if (lessonGroup.timetableId != _timetable.value?.id) { - print( + _log.info( 'Skipping lesson group ${lessonGroup.name} - belongs to different timetable (${lessonGroup.timetableId} vs ${_timetable.value?.id})', ); return; @@ -513,7 +520,7 @@ class TimetableDataManager extends ChangeNotifier { _lessonGroups.value = updatedLessonGroups; _buildLookupMaps(); notifyListeners(); - print( + _log.info( 'Added lesson group to local data: ${lessonGroup.name} (ID: ${lessonGroup.id})', ); } @@ -523,7 +530,7 @@ class TimetableDataManager extends ChangeNotifier { void updateLessonGroup(LessonGroup lessonGroup) { // Only update lesson groups that belong to the current timetable if (lessonGroup.timetableId != _timetable.value?.id) { - print( + _log.info( 'Skipping lesson group update ${lessonGroup.name} - belongs to different timetable (${lessonGroup.timetableId} vs ${_timetable.value?.id})', ); return; @@ -541,7 +548,7 @@ class TimetableDataManager extends ChangeNotifier { _lessonGroups.value = updatedLessonGroups; _buildLookupMaps(); notifyListeners(); - print( + _log.info( 'Updated lesson group in local data: ${lessonGroup.name} (ID: ${lessonGroup.id})', ); } @@ -550,13 +557,14 @@ class TimetableDataManager extends ChangeNotifier { /// Remove a lesson group from the local data void removeLessonGroup(int lessonGroupId) { final currentLessonGroups = _lessonGroups.value; - final updatedLessonGroups = - currentLessonGroups.where((lg) => lg.id != lessonGroupId).toList(); + final updatedLessonGroups = currentLessonGroups + .where((lg) => lg.id != lessonGroupId) + .toList(); if (updatedLessonGroups.length != currentLessonGroups.length) { _lessonGroups.value = updatedLessonGroups; _buildLookupMaps(); notifyListeners(); - print('Removed lesson group from local data: $lessonGroupId'); + _log.info('Removed lesson group from local data: $lessonGroupId'); } } @@ -570,7 +578,9 @@ class TimetableDataManager extends ChangeNotifier { _subjects.value = updatedSubjects; _buildLookupMaps(); notifyListeners(); - print('Added subject to local data: ${subject.name} (ID: ${subject.id})'); + _log.info( + 'Added subject to local data: ${subject.name} (ID: ${subject.id})', + ); } } @@ -586,7 +596,7 @@ class TimetableDataManager extends ChangeNotifier { _subjects.value = updatedSubjects; _buildLookupMaps(); notifyListeners(); - print( + _log.info( 'Updated subject in local data: ${subject.name} (ID: ${subject.id})', ); } @@ -595,13 +605,14 @@ class TimetableDataManager extends ChangeNotifier { /// Remove a subject from the local data void removeSubject(int subjectId) { final currentSubjects = _subjects.value; - final updatedSubjects = - currentSubjects.where((s) => s.id != subjectId).toList(); + final updatedSubjects = currentSubjects + .where((s) => s.id != subjectId) + .toList(); if (updatedSubjects.length != currentSubjects.length) { _subjects.value = updatedSubjects; _buildLookupMaps(); notifyListeners(); - print('Removed subject from local data: $subjectId'); + _log.info('Removed subject from local data: $subjectId'); } } } diff --git a/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_page/new_timetable_page.dart b/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_page/new_timetable_page.dart index 09515cfc..aea2e513 100644 --- a/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_page/new_timetable_page.dart +++ b/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_page/new_timetable_page.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; @@ -22,6 +23,8 @@ export 'widgets/name_field.dart'; export 'widgets/school_semester_dropdown.dart'; export 'widgets/start_date_field.dart'; +final _log = Logger('NewTimetablePage'); + class NewTimetablePage extends WatchingWidget { final Timetable? timetable; @@ -201,15 +204,17 @@ class NewTimetablePage extends WatchingWidget { final userName = sessionManager.userName ?? 'unknown_user'; - print('Creating timetable with:'); - print('- Name: $name'); - print('- Start Date: $startDate'); - print('- End Date: $endDate'); - print( + _log.info('Creating timetable with:'); + _log.info('- Name: $name'); + _log.info('- Start Date: $startDate'); + _log.info('- End Date: $endDate'); + _log.info( '- School Semester ID: ${selectedSemester.value!.id}', ); - print('- Created By: $userName'); - print('- Is Signed In: ${sessionManager.isSignedIn}'); + _log.info('- Created By: $userName'); + _log.info( + '- Is Signed In: ${sessionManager.isSignedIn}', + ); final newTimetable = Timetable( id: _isEditing ? timetable!.id : null, diff --git a/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_slot_page/new_timetable_slot_page.dart b/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_slot_page/new_timetable_slot_page.dart index bfdbfc99..25dc78a6 100644 --- a/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_slot_page/new_timetable_slot_page.dart +++ b/school_data_hub_flutter/lib/features/timetable/presentation/new_timetable_slot_page/new_timetable_slot_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/features/timetable/domain/timetable_manager.dart'; @@ -10,6 +11,8 @@ import 'widgets/end_time_field.dart'; import 'widgets/start_time_field.dart'; import 'widgets/weekday_dropdown.dart'; +final _log = Logger('NewTimetableSlotPage'); + class NewTimetableSlotPage extends WatchingWidget { final TimetableManager timetableManager; final TimetableSlot? timetableSlot; @@ -118,19 +121,17 @@ class NewTimetableSlotPage extends WatchingWidget { // Action Buttons ActionButtons( - onSave: - () => _saveTimetableSlot( - context, - startTimeController, - endTimeController, - selectedWeekday, - selectedWeekdayOption, - ), + onSave: () => _saveTimetableSlot( + context, + startTimeController, + endTimeController, + selectedWeekday, + selectedWeekdayOption, + ), onCancel: () => Navigator.of(context).pop(), - onDelete: - timetableSlot != null - ? () => _deleteTimetableSlot(context) - : null, + onDelete: timetableSlot != null + ? () => _deleteTimetableSlot(context) + : null, ), ], ), @@ -196,16 +197,16 @@ class NewTimetableSlotPage extends WatchingWidget { try { final timetable = timetableManager.timetable.value; - print( + _log.info( 'Current timetable in manager: ${timetable?.name} (ID: ${timetable?.id})', ); if (timetable == null) { - print('No timetable available. Debug info:'); - print( + _log.severe('No timetable available. Debug info:'); + _log.severe( '- TimetableManager has timetable: ${timetableManager.timetable.value != null}', ); - print( + _log.severe( '- TimetableManager has slots: ${timetableManager.timetableSlots.value.length}', ); @@ -243,7 +244,7 @@ class NewTimetableSlotPage extends WatchingWidget { await timetableManager.addTimetableSlot(newTimetableSlot); createdCount++; } catch (e) { - print('Error creating slot for $weekday: $e'); + _log.severe('Error creating slot for $weekday: $e'); } } @@ -319,26 +320,25 @@ class NewTimetableSlotPage extends WatchingWidget { final confirmed = await showDialog( context: context, - builder: - (context) => AlertDialog( - title: const Text('Zeitslot löschen'), - content: const Text( - 'Sind Sie sicher, dass Sie diesen Zeitslot löschen möchten?', + builder: (context) => AlertDialog( + title: const Text('Zeitslot löschen'), + content: const Text( + 'Sind Sie sicher, dass Sie diesen Zeitslot löschen möchten?', + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Abbrechen'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + style: TextButton.styleFrom( + foregroundColor: AppColors.dangerButtonColor, ), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: const Text('Abbrechen'), - ), - TextButton( - onPressed: () => Navigator.of(context).pop(true), - style: TextButton.styleFrom( - foregroundColor: AppColors.dangerButtonColor, - ), - child: const Text('Löschen'), - ), - ], + child: const Text('Löschen'), ), + ], + ), ); if (confirmed == true && context.mounted) { diff --git a/school_data_hub_flutter/lib/features/timetable/presentation/timetable_page/timetable_page.dart b/school_data_hub_flutter/lib/features/timetable/presentation/timetable_page/timetable_page.dart index 3da9096c..4abbb300 100644 --- a/school_data_hub_flutter/lib/features/timetable/presentation/timetable_page/timetable_page.dart +++ b/school_data_hub_flutter/lib/features/timetable/presentation/timetable_page/timetable_page.dart @@ -59,7 +59,7 @@ class TimetablePage extends WatchingWidget { const SizedBox(height: 16), ElevatedButton( onPressed: () async { - print('Manual refresh triggered'); + timetableManager.debugPrintState(); await timetableManager.refreshData(); }, child: const Text('Daten neu laden'), @@ -86,9 +86,8 @@ class TimetablePage extends WatchingWidget { void _navigateToNewLesson() { Navigator.of(context).push( MaterialPageRoute( - builder: - (context) => - NewScheduledLessonPage(timetableManager: timetableManager), + builder: (context) => + NewScheduledLessonPage(timetableManager: timetableManager), ), ); } @@ -104,11 +103,10 @@ class TimetablePage extends WatchingWidget { void _onLessonTap(int lessonId) { Navigator.of(context).push( MaterialPageRoute( - builder: - (context) => NewScheduledLessonPage( - timetableManager: timetableManager, - editingLessonId: lessonId, - ), + builder: (context) => NewScheduledLessonPage( + timetableManager: timetableManager, + editingLessonId: lessonId, + ), ), ); } @@ -116,11 +114,10 @@ class TimetablePage extends WatchingWidget { void _onEmptySlotTap(int slotId) { Navigator.of(context).push( MaterialPageRoute( - builder: - (context) => NewScheduledLessonPage( - timetableManager: timetableManager, - preselectedSlotId: slotId, - ), + builder: (context) => NewScheduledLessonPage( + timetableManager: timetableManager, + preselectedSlotId: slotId, + ), ), ); } diff --git a/school_data_hub_flutter/lib/features/timetable/presentation/widgets/timetable_grid.dart b/school_data_hub_flutter/lib/features/timetable/presentation/widgets/timetable_grid.dart index 616d9d82..5e67606f 100644 --- a/school_data_hub_flutter/lib/features/timetable/presentation/widgets/timetable_grid.dart +++ b/school_data_hub_flutter/lib/features/timetable/presentation/widgets/timetable_grid.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/features/timetable/domain/timetable_manager.dart'; import 'package:school_data_hub_flutter/features/timetable/presentation/classroom_list_page/classroom_list_page.dart'; @@ -6,6 +7,8 @@ import 'package:school_data_hub_flutter/features/timetable/presentation/widgets/ import 'package:school_data_hub_flutter/features/user/domain/user_manager.dart'; import 'package:watch_it/watch_it.dart'; +final _log = Logger('TimetableGrid'); + class TimetableGrid extends WatchingWidget { final TimetableManager timetableManager; final Function(int lessonId) onLessonTap; @@ -45,21 +48,21 @@ class TimetableGrid extends WatchingWidget { final timeSlotPeriods = timetableManager.getTimeSlotPeriods(); // Debug logging - print('TimetableGrid - timeSlotPeriods: ${timeSlotPeriods.length}'); - print( + _log.info('TimetableGrid - timeSlotPeriods: ${timeSlotPeriods.length}'); + _log.info( 'TimetableGrid - allLessonGroupsForWeekday: ${allLessonGroupsForWeekday.length}', ); - print('TimetableGrid - selectedWeekday: $selectedWeekday'); - print( + _log.info('TimetableGrid - selectedWeekday: $selectedWeekday'); + _log.info( 'TimetableGrid - total lesson groups: ${lessonGroupsForWeekday.length}', ); - print( + _log.info( 'TimetableGrid - selected lesson group IDs: ${selectedLessonGroupIds}', ); - print( + _log.info( 'TimetableGrid - scheduled lessons count: ${scheduledLessons.length}', ); - print('TimetableGrid - classrooms count: ${classrooms.length}'); + _log.info('TimetableGrid - classrooms count: ${classrooms.length}'); // Handle empty states if (timeSlotPeriods.isEmpty) { @@ -197,8 +200,9 @@ class _TimetableRow extends StatelessWidget { Widget build(BuildContext context) { // Get the slot for this weekday and period final slotsForPeriod = timetableManager.getSlotsByTimePeriod(period); - final slotForWeekday = - slotsForPeriod.where((slot) => slot.day == weekday).firstOrNull; + final slotForWeekday = slotsForPeriod + .where((slot) => slot.day == weekday) + .firstOrNull; return Row( children: [ @@ -219,10 +223,9 @@ class _TimetableRow extends StatelessWidget { final allLessons = timetableManager.getAllLessonsForSlot( slotForWeekday.id!, ); - lessonForGroup = - allLessons - .where((lesson) => lesson.lessonGroupId == group.id) - .firstOrNull; + lessonForGroup = allLessons + .where((lesson) => lesson.lessonGroupId == group.id) + .firstOrNull; } return Container( @@ -231,21 +234,20 @@ class _TimetableRow extends StatelessWidget { decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), ), - child: - slotForWeekday != null - ? LessonCell( - lesson: lessonForGroup, - slot: slotForWeekday, - onTap: () { - if (lessonForGroup != null) { - onLessonTap(lessonForGroup.id!); - } else { - // Create lesson for this specific group and slot - onEmptySlotTap(slotForWeekday.id!); - } - }, - ) - : const SizedBox.shrink(), + child: slotForWeekday != null + ? LessonCell( + lesson: lessonForGroup, + slot: slotForWeekday, + onTap: () { + if (lessonForGroup != null) { + onLessonTap(lessonForGroup.id!); + } else { + // Create lesson for this specific group and slot + onEmptySlotTap(slotForWeekday.id!); + } + }, + ) + : const SizedBox.shrink(), ); }).toList(), // Add new lesson button @@ -259,10 +261,9 @@ class _TimetableRow extends StatelessWidget { child: Material( color: Colors.transparent, child: InkWell( - onTap: - slotForWeekday != null - ? () => onEmptySlotTap(slotForWeekday.id!) - : null, + onTap: slotForWeekday != null + ? () => onEmptySlotTap(slotForWeekday.id!) + : null, child: const Center( child: Icon(Icons.add, color: Colors.green, size: 24), ), @@ -306,16 +307,15 @@ class _AvailableUsersCell extends WatchingWidget { ); // Filter available users - final availableUsers = - users - .where( - (user) => - user.role == Role.teacher && // Only teachers - user.id != null && - !busyUserIds.contains(user.id) && // Not currently teaching - _hasAvailableTimeUnits(user), // Has time units left - ) - .toList(); + final availableUsers = users + .where( + (user) => + user.role == Role.teacher && // Only teachers + user.id != null && + !busyUserIds.contains(user.id) && // Not currently teaching + _hasAvailableTimeUnits(user), // Has time units left + ) + .toList(); return Container( width: 200, @@ -324,48 +324,47 @@ class _AvailableUsersCell extends WatchingWidget { border: Border.all(color: Colors.grey.shade300), color: Colors.blue.shade50, ), - child: - availableUsers.isEmpty - ? const Center( - child: Text( - 'Keine Lehrer\nverfügbar', - style: TextStyle(fontSize: 10, color: Colors.grey), - textAlign: TextAlign.center, - ), - ) - : Padding( - padding: const EdgeInsets.all(4.0), - child: SingleChildScrollView( - child: Wrap( - spacing: 2, - runSpacing: 2, - children: - availableUsers.map((user) { - final timeUnitsUsed = - _getScheduledLessonsCountForUser(user.id!); - final availableUnits = user.timeUnits - timeUnitsUsed; + child: availableUsers.isEmpty + ? const Center( + child: Text( + 'Keine Lehrer\nverfügbar', + style: TextStyle(fontSize: 10, color: Colors.grey), + textAlign: TextAlign.center, + ), + ) + : Padding( + padding: const EdgeInsets.all(4.0), + child: SingleChildScrollView( + child: Wrap( + spacing: 2, + runSpacing: 2, + children: availableUsers.map((user) { + final timeUnitsUsed = _getScheduledLessonsCountForUser( + user.id!, + ); + final availableUnits = user.timeUnits - timeUnitsUsed; - return Container( - padding: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 2, - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - '${user.userInfo?.fullName ?? 'unbekannt'} ($availableUnits)', - style: const TextStyle( - fontSize: 8, - fontWeight: FontWeight.bold, - ), - ), - ); - }).toList(), - ), + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 4, + vertical: 2, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + '${user.userInfo?.fullName ?? 'unbekannt'} ($availableUnits)', + style: const TextStyle( + fontSize: 8, + fontWeight: FontWeight.bold, + ), + ), + ); + }).toList(), ), ), + ), ); } From 5a9c0f20c69333a960f1476e1699b3108bf09cc5 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Fri, 28 Nov 2025 16:01:58 +0100 Subject: [PATCH 11/52] app: UI --- .../lib/app_utils/shorebird_code_push.dart | 69 ++++++++++--------- .../bottom_nav_bar_no_filter_button.dart | 39 +++++++++++ 2 files changed, 74 insertions(+), 34 deletions(-) create mode 100644 school_data_hub_flutter/lib/common/widgets/generic_components/bottom_nav_bar_no_filter_button.dart diff --git a/school_data_hub_flutter/lib/app_utils/shorebird_code_push.dart b/school_data_hub_flutter/lib/app_utils/shorebird_code_push.dart index de417682..6c5eea01 100644 --- a/school_data_hub_flutter/lib/app_utils/shorebird_code_push.dart +++ b/school_data_hub_flutter/lib/app_utils/shorebird_code_push.dart @@ -1,6 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/bottom_nav_bar_no_filter_button.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; import 'package:shorebird_code_push/shorebird_code_push.dart'; +final _log = Logger('CheckForUpdatesPage'); + class CheckForUpdatesPage extends StatefulWidget { const CheckForUpdatesPage({super.key}); @@ -30,7 +35,7 @@ class _CheckForUpdatesPageState extends State { }) .catchError((Object error) { // If an error occurs, we log it for now. - debugPrint('Error reading current patch: $error'); + _log.severe('Error reading current patch: $error'); }); } @@ -56,7 +61,7 @@ class _CheckForUpdatesPageState extends State { } } catch (error) { // If an error occurs, we log it for now. - debugPrint('Error checking for update: $error'); + _log.severe('Error checking for update: $error'); } finally { setState(() => _isCheckingForUpdates = false); } @@ -67,7 +72,7 @@ class _CheckForUpdatesPageState extends State { ..hideCurrentMaterialBanner() ..showMaterialBanner( const MaterialBanner( - content: Text('Downloading...'), + content: Text('Wird heruntergeladen...'), actions: [ SizedBox(height: 14, width: 14, child: CircularProgressIndicator()), ], @@ -80,9 +85,7 @@ class _CheckForUpdatesPageState extends State { ..hideCurrentMaterialBanner() ..showMaterialBanner( MaterialBanner( - content: Text( - 'Update available for the ${_currentTrack.name} track.', - ), + content: Text('Update verfügbar für ${_currentTrack.name}.'), actions: [ TextButton( onPressed: () async { @@ -103,15 +106,13 @@ class _CheckForUpdatesPageState extends State { ..hideCurrentMaterialBanner() ..showMaterialBanner( MaterialBanner( - content: Text( - 'No update available on the ${_currentTrack.name} track.', - ), + content: Text('Kein Update verfügbar für ${_currentTrack.name}.'), actions: [ TextButton( onPressed: () { ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); }, - child: const Text('Dismiss'), + child: const Text('Verstanden'), ), ], ), @@ -123,13 +124,15 @@ class _CheckForUpdatesPageState extends State { ..hideCurrentMaterialBanner() ..showMaterialBanner( MaterialBanner( - content: const Text('A new patch is ready! Please restart your app.'), + content: const Text( + 'Ein neuer Patch ist verfügbar! Bitte starte die App neu.', + ), actions: [ TextButton( onPressed: () { ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); }, - child: const Text('Dismiss'), + child: const Text('Verstanden'), ), ], ), @@ -141,15 +144,13 @@ class _CheckForUpdatesPageState extends State { ..hideCurrentMaterialBanner() ..showMaterialBanner( MaterialBanner( - content: Text( - 'An error occurred while downloading the update: $error.', - ), + content: Text('Fehler beim Herunterladen des Updates: $error.'), actions: [ TextButton( onPressed: () { ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); }, - child: const Text('Dismiss'), + child: const Text('Verstanden'), ), ], ), @@ -178,10 +179,7 @@ class _CheckForUpdatesPageState extends State { final theme = Theme.of(context); return Scaffold( - appBar: AppBar( - backgroundColor: theme.colorScheme.inversePrimary, - title: const Text('Shorebird Code Push'), - ), + appBar: const GenericAppBar(iconData: Icons.update, title: 'OTA Update'), body: Column( children: [ if (!_isUpdaterAvailable) const _ShorebirdUnavailable(), @@ -199,12 +197,12 @@ class _CheckForUpdatesPageState extends State { ), floatingActionButton: FloatingActionButton( onPressed: _isCheckingForUpdates ? null : _checkForUpdate, - tooltip: 'Check for update', - child: - _isCheckingForUpdates - ? const _LoadingIndicator() - : const Icon(Icons.refresh), + tooltip: 'Auf Updates prüfen', + child: _isCheckingForUpdates + ? const _LoadingIndicator() + : const Icon(Icons.refresh), ), + bottomNavigationBar: const BottomNavBarNoFilterButton(), ); } } @@ -216,13 +214,16 @@ class _ShorebirdUnavailable extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - return Center( - child: Text( - ''' -Shorebird is not available. -Please make sure the app was generated via `shorebird release` and that it is running in release mode.''', - style: theme.textTheme.bodyLarge?.copyWith( - color: theme.colorScheme.error, + return Padding( + padding: const EdgeInsets.all(15.0), + child: Center( + child: Text( + ''' +OTA Update ist nicht verfügbar. +Bitte stelle sicher, dass die App mit `shorebird release` generiert wurde und dass sie im Release-Modus läuft.''', + style: theme.textTheme.bodyLarge?.copyWith( + color: theme.colorScheme.error, + ), ), ), ); @@ -242,9 +243,9 @@ class _CurrentPatchVersion extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text('Current patch version:'), + const Text('Aktuelle Patch-Version:'), Text( - patch != null ? '${patch!.number}' : 'No patch installed', + patch != null ? '${patch!.number}' : 'Kein Patch installiert', style: theme.textTheme.headlineMedium, ), ], diff --git a/school_data_hub_flutter/lib/common/widgets/generic_components/bottom_nav_bar_no_filter_button.dart b/school_data_hub_flutter/lib/common/widgets/generic_components/bottom_nav_bar_no_filter_button.dart new file mode 100644 index 00000000..ad7a526c --- /dev/null +++ b/school_data_hub_flutter/lib/common/widgets/generic_components/bottom_nav_bar_no_filter_button.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/widgets/bottom_nav_bar_layouts.dart'; + +class BottomNavBarNoFilterButton extends StatelessWidget { + const BottomNavBarNoFilterButton({super.key}); + + @override + Widget build(BuildContext context) { + return BottomNavBarLayout( + bottomNavBar: BottomAppBar( + height: 60, + padding: const EdgeInsets.all(10), + shape: null, + color: AppColors.backgroundColor, + child: IconTheme( + data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 800), + child: Row( + children: [ + const Spacer(), + IconButton( + tooltip: 'zurück', + icon: const Icon(Icons.arrow_back, size: 30), + onPressed: () { + Navigator.pop(context); + }, + ), + const Gap(15), + ], + ), + ), + ), + ), + ); + } +} From 742ad0a221b6fe503aa8f9523acff3a214482b43 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Fri, 28 Nov 2025 16:14:20 +0100 Subject: [PATCH 12/52] app: refactor --- .../cached_image_or_download_inage.dart | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/school_data_hub_flutter/lib/common/widgets/cached_image_or_download_inage.dart b/school_data_hub_flutter/lib/common/widgets/cached_image_or_download_inage.dart index bdb2042b..ff7ccd2a 100644 --- a/school_data_hub_flutter/lib/common/widgets/cached_image_or_download_inage.dart +++ b/school_data_hub_flutter/lib/common/widgets/cached_image_or_download_inage.dart @@ -6,14 +6,14 @@ import 'package:school_data_hub_flutter/app_utils/custom_encrypter.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); -final _notificationService = di(); -final _cacheManager = di(); - -Future cachedImageOrDownloadImage( - {required String documentId, required bool decrypt}) async { +Future cachedImageOrDownloadImage({ + required String documentId, + required bool decrypt, +}) async { // First look for the image in the cache - final fileInfo = await _cacheManager.getFileFromCache(documentId); + final cacheManager = di(); + final notificationService = di(); + final fileInfo = await cacheManager.getFileFromCache(documentId); if (fileInfo != null && await fileInfo.file.exists()) { // File is already cached, if necessary, decrypt it before using @@ -21,23 +21,26 @@ Future cachedImageOrDownloadImage( final fileBytes = await fileInfo.file.readAsBytes(); return Image.memory(fileBytes); } - final decryptedImage = - await customEncrypter.decryptEncryptedImage(fileInfo.file); + final decryptedImage = await customEncrypter.decryptEncryptedImage( + fileInfo.file, + ); return decryptedImage; } // The image is not in the cache, so we need to download it - _notificationService.apiRunning(true); - final ByteData? byteData = await _client.files.getImage(documentId); - _notificationService.apiRunning(false); + notificationService.apiRunning(true); + final ByteData? byteData = await di().files.getImage(documentId); + notificationService.apiRunning(false); if (byteData == null) { - di() - .showSnackBar(NotificationType.error, 'Fehler beim Laden des Bildes'); + notificationService.showSnackBar( + NotificationType.error, + 'Fehler beim Laden des Bildes', + ); return Image.asset('assets/dummy-profile-pic.png'); } Uint8List imageBytes = byteData.buffer.asUint8List(); // Cache the image for future use - await _cacheManager.putFile(documentId, imageBytes); + await cacheManager.putFile(documentId, imageBytes); if (!decrypt) { return Image.memory(imageBytes); } @@ -55,7 +58,10 @@ Future cachedPublicImageOrDownloadPublicImage({ required String cacheKey, }) async { // First look for the image in the cache - final fileInfo = await _cacheManager.getFileFromCache(cacheKey); + final cacheManager = di(); + final notificationService = di(); + final client = di(); + final fileInfo = await cacheManager.getFileFromCache(cacheKey); if (fileInfo != null && await fileInfo.file.exists()) { // File is already cached, if necessary, decrypt it before using @@ -64,18 +70,20 @@ Future cachedPublicImageOrDownloadPublicImage({ return Image.memory(fileBytes); } // The image is not in the cache, so we need to download it - _notificationService.apiRunning(true); - final ByteData? byteData = await _client.files.getUnencryptedImage(path); - _notificationService.apiRunning(false); + notificationService.apiRunning(true); + final ByteData? byteData = await client.files.getUnencryptedImage(path); + notificationService.apiRunning(false); if (byteData == null) { - di() - .showSnackBar(NotificationType.error, 'Fehler beim Laden des Bildes'); + notificationService.showSnackBar( + NotificationType.error, + 'Fehler beim Laden des Bildes', + ); return Image.asset('assets/dummy-profile-pic.png'); } Uint8List imageBytes = byteData.buffer.asUint8List(); // Cache the image for future use - await _cacheManager.putFile(cacheKey, imageBytes); + await cacheManager.putFile(cacheKey, imageBytes); return Image.memory(imageBytes); From 1c9c218ece20cc6208e0192d96e767a34dc9396a Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sat, 29 Nov 2025 01:46:24 +0100 Subject: [PATCH 13/52] app: fix init and changing environment (WIP) --- school_data_hub_flutter/.cursorrules | 25 ++ .../lib/common/data/file_upload_service.dart | 14 +- .../domain/filters/filters_state_manager.dart | 18 +- .../qr/qr_carousel_with_controller.dart | 35 ++- .../lib/core/auth/hub_auth_key_manager.dart | 6 +- .../lib/core/di/di_on_active_env.dart | 42 ++- .../lib/core/di/di_on_user_auth.dart | 56 ++-- ...dency_injection.dart => init_manager.dart} | 246 ++++++++++-------- .../lib/core/env/env_manager.dart | 123 +++++---- .../lib/core/session/hub_session_manager.dart | 51 ++-- .../data/schoolday_event_api_service.dart | 6 +- .../schoolday_event_helper_functions.dart | 18 +- .../domain/schoolday_event_manager.dart | 22 +- .../widgets/schoolday_event_pupil_stats.dart | 69 ++--- .../schoolday_event_list_search_bar.dart | 26 +- .../login_page/login_controller.dart | 2 +- .../dialogs/change_env_dialog.dart | 16 +- .../widgets/settings_session_section.dart | 5 +- .../data/authorization_api_service.dart | 3 +- .../books/data/pupil_book_api_service.dart | 3 +- .../data/competence_check_api_service.dart | 3 +- .../data/learning_support_api_service.dart | 3 +- .../matrix/domain/matrix_policy_helper.dart | 4 +- .../matrix/domain/matrix_policy_manager.dart | 2 +- .../users/domain/matrix_user_manager.dart | 9 +- .../pupil/data/pupil_data_api_service.dart | 12 +- .../domain/filters/pupil_filter_manager.dart | 56 ++-- .../domain/school_calendar_manager.dart | 9 +- .../data/school_list_api_service.dart | 8 +- .../features/user/domain/user_manager.dart | 8 +- .../data/pupil_workbook_api_service.dart | 6 +- .../workbooks/data/workbook_api_service.dart | 3 +- school_data_hub_flutter/lib/main.dart | 49 ++-- school_data_hub_flutter/pubspec.lock | 4 +- school_data_hub_flutter/pubspec.yaml | 2 +- 35 files changed, 559 insertions(+), 405 deletions(-) rename school_data_hub_flutter/lib/core/di/{dependency_injection.dart => init_manager.dart} (62%) diff --git a/school_data_hub_flutter/.cursorrules b/school_data_hub_flutter/.cursorrules index 4b694fcc..84faab85 100644 --- a/school_data_hub_flutter/.cursorrules +++ b/school_data_hub_flutter/.cursorrules @@ -5,8 +5,33 @@ Please think out loud and provide - Rationale for decision - next steps checklist +## Context Usage Guidelines + +### Default Context Sources +- **ALWAYS** check `cursor_docs/` folder first when answering questions about: + - Package usage patterns (watch_it, get_it, serverpod, etc.) + - Package-specific best practices and conventions + - API documentation for packages used in this project + - How packages are integrated in this specific codebase + +### How to Use cursor_docs +- When a question involves a package that has documentation in `cursor_docs/`, search and reference that documentation +- Explicitly cite the specific file when using information from `cursor_docs/` (e.g., "According to `cursor_docs/watch_it_readme.md`...") +- Prefer information from `cursor_docs/` over general package documentation when available, as these files contain project-specific context + +### Context Priority Order +1. Project codebase (`lib/`, etc.) - actual implementation patterns +2. `cursor_docs/` folder - project-specific package documentation +3. General package documentation (pub.dev, GitHub) - fallback if not in cursor_docs + +### Available cursor_docs Files +- `cursor_docs/watch_it_readme.md` - watch_it package documentation and usage patterns +- `cursor_docs/get_it.md` - get_it package documentation and usage patterns + ## Dart and flutter rules +- Don't use print() statements!!! Implement or use instead final _log = Logger(); +- Always prefer using watch functions over ListenableBuilder - always use package import even for our project files - Comply to the always_use_package_imports rule - prefer composability breaking down presentation files: diff --git a/school_data_hub_flutter/lib/common/data/file_upload_service.dart b/school_data_hub_flutter/lib/common/data/file_upload_service.dart index c4f10350..5dc2a274 100644 --- a/school_data_hub_flutter/lib/common/data/file_upload_service.dart +++ b/school_data_hub_flutter/lib/common/data/file_upload_service.dart @@ -6,8 +6,6 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); -final _notificationService = di(); final _log = Logger('FileUploadService'); class ClientFileUpload { @@ -22,7 +20,7 @@ class ClientFileUpload { final documentId = const Uuid().v4(); final path = p.join(folder.name, '$documentId${p.extension(file.path)}'); try { - final uploadDescription = await _client.files.getUploadDescription( + final uploadDescription = await di().files.getUploadDescription( StorageId.private.name, path, ); @@ -37,20 +35,20 @@ class ClientFileUpload { final fileStream = file.openRead(); final fileLength = await file.length(); - _notificationService.apiRunning(true); + di().apiRunning(true); await uploader.upload(fileStream, fileLength); - _notificationService.apiRunning(false); + di().apiRunning(false); // Verify the upload try { - final success = await _client.files.verifyUpload( + final success = await di().files.verifyUpload( StorageId.private.name, path, ); return (path: path, success: success); } catch (e) { _log.severe('Upload failed for $path: $e'); - _notificationService.showSnackBar( + di().showSnackBar( NotificationType.error, 'Upload failed for $path: $e', ); @@ -59,7 +57,7 @@ class ClientFileUpload { } } catch (e) { _log.severe('Failed to get upload description for $path: $e'); - _notificationService.showSnackBar( + di().showSnackBar( NotificationType.error, 'Failed to get upload description for $path: $e', ); diff --git a/school_data_hub_flutter/lib/common/domain/filters/filters_state_manager.dart b/school_data_hub_flutter/lib/common/domain/filters/filters_state_manager.dart index 17a17cfd..3212fce9 100644 --- a/school_data_hub_flutter/lib/common/domain/filters/filters_state_manager.dart +++ b/school_data_hub_flutter/lib/common/domain/filters/filters_state_manager.dart @@ -47,8 +47,16 @@ abstract class FiltersStateManager { } class FiltersStateManagerImplementation implements FiltersStateManager { - final _filterStates = - ValueNotifier>(_initialFilterGlobalValues); + FiltersStateManagerImplementation(); + + void init() { + _filterStates.value = {..._initialFilterGlobalValues}; + _filtersActive.value = false; + } + + final _filterStates = ValueNotifier>( + _initialFilterGlobalValues, + ); @override ValueListenable> get filterStates => _filterStates; @@ -69,8 +77,10 @@ class FiltersStateManagerImplementation implements FiltersStateManager { newFilterState[filterState] = value; _filterStates.value = newFilterState; - final filterStatesAreEqualInitialValues = const MapEquality() - .equals(_filterStates.value, _initialFilterGlobalValues); + final filterStatesAreEqualInitialValues = const MapEquality().equals( + _filterStates.value, + _initialFilterGlobalValues, + ); if (filterStatesAreEqualInitialValues) { _filtersActive.value = false; } else { diff --git a/school_data_hub_flutter/lib/common/widgets/qr/qr_carousel_with_controller.dart b/school_data_hub_flutter/lib/common/widgets/qr/qr_carousel_with_controller.dart index c6faa687..2c4a2d1f 100644 --- a/school_data_hub_flutter/lib/common/widgets/qr/qr_carousel_with_controller.dart +++ b/school_data_hub_flutter/lib/common/widgets/qr/qr_carousel_with_controller.dart @@ -69,8 +69,9 @@ class _QrCarouselWithControllerState extends State { return CarouselSlider( carouselController: carouselController, options: CarouselOptions( - viewportFraction: - (mediaQuery.orientation == Orientation.landscape) ? 0.6 : 0.9, + viewportFraction: (mediaQuery.orientation == Orientation.landscape) + ? 0.6 + : 0.9, enlargeCenterPage: true, height: maxHeight, autoPlay: false, @@ -95,14 +96,20 @@ class _QrCarouselWithControllerState extends State { Row( children: [ Gap(mediaQuery.size.width * 0.05), - Text(i.keys.first, - style: const TextStyle( - fontSize: 30, fontWeight: FontWeight.bold)), + Text( + i.keys.first, + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), const Spacer(), Text( '${myListOfMaps.indexOf(i) + 1}/${myListOfMaps.length}', style: const TextStyle( - fontSize: 30, fontWeight: FontWeight.bold), + fontSize: 30, + fontWeight: FontWeight.bold, + ), ), Gap(mediaQuery.size.width * 0.05), ], @@ -115,9 +122,9 @@ class _QrCarouselWithControllerState extends State { version: QrVersions.auto, size: (mediaQuery.orientation == Orientation.landscape || - Platform.isWindows) - ? maxHeight * 0.9 - : maxWidth, + Platform.isWindows) + ? maxHeight * 0.9 + : maxWidth, ), ), ], @@ -133,22 +140,22 @@ class _QrCarouselWithControllerState extends State { @override void dispose() { hardwareKeyboard.removeHandler(keybardHandler); - debugPrint('QrCarouselWithController disposed'); + super.dispose(); } } void showQrCarouselWithController( - Map qrMap, BuildContext context) { + Map qrMap, + BuildContext context, +) { showDialog( context: context, builder: (context) { return Dialog( backgroundColor: Colors.transparent, insetPadding: EdgeInsets.zero, - child: QrCarouselWithController( - qrMap: qrMap, - ), + child: QrCarouselWithController(qrMap: qrMap), ); }, ); diff --git a/school_data_hub_flutter/lib/core/auth/hub_auth_key_manager.dart b/school_data_hub_flutter/lib/core/auth/hub_auth_key_manager.dart index 841a68c0..f0165865 100644 --- a/school_data_hub_flutter/lib/core/auth/hub_auth_key_manager.dart +++ b/school_data_hub_flutter/lib/core/auth/hub_auth_key_manager.dart @@ -19,9 +19,7 @@ class HubAuthKeyManager extends AuthenticationKeyManager { /// Creates a new authentication key manager. By default it will use /// secure storage for storing keys. - HubAuthKeyManager({ - required this.storageKeyForAuthKey, - }); + HubAuthKeyManager({required this.storageKeyForAuthKey}); @override Future get() async { @@ -29,7 +27,7 @@ class HubAuthKeyManager extends AuthenticationKeyManager { _authenticationKey = await _storage.getString(storageKeyForAuthKey); _initialized = true; - _log.info('Initialized - authKey from storage is: $_authenticationKey'); + _log.info('Initialized - authentication key available'); } return _authenticationKey; diff --git a/school_data_hub_flutter/lib/core/di/di_on_active_env.dart b/school_data_hub_flutter/lib/core/di/di_on_active_env.dart index b6791081..c53f97b5 100644 --- a/school_data_hub_flutter/lib/core/di/di_on_active_env.dart +++ b/school_data_hub_flutter/lib/core/di/di_on_active_env.dart @@ -11,6 +11,7 @@ final _log = Logger('DiOnActiveEnv'); class DiOnActiveEnv { static Future registerManagers() async { + _log.info('[DI] Registering managers on active environment scope'); di.registerSingletonWithDependencies(() { return HubAuthKeyManager( storageKeyForAuthKey: di().storageKeyForAuthKey, @@ -18,25 +19,40 @@ class DiOnActiveEnv { }, dependsOn: [EnvManager]); di.registerSingletonWithDependencies(() { + final envManager = di(); + final activeEnv = envManager.activeEnv; + + _log.info('[DI] Registering Client for environment:'); + _log.info('[DI] - Name: ${activeEnv?.serverName}'); + _log.info('[DI] - Server URL: ${activeEnv?.serverUrl}'); + _log.info('[DI] - Run Mode: ${activeEnv?.runMode.name}'); + + final serverUrl = activeEnv!.serverUrl; + _log.info('[DI] Creating Client with server URL: $serverUrl'); + return Client( - di().activeEnv!.serverUrl, + serverUrl, authenticationKeyManager: di(), )..connectivityMonitor = di(); }, dependsOn: [HubAuthKeyManager]); - di.registerSingletonAsync(() async { - // like described in the serverpod documentation - // https://docs.serverpod.dev/concepts/authentication/setup#app-setup - final sessionManager = HubSessionManager( - caller: di().modules.auth, - ); + di.registerSingletonAsync( + () async { + // like described in the serverpod documentation + // https://docs.serverpod.dev/concepts/authentication/setup#app-setup + final sessionManager = HubSessionManager( + caller: di().modules.auth, + ); - // this will initialize the session manager and load the stored user info - // it returns a bool - await sessionManager.initialize(); - _log.info('SessionManager initialized'); - return sessionManager; - }, dependsOn: [EnvManager, Client]); + // this will initialize the session manager and load the stored user info + // it returns a bool + await sessionManager.initialize(); + _log.info('SessionManager initialized'); + return sessionManager; + }, + dependsOn: [EnvManager, Client], + dispose: (param) => param.dispose(), + ); // Register BottomNavManager in active environment scope so it's always available di.registerSingleton(BottomNavManager()); diff --git a/school_data_hub_flutter/lib/core/di/di_on_user_auth.dart b/school_data_hub_flutter/lib/core/di/di_on_user_auth.dart index daf01338..7305ea5c 100644 --- a/school_data_hub_flutter/lib/core/di/di_on_user_auth.dart +++ b/school_data_hub_flutter/lib/core/di/di_on_user_auth.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/common/domain/filters/filters_state_manager.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; @@ -40,7 +38,8 @@ class DiInitOnUserAuth { await pupilIdentityManager.init(); - _log.info('PupilIdentityManager initialized'); + _log.fine('PupilIdentityManager initialized'); + _log.info('########################################################'); return pupilIdentityManager; }); @@ -50,7 +49,8 @@ class DiInitOnUserAuth { await schoolCalendarManager.init(); - _log.info('SchoolCalendarManager initialized'); + _log.fine('SchoolCalendarManager initialized'); + _log.info('########################################################'); return schoolCalendarManager; }); @@ -60,7 +60,8 @@ class DiInitOnUserAuth { await supportCategoryManager.init(); - _log.info('SupportCategoryManager initialized'); + _log.fine('SupportCategoryManager initialized'); + _log.info('########################################################'); return supportCategoryManager; }); @@ -71,6 +72,7 @@ class DiInitOnUserAuth { await pupilManager.init(); _log.info('PupilManager initialized'); + _log.info('########################################################'); return pupilManager; }, dependsOn: [PupilIdentityManager, HubSessionManager]); @@ -81,34 +83,36 @@ class DiInitOnUserAuth { ); di.registerSingletonAsync(() async { - log('Registering BookManager'); final bookManager = BookManager(); await bookManager.init(); - log('BookManager initialized'); + _log.fine('BookManager initialized'); + _log.info('########################################################'); return bookManager; }, dependsOn: []); di.registerSingletonAsync(() async { - log('Registering SchoolDataMainManager'); final schoolDataManager = SchoolDataMainManager(); await schoolDataManager.init(); - log('SchoolDataMainManager initialized'); + _log.fine('SchoolDataMainManager initialized'); + _log.info('########################################################'); return schoolDataManager; }, dependsOn: []); di.registerSingletonAsync(() async { - log('Registering WorkbookManager'); final workbookManager = WorkbookManager(); await workbookManager.init(); - log('WorkbookManager initialized'); + _log.fine('WorkbookManager initialized'); + _log.info('########################################################'); return workbookManager; }, dependsOn: [HubSessionManager, PupilManager]); + di.registerSingletonAsync(() async { final competenceManager = CompetenceManager(); await competenceManager.init(); - _log.info('CompetenceManager initialized'); + _log.fine('CompetenceManager initialized'); + _log.info('########################################################'); return competenceManager; }); @@ -118,10 +122,10 @@ class DiInitOnUserAuth { }, dependsOn: [CompetenceManager]); di.registerSingletonAsync(() async { - log('Registering AuthorizationManager'); final authorizationManager = AuthorizationManager(); await authorizationManager.init(); - log('AuthorizationManager initialized'); + _log.fine('AuthorizationManager initialized'); + _log.info('########################################################'); return authorizationManager; }, dependsOn: [HubSessionManager]); @@ -191,42 +195,46 @@ class DiInitOnUserAuth { ); di.registerSingletonAsync(() async { - _log.info('Registering SchoolListManager'); final schoolListManager = SchoolListManager(); await schoolListManager.init(); - _log.info('SchoolListManager initialized'); + _log.fine('SchoolListManager initialized'); + _log.info('########################################################'); return schoolListManager; }, dependsOn: [HubSessionManager, PupilManager]); di.registerSingletonWithDependencies(() { final schoolListFilterManager = SchoolListFilterManager(); schoolListFilterManager.init(); + _log.fine('SchoolListFilterManager initialized'); + _log.info('########################################################'); return schoolListFilterManager; }, dependsOn: [PupilsFilter, SchoolListManager]); - _log.info('Managers depending on authenticated session initialized'); - di.registerSingletonAsync(() async { - _log.info('Registering UserManager'); final userManager = UserManager(); await userManager.init(); - _log.info('UserManager initialized'); + _log.fine('UserManager initialized'); + _log.info('########################################################'); return userManager; }, dependsOn: [HubSessionManager]); di.registerSingletonAsync(() async { - _log.info('Registering TimetableApiService'); final timetableApiService = TimetableApiService(); - _log.info('TimetableApiService initialized'); + _log.fine('TimetableApiService initialized'); + _log.info('########################################################'); return timetableApiService; }); di.registerSingletonAsync(() async { - _log.info('Registering TimetableManager'); final timetableManager = TimetableManager(); await timetableManager.init(); - _log.info('TimetableManager initialized'); + _log.fine('TimetableManager initialized'); + _log.info('########################################################'); return timetableManager; }, dependsOn: [HubSessionManager, TimetableApiService]); + + _log.info( + '[Init] Managers depending on authentication are being initialized...', + ); } } diff --git a/school_data_hub_flutter/lib/core/di/dependency_injection.dart b/school_data_hub_flutter/lib/core/di/init_manager.dart similarity index 62% rename from school_data_hub_flutter/lib/core/di/dependency_injection.dart rename to school_data_hub_flutter/lib/core/di/init_manager.dart index 1f7b757f..9e268e78 100644 --- a/school_data_hub_flutter/lib/core/di/dependency_injection.dart +++ b/school_data_hub_flutter/lib/core/di/init_manager.dart @@ -16,12 +16,12 @@ enum DiScope { onActiveEnvScope, onLoggedInUserScope, onMatrixEnvScope } final _log = Logger('DiManager'); -class DiManager { - static final DiManager _instance = DiManager._internal(); +class InitManager { + static final InitManager _instance = InitManager._internal(); - factory DiManager() => _instance; + factory InitManager() => _instance; - DiManager._internal(); + InitManager._internal(); static Future registerCoreManagers() async { di.registerSingletonAsync(() async { @@ -48,23 +48,32 @@ class DiManager { }); } - static Future registerManagersOnActiveEnvScope() async { + static Future pushActiveEnvScopeAndRegisterDependentManagers() async { if (di.hasScope(DiScope.onActiveEnvScope.name)) { - _log.info( + _log.warning( 'Active environment scope already exists, skipping registration', ); return; } - di.pushNewScopeAsync( + await di.pushNewScopeAsync( scopeName: DiScope.onActiveEnvScope.name, dispose: () { _log.warning('[DI] Disposing [activeEnvScope]'); }, init: (getIt) async { - _log.info('[DI] Pushing Scope [activeEnvScope]'); + _log.warning( + '########################################################', + ); + _log.fine('[DI] Pushing Scope [activeEnvScope]'); + _log.warning( + '########################################################', + ); + await DiOnActiveEnv.registerManagers(); - _log.info('[DI] Managers dependent on [activeEnvScope] initialized'); + _log.info( + '[Init Scope] Managers dependent on [activeEnvScope] are being initialized...', + ); }, ); } @@ -75,28 +84,41 @@ class DiManager { _log.info('[DI] Registering managers depending on session'); if (di.hasScope(DiScope.onLoggedInUserScope.name)) { - _log.severe( - '[DI] [loggedInUserScope] already exists, dropping [loggedInUserScope] it to reinitialize managers...', + _log.info( + '[DI] [loggedInUserScope] already exists, skipping registration', ); - await di.dropScope(DiScope.onLoggedInUserScope.name); - return; } - di.pushNewScopeAsync( + + await di.pushNewScopeAsync( scopeName: DiScope.onLoggedInUserScope.name, dispose: () { - _log.severe('[DI] Disposing Scope [loggedInUserScope]', [ - StackTrace.current, - ]); + _log.warning( + '########################################################', + ); + + _log.severe('[DI] Disposing Scope [loggedInUserScope]'); + _log.warning( + '########################################################', + ); }, init: (getIt) async { - _log.info('[DI] Pushing Scope [loggedInUserScope]'); - await DiInitOnUserAuth.registerManagers(); - _log.info( - '[DI] Managers depending on authenticated session initialized', + _log.warning( + '########################################################', + ); + + _log.fine('[DI] Pushing Scope [loggedInUserScope]'); + _log.warning( + '########################################################', ); + + await DiInitOnUserAuth.registerManagers(); }, ); + + _log.info( + '[Init] Managers depending on authentication are being initialized...', + ); } static Future dropOnLoggedInUserScope() async { @@ -105,86 +127,79 @@ class DiManager { '[DI] Unregistering managers depending on session - dropping [loggedInUserScope]', ); - di.dropScope( + await di.dropScope( DiScope.onLoggedInUserScope.name, ); // This will dispose the 'logged_in_user_scope' + _log.warning('########################################################'); + + _log.info('[DI] [loggedInUserScope] dropped successfully'); + _log.warning('########################################################'); } else { - _log.info( + _log.severe( '[DI] [loggedInUserScope] does not exist, skipping drop operation', ); } } - static Future switchEnvironmentAndReinitializeManagers() async { - _log.info( - '[DI] switchEnvironmentAndReinitializeManagers: starting environment switch...', - ); + static Future dropOldActiveEnvAndRelatedScopes() async { + _log.severe('[DI] Dropping old active environment scope'); + _log.warning('########################################################'); if (di.hasScope(DiScope.onLoggedInUserScope.name)) { - _log.severe( - '[DI] switchEnvironmentAndReinitializeManagers: [loggedInUserScope] already exists, dropping it...', - ); + _log.severe('[DI] Dropping [loggedInUserScope] ...'); + await di.dropScope(DiScope.onLoggedInUserScope.name); } - //- TODO IMPORTANT: check if this is necessary - makes trouble setting up the first env if (di.hasScope(DiScope.onActiveEnvScope.name)) { - _log.severe('[DI] [activeEnvScope] already exists, dropping it...'); + _log.severe('[DI] [activeEnvScope] exists, dropping it...'); + _log.warning('########################################################'); await di.dropScope(DiScope.onActiveEnvScope.name); } // Also drop matrix scope if it exists (it's environment-dependent) - _log.warning( - '[DI] switchEnvironmentAndReinitializeManagers: dropping matrix scope...', - ); - await dropMatrixScope(); - - _log.info('[DI] Environment managers switched and reinitialized'); - - // Re-register active environment scope with new environment data - // This ensures PupilIdentityManager and other environment-dependent managers are available - await registerManagersOnActiveEnvScope(); - - _log.info( - '[DI] switchEnvironmentAndReinitializeManagers: completed successfully', - ); - } - - static Future cleanupAllEnvironmentManagers() async { - _log.warning('[DI]cleanupAllEnvironmentManagers: starting...'); - try { - _log.warning('[DI]cleanupAllEnvironmentManagers: dropping scopes...'); - if (di.hasScope(DiScope.onLoggedInUserScope.name)) { - _log.warning( - '[DI] cleanupAllEnvironmentManagers: [loggedInUserScope] exists, dropping it ...', - ); - await di.dropScope(DiScope.onLoggedInUserScope.name); - } - - if (di.hasScope(DiScope.onActiveEnvScope.name)) { - await di.dropScope(DiScope.onActiveEnvScope.name); - _log.warning('[DI] dropped [activeEnvScope]'); - } else { - _log.info( - '[DI] [activeEnvScope] does not exist, skipping drop operation', - ); - } - - // Also drop matrix scope if it exists - _log.warning( - '[DI] cleanupAllEnvironmentManagers: dropping matrix scope...', - ); + if (di.hasScope(DiScope.onMatrixEnvScope.name)) { + _log.severe('[DI] dropping matrix scope...'); + _log.warning('########################################################'); await dropMatrixScope(); - _log.warning('[DI]cleanupAllEnvironmentManagers: completed successfully'); - } catch (e) { - _log.severe( - '[DI]cleanupAllEnvironmentManagers: error occurred: $e', - StackTrace.current, - ); - rethrow; } } + // static Future cleanupAllEnvironmentManagers() async { + // _log.warning('[DI]cleanupAllEnvironmentManagers: starting...'); + // try { + // _log.warning('[DI]cleanupAllEnvironmentManagers: dropping scopes...'); + // if (di.hasScope(DiScope.onLoggedInUserScope.name)) { + // _log.warning( + // '[DI] cleanupAllEnvironmentManagers: [loggedInUserScope] exists, dropping it ...', + // ); + // await di.dropScope(DiScope.onLoggedInUserScope.name); + // } + + // if (di.hasScope(DiScope.onActiveEnvScope.name)) { + // await di.dropScope(DiScope.onActiveEnvScope.name); + // _log.warning('[DI] dropped [activeEnvScope]'); + // } else { + // _log.info( + // '[DI] [activeEnvScope] does not exist, skipping drop operation', + // ); + // } + + // // Also drop matrix scope if it exists + // _log.warning( + // '[DI] cleanupAllEnvironmentManagers: dropping matrix scope...', + // ); + // await dropMatrixScope(); + // _log.warning('[DI]cleanupAllEnvironmentManagers: completed successfully'); + // } catch (e) { + // _log.severe( + // '[DI]cleanupAllEnvironmentManagers: error occurred: $e', + // StackTrace.current, + // ); + // rethrow; + // } + // } + static Future registerMatrixManagers( MatrixCredentials? matrixCredentials, ) async { @@ -233,39 +248,50 @@ class DiManager { di.pushNewScopeAsync( scopeName: DiScope.onMatrixEnvScope.name, dispose: () { - _log.info('[DI] Disposing [matrixScope]'); + _log.warning( + '########################################################', + ); + + _log.severe('[DI] Disposing [matrixScope]'); + _log.warning( + '########################################################', + ); }, init: (getIt) async { - _log.info('[DI] Pushing Scope [matrixScope]'); - - di.registerSingletonAsync( - () async { - _log.info('[DI] Registering MatrixPolicyManager'); - - final policyManager = - await MatrixPolicyManager( - matrixCredentials!.url, - matrixCredentials.policyToken, - matrixCredentials.matrixToken, - matrixCredentials.matrixAdmin, - matrixCredentials.encryptionKey, - matrixCredentials.encryptionIv, - ).init(); - - _log.info('[DI] Matrix managers initialized'); - - // Set the registration status to true after the manager is fully initialized - // Since we depend on HubSessionManager, it should be ready now - di() - .changeMatrixPolicyManagerRegistrationStatus(true); - _log.info( - '[DI] Matrix registration status set to true after initialization', - ); - - return policyManager; - }, - dependsOn: [HubSessionManager], - ); // TODO: add dependency to PupilManager? + _log.warning( + '########################################################', + ); + + _log.fine('[DI] Pushing Scope [matrixScope]'); + _log.warning( + '########################################################', + ); + + di.registerSingletonAsync(() async { + _log.info('[DI] Registering MatrixPolicyManager'); + + final policyManager = await MatrixPolicyManager( + matrixCredentials!.url, + matrixCredentials.policyToken, + matrixCredentials.matrixToken, + matrixCredentials.matrixAdmin, + matrixCredentials.encryptionKey, + matrixCredentials.encryptionIv, + ).init(); + + _log.info('[DI] Matrix managers initialized'); + + // Set the registration status to true after the manager is fully initialized + // Since we depend on HubSessionManager, it should be ready now + di().changeMatrixPolicyManagerRegistrationStatus( + true, + ); + _log.info( + '[DI] Matrix registration status set to true after initialization', + ); + + return policyManager; + }, dependsOn: [HubSessionManager]); di.registerSingletonWithDependencies(() { return MatrixPolicyFilterManager(di()); diff --git a/school_data_hub_flutter/lib/core/env/env_manager.dart b/school_data_hub_flutter/lib/core/env/env_manager.dart index c619ff90..4217d677 100644 --- a/school_data_hub_flutter/lib/core/env/env_manager.dart +++ b/school_data_hub_flutter/lib/core/env/env_manager.dart @@ -6,12 +6,13 @@ import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; -import 'package:school_data_hub_flutter/core/di/dependency_injection.dart'; +import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/models/enums.dart'; import 'package:school_data_hub_flutter/core/env/models/env.dart'; import 'package:school_data_hub_flutter/core/models/populated_server_session_data.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_credentials.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; import 'package:watch_it/watch_it.dart'; class EnvManager with ChangeNotifier { @@ -19,22 +20,21 @@ class EnvManager with ChangeNotifier { final _notificationService = di(); - bool _dependentMangagersRegistered = false; - bool get dependentManagersRegistered => _dependentMangagersRegistered; - - final _isAuthenticated = ValueNotifier(false); - // TODO ADVICE: Decouple encryption key for allowing login // - And transfer encryption keys over stream as with the pupil identities // - with additional one-time password to access the stream /// TODO ADVICE: is this proxy authentication flag a hack or is this acceptable? + + final _isAuthenticated = ValueNotifier(false); + + /// ## 🔎 managed observable /// We need to observe in [MaterialApp] if a user is authenticated /// without accessing [HubSessionManager], because if there is not // an active env yet, it will still be unregistered. /// So this is a workaround setting a flag here - /// **CAUTION** - /// Handle this value only with [HubSessionManager] every time + /// + /// **CAUTION**: Handle this value only with [HubSessionManager] every time /// it makes an authentication status change. ValueListenable get isAuthenticated => _isAuthenticated; @@ -48,21 +48,29 @@ class EnvManager with ChangeNotifier { Env? _activeEnv; + /// ## 🔎 managed observable Env? get activeEnv => _activeEnv; Map _environments = {}; + + /// ## 🔎 managed observable Map get envs => _environments; String _defaultEnv = ''; + + /// ## 🔎 managed observable String get defaultEnv => _defaultEnv; final _envIsReady = ValueNotifier(false); + + /// ## 🔎 managed observable ValueListenable get envIsReady => _envIsReady; // Declare storage keys for the environment final String _storageKeyForEnvironments = 'environments_key'; + /// ## 🔎 managed observable String get storageKeyForAuthKey => '${_activeEnv!.serverName}_${_activeEnv!.runMode.name}_hub_auth_key'; @@ -150,8 +158,7 @@ class EnvManager with ChangeNotifier { _log.info('Default Environment set: $_defaultEnv'); _envIsReady.value = true; - await DiManager.registerManagersOnActiveEnvScope(); - setDependentManagersRegistered(true); + await InitManager.pushActiveEnvScopeAndRegisterDependentManagers(); if (await HubSecureStorage().containsKey(storageKeyForMatrixCredentials)) { final matrixCredentialsJson = await HubSecureStorage().getString( @@ -163,7 +170,7 @@ class EnvManager with ChangeNotifier { // Only register matrix managers if they're not already registered if (!di.hasScope(DiScope.onMatrixEnvScope.name)) { - await DiManager.registerMatrixManagers(matrixCredentials); + await InitManager.registerMatrixManagers(matrixCredentials); } else { _log.info( '[DI] Matrix managers already registered, skipping registration', @@ -176,14 +183,15 @@ class EnvManager with ChangeNotifier { /// **TODO:** There should be a better way to handle this. /// We need to set the environment to not ready /// when we add a new environment - void setEnvNotReady() { + void deactivateEnv() { _envIsReady.value = false; _activeEnv = null; - } - - void setDependentManagersRegistered(bool value) { - _dependentMangagersRegistered = value; - _log.info('dependentManagersRegistered: $value'); + _populatedEnvServerData = PopulatedServerSessionData( + schoolSemester: false, + schooldays: false, + competences: false, + supportCategories: false, + ); } Future _environmentsInStorage() async { @@ -253,7 +261,7 @@ class EnvManager with ChangeNotifier { // the modified environments map will be stored // in the [activateEnv] method - activateEnv(envName: env.serverName); + activateDifferentEnv(envName: env.serverName); return; } @@ -293,30 +301,30 @@ class EnvManager with ChangeNotifier { _log.info('Active environment updated: ${_activeEnv!.serverName}'); } - Future activateEnv({required String envName}) async { - _log.info('Activating environment: $envName'); + Future activateDifferentEnv({required String envName}) async { + bool isAnEnvironmentSwitch = _activeEnv?.serverName != envName; + _log.info( + 'Switching environment from [${_activeEnv?.serverName}] to [$envName]', + ); // Mark environment as not ready during switch _envIsReady.value = false; notifyListeners(); - - // 1. Reset or drop managers holding data from the old env - _log.info( - '[DI] Dropping logged in user scope to reset user-dependent managers', - ); - await DiManager.dropOnLoggedInUserScope(); - // Reset environment-dependent managers - _log.info('[DI] Switching environment managers'); - await DiManager.switchEnvironmentAndReinitializeManagers(); - // Note: switchEnvironmentAndReinitializeManagers already calls registerManagersOnActiveEnvScope() + // This will automatically drop all child scopes including loggedInUserScope + + await InitManager.dropOldActiveEnvAndRelatedScopes(); - // 2. Set the selected env as active _activeEnv = _environments[envName]!; _defaultEnv = envName; - _log.info('Environment set as active: ${_activeEnv!.serverName}'); - // Don't notify listeners yet - wait until managers are read + // Log new environment details + _log.info('[ENV] NEW active environment set:'); + _log.info('[ENV] - Name: ${_activeEnv!.serverName}'); + _log.info('[ENV] - Server URL: ${_activeEnv!.serverUrl}'); + _log.info('[ENV] - Run Mode: ${_activeEnv!.runMode.name}'); + + // Don't notify listeners yet - wait until managers are ready // Update storage with new environment configuration final updatedEnvsForStorage = EnvsInStorage( @@ -328,8 +336,13 @@ class EnvManager with ChangeNotifier { await HubSecureStorage().setString(_storageKeyForEnvironments, jsonEnvs); // Managers are already registered by switchEnvironmentAndReinitializeManagers - // Just mark them as ready - setDependentManagersRegistered(true); + + // If this is an environment switch, we need to register the managers again + if (isAnEnvironmentSwitch) { + _log.info('[DI] Registering environment-dependent managers'); + await InitManager.pushActiveEnvScopeAndRegisterDependentManagers(); + } + _log.info( '[DI] Environment-dependent managers are ready for: ${_activeEnv!.serverName}', ); @@ -356,7 +369,7 @@ class EnvManager with ChangeNotifier { _log.info( '[DI] Registering matrix managers for environment ${_activeEnv!.serverName}', ); - await DiManager.registerMatrixManagers(matrixCredentials); + await InitManager.registerMatrixManagers(matrixCredentials); } else { _log.info( '[DI] Matrix managers already registered, skipping registration', @@ -380,25 +393,41 @@ class EnvManager with ChangeNotifier { _log.info('Environment activated and ready: ${_activeEnv!.serverName}'); - // 3. Check if credentials are stored - if so, log in + // Check if credentials are stored - if so, log in // Add a small delay to ensure everything is properly initialized await Future.delayed(const Duration(milliseconds: 100)); await _checkAndAttemptAutoLogin(); - // 4. Register user-dependent managers if auto-login was successful - // This ensures UI components can access auth-dependent managers like PupilsFilter - if (_isAuthenticated.value) { - _log.info( - '[DI] User is authenticated, registering user-dependent managers', - ); - await DiManager.registerManagersDependingOnAuthedSession(); - } + // Note: registerManagersDependingOnAuthedSession is called automatically + // by HubSessionManager.refreshSession() if auto-login succeeds. + // No need to call it here again. // Show success notification after everything is set up _notificationService.showSnackBar( NotificationType.success, 'Umgebung ${_activeEnv!.serverName} aktiviert!', ); + + _log.info('[ENV] Environment switch completed successfully'); + _log.info( + '[ENV] New environment has ${await _checkPupilIdentityCount()} pupil identities', + ); + } + + /// Helper method to log pupil identity count + Future _checkPupilIdentityCount() async { + try { + // We need to wait for all DI to be ready before accessing managers + await di.allReady(); + // PupilIdentityManager is in the loggedInUserScope + if (di.isRegistered()) { + return di().availablePupilIds.length; + } + return 0; + } catch (e) { + _log.warning('Could not get pupil identity count: $e'); + } + return 0; } /// Check for stored credentials and attempt auto-login @@ -504,7 +533,7 @@ class EnvManager with ChangeNotifier { _log.info( '[DI] Switching environment managers after deleting environment $deletedEnvironment', ); - DiManager.switchEnvironmentAndReinitializeManagers(); + InitManager.dropOldActiveEnvAndRelatedScopes(); } else { // if there are no environments left, delete the environments from secure storage @@ -512,7 +541,7 @@ class EnvManager with ChangeNotifier { _log.info( '[DDI]Env $deletedEnvironment deleted. No environments left. Cleaning up all environment managers.', ); - DiManager.cleanupAllEnvironmentManagers(); + InitManager.dropOldActiveEnvAndRelatedScopes(); _activeEnv = null; _defaultEnv = ''; diff --git a/school_data_hub_flutter/lib/core/session/hub_session_manager.dart b/school_data_hub_flutter/lib/core/session/hub_session_manager.dart index 01430d6d..2ac5a86c 100644 --- a/school_data_hub_flutter/lib/core/session/hub_session_manager.dart +++ b/school_data_hub_flutter/lib/core/session/hub_session_manager.dart @@ -8,7 +8,7 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/core/auth/hub_auth_key_manager.dart'; -import 'package:school_data_hub_flutter/core/di/dependency_injection.dart'; +import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/env/utils/env_utils.dart'; import 'package:serverpod_auth_client/serverpod_auth_client.dart' @@ -47,6 +47,7 @@ class HubSessionManager with ChangeNotifier { User? _user; bool _isReady = false; + bool _isDisposed = false; User? get user => _user; bool get isReady => _isReady; @@ -74,6 +75,12 @@ class HubSessionManager with ChangeNotifier { keyManager = caller.client.authenticationKeyManager! as HubAuthKeyManager; } + @override + void dispose() { + _isDisposed = true; + super.dispose(); + } + /// Returns a singleton instance of the session manager static Future get instance async { assert( @@ -110,7 +117,7 @@ class HubSessionManager with ChangeNotifier { // Update streaming connection, if it's open. await caller.client.updateStreamingConnectionAuthenticationKey(key); - notifyListeners(); + if (!_isDisposed) notifyListeners(); } /// Returns true if the user is currently signed in. @@ -128,7 +135,7 @@ class HubSessionManager with ChangeNotifier { await _loadUserInfoFromStorage(); final sessionRefreshed = await refreshSession(); _isReady = true; - notifyListeners(); + if (!_isDisposed) notifyListeners(); return sessionRefreshed; } @@ -148,11 +155,11 @@ class HubSessionManager with ChangeNotifier { _signedInUser = null; - await _handleAuthCallResultInStorage(); + // await _handleAuthCallResultInStorage(); await keyManager.remove(); - notifyListeners(); + if (!_isDisposed) notifyListeners(); /// Don't forget to set the flag in [EnvManager] to false /// to get to the login screen. @@ -233,7 +240,7 @@ class HubSessionManager with ChangeNotifier { await _handleAuthCallResultInStorage(); - notifyListeners(); + if (!_isDisposed) notifyListeners(); if (_signedInUser != null) { /// Don't forget to set the flag in [EnvManager] to false /// to get to the login screen. @@ -245,9 +252,11 @@ class HubSessionManager with ChangeNotifier { _user = await _client.user.getCurrentUser(); - notifyListeners(); + if (!_isDisposed) notifyListeners(); - _log.info('User fetched refreshing session: ${_user?.toJson()}'); + _log.info( + 'User fetched refreshing session: ${_user?.userInfo?.userName}', + ); return false; } else { @@ -291,10 +300,16 @@ class HubSessionManager with ChangeNotifier { jsonDecode(json), ); - notifyListeners(); + if (!_isDisposed) notifyListeners(); } Future _handleAuthCallResultInStorage() async { + if (_isDisposed) { + _log.warning( + 'HubSessionManager is disposed, skipping _handleAuthCallResultInStorage', + ); + return; + } if (signedInUser == null) { _log.warning('No signed user found'); @@ -304,21 +319,25 @@ class HubSessionManager with ChangeNotifier { 'We have a signed user - Saving userinfo to storage with key: $_userInfoStorageKey', ); - _log.fine('User info: ${signedInUser!.toJson()}'); + _log.fine('User info received from auth call: ${signedInUser!.userName}'); await _storage.setString( _userInfoStorageKey, SerializationManager.encode(signedInUser), ); + _log.info( + 'User auth from auth call stored in storage and set: ${signedInUser?.userName}', + ); + _user = await _client.user.getCurrentUser(); - notifyListeners(); - _log.fine( - 'User fetched in _handleAuthCallResultInStorage: ${_user?.toJson()}', - ); + if (!_isDisposed) notifyListeners(); + _log.fine('User fetched from server: ${_user?.userInfo?.userName}'); // We can start now the managers dependent on authentication - await DiManager.registerManagersDependingOnAuthedSession(); + if (!_isDisposed) { + await InitManager.registerManagersDependingOnAuthedSession(); + } } } @@ -333,7 +352,7 @@ class HubSessionManager with ChangeNotifier { if (success) { _signedInUser = await caller.status.getUserInfo(); - notifyListeners(); + if (!_isDisposed) notifyListeners(); return true; } diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart b/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart index 7d9620d7..697b0093 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart @@ -10,12 +10,12 @@ import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); -final _notificationService = di(); -final _hubSessionManager = di(); final _log = Logger('SchooldayEventApiService'); class SchooldayEventApiService { + final _notificationService = di(); + Client get _client => di(); + HubSessionManager get _hubSessionManager => di(); //- post schooldayEvent Future postSchooldayEvent( diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_helper_functions.dart b/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_helper_functions.dart index b66a255d..c321fc16 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_helper_functions.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_helper_functions.dart @@ -1,19 +1,13 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/filters/schoolday_event_filter_manager.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/models/schoolday_event_enums.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilsFilter = di(); - -final _schooldayEventFilterManager = di(); -final _hubSessionManager = di(); -final _schooldayEventManager = di(); - class SchooldayEventsCounts { final int totalSchooldayEvents; final int totalLessonSchooldayEvents; @@ -31,6 +25,14 @@ class SchooldayEventsCounts { } class SchoolDayEventHelper { + static PupilsFilter get _pupilsFilter => di(); + + static SchooldayEventFilterManager get _schooldayEventFilterManager => + di(); + static HubSessionManager get _hubSessionManager => di(); + static SchooldayEventManager get _schooldayEventManager => + di(); + static int pupilsWithSchoolDayEvents() { final List pupils = _pupilsFilter.filteredPupils.value; int pupilsWithEvents = 0; diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart b/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart index 3881273b..c3bd3419 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart @@ -35,14 +35,26 @@ class SchooldayEventManager with ChangeNotifier { } Future init() async { - // we must have a proxy object for every pupil because we need to - // watch them in the UI unconditionally (even if there are no entries) + _pupilManager.addListener(_updatePupilProxies); + _updatePupilProxies(); + // Defer fetching events until next frame to ensure proxies are ready + Future.microtask(() => fetchSchooldayEvents()); + _log.info('SchooldayEventManager initialized'); + } + + void _updatePupilProxies() { final pupilIds = _pupilManager.allPupils.map((e) => e.pupilId).toList(); for (var pupilId in pupilIds) { - _pupilSchooldayEventsMap[pupilId] = PupilSchooldayEventsProxy(); + if (!_pupilSchooldayEventsMap.containsKey(pupilId)) { + _pupilSchooldayEventsMap[pupilId] = PupilSchooldayEventsProxy(); + } } - fetchSchooldayEvents(); - _log.info('SchooldayEventManager initialized'); + } + + @override + void dispose() { + _pupilManager.removeListener(_updatePupilProxies); + super.dispose(); } //- Getters diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/widgets/schoolday_event_pupil_stats.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/widgets/schoolday_event_pupil_stats.dart index a2f9f92c..92d60bb5 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/widgets/schoolday_event_pupil_stats.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/widgets/schoolday_event_pupil_stats.dart @@ -2,39 +2,43 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/filters/schoolday_event_filter_manager.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _schooldayEventFilterManager = di(); -final _schooldayEventManager = di(); - class SchooldayEventPupilStats extends WatchingWidget { final PupilProxy pupil; const SchooldayEventPupilStats({required this.pupil, super.key}); @override Widget build(BuildContext context) { + final _schooldayEventFilterManager = di(); + final _schooldayEventManager = di(); Color admonitionsColor = AppColors.backgroundColor; Color afternoonAdmonitionsColor = AppColors.backgroundColor; final unfilteredEvents = watch( - _schooldayEventManager.getPupilSchooldayEventsProxy(pupil.pupilId)) - .schooldayEvents; + _schooldayEventManager.getPupilSchooldayEventsProxy(pupil.pupilId), + ).schooldayEvents; final schooldavEvents = _schooldayEventFilterManager .filteredSchooldayEvents(unfilteredEvents.values.toList()); final admonitions = schooldavEvents - .where((element) => - element.eventType == SchooldayEventType.admonition || - element.eventType == SchooldayEventType.admonitionAndBanned) + .where( + (element) => + element.eventType == SchooldayEventType.admonition || + element.eventType == SchooldayEventType.admonitionAndBanned, + ) .toList(); final afternoonCareAdmonitions = schooldavEvents - .where((element) => - element.eventType == SchooldayEventType.afternoonCareAdmonition) + .where( + (element) => + element.eventType == SchooldayEventType.afternoonCareAdmonition, + ) .toList(); final parentsMeeting = schooldavEvents .where( - (element) => element.eventType == SchooldayEventType.parentsMeeting) + (element) => element.eventType == SchooldayEventType.parentsMeeting, + ) .toList(); final otherEvents = schooldavEvents @@ -55,10 +59,7 @@ class SchooldayEventPupilStats extends WatchingWidget { mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ - const Icon( - Icons.school_rounded, - color: Colors.red, - ), + const Icon(Icons.school_rounded, color: Colors.red), const Gap(10), Text( admonitions.length.toString(), @@ -72,29 +73,31 @@ class SchooldayEventPupilStats extends WatchingWidget { const Text( 'OGS', style: TextStyle( - fontSize: 13, color: Colors.red, fontWeight: FontWeight.bold), + fontSize: 13, + color: Colors.red, + fontWeight: FontWeight.bold, + ), ), const Gap(5), - Text(afternoonCareAdmonitions.length.toString(), - style: TextStyle( - color: afternoonAdmonitionsColor, - fontWeight: FontWeight.bold, - fontSize: 20, - )), - const Gap(10), - const Text( - '👪️', + Text( + afternoonCareAdmonitions.length.toString(), style: TextStyle( - fontSize: 18, + color: afternoonAdmonitionsColor, + fontWeight: FontWeight.bold, + fontSize: 20, ), ), + const Gap(10), + const Text('👪️', style: TextStyle(fontSize: 18)), const Gap(5), - Text(parentsMeeting.length.toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 20, - )), + Text( + parentsMeeting.length.toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), const Gap(10), const Text('🗒️'), const Gap(5), diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/searchbar/schoolday_event_list_search_bar.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/searchbar/schoolday_event_list_search_bar.dart index dda97e14..336478f8 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/searchbar/schoolday_event_list_search_bar.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/searchbar/schoolday_event_list_search_bar.dart @@ -3,30 +3,30 @@ import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/domain/filters/filters_state_manager.dart'; import 'package:school_data_hub_flutter/common/domain/models/enums.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_search_text_field.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_helper_functions.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/searchbar/schoolday_event_stats_row.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_search_text_field.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilsFilter = di(); - -final _filtersStateManager = di(); - class SchooldayEventListSearchBar extends WatchingWidget { const SchooldayEventListSearchBar({super.key}); @override Widget build(BuildContext context) { + final _pupilsFilter = di(); + + final _filtersStateManager = di(); final pupils = watchValue((PupilsFilter x) => x.filteredPupils); // we need to watch the schoolday events to refresh the counts for the stats watchPropertyValue((SchooldayEventManager m) => m.schooldayEvents); - final filtersActive = - watchValue((FiltersStateManager x) => x.filtersActive); + final filtersActive = watchValue( + (FiltersStateManager x) => x.filtersActive, + ); // Let's get the total numbers for the schoolday events variants final SchooldayEventsCounts schooldayEventsCounts = @@ -58,10 +58,12 @@ class SchooldayEventListSearchBar extends WatchingWidget { child: Row( children: [ Expanded( - child: PupilSearchTextField( - searchType: SearchType.pupil, - hintText: 'Schüler/in suchen', - refreshFunction: _pupilsFilter.refreshs)), + child: PupilSearchTextField( + searchType: SearchType.pupil, + hintText: 'Schüler/in suchen', + refreshFunction: _pupilsFilter.refreshs, + ), + ), const Gap(5), InkWell( onTap: () => showSchooldayEventFilterBottomSheet(context), diff --git a/school_data_hub_flutter/lib/features/app_entry_point/login_page/login_controller.dart b/school_data_hub_flutter/lib/features/app_entry_point/login_page/login_controller.dart index ec679dd5..4d414c55 100644 --- a/school_data_hub_flutter/lib/features/app_entry_point/login_page/login_controller.dart +++ b/school_data_hub_flutter/lib/features/app_entry_point/login_page/login_controller.dart @@ -58,7 +58,7 @@ class LoginController extends State { void changeEnv(String? envName) { setState(() { selectedEnv = envName!; - _envManager.activateEnv(envName: envName); + _envManager.activateDifferentEnv(envName: envName); }); } diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart index 31f29b67..1f0b654e 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart @@ -4,7 +4,7 @@ import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; -import 'package:school_data_hub_flutter/core/di/dependency_injection.dart'; +import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/env/models/env.dart'; import 'package:watch_it/watch_it.dart'; @@ -54,7 +54,7 @@ Future changeEnvironmentDialog({required BuildContext context}) async { if (confirmation != true) return; if (context.mounted) { Navigator.of(context).pop(); - _envManager.activateEnv( + _envManager.activateDifferentEnv( envName: envs[index].serverName, ); } @@ -78,10 +78,10 @@ Future changeEnvironmentDialog({required BuildContext context}) async { const Gap(10), _envManager.activeEnv?.serverName == envs[index].serverName ? const Icon( - Icons.check, - color: Colors.green, - weight: 20, - ) + Icons.check, + color: Colors.green, + weight: 20, + ) : const SizedBox(), ], ), @@ -100,12 +100,12 @@ Future changeEnvironmentDialog({required BuildContext context}) async { _log.info( '[DI] User wants to add a new environment frpm the dialog: dropping logged in user scope first', ); - DiManager.dropOnLoggedInUserScope(); + InitManager.dropOnLoggedInUserScope(); // await di().signOutDevice(); _log.warning( '[DI] User signed out, setting env not ready from the dialog', ); - _envManager.setEnvNotReady(); + _envManager.deactivateEnv(); }, // Add onPressed child: const Text( "NEUE INSTANZ", diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart index 073d3f16..05042b44 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart @@ -6,7 +6,7 @@ import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; -import 'package:school_data_hub_flutter/core/di/dependency_injection.dart'; +import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; @@ -135,14 +135,13 @@ class SettingsSessionSection extends AbstractSettingsSection with WatchItMixin { message: 'Instanz-ID-Schlüssel löschen?', ); if (confirm == true && context.mounted) { - // TODO: move this logic to a _log.warning('[DI] Hang on tight, signing out! '); await di().deleteEnv(); di().signOutDevice(); _log.warning( '[DI] Env deleted, calling [unregisterMaagersDependentOnEnv] from the settings section!', ); - DiManager.cleanupAllEnvironmentManagers(); + InitManager.dropOldActiveEnvAndRelatedScopes(); _notificationService.showSnackBar( NotificationType.success, 'Instanz-ID-Schlüssel gelöscht', diff --git a/school_data_hub_flutter/lib/features/authorizations/data/authorization_api_service.dart b/school_data_hub_flutter/lib/features/authorizations/data/authorization_api_service.dart index b074c430..c0c1aa2a 100644 --- a/school_data_hub_flutter/lib/features/authorizations/data/authorization_api_service.dart +++ b/school_data_hub_flutter/lib/features/authorizations/data/authorization_api_service.dart @@ -6,9 +6,8 @@ import 'package:school_data_hub_flutter/common/services/notification_service.dar import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); - class AuthorizationApiService { + Client get _client => di(); //- AUTHORIZATIONS ------------------------------------------- //- getAuthorizations diff --git a/school_data_hub_flutter/lib/features/books/data/pupil_book_api_service.dart b/school_data_hub_flutter/lib/features/books/data/pupil_book_api_service.dart index 04ca1052..675b0952 100644 --- a/school_data_hub_flutter/lib/features/books/data/pupil_book_api_service.dart +++ b/school_data_hub_flutter/lib/features/books/data/pupil_book_api_service.dart @@ -2,9 +2,8 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); - class PupilBookApiService { + Client get _client => di(); //- create pupil book lending Future postPupilBookLending({ diff --git a/school_data_hub_flutter/lib/features/learning/data/competence_check_api_service.dart b/school_data_hub_flutter/lib/features/learning/data/competence_check_api_service.dart index 7733ca2c..9300c826 100644 --- a/school_data_hub_flutter/lib/features/learning/data/competence_check_api_service.dart +++ b/school_data_hub_flutter/lib/features/learning/data/competence_check_api_service.dart @@ -6,9 +6,8 @@ import 'package:school_data_hub_flutter/common/models/enums.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); - class CompetenceCheckApiService { + Client get _client => di(); //- create Future postCompetenceCheck({ diff --git a/school_data_hub_flutter/lib/features/learning_support/data/learning_support_api_service.dart b/school_data_hub_flutter/lib/features/learning_support/data/learning_support_api_service.dart index 3a80950a..3241fb09 100644 --- a/school_data_hub_flutter/lib/features/learning_support/data/learning_support_api_service.dart +++ b/school_data_hub_flutter/lib/features/learning_support/data/learning_support_api_service.dart @@ -2,9 +2,8 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); - class LearningSupportApiService { + Client get _client => di(); //- LEARNING SUPPORT PLANS ------------------------------------------ Future postLearningSupportPlan( diff --git a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart index 3175a012..00488e5e 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart @@ -5,7 +5,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; -import 'package:school_data_hub_flutter/core/di/dependency_injection.dart'; +import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_credentials.dart'; @@ -76,7 +76,7 @@ class MatrixPolicyHelper { // is the passed credentials are null, we will use the stored ones final validCredentials = passedCredentials ?? storedCredentials; - await DiManager.registerMatrixManagers(validCredentials!); + await InitManager.registerMatrixManagers(validCredentials!); return; } diff --git a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart index 77a78ac1..9997954f 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; -import 'package:school_data_hub_flutter/core/di/dependency_injection.dart'; +import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/data/matrix_api_service.dart'; diff --git a/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_manager.dart b/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_manager.dart index 254a6392..2f770301 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_manager.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_manager.dart @@ -197,7 +197,7 @@ class MatrixUserManager extends ChangeNotifier { required bool isStaff, }) async { final password = MatrixPolicyHelper.generatePassword(); - debugPrint('Generated password: $password'); + _log.info('Generated password: $password'); final bool success = await _matrixApiService.userApi.resetPassword( userId: user.id!, @@ -234,10 +234,9 @@ class MatrixUserManager extends ChangeNotifier { for (String roomId in roomIds) { user.joinRoom(MatrixRoom(id: roomId)); - final updatedUsers = - _matrixUsers.value - .map((e) => e.id == matrixUserId ? user : e) - .toList(); + final updatedUsers = _matrixUsers.value + .map((e) => e.id == matrixUserId ? user : e) + .toList(); _matrixUsers.value = updatedUsers; } diff --git a/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart b/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart index 047c573f..cf703f9a 100644 --- a/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart +++ b/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/data/file_upload_service.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; @@ -7,10 +8,6 @@ import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _notificationService = di(); -final _hubSessionManager = di(); -final _client = di(); - class PupilDataApiService { // Private constructor PupilDataApiService._internal(); @@ -23,6 +20,9 @@ class PupilDataApiService { return _instance; } + final _notificationService = di(); + HubSessionManager get _hubSessionManager => di(); + Client get _client => di(); // - update backend pupil database Future?> updateBackendPupilsDatabase({ @@ -270,6 +270,10 @@ class PupilDataApiService { } Future fetchLastIdentitiesUpdate() async { + final _log = Logger('PupilDataApiService'); + _log.warning('########################################################'); + _log.warning('Client is using server URL: ${_client.host}'); + _log.warning('########################################################'); final lastUpdate = await ClientHelper.apiCall( call: () => _client.pupilIdentity.fetchLastPupilIdentitiesUpdate(), errorMessage: 'Die letzte Aktualisierung konnte nicht geladen werden', diff --git a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_filter_manager.dart b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_filter_manager.dart index a30e8023..a967e5f6 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_filter_manager.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_filter_manager.dart @@ -16,17 +16,20 @@ class PupilFilterManager { // final _searchText = ValueNotifier(''); // ValueListenable get searchText => _searchText; - final _pupilFilterState = - ValueNotifier>(initialPupilFilterValues); + final _pupilFilterState = ValueNotifier>( + initialPupilFilterValues, + ); ValueListenable> get pupilFilterState => _pupilFilterState; - final _sortMode = - ValueNotifier>(initialSortModeValues); + final _sortMode = ValueNotifier>( + initialSortModeValues, + ); ValueListenable> get sortMode => _sortMode; - final _filteredPupils = - ValueNotifier>(di().allPupils); + final _filteredPupils = ValueNotifier>( + di().allPupils, + ); ValueListenable> get filteredPupils => _filteredPupils; PupilFilterManager(); @@ -41,8 +44,10 @@ class PupilFilterManager { _sortMode.value = {...initialSortModeValues}; - di() - .setFilterState(filterState: FilterState.pupilLegacy, value: false); + di().setFilterState( + filterState: FilterState.pupilLegacy, + value: false, + ); di().refreshs(); } @@ -55,15 +60,21 @@ class PupilFilterManager { record.filter: record.value, }; } - final bool pupilFilterStateEqualsInitialState = const MapEquality() - .equals(_pupilFilterState.value, initialPupilFilterValues); + final bool pupilFilterStateEqualsInitialState = const MapEquality().equals( + _pupilFilterState.value, + initialPupilFilterValues, + ); if (pupilFilterStateEqualsInitialState) { - di() - .setFilterState(filterState: FilterState.pupilLegacy, value: false); + di().setFilterState( + filterState: FilterState.pupilLegacy, + value: false, + ); } else { - di() - .setFilterState(filterState: FilterState.pupilLegacy, value: true); + di().setFilterState( + filterState: FilterState.pupilLegacy, + value: true, + ); } di().refreshs(); @@ -75,19 +86,21 @@ class PupilFilterManager { (b.schooldayEvents?.isEmpty ?? true) ? compareLastSchooldayEventDates(a, b) // Handle empty or both empty : (a.schooldayEvents?.isEmpty ?? true) - ? 1 - : -1; // Place empty after non-empty + ? 1 + : -1; // Place empty after non-empty } int comparePupilsByLastNonProcessedSchooldayEvent( - PupilProxy a, PupilProxy b) { + PupilProxy a, + PupilProxy b, + ) { // Handle potential null cases with null-aware operators return (a.schooldayEvents?.isEmpty ?? true) == (b.schooldayEvents?.isEmpty ?? true) ? compareLastSchooldayEventDates(a, b) // Handle empty or both empty : (a.schooldayEvents?.isEmpty ?? true) - ? 1 - : -1; // Place empty after non-empty + ? 1 + : -1; // Place empty after non-empty } int compareLastSchooldayEventDates(PupilProxy a, PupilProxy b) { @@ -95,8 +108,9 @@ class PupilFilterManager { if (a.schooldayEvents!.isNotEmpty && b.schooldayEvents!.isNotEmpty) { final schooldayEventDateA = a.schooldayEvents!.last.schoolday!.schoolday; final schooldayEventDateB = b.schooldayEvents!.last.schoolday!.schoolday; - return schooldayEventDateB - .compareTo(schooldayEventDateA); // Reversed for descending order + return schooldayEventDateB.compareTo( + schooldayEventDateA, + ); // Reversed for descending order } else { // Handle cases where one or both lists are empty return 0; diff --git a/school_data_hub_flutter/lib/features/school_calendar/domain/school_calendar_manager.dart b/school_data_hub_flutter/lib/features/school_calendar/domain/school_calendar_manager.dart index 3c255653..ccc20217 100644 --- a/school_data_hub_flutter/lib/features/school_calendar/domain/school_calendar_manager.dart +++ b/school_data_hub_flutter/lib/features/school_calendar/domain/school_calendar_manager.dart @@ -10,15 +10,12 @@ import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); - -final _envManager = di(); - -final _notificationService = di(); - final _log = Logger('SchooldayManager'); class SchoolCalendarManager { + Client get _client => di(); + EnvManager get _envManager => di(); + final _notificationService = di(); final _schooldays = ValueNotifier>([]); ValueNotifier> get schooldays => _schooldays; diff --git a/school_data_hub_flutter/lib/features/school_lists/data/school_list_api_service.dart b/school_data_hub_flutter/lib/features/school_lists/data/school_list_api_service.dart index 44307eb0..3942bcf5 100644 --- a/school_data_hub_flutter/lib/features/school_lists/data/school_list_api_service.dart +++ b/school_data_hub_flutter/lib/features/school_lists/data/school_list_api_service.dart @@ -1,15 +1,11 @@ -import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); -final _hubSessionManager = di(); - -final _log = Logger('SchoolListApiService'); - class SchoolListApiService { + Client get _client => di(); + HubSessionManager get _hubSessionManager => di(); //- get school lists Future?> fetchSchoolLists() async { diff --git a/school_data_hub_flutter/lib/features/user/domain/user_manager.dart b/school_data_hub_flutter/lib/features/user/domain/user_manager.dart index defe82f4..55de21e1 100644 --- a/school_data_hub_flutter/lib/features/user/domain/user_manager.dart +++ b/school_data_hub_flutter/lib/features/user/domain/user_manager.dart @@ -8,11 +8,9 @@ import 'package:watch_it/watch_it.dart'; class UserManager { final _log = Logger('UserManager'); - final _client = di(); - - final _sessionManager = di(); - - final _notificationService = di(); + Client get _client => di(); + HubSessionManager get _sessionManager => di(); + NotificationService get _notificationService => di(); ValueListenable> get users => _users; final _users = ValueNotifier>([]); diff --git a/school_data_hub_flutter/lib/features/workbooks/data/pupil_workbook_api_service.dart b/school_data_hub_flutter/lib/features/workbooks/data/pupil_workbook_api_service.dart index 57e12c12..200c73a0 100644 --- a/school_data_hub_flutter/lib/features/workbooks/data/pupil_workbook_api_service.dart +++ b/school_data_hub_flutter/lib/features/workbooks/data/pupil_workbook_api_service.dart @@ -3,11 +3,9 @@ import 'package:school_data_hub_flutter/common/services/notification_service.dar import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:watch_it/watch_it.dart'; -final _notificationService = di(); - -final _client = di(); - class PupilWorkbookApiService { + final _notificationService = di(); + Client get _client => di(); //- create Future postNewPupilWorkbook( diff --git a/school_data_hub_flutter/lib/features/workbooks/data/workbook_api_service.dart b/school_data_hub_flutter/lib/features/workbooks/data/workbook_api_service.dart index d37244a0..24b42847 100644 --- a/school_data_hub_flutter/lib/features/workbooks/data/workbook_api_service.dart +++ b/school_data_hub_flutter/lib/features/workbooks/data/workbook_api_service.dart @@ -2,9 +2,8 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:watch_it/watch_it.dart'; -final _client = di(); - class WorkbookApiService { + Client get _client => di(); //- get workbooks Future?> getWorkbooks() async { diff --git a/school_data_hub_flutter/lib/main.dart b/school_data_hub_flutter/lib/main.dart index 29c2589d..8c09a1e5 100644 --- a/school_data_hub_flutter/lib/main.dart +++ b/school_data_hub_flutter/lib/main.dart @@ -8,7 +8,7 @@ import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:school_data_hub_flutter/app_utils/logger/logrecord_formatter.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/core/di/dependency_injection.dart'; +import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/session/serverpod_connectivity_monitor.dart'; import 'package:school_data_hub_flutter/features/app_entry_point/entry_point/entry_point_controller.dart'; @@ -56,7 +56,7 @@ void main() async { ), ); - DiManager.registerCoreManagers(); + InitManager.registerCoreManagers(); await di.allReady(); runApp(const MyApp()); @@ -105,31 +105,30 @@ class MyApp extends WatchingWidget { ], debugShowCheckedModeBanner: false, title: 'Schuldaten Hub', - home: - !isConnected - ? const NoConnectionPage() - : envIsReady - ? FutureBuilder( - future: di.allReady(timeout: const Duration(seconds: 30)), - builder: (context, snapshot) { - if (snapshot.hasError) { - log.shout( - 'Dependency Injection Error: ${snapshot.error}', - snapshot.stackTrace, - ); - return ErrorPage(error: snapshot.error.toString()); - } else if (snapshot.connectionState == ConnectionState.done) { - if (userIsAuthenticated) { - return MainMenuBottomNavigation(); - } else { - return const Login(); - } + home: !isConnected + ? const NoConnectionPage() + : envIsReady + ? FutureBuilder( + future: di.allReady(timeout: const Duration(seconds: 30)), + builder: (context, snapshot) { + if (snapshot.hasError) { + log.shout( + 'Dependency Injection Error: ${snapshot.error}', + snapshot.stackTrace, + ); + return ErrorPage(error: snapshot.error.toString()); + } else if (snapshot.connectionState == ConnectionState.done) { + if (userIsAuthenticated) { + return MainMenuBottomNavigation(); } else { - return const LoadingPage(); + return const Login(); } - }, - ) - : const EntryPoint(), + } else { + return const LoadingPage(); + } + }, + ) + : const EntryPoint(), ); } } diff --git a/school_data_hub_flutter/pubspec.lock b/school_data_hub_flutter/pubspec.lock index d390d6a0..642b162a 100644 --- a/school_data_hub_flutter/pubspec.lock +++ b/school_data_hub_flutter/pubspec.lock @@ -461,10 +461,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22 + sha256: "7872545770c277236fd32b022767576c562ba28366204ff1a5628853cf8f2200" url: "https://pub.dev" source: hosted - version: "10.3.2" + version: "10.3.7" file_selector_linux: dependency: transitive description: diff --git a/school_data_hub_flutter/pubspec.yaml b/school_data_hub_flutter/pubspec.yaml index 5bfc6810..fc517aa4 100644 --- a/school_data_hub_flutter/pubspec.yaml +++ b/school_data_hub_flutter/pubspec.yaml @@ -35,7 +35,7 @@ dependencies: serverpod_auth_shared_flutter: 2.9.1 watch_it: ^1.7.0 package_info_plus: ^9.0.0 - file_picker: ^10.3.2 + file_picker: ^10.3.7 gap: ^3.0.1 flutter_localizations: sdk: flutter From 53941e88df58c3f6c3f95c7db8518ca284cabc4b Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sat, 29 Nov 2025 02:36:34 +0100 Subject: [PATCH 14/52] [app] initialization: get rid of top level private di variables, fixes --- .../domain/attendance_helper_functions.dart | 8 +-- .../filters/attendance_pupil_filter.dart | 10 +-- .../attendance_page/attendance_list_page.dart | 6 +- .../widgets/atendance_list_card.dart | 5 +- .../attendance_view_bottom_navbar.dart | 5 +- .../dialogues/multiple_entries_dialog.dart | 6 +- .../new_schoolday_event_page.dart | 8 ++- .../schoolday_event_list_page.dart | 13 ++-- .../schoolday_event_type_dialog.dart | 5 +- .../widgets/pupil_schoolday_event_card.dart | 12 ++-- .../widgets/pupil_schoolday_events_list.dart | 20 +++--- .../schoolday_event_filter_bottom_sheet.dart | 6 +- .../schoolday_event_pupil_list_card.dart | 9 +-- .../app_main_navigation/tools_page.dart | 6 +- .../widgets/landing_bottom_nav_bar.dart | 5 +- .../filters/authorization_filter_manager.dart | 5 +- .../authorization_pupils_page.dart | 5 +- .../widgets/authorization_pupil_card.dart | 5 +- .../authorization_pupils_bottom_navbar.dart | 6 +- .../authorizations_list_page.dart | 5 +- .../authorization_list_bottom_navbar.dart | 3 +- .../new_authorization_page.dart | 6 +- .../widgets/pupil_book_card.dart | 3 +- .../edit_book_page/edit_book_page.dart | 3 +- .../new_book_page/new_book_page.dart | 3 +- .../learning/domain/competence_helper.dart | 22 +++---- .../filters/competence_filter_manager.dart | 20 +++--- .../competence_list_page.dart | 3 +- .../widgets/competence_tree.dart | 2 +- .../post_or_patch_competence_page.dart | 4 +- .../pupil_competence_card.dart | 3 +- .../pupil_learning_content_workbooks.dart | 3 +- .../learning_support_filter_manager.dart | 10 +-- .../domain/learning_support_helper.dart | 3 +- .../learning_support_list_page.dart | 3 +- .../support_category_status_batches.dart | 3 +- .../new_learning_support_plan_controller.dart | 14 ++-- ...ew_support_category_status_controller.dart | 5 +- .../new_support_category_status_page.dart | 7 +- .../new_support_goal_page.dart | 5 +- .../widgets/support_category_tree.dart | 4 +- .../dialogs/preschool_revision_dialog.dart | 3 +- .../widgets/support_category_status_card.dart | 3 +- .../support_category_status_entry.dart | 3 +- .../support_category_parents_names.dart | 2 +- .../pupil_support_category_tree.dart | 5 +- .../support_goal/support_goal_card.dart | 3 +- .../support_goal_card_banner.dart | 3 +- .../learning_support_plan_pdf_generator.dart | 3 +- .../filters/matrix_policy_filter_manager.dart | 57 +++++++--------- .../matrix/domain/matrix_policy_helper.dart | 8 +-- .../matrix/domain/models/matrix_user.dart | 4 +- .../dialogues/change_power_level_dialog.dart | 3 +- .../rooms/domain/matrix_room_helper.dart | 4 +- .../matrix_rooms_list_page.dart | 3 +- .../widgets/room_list_card.dart | 3 +- .../widgets/room_list_view_bottom_navbar.dart | 5 +- .../widgets/users_in_room_list.dart | 11 ++-- .../new_matrix_room_page.dart | 6 +- .../select_matrix_rooms_list_controller.dart | 9 +-- .../users/domain/matrix_user_helper.dart | 6 +- .../matrix_users_list_page.dart | 3 +- .../widgets/matrix_user_list_card.dart | 4 +- .../matrix_users_list_view_bottom_navbar.dart | 5 +- .../widgets/pupil_rooms_list.dart | 4 +- .../family_language_lessons_list_page.dart | 8 +-- .../pupil_profile_page.dart | 3 +- .../widgets/pupil_profile_heading_card.dart | 3 +- .../pupil_profile_attendance_content.dart | 3 +- ...upil_content_authorization_entry_card.dart | 3 +- .../dialogs/language_dialog.dart | 6 +- .../pupil_profile_communication_content.dart | 5 +- .../pupil_profile_infos_content.dart | 9 +-- .../widgets/avatar_auth_values.dart | 6 +- .../widgets/pupil_media_auth_values.dart | 6 +- .../support_level_history_expansion_tile.dart | 5 +- ..._profile_school_list_pupil_entry_card.dart | 3 +- .../religion_page/religion_list_page.dart | 8 +-- .../select_pupils_list_page.dart | 6 +- .../special_info_list_page.dart | 8 +-- .../pupil_profile_attendance_content.dart | 3 +- .../new_school_semester_page.dart | 3 +- .../schooldays_calendar_page.dart | 5 +- .../filters/school_list_filter_manager.dart | 6 +- .../domain/school_list_helper_functions.dart | 3 +- .../new_list_page/new_school_list_page.dart | 7 +- .../school_list_pupil_entries_page.dart | 66 +++++++++---------- ...hool_list_pupil_entries_bottom_navbar.dart | 7 +- .../widgets/school_list_pupil_entry_card.dart | 11 ++-- .../widgets/school_list_stats_row.dart | 3 +- .../school_lists_page/school_lists_page.dart | 38 +++++------ .../widgets/school_list_card.dart | 5 +- .../services/school_list_pdf_generator.dart | 2 +- .../change_password/change_password_page.dart | 3 +- .../create_user/create_user_page.dart | 3 +- .../reset_user_password_page.dart | 3 +- .../select_users/select_users_page.dart | 4 +- .../domain/pupil_workbook_manager.dart | 5 +- school_data_hub_flutter/lib/main.dart | 5 +- 99 files changed, 308 insertions(+), 395 deletions(-) diff --git a/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart b/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart index 08951680..cb2b0fdb 100644 --- a/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart +++ b/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart @@ -10,12 +10,12 @@ import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolCalendarManager = di(); -final _attendanceManager = di(); -final _pupilManager = di(); - //- lookup functions class AttendanceHelper { + static SchoolCalendarManager get _schoolCalendarManager => + di(); + static AttendanceManager get _attendanceManager => di(); + static PupilManager get _pupilManager => di(); // static int? getMissedSchooldayIndex(PupilProxy pupil, DateTime date) { // final int? foundMissedSchooldayIndex = pupil.missedSchooldays?.indexWhere( // (datematch) => (datematch.schoolday!.schoolday.isSameDate(date))); diff --git a/school_data_hub_flutter/lib/features/_attendance/domain/filters/attendance_pupil_filter.dart b/school_data_hub_flutter/lib/features/_attendance/domain/filters/attendance_pupil_filter.dart index d870a951..01aa7f4d 100644 --- a/school_data_hub_flutter/lib/features/_attendance/domain/filters/attendance_pupil_filter.dart +++ b/school_data_hub_flutter/lib/features/_attendance/domain/filters/attendance_pupil_filter.dart @@ -10,12 +10,12 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _filterStateManager = di(); -final _pupilsFilter = di(); -final _schoolCalendarManager = di(); -final _attendanceManager = di(); - class AttendancePupilFilterManager { + FiltersStateManager get _filterStateManager => di(); + PupilsFilter get _pupilsFilter => di(); + SchoolCalendarManager get _schoolCalendarManager => + di(); + AttendanceManager get _attendanceManager => di(); final _attendancePupilFilterState = ValueNotifier>( initialAttendancePupilFilterValues, diff --git a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/attendance_list_page.dart b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/attendance_list_page.dart index 6efe8f0a..a12a66ad 100644 --- a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/attendance_list_page.dart +++ b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/attendance_list_page.dart @@ -21,16 +21,14 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _attendanceManager = di(); - -final _notificationService = di(); - final _log = Logger('AttendanceListPage'); class AttendanceListPage extends WatchingWidget { const AttendanceListPage({super.key}); @override Widget build(BuildContext context) { + final _attendanceManager = di(); + final _notificationService = di(); final bool isAuthenticated = watchPropertyValue( (HubSessionManager x) => x.isSignedIn, ); diff --git a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/atendance_list_card.dart b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/atendance_list_card.dart index 3c9762a8..a2750296 100644 --- a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/atendance_list_card.dart +++ b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/atendance_list_card.dart @@ -22,9 +22,6 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profil import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avatar.dart'; import 'package:watch_it/watch_it.dart'; -final _attendanceManager = di(); -final _notificationService = di(); - class AttendanceCard extends WatchingWidget { final PupilProxy pupil; final DateTime thisDate; @@ -33,6 +30,8 @@ class AttendanceCard extends WatchingWidget { @override Widget build(BuildContext context) { + final _attendanceManager = di(); + final _notificationService = di(); final FocusNode _dropdownFocusNode = FocusNode(); final missedSchooldaysList = _attendanceManager .getPupilMissedSchooldaysProxy(pupil.pupilId); diff --git a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/attendance_view_bottom_navbar.dart b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/attendance_view_bottom_navbar.dart index ee287280..f19e97b4 100644 --- a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/attendance_view_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/attendance_view_bottom_navbar.dart @@ -14,14 +14,13 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/comm import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolCalendarManager = di(); -final _filterStateManager = di(); - class AttendanceListPageBottomNavBar extends WatchingWidget { const AttendanceListPageBottomNavBar({super.key}); @override Widget build(BuildContext context) { + final _schoolCalendarManager = di(); + final _filterStateManager = di(); DateTime thisDate = watchValue((SchoolCalendarManager x) => x.thisDate).toLocal(); bool filtersOn = watchValue((FiltersStateManager x) => x.filtersActive); diff --git a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/dialogues/multiple_entries_dialog.dart b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/dialogues/multiple_entries_dialog.dart index f7c383c4..10096240 100644 --- a/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/dialogues/multiple_entries_dialog.dart +++ b/school_data_hub_flutter/lib/features/_attendance/presentation/attendance_page/widgets/dialogues/multiple_entries_dialog.dart @@ -12,16 +12,14 @@ import 'package:watch_it/watch_it.dart'; final GlobalKey _missedDatesformKey = GlobalKey(); -final _attendanceManager = di(); - -final _schoolCalendarManager = di(); - // based on https://mobikul.com/creating-stateful-dialog-form-in-flutter/ Future createMissedSchooldayList( BuildContext context, PupilProxy pupil, ) async { + final _attendanceManager = di(); + final _schoolCalendarManager = di(); final DateTime thisDate = _schoolCalendarManager.thisDate.value; return await showDialog( context: context, diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart index 5bb66768..3e073087 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart @@ -14,14 +14,16 @@ import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/ import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolCalendarManager = di(); -final _schooldayEventManager = di(); - class NewSchooldayEventPage extends WatchingWidget { final int pupilId; const NewSchooldayEventPage({super.key, required this.pupilId}); + SchoolCalendarManager get _schoolCalendarManager => + di(); + SchooldayEventManager get _schooldayEventManager => + di(); + String _getDropdownItemText(SchooldayEventType reason) { switch (reason) { case SchooldayEventType.notSet: diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart index 827797e3..91d85b97 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart @@ -13,13 +13,12 @@ import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_fil import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _schooldayEventManager = di(); - class SchooldayEventListPage extends WatchingWidget { const SchooldayEventListPage({super.key}); @override Widget build(BuildContext context) { + final _schooldayEventManager = di(); //-TODO: Filterstate in SchooldayEventSearchbar and SchooldayEventListPageBottomNavBar List pupils = watchValue((PupilsFilter x) => x.filteredPupils); @@ -27,7 +26,9 @@ class SchooldayEventListPage extends WatchingWidget { return Scaffold( backgroundColor: AppColors.canvasColor, appBar: const GenericAppBar( - iconData: Icons.warning_amber_rounded, title: 'Ereignisse'), + iconData: Icons.warning_amber_rounded, + title: 'Ereignisse', + ), body: RefreshIndicator( onRefresh: () async => _schooldayEventManager.fetchSchooldayEvents(), child: Center( @@ -41,9 +42,9 @@ class SchooldayEventListPage extends WatchingWidget { title: SchooldayEventListSearchBar(), ), GenericSliverListWithEmptyListCheck( - items: pupils, - itemBuilder: (_, pupil) => - SchooldayEventPupilListCard(pupil)), + items: pupils, + itemBuilder: (_, pupil) => SchooldayEventPupilListCard(pupil), + ), ], ), ), diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart index e3880772..d38552d3 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart @@ -3,8 +3,6 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _schooldayEventManager = di(); - class SchooldayEventTypeDialog extends WatchingWidget { // Change HookWidget to HookConsumerWidget final SchooldayEvent schooldayEvent; @@ -14,6 +12,9 @@ class SchooldayEventTypeDialog extends WatchingWidget { required this.schooldayEvent, }); + SchooldayEventManager get _schooldayEventManager => + di(); + String _getDropdownItemText(SchooldayEventType reason) { switch (reason) { case SchooldayEventType.notSet: diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart index 4413671d..fe753d53 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart @@ -20,20 +20,16 @@ import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/ import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - -final _schooldayEventManager = di(); - -final _schoolCalendarManager = di(); - -final _notificationService = di(); - class PupilSchooldayEventCard extends StatelessWidget { final SchooldayEvent schooldayEvent; const PupilSchooldayEventCard({required this.schooldayEvent, super.key}); @override Widget build(BuildContext context) { + final _hubSessionManager = di(); + final _schooldayEventManager = di(); + final _schoolCalendarManager = di(); + final _notificationService = di(); final isAuthorized = SessionHelper.isAuthorized(schooldayEvent.createdBy); final isAdmin = _hubSessionManager.isAdmin; diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_events_list.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_events_list.dart index 367a92c9..a72e64a0 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_events_list.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_events_list.dart @@ -10,23 +10,19 @@ import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _schooldayEventFilterManager = di(); - -final _schooldayEventManager = di(); - -final _notificationService = di(); - class PupilSchooldayEventsList extends WatchingWidget { final PupilProxy pupil; const PupilSchooldayEventsList({super.key, required this.pupil}); @override Widget build(BuildContext context) { + final _schooldayEventFilterManager = di(); + final _schooldayEventManager = di(); + final _notificationService = di(); final pupil = this.pupil; - final unfilteredEvents = - watch( - _schooldayEventManager.getPupilSchooldayEventsProxy(pupil.pupilId), - ).schooldayEvents; + final unfilteredEvents = watch( + _schooldayEventManager.getPupilSchooldayEventsProxy(pupil.pupilId), + ).schooldayEvents; final List filteredSchooldayEvents = _schooldayEventFilterManager.filteredSchooldayEvents( unfilteredEvents.values.toList(), @@ -54,8 +50,8 @@ class PupilSchooldayEventsList extends WatchingWidget { onPressed: () async { Navigator.of(context).push( MaterialPageRoute( - builder: - (ctx) => NewSchooldayEventPage(pupilId: pupil.pupilId), + builder: (ctx) => + NewSchooldayEventPage(pupilId: pupil.pupilId), ), ); }, diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart index 306cf983..8c76bf9d 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_filter_bottom_sheet.dart @@ -9,14 +9,12 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/enums.dart' import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/common_pupil_filters.dart'; import 'package:watch_it/watch_it.dart'; -final _schooldayEventFilterManager = di(); - -final _pupilsFilter = di(); - class SchooldayEventFilterBottomSheet extends WatchingWidget { const SchooldayEventFilterBottomSheet({super.key}); @override Widget build(BuildContext context) { + final _schooldayEventFilterManager = di(); + final _pupilsFilter = di(); PupilSortMode sortMode = watchValue((PupilsFilter x) => x.sortMode); final Map activeSchooldayEventFilters = watchValue( diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/schoolday_event_pupil_list_card.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/schoolday_event_pupil_list_card.dart index 8fa9eaf4..f1e15c6b 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/schoolday_event_pupil_list_card.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/schoolday_event_pupil_list_card.dart @@ -17,12 +17,6 @@ import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/ import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_pupil_list_card/widgets/schoolday_event_pupil_stats.dart'; import 'package:watch_it/watch_it.dart'; -final _schooldayEventFilterManager = di(); - -final _schooldayEventManager = di(); - -final _mainMenuBottomNavManager = di(); - class SchooldayEventPupilListCard extends WatchingStatefulWidget { final PupilProxy passedPupil; const SchooldayEventPupilListCard(this.passedPupil, {super.key}); @@ -43,6 +37,9 @@ class _SchooldayEventListCardState extends State { @override Widget build(BuildContext context) { + final _schooldayEventFilterManager = di(); + final _schooldayEventManager = di(); + final _mainMenuBottomNavManager = di(); final PupilProxy pupil = widget.passedPupil; final unfilteredEvents = watch( _schooldayEventManager.getPupilSchooldayEventsProxy(pupil.pupilId), diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart index 8c51b49f..56a4d3ed 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart @@ -16,12 +16,12 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_identi import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_page/timetable_page.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilIdentityManager = di(); -final _hubSessionManager = di(); - class ToolsPage extends WatchingWidget { const ToolsPage({super.key}); + PupilIdentityManager get _pupilIdentityManager => di(); + HubSessionManager get _hubSessionManager => di(); + void importFileWithWindows(String function) async { final fileContent = await pickFileReturnContentAsString(); FilePickerResult? result = await FilePicker.platform.pickFiles(); diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart index 38435eae..c5bac09d 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart @@ -17,9 +17,6 @@ import 'package:school_data_hub_flutter/features/app_settings/settings_page/sett import 'package:school_data_hub_flutter/l10n/app_localizations.dart'; import 'package:watch_it/watch_it.dart'; -final _bottomNavmanager = di(); -final _envManager = di(); - class MainMenuBottomNavigation extends WatchingStatefulWidget { const MainMenuBottomNavigation({super.key}); @@ -129,6 +126,8 @@ class _MainMenuBottomNavigationState extends State { @override Widget build(BuildContext context) { + final _bottomNavmanager = di(); + final _envManager = di(); final locale = AppLocalizations.of(context)!; final tab = watchValue((BottomNavManager x) => x.bottomNavState); diff --git a/school_data_hub_flutter/lib/features/authorizations/domain/filters/authorization_filter_manager.dart b/school_data_hub_flutter/lib/features/authorizations/domain/filters/authorization_filter_manager.dart index d4d20a18..5813b9a2 100644 --- a/school_data_hub_flutter/lib/features/authorizations/domain/filters/authorization_filter_manager.dart +++ b/school_data_hub_flutter/lib/features/authorizations/domain/filters/authorization_filter_manager.dart @@ -4,10 +4,9 @@ import 'package:school_data_hub_flutter/common/domain/filters/filters_state_mana import 'package:school_data_hub_flutter/features/authorizations/domain/authorization_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _authorizationManager = di(); -final _filtersStateManager = di(); - class AuthorizationFilterManager { + AuthorizationManager get _authorizationManager => di(); + FiltersStateManager get _filtersStateManager => di(); final ValueNotifier> _filteredAuthorizations = ValueNotifier>([]); ValueListenable> get filteredAuthorizations => diff --git a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/authorization_pupils_page.dart b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/authorization_pupils_page.dart index 1d1d953f..b66198c6 100644 --- a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/authorization_pupils_page.dart +++ b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/authorization_pupils_page.dart @@ -14,9 +14,6 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _pupilAuthorizationFilterManager = di(); - class AuthorizationPupilsPage extends WatchingWidget { final Authorization authorization; @@ -24,6 +21,8 @@ class AuthorizationPupilsPage extends WatchingWidget { @override Widget build(BuildContext context) { + final _pupilManager = di(); + final _pupilAuthorizationFilterManager = di(); // final filters = watchValue((PupilFilterManager x) => x.filterState); final thisAuthorization = diff --git a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart index 581cf404..f07a1186 100644 --- a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart +++ b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart @@ -17,15 +17,14 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profil import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avatar.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _authorizationManager = di(); - class AuthorizationPupilCard extends WatchingWidget { final int pupilId; final Authorization authorization; const AuthorizationPupilCard(this.pupilId, this.authorization, {super.key}); @override Widget build(BuildContext context) { + final _pupilManager = di(); + final _authorizationManager = di(); final PupilProxy pupil = _pupilManager.getPupilByPupilId(pupilId)!; final thisAuthorization = watchValue( diff --git a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupils_bottom_navbar.dart b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupils_bottom_navbar.dart index c2d0b321..35c3e115 100644 --- a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupils_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupils_bottom_navbar.dart @@ -13,9 +13,6 @@ import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); -final _authorizationManager = di(); - class AuthorizationPupilsBottomNavBar extends WatchingWidget { final Authorization authorization; @@ -27,6 +24,8 @@ class AuthorizationPupilsBottomNavBar extends WatchingWidget { @override Widget build(BuildContext context) { + final _hubSessionManager = di(); + final _authorizationManager = di(); final filtersActive = watchValue((FiltersStateManager x) => x.filtersActive); return BottomNavBarLayout( @@ -102,6 +101,7 @@ BottomAppBar authorizationPupilsBottomNavBar( Authorization authorization, bool filtersOn, List pupilsInAuthorization) { + final _authorizationManager = di(); return BottomAppBar( padding: const EdgeInsets.all(9), shape: null, diff --git a/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart b/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart index 9c96e45e..fdbc5ac9 100644 --- a/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart +++ b/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart @@ -13,14 +13,13 @@ import 'package:school_data_hub_flutter/features/authorizations/presentation/aut import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; import 'package:watch_it/watch_it.dart'; -final _authorizationManager = di(); -final _pupilsFilter = di(); - class AuthorizationsListPage extends WatchingWidget { const AuthorizationsListPage({super.key}); @override Widget build(BuildContext context) { + final _authorizationManager = di(); + final _pupilsFilter = di(); bool filtersOn = watchValue((FiltersStateManager x) => x.filtersActive); List authorizations = diff --git a/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/widgets/authorization_list_bottom_navbar.dart b/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/widgets/authorization_list_bottom_navbar.dart index 44939f6f..85a6ea21 100644 --- a/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/widgets/authorization_list_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/authorizations/presentation/authorizations_list_page/widgets/authorization_list_bottom_navbar.dart @@ -7,13 +7,12 @@ import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/authorizations/presentation/new_authorization_page/new_authorization_page.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - class AuthorizationListBottomNavBar extends StatelessWidget { const AuthorizationListBottomNavBar({super.key}); @override Widget build(BuildContext context) { + final _hubSessionManager = di(); return BottomNavBarLayout( bottomNavBar: BottomAppBar( height: 60, diff --git a/school_data_hub_flutter/lib/features/authorizations/presentation/new_authorization_page/new_authorization_page.dart b/school_data_hub_flutter/lib/features/authorizations/presentation/new_authorization_page/new_authorization_page.dart index 9984da17..ee3ea952 100644 --- a/school_data_hub_flutter/lib/features/authorizations/presentation/new_authorization_page/new_authorization_page.dart +++ b/school_data_hub_flutter/lib/features/authorizations/presentation/new_authorization_page/new_authorization_page.dart @@ -10,9 +10,6 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupil import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avatar.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _authorizationManager = di(); - class NewAuthorizationPage extends StatefulWidget { const NewAuthorizationPage({super.key}); @@ -27,6 +24,9 @@ class NewAuthorizationPageState extends State { TextEditingController(); Set pupilIds = {}; + PupilManager get _pupilManager => di(); + AuthorizationManager get _authorizationManager => di(); + void postNewAuthorization() async { String text1 = authorizationNameController.text; String text2 = authorizationDescriptionController.text; diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart index e6df4f53..0405d99e 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart @@ -13,8 +13,6 @@ import 'package:school_data_hub_flutter/features/books/domain/models/enums.dart' import 'package:school_data_hub_flutter/features/books/domain/models/library_book_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - class PupilBookCard extends WatchingWidget { const PupilBookCard({ required this.pupilBook, @@ -26,6 +24,7 @@ class PupilBookCard extends WatchingWidget { @override Widget build(BuildContext context) { + final _hubSessionManager = di(); final LibraryBookProxy bookProxy = di().getLibraryBookById( pupilBook.libraryBookId, )!; diff --git a/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart b/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart index 6fc8f94e..9f43b422 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/edit_book_page/edit_book_page.dart @@ -11,8 +11,6 @@ import 'package:school_data_hub_flutter/features/books/domain/models/enums.dart' import 'package:school_data_hub_flutter/features/books/presentation/edit_book_page/edit_book_controller.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - class EditBookPage extends StatelessWidget { final EditBookController controller; @@ -20,6 +18,7 @@ class EditBookPage extends StatelessWidget { @override Widget build(BuildContext context) { + final _hubSessionManager = di(); return Scaffold( appBar: const GenericAppBar( iconData: Icons.book, diff --git a/school_data_hub_flutter/lib/features/books/presentation/new_book_page/new_book_page.dart b/school_data_hub_flutter/lib/features/books/presentation/new_book_page/new_book_page.dart index 9db0c5ce..dad25558 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/new_book_page/new_book_page.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/new_book_page/new_book_page.dart @@ -10,8 +10,6 @@ import 'package:school_data_hub_flutter/features/books/domain/models/enums.dart' import 'package:school_data_hub_flutter/features/books/presentation/new_book_page/new_book_controller.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - class NewBookPage extends StatelessWidget { final NewBookController controller; @@ -19,6 +17,7 @@ class NewBookPage extends StatelessWidget { @override Widget build(BuildContext context) { + final _hubSessionManager = di(); return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, diff --git a/school_data_hub_flutter/lib/features/learning/domain/competence_helper.dart b/school_data_hub_flutter/lib/features/learning/domain/competence_helper.dart index 20834cc2..88e659b6 100644 --- a/school_data_hub_flutter/lib/features/learning/domain/competence_helper.dart +++ b/school_data_hub_flutter/lib/features/learning/domain/competence_helper.dart @@ -9,9 +9,8 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _competenceManager = di(); - class CompetenceHelper { + static CompetenceManager get _competenceManager => di(); static List sortCompetences(List competences) { List rootCompetences = []; List childCompetences = []; @@ -47,16 +46,15 @@ class CompetenceHelper { int publicId, ) { if (pupil.competenceChecks != null && pupil.competenceChecks!.isNotEmpty) { - final filteredChecks = - pupil.competenceChecks! - .where( - (element) => - _competenceManager - .findCompetenceById(element.competenceId) - .publicId == - publicId, - ) - .toList(); + final filteredChecks = pupil.competenceChecks! + .where( + (element) => + _competenceManager + .findCompetenceById(element.competenceId) + .publicId == + publicId, + ) + .toList(); if (filteredChecks.isNotEmpty) { return filteredChecks.reduce( (a, b) => a.createdAt.isAfter(b.createdAt) ? a : b, diff --git a/school_data_hub_flutter/lib/features/learning/domain/filters/competence_filter_manager.dart b/school_data_hub_flutter/lib/features/learning/domain/filters/competence_filter_manager.dart index e268886b..a023f8d1 100644 --- a/school_data_hub_flutter/lib/features/learning/domain/filters/competence_filter_manager.dart +++ b/school_data_hub_flutter/lib/features/learning/domain/filters/competence_filter_manager.dart @@ -5,24 +5,25 @@ import 'package:school_data_hub_flutter/features/learning/domain/competence_mana import 'package:school_data_hub_flutter/features/learning/domain/filters/enums.dart'; import 'package:watch_it/watch_it.dart'; -final _competenceManager = di(); - final _log = Logger('CompetenceFilterManager'); class CompetenceFilterManager { - final _filterState = - ValueNotifier>(initialCompetenceFilterValues); + CompetenceManager get _competenceManager => di(); + final _filterState = ValueNotifier>( + initialCompetenceFilterValues, + ); ValueListenable> get filterState => _filterState; - final _filteredCompetences = - ValueNotifier>(_competenceManager.competences.value); + final _filteredCompetences = ValueNotifier>([]); ValueListenable> get filteredCompetences => _filteredCompetences; final _filtersOn = ValueNotifier(false); ValueListenable get filtersOn => _filtersOn; - CompetenceFilterManager(); + CompetenceFilterManager() { + _filteredCompetences.value = _competenceManager.competences.value; + } refreshFilteredCompetences(List competences) { _filteredCompetences.value = competences; @@ -38,10 +39,7 @@ class CompetenceFilterManager { } void setFilter(CompetenceFilter filter, bool isActive) { - _filterState.value = { - ..._filterState.value, - filter: isActive, - }; + _filterState.value = {..._filterState.value, filter: isActive}; filterCompetences(); } diff --git a/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/competence_list_page.dart b/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/competence_list_page.dart index 2d6c0815..d19895c6 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/competence_list_page.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/competence_list_page.dart @@ -8,13 +8,12 @@ import 'package:school_data_hub_flutter/features/learning/presentation/competenc import 'package:school_data_hub_flutter/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart'; import 'package:watch_it/watch_it.dart'; -final _competenceManager = di(); - class CompetenceListPage extends WatchingWidget { const CompetenceListPage({super.key}); @override Widget build(BuildContext context) { + final _competenceManager = di(); navigateToNewOrPatchCompetencePage( {int? competenceId, Competence? competence}) { Navigator.of(context).push(MaterialPageRoute( diff --git a/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/widgets/competence_tree.dart b/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/widgets/competence_tree.dart index be680f28..011b3c79 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/widgets/competence_tree.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/competence_list_page/widgets/competence_tree.dart @@ -7,7 +7,6 @@ import 'package:school_data_hub_flutter/features/learning/presentation/competenc import 'package:school_data_hub_flutter/features/learning/presentation/competence_list_page/widgets/last_child_competence_card.dart'; import 'package:watch_it/watch_it.dart'; -final _competenceManager = di(); List buildCommonCompetenceTree({ required Function({int? competenceId, Competence? competence}) navigateToNewOrPatchCompetencePage, @@ -17,6 +16,7 @@ List buildCommonCompetenceTree({ required List competences, required BuildContext context, }) { + final _competenceManager = di(); // Separate competences into two lists: one for those without a parent competence and one for those with a parent competence List rootCompetences = []; List childCompetences = []; diff --git a/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart b/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart index dcde4719..0b8bd549 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart @@ -8,8 +8,6 @@ import 'package:school_data_hub_flutter/common/widgets/themed_filter_chip.dart'; import 'package:school_data_hub_flutter/features/learning/domain/competence_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _competenceManager = di(); - class PostOrPatchCompetencePage extends StatefulWidget { final int? parentCompetence; final Competence? competence; @@ -29,6 +27,8 @@ class PostOrPatchCompetencePageState extends State { final TextEditingController indicatorsFieldController = TextEditingController(); + CompetenceManager get _competenceManager => di(); + void postNewCompetence() async { if (competenceLevel.isEmpty) { informationDialog(context, 'Kompetenzstufe auswählen', diff --git a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/pupil_competence_card.dart b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/pupil_competence_card.dart index 1185b8a2..d1913a90 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/pupil_competence_card.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/pupil_competence_card.dart @@ -13,8 +13,6 @@ import 'package:school_data_hub_flutter/features/learning/presentation/widgets/d import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _competenceManager = di(); - class PupilCompetenceCard extends WatchingWidget { final Color backgroundColor; final bool isReport; @@ -46,6 +44,7 @@ class PupilCompetenceCard extends WatchingWidget { @override Widget build(BuildContext context) { + final _competenceManager = di(); watch(pupil); final isExpandedValue = createOnce>( () => ValueNotifier(false), diff --git a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_learning_content/pupil_learning_content_workbooks.dart b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_learning_content/pupil_learning_content_workbooks.dart index ecdf27cf..10920bf3 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_learning_content/pupil_learning_content_workbooks.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_learning_content/pupil_learning_content_workbooks.dart @@ -12,14 +12,13 @@ import 'package:school_data_hub_flutter/features/workbooks/presentation/new_work import 'package:school_data_hub_flutter/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - class PupilLearningContentWorkbooks extends StatelessWidget { final PupilProxy pupil; const PupilLearningContentWorkbooks({required this.pupil, super.key}); @override Widget build(BuildContext context) { + final _hubSessionManager = di(); return Column( children: [ const Row( diff --git a/school_data_hub_flutter/lib/features/learning_support/domain/filters/learning_support_filter_manager.dart b/school_data_hub_flutter/lib/features/learning_support/domain/filters/learning_support_filter_manager.dart index c8878972..fdb0ffb0 100644 --- a/school_data_hub_flutter/lib/features/learning_support/domain/filters/learning_support_filter_manager.dart +++ b/school_data_hub_flutter/lib/features/learning_support/domain/filters/learning_support_filter_manager.dart @@ -10,15 +10,15 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_helper_functions.dart'; import 'package:watch_it/watch_it.dart'; -final _filtersStateManager = di(); -final _pupilFilterManager = di(); -final _pupilsFilter = di(); -final _learningSupportManager = di(); - typedef SupportLevelFilterRecord = ({SupportLevelType filter, bool value}); typedef SupportAreaFilterRecord = ({SupportArea filter, bool value}); class LearningSupportFilterManager { + FiltersStateManager get _filtersStateManager => di(); + PupilFilterManager get _pupilFilterManager => di(); + PupilsFilter get _pupilsFilter => di(); + SupportCategoryManager get _learningSupportManager => + di(); final _supportLevelFilterState = ValueNotifier>( initialSupportLevelFilterValues); ValueListenable> get supportLevelFilterState => diff --git a/school_data_hub_flutter/lib/features/learning_support/domain/learning_support_helper.dart b/school_data_hub_flutter/lib/features/learning_support/domain/learning_support_helper.dart index 1b5e5fcc..1a0f440b 100644 --- a/school_data_hub_flutter/lib/features/learning_support/domain/learning_support_helper.dart +++ b/school_data_hub_flutter/lib/features/learning_support/domain/learning_support_helper.dart @@ -5,9 +5,8 @@ import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _sessionManager = di(); - class LearningSupportHelper { + static HubSessionManager get _sessionManager => di(); //- overview numbers functions static int developmentPlan1Pupils(List filteredPupils) { List developmentPlan1Pupils = []; diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart index ce91cc06..0c9ad497 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart @@ -13,13 +13,12 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); - class LearningSupportListPage extends WatchingWidget { const LearningSupportListPage({super.key}); @override Widget build(BuildContext context) { + final _pupilManager = di(); bool filtersOn = watchValue((FiltersStateManager x) => x.filtersActive); // These come from the PupilFilterManager diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/widgets/support_category_status_batches.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/widgets/support_category_status_batches.dart index 77342746..5ceb6c49 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/widgets/support_category_status_batches.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/learning_support_list_page/widgets/support_category_status_batches.dart @@ -6,8 +6,6 @@ import 'package:school_data_hub_flutter/features/learning_support/domain/support import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportManager = di(); - class SupportCategoryStatusBatches extends StatelessWidget { final PupilProxy pupil; const SupportCategoryStatusBatches({ @@ -17,6 +15,7 @@ class SupportCategoryStatusBatches extends StatelessWidget { @override Widget build(BuildContext context) { + final _learningSupportManager = di(); List supportCategoryStatuses = pupil.supportCategoryStatuses!; List widgetList = []; diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/new_learning_support_plan/controller/new_learning_support_plan_controller.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/new_learning_support_plan/controller/new_learning_support_plan_controller.dart index 51460a6c..ef0918a6 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/new_learning_support_plan/controller/new_learning_support_plan_controller.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/new_learning_support_plan/controller/new_learning_support_plan_controller.dart @@ -9,12 +9,6 @@ import 'package:school_data_hub_flutter/features/school_calendar/domain/school_c import 'package:school_data_hub_flutter/features/user/domain/user_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportPlanManager = di(); -final _schoolCalendarManager = di(); -final _hubSessionManager = di(); -final _notificationService = di(); -final _userManager = di(); - /// StatefulWidget for creating a new learning support plan for a pupil. /// /// This widget provides a form interface for: @@ -52,6 +46,14 @@ class NewLearningSupportPlanController extends State { PupilProxy get pupil => widget.pupil; + LearningSupportManager get _learningSupportPlanManager => + di(); + SchoolCalendarManager get _schoolCalendarManager => + di(); + HubSessionManager get _hubSessionManager => di(); + NotificationService get _notificationService => di(); + UserManager get _userManager => di(); + @override void initState() { super.initState(); diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/controller/new_support_category_status_controller.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/controller/new_support_category_status_controller.dart index c97a0f88..2cbb2421 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/controller/new_support_category_status_controller.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/controller/new_support_category_status_controller.dart @@ -3,8 +3,6 @@ import 'package:school_data_hub_flutter/features/learning_support/domain/learnin import 'package:school_data_hub_flutter/features/learning_support/presentation/new_support_category_status_page/new_support_category_status_page.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportPlanManager = di(); - class NewSupportCategoryStatus extends StatefulWidget { final String appBarTitle; final int pupilId; @@ -26,6 +24,9 @@ class NewSupportCategoryStatus extends StatefulWidget { class NewSupportCategoryStatusController extends State { + LearningSupportManager get _learningSupportPlanManager => + di(); + @override void initState() { super.initState(); diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/new_support_category_status_page.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/new_support_category_status_page.dart index 52e51459..a806e7db 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/new_support_category_status_page.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_category_status_page/new_support_category_status_page.dart @@ -12,16 +12,15 @@ import 'package:school_data_hub_flutter/features/learning_support/presentation/w import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _learningSupportPlanManager = di(); -final _supportCategoryManager = di(); - class NewSupportCategoryStatusPage extends StatelessWidget { final NewSupportCategoryStatusController controller; const NewSupportCategoryStatusPage(this.controller, {super.key}); @override Widget build(BuildContext context) { + final _pupilManager = di(); + final _learningSupportPlanManager = di(); + final _supportCategoryManager = di(); return Theme( data: ThemeData( unselectedWidgetColor: Colors.white, diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_goal_page/new_support_goal_page.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_goal_page/new_support_goal_page.dart index 4c2d150e..69d7cd37 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_goal_page/new_support_goal_page.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/new_support_goal_page/new_support_goal_page.dart @@ -10,15 +10,14 @@ import 'package:school_data_hub_flutter/features/learning_support/presentation/w import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _learningSupportManager = di(); - class NewSupportGoalPage extends StatelessWidget { final NewSupportCategoryStatusController controller; const NewSupportGoalPage(this.controller, {super.key}); @override Widget build(BuildContext context) { + final _pupilManager = di(); + final _learningSupportManager = di(); return Theme( data: ThemeData( unselectedWidgetColor: Colors.white, diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/support_category_list_page/widgets/support_category_tree.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/support_category_list_page/widgets/support_category_tree.dart index b20e9e4f..bd051f82 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/support_category_list_page/widgets/support_category_tree.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/support_category_list_page/widgets/support_category_tree.dart @@ -4,8 +4,6 @@ import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/features/learning_support/domain/support_category_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportManager = di(); - class SupportCategoryTree extends StatelessWidget { final int? parentId; final int indentation; @@ -18,6 +16,7 @@ class SupportCategoryTree extends StatelessWidget { @override Widget build(BuildContext context) { + final _learningSupportManager = di(); List goalCategoryWidgets = []; List goalCategories = @@ -122,6 +121,7 @@ class SupportCategoryTree extends StatelessWidget { List buildCategoryTree( int? parentId, int indentation, Color? backGroundColor) { + final _learningSupportManager = di(); List goalCategoryWidgets = []; List supportCategories = diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/dialogs/preschool_revision_dialog.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/dialogs/preschool_revision_dialog.dart index 5c6f94a2..07617d66 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/dialogs/preschool_revision_dialog.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/dialogs/preschool_revision_dialog.dart @@ -8,13 +8,12 @@ import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart import 'package:watch_it/watch_it.dart'; // based on https://mobikul.com/creating-stateful-dialog-form-in-flutter/ -final _hubSessionManager = di(); - Future preschoolRevisionDialog( BuildContext context, PupilProxy pupil, PreSchoolMedicalStatus? value, ) async { + final _hubSessionManager = di(); return await showDialog( context: context, builder: (context) { diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_card.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_card.dart index a7574107..b3c894d2 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_card.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_card.dart @@ -10,8 +10,6 @@ import 'package:school_data_hub_flutter/features/learning_support/presentation/w import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportManager = di(); - class SupportCategoryStatusCard extends StatelessWidget { final PupilProxy pupil; final List statusesWithSameGoalCategory; @@ -24,6 +22,7 @@ class SupportCategoryStatusCard extends StatelessWidget { @override Widget build(BuildContext context) { + final _learningSupportManager = di(); final int supportCategoryId = statusesWithSameGoalCategory[0].supportCategoryId; final Color supportCategoryColor = diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_entry/support_category_status_entry.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_entry/support_category_status_entry.dart index 4d525c08..2b6ef7c2 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_entry/support_category_status_entry.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_catagory_status/widgets/support_category_status_entry/support_category_status_entry.dart @@ -13,8 +13,6 @@ import 'package:school_data_hub_flutter/features/learning_support/presentation/w import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportPlanManager = di(); - class SupportCategoryStatusEntry extends StatelessWidget { final PupilProxy pupil; final SupportCategoryStatus status; @@ -27,6 +25,7 @@ class SupportCategoryStatusEntry extends StatelessWidget { @override Widget build(BuildContext context) { + final _learningSupportPlanManager = di(); final bool authorizedToChangeStatus = LearningSupportHelper.isAuthorizedToChangeStatus(status); return Padding( diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_parents_names.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_parents_names.dart index 41197bb4..ca62dd09 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_parents_names.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_parents_names.dart @@ -4,7 +4,6 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/features/learning_support/domain/support_category_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportManager = di(); // class CategoryTreeParentsNames extends StatelessWidget { // final int categoryId; // final Color categoryColor; @@ -82,6 +81,7 @@ final _learningSupportManager = di(); List categoryTreeAncestorsNames( {required int categoryId, required Color categoryColor}) { + final _learningSupportManager = di(); // Create an empty list to store ancestors List ancestors = []; diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_widgets/pupil_support_category_tree.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_widgets/pupil_support_category_tree.dart index 0fb9aa12..d8b253f7 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_widgets/pupil_support_category_tree.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_category_widgets/pupil_support_category_tree.dart @@ -9,9 +9,6 @@ import 'package:school_data_hub_flutter/features/learning_support/presentation/s import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportManager = di(); -final _hubSessionManager = di(); - List pupilSupportCategoryTree({ required BuildContext context, required PupilProxy pupil, @@ -21,6 +18,8 @@ List pupilSupportCategoryTree({ required SelectCategoryPageController controller, required String elementType, }) { + final _learningSupportManager = di(); + final _hubSessionManager = di(); List supportCategoryWidgets = []; List supportCategories = diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card.dart index e29c0432..91152a30 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card.dart @@ -10,8 +10,6 @@ import 'package:school_data_hub_flutter/features/learning_support/presentation/w import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportManager = di(); - class SupportGoalCard extends StatelessWidget { final PupilProxy pupil; final int goalIndex; @@ -23,6 +21,7 @@ class SupportGoalCard extends StatelessWidget { @override Widget build(BuildContext context) { + final _learningSupportManager = di(); return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: ClipRRect( diff --git a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card_banner.dart b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card_banner.dart index 73504355..06d4bcc3 100644 --- a/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card_banner.dart +++ b/school_data_hub_flutter/lib/features/learning_support/presentation/widgets/support_goal/support_goal_card_banner.dart @@ -3,14 +3,13 @@ import 'package:school_data_hub_flutter/features/learning_support/domain/learnin import 'package:school_data_hub_flutter/features/learning_support/domain/support_category_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _learningSupportManager = di(); - class SupportCategoryCardBanner extends StatelessWidget { final int categoryId; const SupportCategoryCardBanner({required this.categoryId, super.key}); @override Widget build(BuildContext context) { + final _learningSupportManager = di(); return Container( decoration: BoxDecoration( color: LearningSupportHelper.getRootSupportCategoryColor( diff --git a/school_data_hub_flutter/lib/features/learning_support/services/learning_support_plan_pdf_generator.dart b/school_data_hub_flutter/lib/features/learning_support/services/learning_support_plan_pdf_generator.dart index 1eb1a885..25475064 100644 --- a/school_data_hub_flutter/lib/features/learning_support/services/learning_support_plan_pdf_generator.dart +++ b/school_data_hub_flutter/lib/features/learning_support/services/learning_support_plan_pdf_generator.dart @@ -18,9 +18,10 @@ import 'package:school_data_hub_flutter/features/school_calendar/domain/school_c import 'package:watch_it/watch_it.dart'; final _log = Logger('LearningSupportPlanPdfGenerator'); -final _schoolCalendarManager = di(); class LearningSupportPlanPdfGenerator { + static SchoolCalendarManager get _schoolCalendarManager => + di(); static Future generateLearningSupportPlanPdf({ required LearningSupportPlan plan, required PupilProxy pupil, diff --git a/school_data_hub_flutter/lib/features/matrix/domain/filters/matrix_policy_filter_manager.dart b/school_data_hub_flutter/lib/features/matrix/domain/filters/matrix_policy_filter_manager.dart index 54c32567..2dcf939f 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/filters/matrix_policy_filter_manager.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/filters/matrix_policy_filter_manager.dart @@ -3,23 +3,16 @@ import 'package:flutter/material.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_room.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_user.dart'; -import 'package:watch_it/watch_it.dart'; - -final _matrixPolicyManager = di(); class MatrixPolicyFilterManager { final _filtersOn = ValueNotifier(false); ValueListenable get filtersOn => _filtersOn; - final _filteredMatrixUsers = ValueNotifier>( - _matrixPolicyManager.matrixUsers.value, - ); + final _filteredMatrixUsers = ValueNotifier>([]); ValueListenable> get filteredMatrixUsers => _filteredMatrixUsers; - final _filteredMatrixRooms = ValueNotifier>( - _matrixPolicyManager.matrixRooms.value, - ); + final _filteredMatrixRooms = ValueNotifier>([]); ValueListenable> get filteredMatrixRooms => _filteredMatrixRooms; @@ -34,6 +27,8 @@ class MatrixPolicyFilterManager { MatrixPolicyFilterManager(MatrixPolicyManager matrixPolicyManager) : _policyManager = matrixPolicyManager { + _filteredMatrixUsers.value = _policyManager.matrixUsers.value; + _filteredMatrixRooms.value = _policyManager.matrixRooms.value; refreshFilteredMatrixUsers(); _policyManager.addListener(refreshFilteredMatrixUsers); _policyManager.rooms.matrixRooms.addListener(reactWhenRoomListChanges); @@ -49,8 +44,8 @@ class MatrixPolicyFilterManager { void resetAllMatrixFilters() { _searchText.value = ''; - _filteredMatrixUsers.value = _matrixPolicyManager.matrixUsers.value; - _filteredMatrixRooms.value = _matrixPolicyManager.matrixRooms.value; + _filteredMatrixUsers.value = _policyManager.matrixUsers.value; + _filteredMatrixRooms.value = _policyManager.matrixRooms.value; _filtersOn.value = false; _searchController.value.clear(); } @@ -60,7 +55,7 @@ class MatrixPolicyFilterManager { } void reactWhenRoomListChanges() { - _filteredMatrixRooms.value = _matrixPolicyManager.matrixRooms.value; + _filteredMatrixRooms.value = _policyManager.matrixRooms.value; } void refreshFilteredMatrixRooms() { @@ -70,21 +65,18 @@ class MatrixPolicyFilterManager { void setUsersFilterText(String text) { if (text == '') { _searchText.value = text; - _filteredMatrixUsers.value = _matrixPolicyManager.matrixUsers.value; + _filteredMatrixUsers.value = _policyManager.matrixUsers.value; _filtersOn.value = false; return; } - List matrixUsers = List.from( - _matrixPolicyManager.matrixUsers.value, - ); + List matrixUsers = List.from(_policyManager.matrixUsers.value); List filteredMatrixUsers = []; - filteredMatrixUsers = - matrixUsers - .where( - (MatrixUser user) => - user.displayName.toLowerCase().contains(text.toLowerCase()), - ) - .toList(); + filteredMatrixUsers = matrixUsers + .where( + (MatrixUser user) => + user.displayName.toLowerCase().contains(text.toLowerCase()), + ) + .toList(); _filteredMatrixUsers.value = filteredMatrixUsers; _filtersOn.value = true; } @@ -92,22 +84,21 @@ class MatrixPolicyFilterManager { setRoomsFilterText(String text) { if (text == '') { _searchText.value = text; - _filteredMatrixRooms.value = _matrixPolicyManager.matrixRooms.value; + _filteredMatrixRooms.value = _policyManager.matrixRooms.value; _filtersOn.value = false; return; } final List matrixRooms = List.from( - _matrixPolicyManager.matrixRooms.value, + _policyManager.matrixRooms.value, ); List filteredMatrixRooms = []; - filteredMatrixRooms = - matrixRooms - .where( - (MatrixRoom room) => - room.name!.toLowerCase().contains(text.toLowerCase()) || - room.id.toLowerCase().contains(text.toLowerCase()), - ) - .toList(); + filteredMatrixRooms = matrixRooms + .where( + (MatrixRoom room) => + room.name!.toLowerCase().contains(text.toLowerCase()) || + room.id.toLowerCase().contains(text.toLowerCase()), + ) + .toList(); _filteredMatrixRooms.value = filteredMatrixRooms; _filtersOn.value = true; } diff --git a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart index 00488e5e..7ad05879 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart @@ -17,11 +17,11 @@ import 'package:watch_it/watch_it.dart'; final _log = Logger('MatrixPolicyHelperFunctions'); -final _matrixPolicyManager = di(); -final _secureStorage = HubSecureStorage(); -final _envManager = di(); - class MatrixPolicyHelper { + static MatrixPolicyManager get _matrixPolicyManager => + di(); + static HubSecureStorage get _secureStorage => HubSecureStorage(); + static EnvManager get _envManager => di(); static Future registerMatrixPolicyManager({ MatrixCredentials? passedCredentials, }) async { diff --git a/school_data_hub_flutter/lib/features/matrix/domain/models/matrix_user.dart b/school_data_hub_flutter/lib/features/matrix/domain/models/matrix_user.dart index ac9d2eeb..3cad0f1e 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/models/matrix_user.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/models/matrix_user.dart @@ -6,8 +6,6 @@ import 'package:watch_it/watch_it.dart'; part 'matrix_user.g.dart'; -final _matrixPolicyManager = di(); - @JsonSerializable() class JoinedRoom { final String roomId; @@ -62,6 +60,8 @@ class MatrixUser extends ChangeNotifier { List get matrixRooms => _matrixRooms; bool get isParent => _id.contains('_e') ? true : false; + MatrixPolicyManager get _matrixPolicyManager => di(); + // Setters set displayName(String value) { if (_displayName != value) { diff --git a/school_data_hub_flutter/lib/features/matrix/presentation/widgets/dialogues/change_power_level_dialog.dart b/school_data_hub_flutter/lib/features/matrix/presentation/widgets/dialogues/change_power_level_dialog.dart index e2509074..46075281 100644 --- a/school_data_hub_flutter/lib/features/matrix/presentation/widgets/dialogues/change_power_level_dialog.dart +++ b/school_data_hub_flutter/lib/features/matrix/presentation/widgets/dialogues/change_power_level_dialog.dart @@ -4,8 +4,6 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); - final GlobalKey _formKey = GlobalKey(); final TextEditingController _textEditingController = TextEditingController(); @@ -15,6 +13,7 @@ final TextEditingController _textEditingController = TextEditingController(); Future changePowerLevelsDialog( BuildContext context, PupilProxy pupil) async { + final _pupilManager = di(); return await showDialog( context: context, builder: (context) { diff --git a/school_data_hub_flutter/lib/features/matrix/rooms/domain/matrix_room_helper.dart b/school_data_hub_flutter/lib/features/matrix/rooms/domain/matrix_room_helper.dart index 7151e9cf..f9244c9b 100644 --- a/school_data_hub_flutter/lib/features/matrix/rooms/domain/matrix_room_helper.dart +++ b/school_data_hub_flutter/lib/features/matrix/rooms/domain/matrix_room_helper.dart @@ -4,9 +4,9 @@ import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_roo import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_user.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); - class MatrixRoomHelper { + static MatrixPolicyManager get _matrixPolicyManager => + di(); static List usersInRoom(String roomId) { final List users = List.from( _matrixPolicyManager.matrixUsers.value, diff --git a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/matrix_rooms_list_page.dart b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/matrix_rooms_list_page.dart index 1f3b1f62..656ff015 100644 --- a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/matrix_rooms_list_page.dart +++ b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/matrix_rooms_list_page.dart @@ -11,13 +11,12 @@ import 'package:school_data_hub_flutter/features/matrix/rooms/presentation/matri import 'package:school_data_hub_flutter/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_view_bottom_navbar.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); - class MatrixRoomsListPage extends WatchingWidget { const MatrixRoomsListPage({super.key}); @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); List matrixRooms = watchValue((MatrixPolicyFilterManager x) => x.filteredMatrixRooms); diff --git a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_card.dart b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_card.dart index afd9fc13..66f7115c 100644 --- a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_card.dart +++ b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_card.dart @@ -13,14 +13,13 @@ import 'package:school_data_hub_flutter/features/matrix/rooms/presentation/matri import 'package:school_data_hub_flutter/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/users_in_room_list.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); - class RoomListCard extends WatchingWidget { final MatrixRoom matrixRoom; const RoomListCard(this.matrixRoom, {super.key}); @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); final _tileController = createOnce( () => CustomExpansionTileController(), ); diff --git a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_view_bottom_navbar.dart b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_view_bottom_navbar.dart index 9a3aaeac..7e3d1a92 100644 --- a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_view_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/room_list_view_bottom_navbar.dart @@ -8,14 +8,13 @@ import 'package:school_data_hub_flutter/features/matrix/users/presentation/matri import 'package:school_data_hub_flutter/features/matrix/rooms/presentation/new_matrix_room_page/new_matrix_room_page.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); -final _matrixPolicyFilterManager = di(); - class RoomListPageBottomNavBar extends WatchingWidget { const RoomListPageBottomNavBar({super.key}); @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); + final _matrixPolicyFilterManager = di(); final bool filtersOn = watchValue((MatrixPolicyFilterManager x) => x.filtersOn); final bool pendingChanges = diff --git a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/users_in_room_list.dart b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/users_in_room_list.dart index fcce1afe..72cb2370 100644 --- a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/users_in_room_list.dart +++ b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/matrix_rooms_list_page/widgets/users_in_room_list.dart @@ -9,8 +9,6 @@ import 'package:school_data_hub_flutter/features/matrix/users/domain/matrix_user import 'package:school_data_hub_flutter/features/matrix/users/presentation/select_matrix_users_list_page/controller/select_matrix_users_list_controller.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); - class MatrixUsersInRoomList extends WatchingWidget { final List matrixUsers; final String roomId; @@ -23,6 +21,7 @@ class MatrixUsersInRoomList extends WatchingWidget { @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); return Column( children: [ Padding( @@ -41,10 +40,9 @@ class MatrixUsersInRoomList extends WatchingWidget { final List selectedUserIds = await Navigator.of(context).push( MaterialPageRoute( - builder: - (ctx) => SelectMatrixUsersList( - MatrixUserHelper.usersFromUserIds(availableUsers), - ), + builder: (ctx) => SelectMatrixUsersList( + MatrixUserHelper.usersFromUserIds(availableUsers), + ), ), ) ?? []; @@ -105,6 +103,7 @@ class MatrixUsersInRoomListItem extends WatchingWidget { @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); watch(matrixUser); final MatrixRoom room = watch( _matrixPolicyManager.rooms.getRoomById(roomId), diff --git a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/new_matrix_room_page/new_matrix_room_page.dart b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/new_matrix_room_page/new_matrix_room_page.dart index 336d8d1b..de49f508 100644 --- a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/new_matrix_room_page/new_matrix_room_page.dart +++ b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/new_matrix_room_page/new_matrix_room_page.dart @@ -7,9 +7,6 @@ import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_man import 'package:school_data_hub_flutter/features/matrix/rooms/data/matrix_room_api_service.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); -final _notificationService = di(); - class NewMatrixRoomPage extends StatefulWidget { const NewMatrixRoomPage({super.key}); @@ -25,6 +22,9 @@ class _NewMatrixRoomPageState extends State { ChatTypePreset selectedRoomType = ChatTypePreset.private; bool isProcessing = false; + MatrixPolicyManager get _matrixPolicyManager => di(); + NotificationService get _notificationService => di(); + @override void dispose() { nameController.dispose(); diff --git a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/select_matrix_rooms_list_page/controller/select_matrix_rooms_list_controller.dart b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/select_matrix_rooms_list_page/controller/select_matrix_rooms_list_controller.dart index 1c338178..21962594 100644 --- a/school_data_hub_flutter/lib/features/matrix/rooms/presentation/select_matrix_rooms_list_page/controller/select_matrix_rooms_list_controller.dart +++ b/school_data_hub_flutter/lib/features/matrix/rooms/presentation/select_matrix_rooms_list_page/controller/select_matrix_rooms_list_controller.dart @@ -8,10 +8,6 @@ import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupil_filt import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupil_filter_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilFilterManager = di(); -final _matrixPolicyManager = di(); -final _matrixPolicyFilterManager = di(); - class SelectMatrixRoomsList extends WatchingStatefulWidget { final List? selectableRooms; const SelectMatrixRoomsList(this.selectableRooms, {super.key}); @@ -35,6 +31,11 @@ class SelectMatrixRoomsListController extends State { bool isSelectAllMode = false; bool isSelectMode = false; + PupilFilterManager get _pupilFilterManager => di(); + MatrixPolicyManager get _matrixPolicyManager => di(); + MatrixPolicyFilterManager get _matrixPolicyFilterManager => + di(); + @override void initState() { //_pupilFilterManager.refreshFilteredPupils(); diff --git a/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_helper.dart b/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_helper.dart index 93ed795a..08ae95ab 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_helper.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/domain/matrix_user_helper.dart @@ -6,10 +6,10 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); -final _pupilManager = di(); - class MatrixUserHelper { + static MatrixPolicyManager get _matrixPolicyManager => + di(); + static PupilManager get _pupilManager => di(); static List usersFromUserIds(List userIds) { final List users = List.from( _matrixPolicyManager.matrixUsers.value, diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart index 14c525f7..b05e21b1 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart @@ -11,13 +11,12 @@ import 'package:school_data_hub_flutter/features/matrix/users/presentation/matri import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_users_list_view_bottom_navbar.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); - class MatrixUsersListPage extends WatchingWidget { const MatrixUsersListPage({super.key}); @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); List matrixUsers = watchValue((MatrixPolicyFilterManager x) => x.filteredMatrixUsers); diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_user_list_card.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_user_list_card.dart index fd7120fe..ab6772a8 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_user_list_card.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_user_list_card.dart @@ -22,8 +22,6 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); - class MatrixUsersListCard extends WatchingStatefulWidget { final MatrixUser matrixUser; const MatrixUsersListCard(this.matrixUser, {super.key}); @@ -42,6 +40,8 @@ class _MatrixUsersListCardState extends State { PupilProxy? pupil; + MatrixPolicyManager get _matrixPolicyManager => di(); + Future _showMessageDialog( BuildContext context, MatrixUser matrixUser, diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_users_list_view_bottom_navbar.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_users_list_view_bottom_navbar.dart index 67efcea1..82688890 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_users_list_view_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_users_list_view_bottom_navbar.dart @@ -9,14 +9,13 @@ import 'package:school_data_hub_flutter/features/matrix/users/presentation/new_m import 'package:school_data_hub_flutter/features/matrix/users/presentation/select_matrix_users_list_page/controller/select_matrix_users_list_controller.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); -final _matrixPolicyFilterManager = di(); - class MatrixUsersListViewBottomNavbar extends WatchingWidget { const MatrixUsersListViewBottomNavbar({super.key}); @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); + final _matrixPolicyFilterManager = di(); final bool filtersOn = watchValue( (MatrixPolicyFilterManager x) => x.filtersOn, ); diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart index 1f0b5d6b..88c1fb50 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/widgets/pupil_rooms_list.dart @@ -9,8 +9,6 @@ import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_roo import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_user.dart'; import 'package:watch_it/watch_it.dart'; -final _matrixPolicyManager = di(); - class MatrixUserRoomsList extends WatchingWidget { final MatrixUser matrixUser; final List matrixRooms; @@ -22,6 +20,7 @@ class MatrixUserRoomsList extends WatchingWidget { @override Widget build(BuildContext context) { + final _matrixPolicyManager = di(); List namedMatrixRooms = _matrixPolicyManager.matrixRooms.value; final user = watch(matrixUser); @@ -155,6 +154,7 @@ List roomsList( List matrixRooms, BuildContext context, ) { + final _matrixPolicyManager = di(); List namedMatrixRooms = _matrixPolicyManager.matrixRooms.value; return [ diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart index 46fbc3c8..c14385bb 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart @@ -13,11 +13,8 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/family_langu import 'package:school_data_hub_flutter/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_list_search_bar.dart'; import 'package:watch_it/watch_it.dart'; -final _filterStateManager = di(); - -final _pupilManager = di(); - List familyLanguageLessonsFilter(List pupils) { + final _filterStateManager = di(); List filteredPupils = []; bool filtersOn = false; for (PupilProxy pupil in pupils) { @@ -38,6 +35,7 @@ List familyLanguageLessonsFilter(List pupils) { } void _onPop(bool didPop, dynamic result) { + final _filterStateManager = di(); _filterStateManager.resetFilters(); } @@ -46,6 +44,8 @@ class FamilyLanguageLessonsListPage extends WatchingWidget { @override Widget build(BuildContext context) { + final _filterStateManager = di(); + final _pupilManager = di(); List filteredPupils = watchValue( (PupilsFilter x) => x.filteredPupils, ); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart index eb8c3243..4fb03f3b 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart @@ -12,8 +12,6 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profil import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/pupil_profile_page_content.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); - class PupilProfilePage extends WatchingWidget { final PupilProxy pupil; @@ -21,6 +19,7 @@ class PupilProfilePage extends WatchingWidget { @override Widget build(BuildContext context) { + final _pupilManager = di(); return Scaffold( backgroundColor: AppColors.pupilProfileBackgroundColor, body: RefreshIndicator( diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_heading_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_heading_card.dart index 79f2ea4d..3303e144 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_heading_card.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_heading_card.dart @@ -6,14 +6,13 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avatar.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - class PupilProfileHeadingCard extends WatchingWidget { final PupilProxy passedPupil; const PupilProfileHeadingCard({required this.passedPupil, super.key}); @override Widget build(BuildContext context) { + final _hubSessionManager = di(); final pupil = watch(passedPupil); return Card( child: Row( diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart index eed11568..ab10bacf 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/attendance_content/pupil_profile_attendance_content.dart @@ -11,14 +11,13 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:watch_it/watch_it.dart'; -final _attendanceManager = di(); - class PupilProfileAttendanceContent extends StatelessWidget { final PupilProxy pupil; const PupilProfileAttendanceContent({required this.pupil, super.key}); @override Widget build(BuildContext context) { + final _attendanceManager = di(); final missedHoursForActualReport = AttendanceHelper.missedHoursforSemesterOrSchoolyear(pupil); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart index 9231875a..be1f6ab0 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart @@ -14,8 +14,6 @@ import 'package:school_data_hub_flutter/features/authorizations/presentation/aut import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _authorizationManager = di(); - class PupilContentAuthorizationEntryCard extends WatchingWidget { final Authorization authorization; final PupilAuthorization pupilAuthorization; @@ -29,6 +27,7 @@ class PupilContentAuthorizationEntryCard extends WatchingWidget { @override Widget build(BuildContext context) { + final _authorizationManager = di(); final thisAuthorization = watchValue( (AuthorizationManager x) => x.authorizations, ).firstWhere((authorization) => authorization.id == this.authorization.id); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart index 5a7d94a5..fae8776a 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart @@ -9,10 +9,6 @@ import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog_dropdown.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); - -final _hubSessionManager = di(); - // based on https://mobikul.com/creating-stateful-dialog-form-in-flutter/ // TODO: It must be a better way to do this @@ -21,6 +17,8 @@ Future languageDialog( PupilProxy pupil, CommunicationSubject subject, ) async { + final _pupilManager = di(); + final _hubSessionManager = di(); CommunicationSkills? languageValue; switch (subject) { case CommunicationSubject.pupil: diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart index dc3d1ca3..33c40d3f 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart @@ -15,15 +15,14 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profil import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/widgets/pupil_profile_content_widgets.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _hubSessionManager = di(); - class PupilProfileCommunicationContent extends WatchingWidget { final PupilProxy pupil; const PupilProfileCommunicationContent({required this.pupil, super.key}); @override Widget build(BuildContext context) { + final _pupilManager = di(); + final _hubSessionManager = di(); final communicationPupil = watchPropertyValue( (m) => m.communicationPupil, target: pupil, diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart index 7c3f0fc6..2c75ae6a 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart @@ -23,12 +23,6 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profil import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avatar.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); - -final _matrixPolicyManager = di(); - -final _hubSessionManager = di(); - class PupilProfileInfosContent extends WatchingWidget { final PupilProxy pupil; const PupilProfileInfosContent({required this.pupil, super.key}); @@ -38,6 +32,9 @@ class PupilProfileInfosContent extends WatchingWidget { @override Widget build(BuildContext context) { + final _pupilManager = di(); + final _matrixPolicyManager = di(); + final _hubSessionManager = di(); final pupilSiblings = _pupilManager.getSiblings(pupil); watch(pupil); return Container( diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart index 08675d27..9a00269f 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart @@ -13,16 +13,14 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - -final _notificationService = di(); - class AvatarAuthValues extends WatchingWidget { final PupilProxy pupil; const AvatarAuthValues({required this.pupil, super.key}); @override Widget build(BuildContext context) { + final _hubSessionManager = di(); + final _notificationService = di(); final avatarAuth = watchPropertyValue((m) => m.avatarAuth, target: pupil); final avatarAuthId = watchPropertyValue( (m) => m.avatarAuthId, diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart index e212cd04..e367fe36 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart @@ -13,16 +13,14 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); - -final _notificationService = di(); - class PublicMediaAuthValues extends WatchingWidget { final PupilProxy pupil; const PublicMediaAuthValues({required this.pupil, super.key}); @override Widget build(BuildContext context) { + final _hubSessionManager = di(); + final _notificationService = di(); final publicMediaAuth = watchPropertyValue( (m) => m.publicMediaAuth, target: pupil, diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart index d1bc8dd6..ea933cf2 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart @@ -21,11 +21,10 @@ class SupportLevelHistoryExpansionTile extends StatefulWidget { _SupportLevelHistoryExpansionTileState(); } -final _hubSessionManager = di(); -final _pupilManager = di(); - class _SupportLevelHistoryExpansionTileState extends State { + HubSessionManager get _hubSessionManager => di(); + PupilManager get _pupilManager => di(); late ExpansibleController _tileController; @override diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_profile_school_list_pupil_entry_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_profile_school_list_pupil_entry_card.dart index 770bb5f5..ec1da99f 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_profile_school_list_pupil_entry_card.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/school_list_content/widgets/pupil_profile_school_list_pupil_entry_card.dart @@ -10,8 +10,6 @@ import 'package:school_data_hub_flutter/features/school_lists/domain/school_list import 'package:school_data_hub_flutter/features/school_lists/presentation/school_list_pupil_entries_page/school_list_pupil_entries_page.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); - class PupilProfileSchoolListPupilEntryCard extends WatchingWidget { final PupilListEntryProxy pupilListEntryProxy; const PupilProfileSchoolListPupilEntryCard({ @@ -21,6 +19,7 @@ class PupilProfileSchoolListPupilEntryCard extends WatchingWidget { @override Widget build(BuildContext context) { + final _schoolListManager = di(); final pupilListEntry = watch(pupilListEntryProxy).pupilEntry; final schoolList = _schoolListManager.getSchoolListById( pupilListEntry.schoolListId, diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart index 72f84a50..0cdeb53a 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart @@ -13,11 +13,8 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/religion_pag import 'package:school_data_hub_flutter/features/pupil/presentation/religion_page/widgets/religion_list_search_bar.dart'; import 'package:watch_it/watch_it.dart'; -final _filterStateManager = di(); - -final _pupilManager = di(); - List religionFilter(List pupils) { + final _filterStateManager = di(); List filteredPupils = []; bool filtersOn = false; for (PupilProxy pupil in pupils) { @@ -36,6 +33,7 @@ List religionFilter(List pupils) { } void _onPop(bool didPop, dynamic result) { + final _filterStateManager = di(); _filterStateManager.resetFilters(); } @@ -44,6 +42,8 @@ class ReligionListPage extends WatchingWidget { @override Widget build(BuildContext context) { + final _filterStateManager = di(); + final _pupilManager = di(); List filteredPupils = watchValue((PupilsFilter x) => x.filteredPupils); List pupils = religionFilter(filteredPupils); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart index 0dcc154f..4ccdc788 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart @@ -16,9 +16,6 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupil import 'package:school_data_hub_flutter/l10n/app_localizations.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _pupilFilerManager = di(); - class SelectPupilsListPage extends WatchingStatefulWidget { final List? selectablePupils; @@ -37,6 +34,9 @@ class _SelectPupilsListPageState extends State { bool isSelectAllMode = false; bool isSelectMode = false; + PupilManager get _pupilManager => di(); + PupilFilterManager get _pupilFilerManager => di(); + @override void initState() { setState(() { diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/special_info_page/special_info_list_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/special_info_page/special_info_list_page.dart index 2651efc0..afa8dc45 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/special_info_page/special_info_list_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/special_info_page/special_info_list_page.dart @@ -13,11 +13,8 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/special_info import 'package:school_data_hub_flutter/features/pupil/presentation/special_info_page/widgets/special_info_list_search_bar.dart'; import 'package:watch_it/watch_it.dart'; -final _filterStateManager = di(); - -final _pupilManager = di(); - List specialInfoFilter(List pupils) { + final _filterStateManager = di(); List filteredPupils = []; bool filtersOn = false; for (PupilProxy pupil in pupils) { @@ -36,6 +33,7 @@ List specialInfoFilter(List pupils) { } void _onPop(bool didPop, dynamic result) { + final _filterStateManager = di(); _filterStateManager.resetFilters(); } @@ -44,6 +42,8 @@ class SpecialInfoListPage extends WatchingWidget { @override Widget build(BuildContext context) { + final _filterStateManager = di(); + final _pupilManager = di(); List filteredPupils = watchValue((PupilsFilter x) => x.filteredPupils); List pupils = specialInfoFilter(filteredPupils); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_profile_attendance_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_profile_attendance_content.dart index 229647e9..503ba691 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_profile_attendance_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_profile_attendance_content.dart @@ -11,14 +11,13 @@ import 'package:school_data_hub_flutter/features/_attendance/presentation/widget import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:watch_it/watch_it.dart'; -final _attendanceManager = di(); - class PupilAttendanceContent extends WatchingWidget { final PupilProxy pupil; const PupilAttendanceContent({required this.pupil, super.key}); @override Widget build(BuildContext context) { + final _attendanceManager = di(); final missedHoursForActualReport = AttendanceHelper.missedHoursforSemesterOrSchoolyear(pupil); List missedSchooldays = diff --git a/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart b/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart index ffc3c9c5..c2ff5398 100644 --- a/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart +++ b/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart @@ -6,8 +6,6 @@ import 'package:school_data_hub_flutter/features/school_calendar/domain/school_c import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/widgets/date_picker_button.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolCalendarManager = di(); - class NewSchoolSemesterPage extends StatefulWidget { const NewSchoolSemesterPage({super.key}); @@ -33,6 +31,7 @@ class _NewSchoolSemesterPageState extends State { @override Widget build(BuildContext context) { + final _schoolCalendarManager = di(); return Scaffold( backgroundColor: AppColors.canvasColor, appBar: AppBar( diff --git a/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/schooldays_calendar_page/schooldays_calendar_page.dart b/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/schooldays_calendar_page/schooldays_calendar_page.dart index c4d4deb2..09549a73 100644 --- a/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/schooldays_calendar_page/schooldays_calendar_page.dart +++ b/school_data_hub_flutter/lib/features/school_calendar/presentation/new_school_semester_page/schooldays_calendar_page/schooldays_calendar_page.dart @@ -10,8 +10,6 @@ import 'package:school_data_hub_flutter/features/school_calendar/domain/school_c import 'package:table_calendar/table_calendar.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolCalendarManager = di(); - class SchooldaysCalendarPage extends WatchingStatefulWidget { const SchooldaysCalendarPage({super.key}); @@ -31,6 +29,9 @@ class SchooldaysCalendarState extends State { ); final kLastDay = DateTime(DateTime.now().toLocal().year + 2, 8, 31); + SchoolCalendarManager get _schoolCalendarManager => + di(); + List _getEventsForDay(DateTime day) { final schooldays = _schoolCalendarManager.schooldays.value; if (schooldays.any((element) => element.schoolday == day)) { diff --git a/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart b/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart index 01100639..5e8eeae1 100644 --- a/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart +++ b/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart @@ -7,12 +7,12 @@ import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupil_filt import 'package:school_data_hub_flutter/features/school_lists/domain/school_list_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); final _log = Logger('SchoolListFilterManager'); -final _filtersStateManager = di(); -final _pupilFilterManager = di(); class SchoolListFilterManager { + SchoolListManager get _schoolListManager => di(); + FiltersStateManager get _filtersStateManager => di(); + PupilFilterManager get _pupilFilterManager => di(); final _filteredSchoolLists = ValueNotifier>([]); ValueListenable> get filteredSchoolLists => _filteredSchoolLists; diff --git a/school_data_hub_flutter/lib/features/school_lists/domain/school_list_helper_functions.dart b/school_data_hub_flutter/lib/features/school_lists/domain/school_list_helper_functions.dart index de625fba..64b56ce6 100644 --- a/school_data_hub_flutter/lib/features/school_lists/domain/school_list_helper_functions.dart +++ b/school_data_hub_flutter/lib/features/school_lists/domain/school_list_helper_functions.dart @@ -4,9 +4,8 @@ import 'package:school_data_hub_flutter/features/school_lists/domain/models/pupi import 'package:school_data_hub_flutter/features/school_lists/domain/school_list_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); - class SchoolListHelper { + static SchoolListManager get _schoolListManager => di(); static String listOwner(int listId) { final SchoolList schoolList = _schoolListManager.getSchoolListById(listId); return schoolList.createdBy; diff --git a/school_data_hub_flutter/lib/features/school_lists/presentation/new_list_page/new_school_list_page.dart b/school_data_hub_flutter/lib/features/school_lists/presentation/new_list_page/new_school_list_page.dart index b4e99fe2..328c2f82 100644 --- a/school_data_hub_flutter/lib/features/school_lists/presentation/new_list_page/new_school_list_page.dart +++ b/school_data_hub_flutter/lib/features/school_lists/presentation/new_list_page/new_school_list_page.dart @@ -12,15 +12,14 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avat import 'package:school_data_hub_flutter/features/school_lists/domain/school_list_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); -final _pupilManager = di(); -final _hubSessionManager = di(); - class NewSchoolListPage extends WatchingWidget { final SchoolList? initialSchoolList; const NewSchoolListPage({this.initialSchoolList, super.key}); @override Widget build(BuildContext context) { + final _schoolListManager = di(); + final _pupilManager = di(); + final _hubSessionManager = di(); // Create text editing controllers using createOnce final schoolListNameController = createOnce( () => TextEditingController(), diff --git a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/school_list_pupil_entries_page.dart b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/school_list_pupil_entries_page.dart index 4141ec2f..15ee39d5 100644 --- a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/school_list_pupil_entries_page.dart +++ b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/school_list_pupil_entries_page.dart @@ -15,11 +15,6 @@ import 'package:school_data_hub_flutter/features/school_lists/presentation/schoo import 'package:school_data_hub_flutter/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entry_card.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); -final _schoolListFilterManager = di(); - -final _pupilManager = di(); - class SchoolListPupilEntriesPage extends WatchingWidget { final SchoolList schoolList; @@ -27,10 +22,12 @@ class SchoolListPupilEntriesPage extends WatchingWidget { @override Widget build(BuildContext context) { - final unfilteredPupilListEntries = - watch( - _schoolListManager.getPupilEntriesProxyFromSchoolList(schoolList.id!), - ).pupilEntries.values.map((e) => e.pupilEntry).toList(); + final _schoolListManager = di(); + final _schoolListFilterManager = di(); + final _pupilManager = di(); + final unfilteredPupilListEntries = watch( + _schoolListManager.getPupilEntriesProxyFromSchoolList(schoolList.id!), + ).pupilEntries.values.map((e) => e.pupilEntry).toList(); final pupilListEntries = _schoolListFilterManager .addPupilEntryFiltersToFilteredPupils(unfilteredPupilListEntries); @@ -38,14 +35,13 @@ class SchoolListPupilEntriesPage extends WatchingWidget { (PupilsFilter x) => x.filteredPupils, ); - List pupilsInList = - filteredPupils - .where( - (pupil) => pupilListEntries.any( - (pupilList) => pupilList.pupilId == pupil.pupilId, - ), - ) - .toList(); + List pupilsInList = filteredPupils + .where( + (pupil) => pupilListEntries.any( + (pupilList) => pupilList.pupilId == pupil.pupilId, + ), + ) + .toList(); return Scaffold( backgroundColor: AppColors.canvasColor, @@ -68,27 +64,27 @@ class SchoolListPupilEntriesPage extends WatchingWidget { ), pupilsInList.isEmpty ? const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(8.0), - child: Text( - 'Keine Ergebnisse', - style: TextStyle(fontSize: 18), + child: Center( + child: Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'Keine Ergebnisse', + style: TextStyle(fontSize: 18), + ), ), ), - ), - ) + ) : SliverList( - delegate: SliverChildBuilderDelegate(( - BuildContext context, - int index, - ) { - return SchoolListPupilEntryCard( - pupilsInList[index].pupilId, - schoolList.id!, - ); - }, childCount: pupilsInList.length), - ), + delegate: SliverChildBuilderDelegate(( + BuildContext context, + int index, + ) { + return SchoolListPupilEntryCard( + pupilsInList[index].pupilId, + schoolList.id!, + ); + }, childCount: pupilsInList.length), + ), ], ), ), diff --git a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entries_bottom_navbar.dart b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entries_bottom_navbar.dart index 464c8d45..3cb6dbe0 100644 --- a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entries_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entries_bottom_navbar.dart @@ -17,10 +17,6 @@ import 'package:school_data_hub_flutter/features/user/domain/user_manager.dart'; import 'package:school_data_hub_flutter/features/user/presentation/select_users/select_users_page.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); -final _hubSessionManager = di(); -final _pupilManager = di(); - class SchoolListPupilEntriesBottomNavBar extends StatelessWidget { final int listId; @@ -33,6 +29,9 @@ class SchoolListPupilEntriesBottomNavBar extends StatelessWidget { @override Widget build(BuildContext context) { + final _schoolListManager = di(); + final _hubSessionManager = di(); + final _pupilManager = di(); final schoolList = _schoolListManager.getSchoolListById(listId); return BottomNavBarLayout( bottomNavBar: BottomAppBar( diff --git a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entry_card.dart b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entry_card.dart index fce5cf5f..4470ecbb 100644 --- a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entry_card.dart +++ b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_pupil_entry_card.dart @@ -17,12 +17,6 @@ import 'package:school_data_hub_flutter/features/school_lists/domain/school_list import 'package:school_data_hub_flutter/features/school_lists/domain/school_list_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _pupilManager = di(); -final _mainMenuBottomNavManager = di(); -final _hubSessionManager = di(); -final _notificationService = di(); -final _schoolListManager = di(); - class SchoolListPupilEntryCard extends WatchingWidget { final int pupilId; @@ -32,6 +26,11 @@ class SchoolListPupilEntryCard extends WatchingWidget { @override Widget build(BuildContext context) { + final _pupilManager = di(); + final _mainMenuBottomNavManager = di(); + final _hubSessionManager = di(); + final _notificationService = di(); + final _schoolListManager = di(); final PupilProxy pupil = _pupilManager.getPupilByPupilId(pupilId)!; final PupilListEntry pupilEntry = diff --git a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_stats_row.dart b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_stats_row.dart index 7db8c6a1..966927c4 100644 --- a/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_stats_row.dart +++ b/school_data_hub_flutter/lib/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_stats_row.dart @@ -10,8 +10,6 @@ import 'package:school_data_hub_flutter/features/school_lists/domain/school_list import 'package:school_data_hub_flutter/features/school_lists/presentation/new_list_page/new_school_list_page.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); - class SchoolListStatsRow extends WatchingWidget { final SchoolList schoolList; final List pupils; @@ -23,6 +21,7 @@ class SchoolListStatsRow extends WatchingWidget { @override Widget build(BuildContext context) { + final _schoolListManager = di(); watch( _schoolListManager.getPupilEntriesProxyFromSchoolList(schoolList.id!), ); diff --git a/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/school_lists_page.dart b/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/school_lists_page.dart index 567fdc32..eef1fce0 100644 --- a/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/school_lists_page.dart +++ b/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/school_lists_page.dart @@ -11,20 +11,20 @@ import 'package:school_data_hub_flutter/features/school_lists/presentation/schoo import 'package:school_data_hub_flutter/features/school_lists/presentation/school_lists_page/widgets/school_lists_bottom_navbar.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListFilterManager = di(); -final _schoolListManager = di(); - class SchoolListsPage extends WatchingWidget { const SchoolListsPage({super.key}); @override Widget build(BuildContext context) { + final _schoolListFilterManager = di(); + final _schoolListManager = di(); bool filtersOn = watchValue((SchoolListFilterManager x) => x.filterState); // List schoolLists = // watchPropertyValue((SchoolListManager x) => x.schoolLists); // _schoolListFilterManager.updateFilteredSchoolLists(schoolLists); - List filteredSchoolLists = - watchValue((SchoolListFilterManager x) => x.filteredSchoolLists); + List filteredSchoolLists = watchValue( + (SchoolListFilterManager x) => x.filteredSchoolLists, + ); // List visibleSchoolLists = schoolLists // .where((element) => // element.visibility == 'public' || @@ -43,10 +43,7 @@ class SchoolListsPage extends WatchingWidget { children: [ Icon(Icons.rule_rounded, size: 25, color: Colors.white), Gap(10), - Text( - 'Listen', - style: AppStyles.appBarTextStyle, - ), + Text('Listen', style: AppStyles.appBarTextStyle), ], ), ), @@ -59,15 +56,13 @@ class SchoolListsPage extends WatchingWidget { children: [ Padding( padding: const EdgeInsets.only( - left: 10.0, top: 15.0, right: 10.00), + left: 10.0, + top: 15.0, + right: 10.00, + ), child: Row( children: [ - const Text( - 'Gesamt:', - style: TextStyle( - fontSize: 13, - ), - ), + const Text('Gesamt:', style: TextStyle(fontSize: 13)), const Gap(10), Text( filteredSchoolLists.length.toString(), @@ -86,10 +81,10 @@ class SchoolListsPage extends WatchingWidget { children: [ Expanded( child: SchoolListSearchTextField( - searchType: SearchType.list, - hintText: 'Liste suchen', - refreshFunction: - _schoolListManager.fetchSchoolLists), + searchType: SearchType.list, + hintText: 'Liste suchen', + refreshFunction: _schoolListManager.fetchSchoolLists, + ), ), //--------------------------------- InkWell( @@ -125,7 +120,8 @@ class SchoolListsPage extends WatchingWidget { itemCount: filteredSchoolLists.length, itemBuilder: (BuildContext context, int index) { return SchoolListCard( - schoolList: filteredSchoolLists[index]); + schoolList: filteredSchoolLists[index], + ); }, ), ), diff --git a/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/widgets/school_list_card.dart b/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/widgets/school_list_card.dart index 303c7895..1b1d9809 100644 --- a/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/widgets/school_list_card.dart +++ b/school_data_hub_flutter/lib/features/school_lists/presentation/school_lists_page/widgets/school_list_card.dart @@ -10,15 +10,14 @@ import 'package:school_data_hub_flutter/features/school_lists/presentation/schoo import 'package:school_data_hub_flutter/features/school_lists/presentation/school_list_pupil_entries_page/widgets/school_list_stats_row.dart'; import 'package:watch_it/watch_it.dart'; -final _schoolListManager = di(); -final _hubSessionManager = di(); - class SchoolListCard extends WatchingWidget { final SchoolList schoolList; const SchoolListCard({required this.schoolList, super.key}); @override Widget build(BuildContext context) { + final _schoolListManager = di(); + final _hubSessionManager = di(); final schoolList = watchPropertyValue( (SchoolListManager x) => x.schoolLists, ).firstWhere((element) => element.listId == this.schoolList.listId); diff --git a/school_data_hub_flutter/lib/features/school_lists/services/school_list_pdf_generator.dart b/school_data_hub_flutter/lib/features/school_lists/services/school_list_pdf_generator.dart index 54ff6ded..88113b05 100644 --- a/school_data_hub_flutter/lib/features/school_lists/services/school_list_pdf_generator.dart +++ b/school_data_hub_flutter/lib/features/school_lists/services/school_list_pdf_generator.dart @@ -18,9 +18,9 @@ import 'package:school_data_hub_flutter/features/school_lists/domain/school_list import 'package:watch_it/watch_it.dart'; final _log = Logger('SchoolListPdfGenerator'); -final _schoolListManager = di(); class SchoolListPdfGenerator { + static SchoolListManager get _schoolListManager => di(); static Future generateSchoolListPdf({ required SchoolList schoolList, required List pupils, diff --git a/school_data_hub_flutter/lib/features/user/presentation/change_password/change_password_page.dart b/school_data_hub_flutter/lib/features/user/presentation/change_password/change_password_page.dart index 5b08ba88..45d93712 100644 --- a/school_data_hub_flutter/lib/features/user/presentation/change_password/change_password_page.dart +++ b/school_data_hub_flutter/lib/features/user/presentation/change_password/change_password_page.dart @@ -6,8 +6,6 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/information_dialo import 'package:school_data_hub_flutter/features/user/domain/user_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _userManager = di(); - /// A page for users to change their password. /// /// Usage example: @@ -24,6 +22,7 @@ class UserChangePasswordPage extends WatchingWidget { @override Widget build(BuildContext context) { + final _userManager = di(); final TextEditingController oldPasswordController = createOnce( () => TextEditingController(), ); diff --git a/school_data_hub_flutter/lib/features/user/presentation/create_user/create_user_page.dart b/school_data_hub_flutter/lib/features/user/presentation/create_user/create_user_page.dart index c1944c3b..bcbab7d0 100644 --- a/school_data_hub_flutter/lib/features/user/presentation/create_user/create_user_page.dart +++ b/school_data_hub_flutter/lib/features/user/presentation/create_user/create_user_page.dart @@ -10,13 +10,12 @@ import 'package:school_data_hub_flutter/features/user/presentation/create_user/w import 'package:school_data_hub_flutter/features/user/presentation/widgets/roles_dropdown.dart'; import 'package:watch_it/watch_it.dart'; -final _userManager = di(); - class CreateUserPage extends WatchingWidget { const CreateUserPage({super.key}); @override Widget build(BuildContext context) { + final _userManager = di(); final TextEditingController fullNameController = createOnce( () => TextEditingController(), ); diff --git a/school_data_hub_flutter/lib/features/user/presentation/reset_password/reset_user_password_page.dart b/school_data_hub_flutter/lib/features/user/presentation/reset_password/reset_user_password_page.dart index f60cf8d7..2909ecb4 100644 --- a/school_data_hub_flutter/lib/features/user/presentation/reset_password/reset_user_password_page.dart +++ b/school_data_hub_flutter/lib/features/user/presentation/reset_password/reset_user_password_page.dart @@ -8,8 +8,6 @@ import 'package:school_data_hub_flutter/features/user/domain/user_manager.dart'; import 'package:school_data_hub_flutter/features/user/presentation/reset_password/widgets/user_selection_dropdown.dart'; import 'package:watch_it/watch_it.dart'; -final _userManager = di(); - /// A page for admins to reset a user's password. /// /// Usage example: @@ -26,6 +24,7 @@ class ResetUserPasswordPage extends WatchingWidget { @override Widget build(BuildContext context) { + final _userManager = di(); final selectedUser = createOnce(() => ValueNotifier(null)); final TextEditingController newPasswordController = createOnce( () => TextEditingController(), diff --git a/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart b/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart index 5a1cc2b7..750425e8 100644 --- a/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart +++ b/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart @@ -12,8 +12,6 @@ import 'package:school_data_hub_flutter/features/user/presentation/select_users/ import 'package:school_data_hub_flutter/features/user/presentation/select_users/widgets/select_users_view_bottom_navbar.dart'; import 'package:watch_it/watch_it.dart'; -final _userManager = di(); - class SelectUsersPage extends WatchingStatefulWidget { final List selectableUsers; final String? authorizedUsers; @@ -35,6 +33,8 @@ class _SelectUsersPageState extends State { bool isSelectAllMode = false; bool isSelectMode = false; + UserManager get _userManager => di(); + @override void initState() { if (widget.authorizedUsers != null && widget.authorizedUsers!.isNotEmpty) { diff --git a/school_data_hub_flutter/lib/features/workbooks/domain/pupil_workbook_manager.dart b/school_data_hub_flutter/lib/features/workbooks/domain/pupil_workbook_manager.dart index d4ca5307..d53a15f5 100644 --- a/school_data_hub_flutter/lib/features/workbooks/domain/pupil_workbook_manager.dart +++ b/school_data_hub_flutter/lib/features/workbooks/domain/pupil_workbook_manager.dart @@ -6,10 +6,9 @@ import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/workbooks/data/pupil_workbook_api_service.dart'; import 'package:watch_it/watch_it.dart'; -final _hubSessionManager = di(); -final _notificationService = di(); - class PupilWorkbookManager with ChangeNotifier { + HubSessionManager get _hubSessionManager => di(); + NotificationService get _notificationService => di(); final Map> _pupilWorkbooks = {}; final _pupilWorkbookApiService = PupilWorkbookApiService(); diff --git a/school_data_hub_flutter/lib/main.dart b/school_data_hub_flutter/lib/main.dart index 8c09a1e5..6837c025 100644 --- a/school_data_hub_flutter/lib/main.dart +++ b/school_data_hub_flutter/lib/main.dart @@ -81,7 +81,6 @@ class MyApp extends WatchingWidget { // Watch for environment changes - this will trigger rebuilds when EnvManager notifies final envManager = watchIt(); - final String? currentEnv = envManager.activeEnv?.serverName; final bool envIsReady = watchValue((EnvManager x) => x.envIsReady); final bool userIsAuthenticated = watchValue( @@ -119,7 +118,7 @@ class MyApp extends WatchingWidget { return ErrorPage(error: snapshot.error.toString()); } else if (snapshot.connectionState == ConnectionState.done) { if (userIsAuthenticated) { - return MainMenuBottomNavigation(); + return const MainMenuBottomNavigation(); } else { return const Login(); } @@ -128,6 +127,8 @@ class MyApp extends WatchingWidget { } }, ) + : envManager.activeEnv != null + ? const LoadingPage() : const EntryPoint(), ); } From bad2132d3273ca487f91957b19ea2268cb57c25e Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sat, 29 Nov 2025 12:37:02 +0100 Subject: [PATCH 15/52] [APP} Implement lazy initialitation of MatrixPolicyManager --- .../cursor_docs/get_it_async_objects.md | 395 ++++++++++++++++++ .../lib/core/di/di_on_active_env.dart | 22 +- .../lib/core/di/init_manager.dart | 325 +++++++++----- .../lib/core/env/env_manager.dart | 14 +- .../lib/core/session/hub_session_manager.dart | 15 +- .../widgets/pupil_lists_buttons.dart | 6 +- .../matrix/domain/matrix_policy_helper.dart | 5 +- .../matrix_users_list_page.dart | 91 +++- .../dialogs/language_dialog.dart | 36 +- .../pupil_profile_communication_content.dart | 2 - .../support_level_history_expansion_tile.dart | 2 - .../select_users/select_users_page.dart | 64 ++- .../new_workbook_page/new_workbook_page.dart | 89 ++-- .../widgets/pupil_workbook_card.dart | 43 +- .../widgets/workbook_card.dart | 35 +- 15 files changed, 873 insertions(+), 271 deletions(-) create mode 100644 school_data_hub_flutter/cursor_docs/get_it_async_objects.md diff --git a/school_data_hub_flutter/cursor_docs/get_it_async_objects.md b/school_data_hub_flutter/cursor_docs/get_it_async_objects.md new file mode 100644 index 00000000..718f1b17 --- /dev/null +++ b/school_data_hub_flutter/cursor_docs/get_it_async_objects.md @@ -0,0 +1,395 @@ + + +# Async Objects + +## Overview + +GetIt provides comprehensive support for asynchronous object creation and initialization. This is essential for objects that need to perform async operations during creation (database connections, network calls, file I/O) or that depend on other async objects being ready first. + + + +## Quick Reference + +### Async Registration Methods + +| Method | When Created | How Many Instances | Lifetime | Best For | +|--------|--------------|-------------------|----------|----------| +| [registerFactoryAsync](#registerfactoryasync) | Every `getAsync()` | Many | Per request | Async operations on each access | +| [registerCachedFactoryAsync](#registercachedfactoryasync) | First access + after GC | Reused while in memory | Until garbage collected | Performance optimization for expensive async operations | +| [registerSingletonAsync](#registersingletonasync) | Immediately at registration | One | Permanent | App-level services with async setup | +| [registerLazySingletonAsync](#registerlazysingleton) | First `getAsync()` | One | Permanent | Expensive async services not always needed | +| [registerSingletonWithDependencies](#sync-singletons-with-dependencies) | After dependencies ready | One | Permanent | Services depending on other services | + +## Async Factories + +Async factories create a new instance on each call to `getAsync()` by executing an asynchronous factory function. + +### registerFactoryAsync + +Creates a new instance every time you call `getAsync()`. + +<<< @/../code_samples/lib/get_it/register_factory_async_signature.dart#example + +Parameters: +- `factoryFunc` - Async function that creates and returns the instance +- `instanceName` - Optional name to register multiple factories of the same type + +Example: + + + +<<< @/../code_samples/lib/get_it/async_factory_basic.dart#example + +### registerCachedFactoryAsync + +Like `registerFactoryAsync`, but caches the instance with a weak reference. Returns the cached instance if it's still in memory; otherwise creates a new one. + +<<< @/../code_samples/lib/get_it/register_cached_factory_async_signature.dart#example + +Example: + + + +<<< @/../code_samples/lib/get_it/async_cached_factory_example.dart#example + +### Async Factories with Parameters + +Like regular factories, async factories can accept up to two parameters. + +<<< @/../code_samples/lib/get_it/async_factory_param_signatures.dart#example + +Example: + + + +<<< @/../code_samples/lib/get_it/async_factory_param_example.dart#example + +## Async Singletons + +Async singletons are created once with async initialization and live for the lifetime of the registration (until unregistered or scope is popped). + +### registerSingletonAsync + +Registers a singleton with an async factory function that's executed immediately. The singleton is marked as ready when the factory function completes (unless `signalsReady` is true). + +<<< @/../code_samples/lib/get_it/register_singleton_async_signature.dart#example + +Parameters: +- `factoryFunc` - Async function that creates the singleton instance +- `instanceName` - Optional name to register multiple singletons of the same type +- `dependsOn` - List of types this singleton depends on (waits for them to be ready first) +- `signalsReady` - If true, you must manually call `signalReady()` to mark as ready +- `dispose` - Optional cleanup function called when unregistering or resetting +- `onCreated` - Optional callback invoked after the instance is created + +Example: + + + +<<< @/../code_samples/lib/get_it/async_singleton_example.dart#example + +::: details Common Mistake: Using signalsReady with registerSingletonAsync +Most of the time, you DON'T need `signalsReady: true` with `registerSingletonAsync`. + +The async factory completion automatically signals ready when it returns. Only use `signalsReady: true` if you need multi-stage initialization where the factory completes but you have additional async work before the instance is truly ready. + +Common error pattern: + +<<< @/../code_samples/lib/get_it/signal_ready_error_example.dart#example + +Why it fails: You can't call `signalReady(instance)` from inside the factory because the instance isn't registered yet. + +Correct alternatives: + +Option 1 - Let async factory auto-signal (recommended): + +<<< @/../code_samples/lib/get_it/signal_ready_correct_option1.dart#example + +Option 2 - Use registerSingleton for post-registration signaling: + +<<< @/../code_samples/lib/get_it/signal_ready_correct_option2.dart#example + +Option 3 - Implement WillSignalReady interface: + +<<< @/../code_samples/lib/get_it/signal_ready_correct_option3.dart#example + +See [Manual Ready Signaling](#manual-ready-signaling) for more details. +::: + +### registerLazySingletonAsync + +Registers a singleton with an async factory function that's executed on first access (when you call `getAsync()` for the first time). + +<<< @/../code_samples/lib/get_it/register_lazy_singleton_async_signature.dart#example + +Parameters: +- `factoryFunc` - Async function that creates the singleton instance +- `instanceName` - Optional name to register multiple singletons of the same type +- `dispose` - Optional cleanup function called when unregistering or resetting +- `onCreated` - Optional callback invoked after the instance is created +- `useWeakReference` - If true, uses weak reference (allows garbage collection if not used) + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_2642b731.dart#example + + +::: warning Lazy Async Singletons and allReady() +`registerLazySingletonAsync` does not block `allReady()` because the factory function is not called until first access. However, once accessed, you can use `isReady()` to wait for its completion. +::: + +## Accessing Async Objects + +::: tip Async Objects Become Normal After Initialization +Once an async singleton has completed initialization (you've awaited `allReady()` or `isReady()`), you can access it like a regular singleton using `get()` instead of `getAsync()`. The async methods are only needed during the initialization phase or when accessing async factories. + +```dart +// During startup - wait for initialization +await getIt.allReady(); + +// After ready - access normally (no await needed) +final database = getIt(); // Not getAsync! +final apiClient = getIt(); +``` +::: + +### getAsync() + +Retrieves an instance created by an async factory or waits for an async singleton to complete initialization. + +<<< @/../code_samples/lib/get_it/async_objects_4c3dc27e_signature.dart#example + + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_5324c9ca_signature.dart#example + + +::: tip Getting Multiple Async Instances +If you need to retrieve multiple async registrations of the same type, see the [Multiple Registrations](/documentation/get_it/multiple_registrations#async-version) chapter for `getAllAsync()` documentation. +::: + +## Dependency Management + +### Using dependsOn + +The `dependsOn` parameter ensures initialization order. When you register a singleton with `dependsOn`, its factory function won't execute until all listed dependencies have signaled ready. + +Example - Sequential initialization: + +<<< @/../code_samples/lib/get_it/async_objects_ae083a64.dart#example + + +## Sync Singletons with Dependencies + +Sometimes you have a regular (sync) singleton that depends on other async singletons being ready first. Use `registerSingletonWithDependencies` for this pattern. + +<<< @/../code_samples/lib/get_it/async_objects_e093effa_signature.dart#example + + +Parameters: +- `factoryFunc` - Sync function that creates the singleton instance (called after dependencies are ready) +- `instanceName` - Optional name to register multiple singletons of the same type +- `dependsOn` - List of types this singleton depends on (waits for them to be ready first) +- `signalsReady` - If true, you must manually call `signalReady()` to mark as ready +- `dispose` - Optional cleanup function called when unregistering or resetting + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_fc40829b.dart#example + + +## Startup Orchestration + +GetIt provides several functions to coordinate async initialization and wait for services to be ready. + +### allReady() + +Returns a `Future` that completes when all async singletons and singletons with `signalsReady` have completed their initialization. + +<<< @/../code_samples/lib/get_it/async_objects_93c1b617_signature.dart#example + + +Parameters: +- `timeout` - Optional timeout; throws `WaitingTimeOutException` if not ready in time +- `ignorePendingAsyncCreation` - If true, only waits for manual signals, ignores async singletons + +Example with FutureBuilder: + +<<< @/../code_samples/lib/get_it/async_objects_bbdb298c.dart#example + + +Example with timeout: + +<<< @/../code_samples/lib/get_it/async_objects_864b4c27.dart#example + + +Calling allReady() multiple times: + +You can call `allReady()` multiple times. After the first `allReady()` completes, if you register new async singletons, you can await `allReady()` again to wait for the new ones. + +<<< @/../code_samples/lib/get_it/async_objects_28d751fd.dart#example + + +This pattern is especially useful with scopes where each scope needs its own initialization: + +<<< @/../code_samples/lib/get_it/async_objects_d0a62ccd.dart#example + + +### isReady() + +Returns a `Future` that completes when a specific singleton is ready. + +<<< @/../code_samples/lib/get_it/async_objects_c603af1e_signature.dart#example + + +Parameters: +- `T` - Type of the singleton to wait for +- `instance` - Alternatively, wait for a specific instance object +- `instanceName` - Wait for named registration +- `timeout` - Optional timeout; throws `WaitingTimeOutException` if not ready in time +- `callee` - Optional parameter for debugging (helps identify who's waiting) + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_21920847.dart#example + + +### isReadySync() + +Checks if a singleton is ready without waiting (returns immediately). + +<<< @/../code_samples/lib/get_it/async_objects_7d06e64a_signature.dart#example + + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_96ff9c4e.dart#example + + +### allReadySync() + +Checks if all async singletons are ready without waiting. + +<<< @/../code_samples/lib/get_it/async_objects_90ee78c4_signature.dart#example + + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_4ef84c96.dart#example + + +### Named Dependencies with InitDependency + +If you have named registrations, use `InitDependency` to specify both type and instance name. + +<<< @/../code_samples/lib/get_it/async_objects_55d54ef7.dart#example + + +## Manual Ready Signaling + +Sometimes you need more control over when a singleton signals it's ready. This is useful when initialization involves multiple steps or callbacks. + +### Using signalsReady Parameter + +When you set `signalsReady: true` during registration, GetIt won't automatically mark the singleton as ready. You must manually call `signalReady()`. + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_f2965023.dart#example + + +### Using WillSignalReady Interface + +Instead of passing `signalsReady: true`, implement the `WillSignalReady` interface. GetIt automatically detects this and waits for manual signaling. + +<<< @/../code_samples/lib/get_it/async_objects_62e38c5b.dart#example + + +### signalReady() + +Manually signals that a singleton is ready. + +<<< @/../code_samples/lib/get_it/async_objects_af1df8a2_signature.dart#example + + +Parameters: +- `instance` - The instance that's ready (passing `null` is legacy and not recommended) + +Example: + + +<<< @/../code_samples/lib/get_it/async_objects_174d24d3.dart#example + + +::: tip Legacy Feature +`signalReady(null)` (global ready signal without an instance) is a legacy feature from earlier versions of GetIt. It's recommended to use async registrations (`registerSingletonAsync`, etc.) or instance-specific signaling instead. The global signal approach is less clear about what's being initialized and doesn't integrate well with dependency management. + +Note: The global `signalReady(null)` will throw an error if you have any async registrations or instances with `signalsReady: true` that haven't signaled yet. Instance-specific signaling works fine alongside async registrations. +::: + +## Best Practices + +::: details 1. Prefer registerSingletonAsync for App Initialization + +For services needed at app startup, use `registerSingletonAsync` (not lazy) so they start initializing immediately. + +<<< @/../code_samples/lib/get_it/async_objects_6e8c86b1.dart#example +::: + +::: details 2. Use dependsOn to Express Dependencies + +Let GetIt manage initialization order instead of manually orchestrating with `isReady()`. + +<<< @/../code_samples/lib/get_it/async_objects_a3cbd191.dart#example +::: + +::: details 3. Use FutureBuilder for Splash Screens + +Display a loading screen while services initialize. + +<<< @/../code_samples/lib/get_it/async_objects_d275974b.dart#example +::: + +::: details 4. Always Set Timeouts for allReady() + +Prevent your app from hanging indefinitely if initialization fails. + +<<< @/../code_samples/lib/get_it/async_objects_a6be16da.dart#example +::: + + +## Common Patterns + +### Pattern 1: Layered Initialization + +<<< @/../code_samples/lib/get_it/async_objects_65faea06.dart#example + +::: details Pattern 2: Conditional Initialization + +<<< @/../code_samples/lib/get_it/async_objects_80efa70c.dart#example +::: + +::: details Pattern 3: Progress Tracking + +<<< @/../code_samples/lib/get_it/async_objects_3be0569c.dart#example +::: + +::: details Pattern 4: Retry on Failure + +<<< @/../code_samples/lib/get_it/async_objects_b64e81ba.dart#example +::: + + +## Further Reading + +- [Detailed blog post on async factories and startup orchestration](https://blog.burkharts.net/lets-get-this-party-started-startup-orchestration-with-getit) +- [Scopes Documentation](/documentation/get_it/scopes) - Async initialization within scopes +- [Testing Documentation](/documentation/get_it/testing) - Mocking async services in tests \ No newline at end of file diff --git a/school_data_hub_flutter/lib/core/di/di_on_active_env.dart b/school_data_hub_flutter/lib/core/di/di_on_active_env.dart index c53f97b5..b8716f8c 100644 --- a/school_data_hub_flutter/lib/core/di/di_on_active_env.dart +++ b/school_data_hub_flutter/lib/core/di/di_on_active_env.dart @@ -7,11 +7,11 @@ import 'package:school_data_hub_flutter/core/session/serverpod_connectivity_moni import 'package:school_data_hub_flutter/features/app_main_navigation/domain/main_menu_bottom_nav_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _log = Logger('DiOnActiveEnv'); +final _log = Logger('[Init][OnActiveEnv]'); -class DiOnActiveEnv { +class InitOnActiveEnv { static Future registerManagers() async { - _log.info('[DI] Registering managers on active environment scope'); + _log.info('Registering managers on active environment scope'); di.registerSingletonWithDependencies(() { return HubAuthKeyManager( storageKeyForAuthKey: di().storageKeyForAuthKey, @@ -21,14 +21,11 @@ class DiOnActiveEnv { di.registerSingletonWithDependencies(() { final envManager = di(); final activeEnv = envManager.activeEnv; - - _log.info('[DI] Registering Client for environment:'); - _log.info('[DI] - Name: ${activeEnv?.serverName}'); - _log.info('[DI] - Server URL: ${activeEnv?.serverUrl}'); - _log.info('[DI] - Run Mode: ${activeEnv?.runMode.name}'); - final serverUrl = activeEnv!.serverUrl; - _log.info('[DI] Creating Client with server URL: $serverUrl'); + _log.info('Registering Client for environment:'); + _log.info( + ' - Name: [${activeEnv.serverName}] Server URL: [${activeEnv.serverUrl}] Run Mode: [${activeEnv.runMode.name}]', + ); return Client( serverUrl, @@ -47,7 +44,10 @@ class DiOnActiveEnv { // this will initialize the session manager and load the stored user info // it returns a bool await sessionManager.initialize(); - _log.info('SessionManager initialized'); + _log.info('HubSessionManager initialized'); + _log.warning( + '========================================================', + ); return sessionManager; }, dependsOn: [EnvManager, Client], diff --git a/school_data_hub_flutter/lib/core/di/init_manager.dart b/school_data_hub_flutter/lib/core/di/init_manager.dart index 9e268e78..8d319dc4 100644 --- a/school_data_hub_flutter/lib/core/di/init_manager.dart +++ b/school_data_hub_flutter/lib/core/di/init_manager.dart @@ -1,5 +1,8 @@ +import 'dart:convert'; + import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:logging/logging.dart'; +import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/core/di/di_on_active_env.dart'; import 'package:school_data_hub_flutter/core/di/di_on_user_auth.dart'; @@ -14,7 +17,7 @@ import 'package:watch_it/watch_it.dart'; enum DiScope { onActiveEnvScope, onLoggedInUserScope, onMatrixEnvScope } -final _log = Logger('DiManager'); +final _log = Logger('InitManager'); class InitManager { static final InitManager _instance = InitManager._internal(); @@ -62,17 +65,15 @@ class InitManager { _log.warning('[DI] Disposing [activeEnvScope]'); }, init: (getIt) async { - _log.warning( - '########################################################', - ); + _log.fine('========================================================'); _log.fine('[DI] Pushing Scope [activeEnvScope]'); _log.warning( - '########################################################', + '========================================================', ); - await DiOnActiveEnv.registerManagers(); + await InitOnActiveEnv.registerManagers(); _log.info( - '[Init Scope] Managers dependent on [activeEnvScope] are being initialized...', + ' Managers dependent on [activeEnvScope] are being initialized...', ); }, ); @@ -200,122 +201,256 @@ class InitManager { // } // } - static Future registerMatrixManagers( - MatrixCredentials? matrixCredentials, - ) async { + /// Reads matrix credentials from secure storage. + /// Returns null if credentials don't exist or are invalid. + static Future _readMatrixCredentialsFromStorage() async { try { - _log.info( - '[DI] registerMatrixManagers: checking if matrix scope exists...', + // Wait for EnvManager to be ready before accessing it + await di.isReady(); + final envManager = await di.getAsync(); + final secureStorage = HubSecureStorage(); + final storageKey = envManager.storageKeyForMatrixCredentials; + + _log.info('[DI] Reading matrix credentials from secure storage'); + final String? matrixStoredValues = await secureStorage.getString( + storageKey, ); - final hasScope = di.hasScope(DiScope.onMatrixEnvScope.name); - _log.info('[DI] registerMatrixManagers: hasScope = $hasScope'); - // Drop existing matrix scope if it exists - if (hasScope) { - _log.info( - '[DI] Dropping existing matrix scope before registering new one', + if (matrixStoredValues == null) { + _log.warning('[DI] No matrix credentials found in secure storage'); + return null; + } + + final credentials = MatrixCredentials.fromJson( + jsonDecode(matrixStoredValues), + ); + + _log.info('[DI] Matrix credentials successfully read from storage'); + return credentials; + } catch (e) { + _log.severe( + '[DI] Error reading matrix credentials from storage: $e', + StackTrace.current, + ); + return null; + } + } + + /// Creates the matrix scope and registers managers with the given credentials. + /// Returns the initialized MatrixPolicyManager instance. + static Future _createMatrixScopeAndRegisterManagers( + MatrixCredentials credentials, + ) async { + _log.info('[DI] Creating matrix scope and registering managers'); + + // Drop existing matrix scope if it exists + if (di.hasScope(DiScope.onMatrixEnvScope.name)) { + _log.info('[DI] Dropping existing matrix scope before creating new one'); + await di.dropScope(DiScope.onMatrixEnvScope.name); + await Future.delayed(const Duration(milliseconds: 50)); + } + + await di.pushNewScopeAsync( + scopeName: DiScope.onMatrixEnvScope.name, + dispose: () { + _log.warning( + '########################################################', ); - await di.dropScope(DiScope.onMatrixEnvScope.name); - // Add a small delay to ensure the scope is fully dropped - await Future.delayed(const Duration(milliseconds: 50)); - // Check if scope was actually dropped - final scopeStillExists = di.hasScope(DiScope.onMatrixEnvScope.name); - _log.info( - '[DI] registerMatrixManagers: scope still exists after drop: $scopeStillExists', + _log.severe('[DI] Disposing [matrixScope]'); + _log.warning( + '########################################################', + ); + }, + init: (getIt) async { + _log.warning( + '########################################################', ); - // If scope still exists, try to force drop it - if (scopeStillExists) { - _log.warning( - '[DI] Matrix scope still exists after drop, trying force drop...', + _log.fine('[DI] Pushing Scope [matrixScope]'); + _log.warning( + '########################################################', + ); + + // Register the manager inside the matrix scope + getIt.registerLazySingletonAsync(() async { + _log.info('[DI] Registering MatrixPolicyManager in matrix scope'); + + // Wait for HubSessionManager to be ready (we check this inside the factory + // since registerLazySingletonAsync doesn't support dependsOn) + await di.isReady(); + + final policyManager = await MatrixPolicyManager( + credentials.url, + credentials.policyToken, + credentials.matrixToken, + credentials.matrixAdmin, + credentials.encryptionKey, + credentials.encryptionIv, + ).init(); + + _log.info('[DI] Matrix managers initialized'); + + // Set the registration status to true after the manager is fully initialized + // Since we depend on HubSessionManager, it should be ready now + di().changeMatrixPolicyManagerRegistrationStatus( + true, ); + _log.info( + '[DI] Matrix registration status set to true after initialization', + ); + + return policyManager; + }); + + getIt.registerSingletonWithDependencies(() { + return MatrixPolicyFilterManager(getIt()); + }, dependsOn: [MatrixPolicyManager]); + }, + ); + + // Wait for the manager to be ready + await di.isReady(); + + // Get the manager instance from the matrix scope + // Use getAsync to avoid triggering the factory again + final manager = await di.getAsync(); + _log.info('[DI] MatrixPolicyManager retrieved from matrix scope'); + + di().showSnackBar( + NotificationType.success, + '[DI] Matrix-Räumeverwaltung initialisiert', + ); + + return manager; + } + + /// Registers matrix managers lazily. If credentials are passed, they are stored + /// in secure storage. The actual scope and managers are created on first access. + /// + /// [storageKey] is optional. If provided, it will be used instead of accessing + /// EnvManager from DI. This is useful when called during EnvManager initialization + /// to avoid deadlocks. + static Future registerMatrixManagers( + MatrixCredentials? matrixCredentials, { + String? storageKey, + }) async { + try { + _log.info('[DI] registerMatrixManagers: starting lazy registration'); + // Note: setIsMatrixSessionConfigured will be called when the manager is actually initialized + // If credentials are passed, store them in secure storage + if (matrixCredentials != null) { + _log.info('[DI] Storing matrix credentials in secure storage'); + final secureStorage = HubSecureStorage(); + + // Use provided storage key, or get it from EnvManager if available + String? keyToUse = storageKey; + if (keyToUse == null) { + // Only try to access EnvManager if storage key wasn't provided + // This avoids deadlocks when called during EnvManager initialization try { - await di.dropScope(DiScope.onMatrixEnvScope.name); - await Future.delayed(const Duration(milliseconds: 100)); - final scopeStillExistsAfterForce = di.hasScope( - DiScope.onMatrixEnvScope.name, - ); - _log.info( - '[DI] After force drop, scope still exists: $scopeStillExistsAfterForce', - ); + await di.isReady(); + final envManager = await di.getAsync(); + keyToUse = envManager.storageKeyForMatrixCredentials; } catch (e) { - _log.warning('[DI] Error during force drop: $e'); + _log.warning( + '[DI] Could not access EnvManager, credentials may not be stored: $e', + ); + // Continue without storing - credentials might already be in storage + keyToUse = null; } } - } - - di.pushNewScopeAsync( - scopeName: DiScope.onMatrixEnvScope.name, - dispose: () { - _log.warning( - '########################################################', - ); - _log.severe('[DI] Disposing [matrixScope]'); - _log.warning( - '########################################################', - ); - }, - init: (getIt) async { - _log.warning( - '########################################################', + if (keyToUse != null) { + await secureStorage.setString( + keyToUse, + jsonEncode(matrixCredentials.toJson()), ); + _log.info('[DI] Matrix credentials stored successfully'); - _log.fine('[DI] Pushing Scope [matrixScope]'); - _log.warning( - '########################################################', + // Mark that matrix session is configured immediately when credentials are available + // This ensures UI updates correctly even if the manager is initialized lazily + // Use .then() to avoid deadlock since HubSessionManager depends on EnvManager + if (di.isRegistered()) { + di.isReady().then((_) { + di().setIsMatrixSessionConfigured(true); + _log.info( + '[DI] Matrix session configured set to true (credentials available)', + ); + }); + } else { + _log.warning( + '[DI] HubSessionManager not registered yet, cannot set matrix session configured', + ); + } + } else { + _log.info( + '[DI] Skipping credential storage (storage key not available)', ); + } + } - di.registerSingletonAsync(() async { - _log.info('[DI] Registering MatrixPolicyManager'); + // Register the async singleton factory. + // Note: GetIt may call the factory during registration for validation, + // but with signalsReady: false, it won't block allReady(). + // The actual initialization only happens when code explicitly accesses it. + if (!di.isRegistered()) { + _log.info( + '[DI] Registering MatrixPolicyManager async singleton factory', + ); - final policyManager = await MatrixPolicyManager( - matrixCredentials!.url, - matrixCredentials.policyToken, - matrixCredentials.matrixToken, - matrixCredentials.matrixAdmin, - matrixCredentials.encryptionKey, - matrixCredentials.encryptionIv, - ).init(); + di.registerLazySingletonAsync(() async { + _log.info( + '[DI] MatrixPolicyManager factory called (parent scope) - initializing...', + ); - _log.info('[DI] Matrix managers initialized'); + // Wait for HubSessionManager to be ready + await di.isReady(); + + // Check if matrix scope already exists and manager is registered there + if (di.hasScope(DiScope.onMatrixEnvScope.name)) { + _log.info('[DI] Matrix scope already exists, checking for manager'); + try { + await di.isReady(); + final manager = await di.getAsync(); + _log.info( + '[DI] Found existing MatrixPolicyManager in matrix scope', + ); + return manager; + } catch (e) { + _log.warning( + '[DI] Matrix scope exists but manager not found, will create new one: $e', + ); + } + } - // Set the registration status to true after the manager is fully initialized - // Since we depend on HubSessionManager, it should be ready now - di().changeMatrixPolicyManagerRegistrationStatus( - true, - ); - _log.info( - '[DI] Matrix registration status set to true after initialization', + // Read credentials from storage + final credentials = await _readMatrixCredentialsFromStorage(); + if (credentials == null) { + throw Exception( + 'Matrix credentials not found in secure storage. ' + 'Please configure matrix environment first.', ); + } - return policyManager; - }, dependsOn: [HubSessionManager]); - - di.registerSingletonWithDependencies(() { - return MatrixPolicyFilterManager(di()); - }, dependsOn: [MatrixPolicyManager]); - }, - ); + // Create scope and register managers, get the manager instance + // This will register the manager inside the matrix scope + final manager = await _createMatrixScopeAndRegisterManagers( + credentials, + ); + _log.info('[DI] MatrixPolicyManager successfully initialized'); + return manager; + }); - di().showSnackBar( - NotificationType.success, - '[DI] Matrix-Räumeverwaltung initialisiert', - ); + _log.info('[DI] MatrixPolicyManager factory registered'); + } else { + _log.info('[DI] MatrixPolicyManager already registered, skipping'); + } } catch (e) { _log.severe( '[DI] Error registering matrix managers: $e', StackTrace.current, ); - // Try to clean up any partial registration - try { - if (di.hasScope(DiScope.onMatrixEnvScope.name)) { - await di.dropScope(DiScope.onMatrixEnvScope.name); - } - } catch (cleanupError) { - _log.warning('[DI] Error during cleanup: $cleanupError'); - } rethrow; } } diff --git a/school_data_hub_flutter/lib/core/env/env_manager.dart b/school_data_hub_flutter/lib/core/env/env_manager.dart index 4217d677..4b11175f 100644 --- a/school_data_hub_flutter/lib/core/env/env_manager.dart +++ b/school_data_hub_flutter/lib/core/env/env_manager.dart @@ -170,11 +170,16 @@ class EnvManager with ChangeNotifier { // Only register matrix managers if they're not already registered if (!di.hasScope(DiScope.onMatrixEnvScope.name)) { - await InitManager.registerMatrixManagers(matrixCredentials); + await InitManager.registerMatrixManagers( + matrixCredentials, + storageKey: storageKeyForMatrixCredentials, + ); } else { _log.info( '[DI] Matrix managers already registered, skipping registration', ); + // Ensure session configured flag is set even if skipping registration + di().setIsMatrixSessionConfigured(true); } } return; @@ -369,11 +374,16 @@ class EnvManager with ChangeNotifier { _log.info( '[DI] Registering matrix managers for environment ${_activeEnv!.serverName}', ); - await InitManager.registerMatrixManagers(matrixCredentials); + await InitManager.registerMatrixManagers( + matrixCredentials, + storageKey: storageKeyForMatrixCredentials, + ); } else { _log.info( '[DI] Matrix managers already registered, skipping registration', ); + // Ensure session configured flag is set even if skipping registration + di().setIsMatrixSessionConfigured(true); } } else { _log.info( diff --git a/school_data_hub_flutter/lib/core/session/hub_session_manager.dart b/school_data_hub_flutter/lib/core/session/hub_session_manager.dart index 2ac5a86c..89974804 100644 --- a/school_data_hub_flutter/lib/core/session/hub_session_manager.dart +++ b/school_data_hub_flutter/lib/core/session/hub_session_manager.dart @@ -33,6 +33,13 @@ class HubSessionManager with ChangeNotifier { String get _userInfoStorageKey => _envManager.storageKeyForUserInfo; bool _matrixPolicyManagerRegistrationStatus = false; + bool _isMatrixSessionConfigured = false; + bool get isMatrixSessionConfigured => _isMatrixSessionConfigured; + + void setIsMatrixSessionConfigured(bool isConfigured) { + _isMatrixSessionConfigured = isConfigured; + notifyListeners(); + } bool get matrixPolicyManagerRegistrationStatus => _matrixPolicyManagerRegistrationStatus; @@ -363,14 +370,18 @@ class HubSessionManager with ChangeNotifier { } } - // TODO URGENT DI REGISTRATION MATRIX COLD INIT void changeMatrixPolicyManagerRegistrationStatus(bool isRegistered) { if (_matrixPolicyManagerRegistrationStatus != isRegistered) { _log.info( 'MatrixPolicyManager registration status changing from $_matrixPolicyManagerRegistrationStatus to $isRegistered', ); _matrixPolicyManagerRegistrationStatus = isRegistered; - + _isMatrixSessionConfigured = isRegistered; + if (isRegistered) { + _log.info('Matrix session is configured'); + } else { + _log.info('Matrix session is not configured'); + } notifyListeners(); _log.info( diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart index 36e3119e..38497847 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart @@ -27,11 +27,9 @@ class PupilListButtons extends WatchingWidget { // Always call watch methods at the top level, not conditionally final isReady = watchPropertyValue((HubSessionManager x) => x.isReady); - final isTester = watchPropertyValue( - (HubSessionManager x) => x.user?.userFlags.isTester ?? false, - ); + final matrixSessionConfigured = watchPropertyValue( - (HubSessionManager x) => x.matrixPolicyManagerRegistrationStatus, + (HubSessionManager x) => x.isMatrixSessionConfigured, ); return Wrap( diff --git a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart index 7ad05879..daa51d91 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart @@ -76,7 +76,10 @@ class MatrixPolicyHelper { // is the passed credentials are null, we will use the stored ones final validCredentials = passedCredentials ?? storedCredentials; - await InitManager.registerMatrixManagers(validCredentials!); + await InitManager.registerMatrixManagers( + validCredentials!, + storageKey: _secureStorageKey, + ); return; } diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart index b05e21b1..483ee299 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_sliver_search_app_bar.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/filters/matrix_policy_filter_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_manager.dart'; -import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_user.dart'; import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_user_list_card.dart'; import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_user_list_searchbar.dart'; import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/widgets/matrix_users_list_view_bottom_navbar.dart'; @@ -16,16 +16,76 @@ class MatrixUsersListPage extends WatchingWidget { @override Widget build(BuildContext context) { - final _matrixPolicyManager = di(); - List matrixUsers = - watchValue((MatrixPolicyFilterManager x) => x.filteredMatrixUsers); + final notificationData = watchValue( + (NotificationService x) => x.notification, + ); + + return FutureBuilder( + future: di.getAsync(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Scaffold( + backgroundColor: AppColors.canvasColor, + appBar: const GenericAppBar( + iconData: Icons.chat_rounded, + title: 'Matrix-Konten', + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Bitte warten', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey[800], + ), + ), + const Gap(20), + if (notificationData.message.isNotEmpty) ...[ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + notificationData.message, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 16, color: Colors.grey[800]), + ), + ), + const Gap(20), + ], + const CircularProgressIndicator(), + ], + ), + ), + ); + } + + return _MatrixUsersListContent(matrixPolicyManager: snapshot.data!); + }, + ); + } +} + +class _MatrixUsersListContent extends WatchingWidget { + final MatrixPolicyManager matrixPolicyManager; + + const _MatrixUsersListContent({required this.matrixPolicyManager}); + + @override + Widget build(BuildContext context) { + final matrixUsers = watchValue( + (MatrixPolicyFilterManager x) => x.filteredMatrixUsers, + ); return Scaffold( backgroundColor: AppColors.canvasColor, appBar: const GenericAppBar( - iconData: Icons.chat_rounded, title: 'Matrix-Konten'), + iconData: Icons.chat_rounded, + title: 'Matrix-Konten', + ), body: RefreshIndicator( - onRefresh: () async => _matrixPolicyManager.fetchMatrixPolicy(), + onRefresh: () async => matrixPolicyManager.fetchMatrixPolicy(), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 800), @@ -33,8 +93,9 @@ class MatrixUsersListPage extends WatchingWidget { slivers: [ const SliverGap(5), GenericSliverSearchAppBar( - title: MatrixUsersListSearchBar(matrixUsers: matrixUsers), - height: 110), + title: MatrixUsersListSearchBar(matrixUsers: matrixUsers), + height: 110, + ), matrixUsers.isEmpty ? const SliverToBoxAdapter( child: Center( @@ -48,14 +109,12 @@ class MatrixUsersListPage extends WatchingWidget { ), ) : SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - // TODO: Implement this - return MatrixUsersListCard(matrixUsers[index]); - }, - childCount: matrixUsers - .length, // Adjust this based on your data - ), + delegate: SliverChildBuilderDelegate(( + BuildContext context, + int index, + ) { + return MatrixUsersListCard(matrixUsers[index]); + }, childCount: matrixUsers.length), ), ], ), diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart index fae8776a..453961fc 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart @@ -4,7 +4,6 @@ import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/enums.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog_dropdown.dart'; import 'package:watch_it/watch_it.dart'; @@ -17,7 +16,6 @@ Future languageDialog( PupilProxy pupil, CommunicationSubject subject, ) async { - final _pupilManager = di(); final _hubSessionManager = di(); CommunicationSkills? languageValue; switch (subject) { @@ -125,30 +123,28 @@ Future languageDialog( ); break; case CommunicationSubject.tutor1: - final tutorInfo = - pupil.tutorInfo != null - ? pupil.tutorInfo!.copyWith( - communicationTutor1: languageValue, - ) - : TutorInfo( - communicationTutor1: languageValue, - createdBy: _hubSessionManager.userName!, - ); + final tutorInfo = pupil.tutorInfo != null + ? pupil.tutorInfo!.copyWith( + communicationTutor1: languageValue, + ) + : TutorInfo( + communicationTutor1: languageValue, + createdBy: _hubSessionManager.userName!, + ); PupilMutator().updateTutorInfo( pupilId: pupil.pupilId, tutorInfo: tutorInfo, ); break; case CommunicationSubject.tutor2: - final tutorInfo = - pupil.tutorInfo != null - ? pupil.tutorInfo!.copyWith( - communicationTutor2: languageValue, - ) - : TutorInfo( - communicationTutor2: languageValue, - createdBy: _hubSessionManager.userName!, - ); + final tutorInfo = pupil.tutorInfo != null + ? pupil.tutorInfo!.copyWith( + communicationTutor2: languageValue, + ) + : TutorInfo( + communicationTutor2: languageValue, + createdBy: _hubSessionManager.userName!, + ); PupilMutator().updateTutorInfo( pupilId: pupil.pupilId, tutorInfo: tutorInfo, diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart index 33c40d3f..975841f6 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/pupil_profile_communication_content.dart @@ -8,7 +8,6 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/information_dialo import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/enums.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/communication_values.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/communication_content/dialogs/language_dialog.dart'; @@ -21,7 +20,6 @@ class PupilProfileCommunicationContent extends WatchingWidget { @override Widget build(BuildContext context) { - final _pupilManager = di(); final _hubSessionManager = di(); final communicationPupil = watchPropertyValue( (m) => m.communicationPupil, diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart index ea933cf2..5c4c43b1 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/support_level_history_expansion_tile.dart @@ -8,7 +8,6 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dial import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/learning_support/presentation/widgets/dialogs/support_level_dialog.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; import 'package:watch_it/watch_it.dart'; @@ -24,7 +23,6 @@ class SupportLevelHistoryExpansionTile extends StatefulWidget { class _SupportLevelHistoryExpansionTileState extends State { HubSessionManager get _hubSessionManager => di(); - PupilManager get _pupilManager => di(); late ExpansibleController _tileController; @override diff --git a/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart b/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart index 750425e8..2c44f85d 100644 --- a/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart +++ b/school_data_hub_flutter/lib/features/user/presentation/select_users/select_users_page.dart @@ -39,15 +39,14 @@ class _SelectUsersPageState extends State { void initState() { if (widget.authorizedUsers != null && widget.authorizedUsers!.isNotEmpty) { final authorizedUserNames = widget.authorizedUsers!; - final authorizedUsers = - widget.selectableUsers - .where( - (user) => - user.userInfo?.userName != null && - authorizedUserNames.contains(user.userInfo!.userName!), - ) - .map((user) => user.id!) - .toList(); + final authorizedUsers = widget.selectableUsers + .where( + (user) => + user.userInfo?.userName != null && + authorizedUserNames.contains(user.userInfo!.userName!), + ) + .map((user) => user.id!) + .toList(); if (authorizedUsers.isNotEmpty) { setState(() { isSelectMode = true; @@ -93,11 +92,10 @@ class _SelectUsersPageState extends State { isSelectAllMode = !isSelectAllMode; if (isSelectAllMode) { isSelectMode = true; - selectedUserIds = - selectableUsers - .where((user) => user.id != null) - .map((user) => user.id!) - .toList(); + selectedUserIds = selectableUsers + .where((user) => user.id != null) + .map((user) => user.id!) + .toList(); } else { isSelectMode = false; selectedUserIds.clear(); @@ -121,18 +119,16 @@ class _SelectUsersPageState extends State { final List allUsers = watchValue((UserManager x) => x.users); // Filter to only include selectable users - final List selectableUsers = - widget.selectableUsers != null - ? allUsers - .where( - (user) => widget.selectableUsers!.any( - (selectableUser) => - selectableUser.id != null && - selectableUser.id == user.id, - ), - ) - .toList() - : allUsers; + final List selectableUsers = widget.selectableUsers.isNotEmpty + ? allUsers + .where( + (user) => widget.selectableUsers.any( + (selectableUser) => + selectableUser.id != null && selectableUser.id == user.id, + ), + ) + .toList() + : allUsers; return Scaffold( backgroundColor: AppColors.canvasColor, @@ -157,15 +153,13 @@ class _SelectUsersPageState extends State { ), GenericSliverListWithEmptyListCheck( items: selectableUsers, - itemBuilder: - (_, user) => SelectUsersListCard( - isSelectMode: isSelectMode, - isSelected: - user.id != null && - selectedUserIds.contains(user.id!), - passedUser: user, - onCardPress: onCardPress, - ), + itemBuilder: (_, user) => SelectUsersListCard( + isSelectMode: isSelectMode, + isSelected: + user.id != null && selectedUserIds.contains(user.id!), + passedUser: user, + onCardPress: onCardPress, + ), ), ], ), diff --git a/school_data_hub_flutter/lib/features/workbooks/presentation/new_workbook_page/new_workbook_page.dart b/school_data_hub_flutter/lib/features/workbooks/presentation/new_workbook_page/new_workbook_page.dart index 0429a6e5..801ee9ee 100644 --- a/school_data_hub_flutter/lib/features/workbooks/presentation/new_workbook_page/new_workbook_page.dart +++ b/school_data_hub_flutter/lib/features/workbooks/presentation/new_workbook_page/new_workbook_page.dart @@ -15,37 +15,41 @@ class NewWorkbookPage extends WatchingWidget { final bool isEdit; final Workbook? workbook; - const NewWorkbookPage( - {required this.isEdit, - this.name, - required this.isbn, - this.subject, - this.level, - this.amount, - this.workbook, - super.key}); + const NewWorkbookPage({ + required this.isEdit, + this.name, + required this.isbn, + this.subject, + this.level, + this.amount, + this.workbook, + super.key, + }); @override Widget build(BuildContext context) { - final workbookNameTextFieldController = - createOnce(() => TextEditingController()); - final subjectTextFieldController = - createOnce(() => TextEditingController()); + final workbookNameTextFieldController = createOnce( + () => TextEditingController(), + ); + final subjectTextFieldController = createOnce( + () => TextEditingController(), + ); final levelTextFieldController = createOnce(() => TextEditingController()); final amountTextFieldController = createOnce(() => TextEditingController()); callOnce((context) async { await di().fetchWorkbookByIsbn(isbn); - final workbook = di() - .workbooks - .value - .firstWhere((element) => element.isbn == isbn); + // final workbook = di() + // .workbooks + // .value + // .firstWhere((element) => element.isbn == isbn); if (isEdit) { workbookNameTextFieldController.text = name ?? ''; subjectTextFieldController.text = subject ?? ''; levelTextFieldController.text = level ?? ''; - amountTextFieldController.text = - amount != null ? amount!.toString() : ''; + amountTextFieldController.text = amount != null + ? amount!.toString() + : ''; } }); Future updateWorkbook() async { @@ -117,33 +121,44 @@ class NewWorkbookPage extends WatchingWidget { ], ), TextField( - style: const TextStyle( - color: Colors.black, fontWeight: FontWeight.bold), - minLines: 2, - maxLines: 2, - controller: workbookNameTextFieldController, - decoration: AppStyles.textFieldDecoration( - labelText: 'Name des Heftes')), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + minLines: 2, + maxLines: 2, + controller: workbookNameTextFieldController, + decoration: AppStyles.textFieldDecoration( + labelText: 'Name des Heftes', + ), + ), const Gap(20), TextField( style: const TextStyle( - color: Colors.black, fontWeight: FontWeight.bold), + color: Colors.black, + fontWeight: FontWeight.bold, + ), minLines: 1, maxLines: 1, controller: subjectTextFieldController, - decoration: - AppStyles.textFieldDecoration(labelText: 'Fach'), + decoration: AppStyles.textFieldDecoration( + labelText: 'Fach', + ), ), const Gap(20), TextField( - minLines: 1, - maxLines: 1, - controller: levelTextFieldController, - style: const TextStyle( - color: Colors.black, fontWeight: FontWeight.bold), - decoration: AppStyles.textFieldDecoration( - labelText: 'Kompetenzstufe')), + minLines: 1, + maxLines: 1, + controller: levelTextFieldController, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + decoration: AppStyles.textFieldDecoration( + labelText: 'Kompetenzstufe', + ), + ), // const Gap(20), // TextField( // style: const TextStyle( @@ -163,7 +178,7 @@ class NewWorkbookPage extends WatchingWidget { style: AppStyles.buttonTextStyle, ), ), - const Gap(15) + const Gap(15), ], ElevatedButton( style: AppStyles.successButtonStyle, diff --git a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart index c4d63ed8..a1a7a4b9 100644 --- a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart +++ b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart @@ -90,30 +90,25 @@ class PupilWorkbookCard extends WatchingWidget { // await di() // .postWorkbookFile(file, workbook.isbn); }, - onLongPress: (workbook.imageUrl == null) - ? () {} - : () async { - if (workbook.imageUrl == null) { - return; - } - final bool? result = await confirmationDialog( - context: context, - title: 'Bild löschen', - message: 'Bild löschen?', - ); - if (result != true) return; - // TODO: Uncomment when API is ready - di().showSnackBar( - NotificationType.warning, - 'Not implemented yet', - ); - // await di() - // .deleteAuthorizationFile( - // pupil.internalId, - // authorizationId, - // pupilAuthorization.fileId!, - // ); - }, + onLongPress: () async { + final bool? result = await confirmationDialog( + context: context, + title: 'Bild löschen', + message: 'Bild löschen?', + ); + if (result != true) return; + // TODO: Uncomment when API is ready + di().showSnackBar( + NotificationType.warning, + 'Not implemented yet', + ); + // await di() + // .deleteAuthorizationFile( + // pupil.internalId, + // authorizationId, + // pupilAuthorization.fileId!, + // ); + }, child: // workbook.imageUrl != null // ? Provider.value( diff --git a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart index 97879f61..2495d499 100644 --- a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart +++ b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart @@ -76,27 +76,22 @@ class WorkbookCard extends WatchingWidget { // await di() // .postWorkbookFile(file, workbook.isbn); }, - onLongPress: (workbook.imageUrl == null) - ? () {} - : () async { - if (workbook.imageUrl == null) { - return; - } - final bool? result = await confirmationDialog( - context: context, - title: 'Bild löschen', - message: 'Bild löschen?', - ); - if (result != true) return; - // TODO: implement when ready - di().showSnackBar( - NotificationType.warning, - 'Not implemented yet', - ); + onLongPress: () async { + final bool? result = await confirmationDialog( + context: context, + title: 'Bild löschen', + message: 'Bild löschen?', + ); + if (result != true) return; + // TODO: implement when ready + di().showSnackBar( + NotificationType.warning, + 'Not implemented yet', + ); - // await di() - // .deleteWorkbookFile(workbook.isbn); - }, + // await di() + // .deleteWorkbookFile(workbook.isbn); + }, child: workbook.imageUrl != null ? UnencryptedImageInCard( cacheKey: workbook.isbn.toString(), From 3a059a89ff2f2009ee7028c2647c035e6872e720 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sat, 29 Nov 2025 17:35:13 +0100 Subject: [PATCH 16/52] [APP] Fix init flows and structure log statements --- .../app_utils/logger/logrecord_formatter.dart | 29 +- .../lib/core/env/env_manager.dart | 26 +- .../lib/core/{di => init}/init_manager.dart | 348 +++++++----------- .../init_on_active_env.dart} | 12 +- .../init_on_user_auth.dart} | 45 +-- .../lib/core/session/hub_session_manager.dart | 8 +- .../dialogs/change_env_dialog.dart | 2 +- .../widgets/settings_session_section.dart | 2 +- .../matrix/domain/matrix_policy_helper.dart | 14 +- .../matrix/domain/matrix_policy_manager.dart | 12 +- .../matrix/services/api/api_client.dart | 10 +- .../pupil/data/pupil_data_api_service.dart | 5 - .../pupil/domain/pupil_identity_manager.dart | 2 +- .../filters/school_list_filter_manager.dart | 37 +- .../school_list_pupil_entries_proxy.dart | 11 +- .../domain/school_list_manager.dart | 25 +- school_data_hub_flutter/lib/main.dart | 4 +- 17 files changed, 232 insertions(+), 360 deletions(-) rename school_data_hub_flutter/lib/core/{di => init}/init_manager.dart (53%) rename school_data_hub_flutter/lib/core/{di/di_on_active_env.dart => init/init_on_active_env.dart} (85%) rename school_data_hub_flutter/lib/core/{di/di_on_user_auth.dart => init/init_on_user_auth.dart} (85%) diff --git a/school_data_hub_flutter/lib/app_utils/logger/logrecord_formatter.dart b/school_data_hub_flutter/lib/app_utils/logger/logrecord_formatter.dart index 9373b3ca..0c35db92 100644 --- a/school_data_hub_flutter/lib/app_utils/logger/logrecord_formatter.dart +++ b/school_data_hub_flutter/lib/app_utils/logger/logrecord_formatter.dart @@ -26,7 +26,7 @@ class BlockFormatter extends LogRecordFormatter { BlockFormatter._(this.block); BlockFormatter.formatRecord(String Function(LogRecord rec) formatter) - : this._((rec, sb) => sb.write(formatter(rec))); + : this._((rec, sb) => sb.write(formatter(rec))); final void Function(LogRecord rec, StringBuffer sb) block; @@ -45,7 +45,7 @@ typedef CausedByInfoFetcher = CausedByInfo? Function(Object? error); class DefaultLogRecordFormatter extends LogRecordFormatter { const DefaultLogRecordFormatter({this.prefix}); DefaultLogRecordFormatter.withIsolatePrefix() - : this(prefix: '[${Isolate.current.debugName ?? 'unnamed'}] '); + : this(prefix: '[${Isolate.current.debugName ?? 'unnamed'}] '); final String? prefix; @@ -58,9 +58,11 @@ class DefaultLogRecordFormatter extends LogRecordFormatter { @override StringBuffer formatToStringBuffer(LogRecord rec, StringBuffer sb) { - sb.write('${prefix ?? ''}[${rec.loggerName}][${rec.level.name}]: ' - // '${prefix ?? ''}${rec.time} - [${rec.loggerName}] [${rec.level.name}]: ' - '${rec.message}'); + sb.write( + '${prefix ?? ''}[${rec.loggerName}]: ' + // '${prefix ?? ''}${rec.time} - [${rec.loggerName}] [${rec.level.name}]: ' + '${rec.message}', + ); void formatErrorAndStackTrace(final Object? error, StackTrace? stackTrace) { if (error != null) { @@ -93,16 +95,19 @@ class DefaultLogRecordFormatter extends LogRecordFormatter { /// dart:io logger which adds ansi escape characters to set the color /// of the output depending on log level. class ColorFormatter extends LogRecordFormatter { - const ColorFormatter( - [this.wrappedFormatter = const DefaultLogRecordFormatter()]); + const ColorFormatter([ + this.wrappedFormatter = const DefaultLogRecordFormatter(), + ]); final LogRecordFormatter wrappedFormatter; static final Map _colorCache = {}; @override StringBuffer formatToStringBuffer(LogRecord rec, StringBuffer sb) { - final color = - _colorCache.putIfAbsent(rec.level, () => _colorForLevel(rec.level)); + final color = _colorCache.putIfAbsent( + rec.level, + () => _colorForLevel(rec.level), + ); if (color != null) { sb.write(color.escape); wrappedFormatter.formatToStringBuffer(rec, sb); @@ -135,8 +140,10 @@ class _AnsiCombination { _AnsiCombination._(this.escape, this.resetEscape); _AnsiCombination.combine(List codes) - : this._(codes.map((code) => code.escape).join(), - codes.map((code) => code.reset?.escape).join()); + : this._( + codes.map((code) => code.escape).join(), + codes.map((code) => code.reset?.escape).join(), + ); final String escape; final String resetEscape; diff --git a/school_data_hub_flutter/lib/core/env/env_manager.dart b/school_data_hub_flutter/lib/core/env/env_manager.dart index 4b11175f..a537ce80 100644 --- a/school_data_hub_flutter/lib/core/env/env_manager.dart +++ b/school_data_hub_flutter/lib/core/env/env_manager.dart @@ -6,12 +6,11 @@ import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; -import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/models/enums.dart'; import 'package:school_data_hub_flutter/core/env/models/env.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:school_data_hub_flutter/core/models/populated_server_session_data.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; -import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_credentials.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; import 'package:watch_it/watch_it.dart'; @@ -161,19 +160,9 @@ class EnvManager with ChangeNotifier { await InitManager.pushActiveEnvScopeAndRegisterDependentManagers(); if (await HubSecureStorage().containsKey(storageKeyForMatrixCredentials)) { - final matrixCredentialsJson = await HubSecureStorage().getString( - storageKeyForMatrixCredentials, - ); - final matrixCredentials = MatrixCredentials.fromJson( - json.decode(matrixCredentialsJson!) as Map, - ); - // Only register matrix managers if they're not already registered if (!di.hasScope(DiScope.onMatrixEnvScope.name)) { - await InitManager.registerMatrixManagers( - matrixCredentials, - storageKey: storageKeyForMatrixCredentials, - ); + await InitManager.registerMatrixManagers(); } else { _log.info( '[DI] Matrix managers already registered, skipping registration', @@ -357,12 +346,6 @@ class EnvManager with ChangeNotifier { _log.info( '[DI] Found matrix credentials for environment ${_activeEnv!.serverName}', ); - final matrixCredentialsJson = await HubSecureStorage().getString( - storageKeyForMatrixCredentials, - ); - final matrixCredentials = MatrixCredentials.fromJson( - json.decode(matrixCredentialsJson!) as Map, - ); // Only register matrix managers if they're not already registered final hasMatrixScope = di.hasScope(DiScope.onMatrixEnvScope.name); @@ -374,10 +357,7 @@ class EnvManager with ChangeNotifier { _log.info( '[DI] Registering matrix managers for environment ${_activeEnv!.serverName}', ); - await InitManager.registerMatrixManagers( - matrixCredentials, - storageKey: storageKeyForMatrixCredentials, - ); + await InitManager.registerMatrixManagers(); } else { _log.info( '[DI] Matrix managers already registered, skipping registration', diff --git a/school_data_hub_flutter/lib/core/di/init_manager.dart b/school_data_hub_flutter/lib/core/init/init_manager.dart similarity index 53% rename from school_data_hub_flutter/lib/core/di/init_manager.dart rename to school_data_hub_flutter/lib/core/init/init_manager.dart index 8d319dc4..ff74379b 100644 --- a/school_data_hub_flutter/lib/core/di/init_manager.dart +++ b/school_data_hub_flutter/lib/core/init/init_manager.dart @@ -4,9 +4,9 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; -import 'package:school_data_hub_flutter/core/di/di_on_active_env.dart'; -import 'package:school_data_hub_flutter/core/di/di_on_user_auth.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/core/init/init_on_active_env.dart'; +import 'package:school_data_hub_flutter/core/init/init_on_user_auth.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/core/session/serverpod_connectivity_monitor.dart'; import 'package:school_data_hub_flutter/core/updater/shorebird_update_manager.dart'; @@ -17,7 +17,7 @@ import 'package:watch_it/watch_it.dart'; enum DiScope { onActiveEnvScope, onLoggedInUserScope, onMatrixEnvScope } -final _log = Logger('InitManager'); +final _log = Logger('[Init][Manager]'); class InitManager { static final InitManager _instance = InitManager._internal(); @@ -62,13 +62,15 @@ class InitManager { await di.pushNewScopeAsync( scopeName: DiScope.onActiveEnvScope.name, dispose: () { - _log.warning('[DI] Disposing [activeEnvScope]'); + _log.info('Dropping scope[activeEnvScope]'); + _log.warning( + '======================================================== [SCOPE] [activeEnvScope]', + ); }, init: (getIt) async { - _log.fine('========================================================'); - _log.fine('[DI] Pushing Scope [activeEnvScope]'); - _log.warning( - '========================================================', + _log.info('Pushing scope [activeEnvScope]'); + _log.fine( + '======================================================== [SCOPE] [activeEnvScope]', ); await InitOnActiveEnv.registerManagers(); @@ -82,157 +84,82 @@ class InitManager { /// These managers are initialized after the session manager authenticates /// the session. it is called in the [HubSessionManager] class after the session is authenticated. static Future registerManagersDependingOnAuthedSession() async { - _log.info('[DI] Registering managers depending on session'); + _log.info('Registering managers depending on session'); if (di.hasScope(DiScope.onLoggedInUserScope.name)) { - _log.info( - '[DI] [loggedInUserScope] already exists, skipping registration', - ); + _log.info('[loggedInUserScope] already exists, skipping registration'); return; } await di.pushNewScopeAsync( scopeName: DiScope.onLoggedInUserScope.name, dispose: () { + _log.info('Dropping scope [loggedInUserScope]'); _log.warning( - '########################################################', - ); - - _log.severe('[DI] Disposing Scope [loggedInUserScope]'); - _log.warning( - '########################################################', + '======================================================== [SCOPE] [loggedInUserScope]', ); }, init: (getIt) async { - _log.warning( - '########################################################', + _log.info('Pushing scope [loggedInUserScope]'); + _log.fine( + '======================================================== [SCOPE] [loggedInUserScope]', ); - _log.fine('[DI] Pushing Scope [loggedInUserScope]'); - _log.warning( - '########################################################', - ); - - await DiInitOnUserAuth.registerManagers(); + await InitOnUserAuth.registerManagers(); }, ); - - _log.info( - '[Init] Managers depending on authentication are being initialized...', - ); } static Future dropOnLoggedInUserScope() async { if (di.hasScope(DiScope.onLoggedInUserScope.name)) { - _log.severe( - '[DI] Unregistering managers depending on session - dropping [loggedInUserScope]', - ); + _log.info('Dropping scope [loggedInUserScope]...'); await di.dropScope( DiScope.onLoggedInUserScope.name, ); // This will dispose the 'logged_in_user_scope' - _log.warning('########################################################'); - - _log.info('[DI] [loggedInUserScope] dropped successfully'); - _log.warning('########################################################'); + _log.info('[loggedInUserScope] dropped successfully'); + _log.warning( + '======================================================== [SCOPE] [loggedInUserScope]', + ); } else { _log.severe( - '[DI] [loggedInUserScope] does not exist, skipping drop operation', + '[loggedInUserScope] does not exist, skipping drop operation', + StackTrace.current, + ); + _log.severe( + '======================================================== [SCOPE] [loggedInUserScope]', ); } } static Future dropOldActiveEnvAndRelatedScopes() async { - _log.severe('[DI] Dropping old active environment scope'); - _log.warning('########################################################'); + _log.info('Dropping old [activeEnv] and related scopes...'); if (di.hasScope(DiScope.onLoggedInUserScope.name)) { - _log.severe('[DI] Dropping [loggedInUserScope] ...'); - await di.dropScope(DiScope.onLoggedInUserScope.name); + _log.info('[loggedInUserScope] dropped successfully'); + _log.warning( + '======================================================== [SCOPE] [loggedInUserScope]', + ); } if (di.hasScope(DiScope.onActiveEnvScope.name)) { - _log.severe('[DI] [activeEnvScope] exists, dropping it...'); - _log.warning('########################################################'); await di.dropScope(DiScope.onActiveEnvScope.name); + _log.info('[activeEnvScope] dropped successfully'); + _log.warning( + '======================================================== [SCOPE] [activeEnvScope]', + ); } // Also drop matrix scope if it exists (it's environment-dependent) if (di.hasScope(DiScope.onMatrixEnvScope.name)) { - _log.severe('[DI] dropping matrix scope...'); - _log.warning('########################################################'); + _log.info('Matrix scope exists, dropping it...'); + _log.warning('========================================================'); await dropMatrixScope(); - } - } - - // static Future cleanupAllEnvironmentManagers() async { - // _log.warning('[DI]cleanupAllEnvironmentManagers: starting...'); - // try { - // _log.warning('[DI]cleanupAllEnvironmentManagers: dropping scopes...'); - // if (di.hasScope(DiScope.onLoggedInUserScope.name)) { - // _log.warning( - // '[DI] cleanupAllEnvironmentManagers: [loggedInUserScope] exists, dropping it ...', - // ); - // await di.dropScope(DiScope.onLoggedInUserScope.name); - // } - - // if (di.hasScope(DiScope.onActiveEnvScope.name)) { - // await di.dropScope(DiScope.onActiveEnvScope.name); - // _log.warning('[DI] dropped [activeEnvScope]'); - // } else { - // _log.info( - // '[DI] [activeEnvScope] does not exist, skipping drop operation', - // ); - // } - - // // Also drop matrix scope if it exists - // _log.warning( - // '[DI] cleanupAllEnvironmentManagers: dropping matrix scope...', - // ); - // await dropMatrixScope(); - // _log.warning('[DI]cleanupAllEnvironmentManagers: completed successfully'); - // } catch (e) { - // _log.severe( - // '[DI]cleanupAllEnvironmentManagers: error occurred: $e', - // StackTrace.current, - // ); - // rethrow; - // } - // } - - /// Reads matrix credentials from secure storage. - /// Returns null if credentials don't exist or are invalid. - static Future _readMatrixCredentialsFromStorage() async { - try { - // Wait for EnvManager to be ready before accessing it - await di.isReady(); - final envManager = await di.getAsync(); - final secureStorage = HubSecureStorage(); - final storageKey = envManager.storageKeyForMatrixCredentials; - - _log.info('[DI] Reading matrix credentials from secure storage'); - final String? matrixStoredValues = await secureStorage.getString( - storageKey, - ); - - if (matrixStoredValues == null) { - _log.warning('[DI] No matrix credentials found in secure storage'); - return null; - } - - final credentials = MatrixCredentials.fromJson( - jsonDecode(matrixStoredValues), - ); - - _log.info('[DI] Matrix credentials successfully read from storage'); - return credentials; - } catch (e) { - _log.severe( - '[DI] Error reading matrix credentials from storage: $e', - StackTrace.current, + _log.info('[matrixScope] dropped successfully'); + _log.warning( + '======================================================== [SCOPE] [matrixScope]', ); - return null; } } @@ -241,11 +168,11 @@ class InitManager { static Future _createMatrixScopeAndRegisterManagers( MatrixCredentials credentials, ) async { - _log.info('[DI] Creating matrix scope and registering managers'); + _log.info('Creating matrix scope and registering managers'); // Drop existing matrix scope if it exists if (di.hasScope(DiScope.onMatrixEnvScope.name)) { - _log.info('[DI] Dropping existing matrix scope before creating new one'); + _log.info('Dropping existing matrix scope before creating new one'); await di.dropScope(DiScope.onMatrixEnvScope.name); await Future.delayed(const Duration(milliseconds: 50)); } @@ -253,28 +180,20 @@ class InitManager { await di.pushNewScopeAsync( scopeName: DiScope.onMatrixEnvScope.name, dispose: () { + _log.fine('Dropping scope [matrixScope]'); _log.warning( - '########################################################', - ); - - _log.severe('[DI] Disposing [matrixScope]'); - _log.warning( - '########################################################', + '======================================================== [SCOPE] [matrixScope]', ); }, init: (getIt) async { - _log.warning( - '########################################################', - ); - - _log.fine('[DI] Pushing Scope [matrixScope]'); - _log.warning( - '########################################################', + _log.info('Pushing scope [matrixScope]'); + _log.fine( + '======================================================== [SCOPE] [matrixScope]', ); // Register the manager inside the matrix scope getIt.registerLazySingletonAsync(() async { - _log.info('[DI] Registering MatrixPolicyManager in matrix scope'); + _log.info('Registering MatrixPolicyManager in matrix scope'); // Wait for HubSessionManager to be ready (we check this inside the factory // since registerLazySingletonAsync doesn't support dependsOn) @@ -289,7 +208,7 @@ class InitManager { credentials.encryptionIv, ).init(); - _log.info('[DI] Matrix managers initialized'); + _log.info('Matrix managers initialized'); // Set the registration status to true after the manager is fully initialized // Since we depend on HubSessionManager, it should be ready now @@ -297,7 +216,7 @@ class InitManager { true, ); _log.info( - '[DI] Matrix registration status set to true after initialization', + 'Matrix registration status set to true after initialization', ); return policyManager; @@ -315,11 +234,11 @@ class InitManager { // Get the manager instance from the matrix scope // Use getAsync to avoid triggering the factory again final manager = await di.getAsync(); - _log.info('[DI] MatrixPolicyManager retrieved from matrix scope'); + _log.info('MatrixPolicyManager retrieved from matrix scope'); di().showSnackBar( NotificationType.success, - '[DI] Matrix-Räumeverwaltung initialisiert', + 'Matrix-Räumeverwaltung initialisiert', ); return manager; @@ -331,77 +250,36 @@ class InitManager { /// [storageKey] is optional. If provided, it will be used instead of accessing /// EnvManager from DI. This is useful when called during EnvManager initialization /// to avoid deadlocks. - static Future registerMatrixManagers( - MatrixCredentials? matrixCredentials, { - String? storageKey, - }) async { + static Future registerMatrixManagers() async { try { - _log.info('[DI] registerMatrixManagers: starting lazy registration'); + _log.info('RegisterMatrixManagers: starting lazy registration'); // Note: setIsMatrixSessionConfigured will be called when the manager is actually initialized - // If credentials are passed, store them in secure storage - if (matrixCredentials != null) { - _log.info('[DI] Storing matrix credentials in secure storage'); - final secureStorage = HubSecureStorage(); - - // Use provided storage key, or get it from EnvManager if available - String? keyToUse = storageKey; - if (keyToUse == null) { - // Only try to access EnvManager if storage key wasn't provided - // This avoids deadlocks when called during EnvManager initialization - try { - await di.isReady(); - final envManager = await di.getAsync(); - keyToUse = envManager.storageKeyForMatrixCredentials; - } catch (e) { - _log.warning( - '[DI] Could not access EnvManager, credentials may not be stored: $e', - ); - // Continue without storing - credentials might already be in storage - keyToUse = null; - } - } - - if (keyToUse != null) { - await secureStorage.setString( - keyToUse, - jsonEncode(matrixCredentials.toJson()), - ); - _log.info('[DI] Matrix credentials stored successfully'); - // Mark that matrix session is configured immediately when credentials are available - // This ensures UI updates correctly even if the manager is initialized lazily - // Use .then() to avoid deadlock since HubSessionManager depends on EnvManager - if (di.isRegistered()) { - di.isReady().then((_) { - di().setIsMatrixSessionConfigured(true); - _log.info( - '[DI] Matrix session configured set to true (credentials available)', - ); - }); - } else { - _log.warning( - '[DI] HubSessionManager not registered yet, cannot set matrix session configured', - ); - } - } else { + // Mark that matrix session is configured immediately + // This ensures UI updates correctly even if the manager is initialized lazily + // Use .then() to avoid deadlock since HubSessionManager depends on EnvManager + if (di.isRegistered()) { + di.isReady().then((_) { + di().setIsMatrixSessionConfigured(true); _log.info( - '[DI] Skipping credential storage (storage key not available)', + 'Matrix session configured set to true (credentials assumed available)', ); - } + }); + } else { + _log.warning( + 'HubSessionManager not registered yet, cannot set matrix session configured', + ); } - // Register the async singleton factory. // Note: GetIt may call the factory during registration for validation, // but with signalsReady: false, it won't block allReady(). // The actual initialization only happens when code explicitly accesses it. if (!di.isRegistered()) { - _log.info( - '[DI] Registering MatrixPolicyManager async singleton factory', - ); + _log.info('Registering MatrixPolicyManager async singleton lazily'); di.registerLazySingletonAsync(() async { _log.info( - '[DI] MatrixPolicyManager factory called (parent scope) - initializing...', + 'MatrixPolicyManager called (parent scope) - initializing...', ); // Wait for HubSessionManager to be ready @@ -409,17 +287,15 @@ class InitManager { // Check if matrix scope already exists and manager is registered there if (di.hasScope(DiScope.onMatrixEnvScope.name)) { - _log.info('[DI] Matrix scope already exists, checking for manager'); + _log.info('Matrix scope already exists, checking for manager'); try { await di.isReady(); final manager = await di.getAsync(); - _log.info( - '[DI] Found existing MatrixPolicyManager in matrix scope', - ); + _log.info('Found existing MatrixPolicyManager in matrix scope'); return manager; } catch (e) { _log.warning( - '[DI] Matrix scope exists but manager not found, will create new one: $e', + 'Matrix scope exists but manager not found, will create new one: $e', ); } } @@ -438,51 +314,75 @@ class InitManager { final manager = await _createMatrixScopeAndRegisterManagers( credentials, ); - _log.info('[DI] MatrixPolicyManager successfully initialized'); + _log.info('MatrixPolicyManager successfully initialized'); return manager; }); - _log.info('[DI] MatrixPolicyManager factory registered'); + _log.info('MatrixPolicyManager factory registered'); } else { - _log.info('[DI] MatrixPolicyManager already registered, skipping'); + _log.info('MatrixPolicyManager already registered, skipping'); } + } catch (e) { + _log.severe('Error registering matrix managers: $e', StackTrace.current); + rethrow; + } + } + + /// Reads matrix credentials from secure storage. + /// Returns null if credentials don't exist or are invalid. + static Future _readMatrixCredentialsFromStorage() async { + try { + // Wait for EnvManager to be ready before accessing it + await di.isReady(); + final envManager = await di.getAsync(); + final secureStorage = HubSecureStorage(); + final storageKey = envManager.storageKeyForMatrixCredentials; + + _log.info('Reading matrix credentials from secure storage'); + final String? matrixStoredValues = await secureStorage.getString( + storageKey, + ); + + if (matrixStoredValues == null) { + _log.warning('No matrix credentials found in secure storage'); + return null; + } + + final credentials = MatrixCredentials.fromJson( + jsonDecode(matrixStoredValues), + ); + + _log.info('Matrix credentials successfully read from storage'); + return credentials; } catch (e) { _log.severe( - '[DI] Error registering matrix managers: $e', + 'Error reading matrix credentials from storage: $e', StackTrace.current, ); - rethrow; + return null; } } static Future dropMatrixScope() async { try { - _log.info('[DI] dropMatrixScope: checking if matrix scope exists...'); final hasScope = di.hasScope(DiScope.onMatrixEnvScope.name); - _log.info('[DI] dropMatrixScope: hasScope = $hasScope'); if (hasScope) { - // Clear matrix credentials before dropping the scope - try { - if (di.isRegistered()) { - final matrixManager = di(); - matrixManager.clearMatrixCredentials(); - _log.info('[DI] Matrix credentials cleared before dropping scope'); - } - } catch (e) { - _log.warning('[DI] Error clearing matrix credentials: $e'); - } - - _log.info('[DI] Dropping matrix scope'); + _log.info('Dropping scope [matrixScope]...'); + _log.warning( + '======================================================== [SCOPE] [matrixScope]', + ); await di.dropScope(DiScope.onMatrixEnvScope.name); + _log.info('[matrixScope] dropped successfully'); + _log.warning( + '======================================================== [SCOPE] [matrixScope]', + ); // Add a small delay to ensure the scope is fully dropped - await Future.delayed(const Duration(milliseconds: 50)); + // await Future.delayed(const Duration(milliseconds: 50)); // Check if scope was actually dropped final scopeStillExists = di.hasScope(DiScope.onMatrixEnvScope.name); - _log.info( - '[DI] Matrix scope dropped successfully, scope still exists: $scopeStillExists', - ); + if (!scopeStillExists) _log.info('Matrix scope dropped successfully!'); // Reset the registration status in HubSessionManager try { @@ -490,16 +390,16 @@ class InitManager { di().changeMatrixPolicyManagerRegistrationStatus( false, ); - _log.info('[DI] Matrix registration status reset to false'); + _log.info('Matrix registration status reset to false'); } } catch (e) { - _log.warning('[DI] Error resetting matrix registration status: $e'); + _log.warning('Error resetting matrix registration status: $e'); } } else { - _log.info('[DI] Matrix scope does not exist, skipping drop operation'); + _log.info('Matrix scope does not exist, skipping drop operation'); } } catch (e) { - _log.warning('[DI] Error dropping matrix scope: $e'); + _log.warning('Error dropping matrix scope: $e'); // Don't rethrow as this is cleanup operation } } diff --git a/school_data_hub_flutter/lib/core/di/di_on_active_env.dart b/school_data_hub_flutter/lib/core/init/init_on_active_env.dart similarity index 85% rename from school_data_hub_flutter/lib/core/di/di_on_active_env.dart rename to school_data_hub_flutter/lib/core/init/init_on_active_env.dart index b8716f8c..7008fa4b 100644 --- a/school_data_hub_flutter/lib/core/di/di_on_active_env.dart +++ b/school_data_hub_flutter/lib/core/init/init_on_active_env.dart @@ -22,9 +22,9 @@ class InitOnActiveEnv { final envManager = di(); final activeEnv = envManager.activeEnv; final serverUrl = activeEnv!.serverUrl; - _log.info('Registering Client for environment:'); - _log.info( - ' - Name: [${activeEnv.serverName}] Server URL: [${activeEnv.serverUrl}] Run Mode: [${activeEnv.runMode.name}]', + _log.info('ClientURL: [${activeEnv.serverUrl}]'); + _log.fine( + '======================================================== [CLIENT] [${activeEnv.serverName}] [${activeEnv.runMode.name}]', ); return Client( @@ -44,10 +44,8 @@ class InitOnActiveEnv { // this will initialize the session manager and load the stored user info // it returns a bool await sessionManager.initialize(); - _log.info('HubSessionManager initialized'); - _log.warning( - '========================================================', - ); + _log.fine('HubSessionManager initialized'); + _log.info('========================================================'); return sessionManager; }, dependsOn: [EnvManager, Client], diff --git a/school_data_hub_flutter/lib/core/di/di_on_user_auth.dart b/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart similarity index 85% rename from school_data_hub_flutter/lib/core/di/di_on_user_auth.dart rename to school_data_hub_flutter/lib/core/init/init_on_user_auth.dart index 7305ea5c..893ac25e 100644 --- a/school_data_hub_flutter/lib/core/di/di_on_user_auth.dart +++ b/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart @@ -29,9 +29,9 @@ import 'package:school_data_hub_flutter/features/user/domain/user_manager.dart'; import 'package:school_data_hub_flutter/features/workbooks/domain/workbook_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _log = Logger('DiOnUserAuth'); +final _log = Logger('[Init][OnUserAuth]'); -class DiInitOnUserAuth { +class InitOnUserAuth { static Future registerManagers() async { di.registerSingletonAsync(() async { final pupilIdentityManager = PupilIdentityManager(); @@ -39,7 +39,7 @@ class DiInitOnUserAuth { await pupilIdentityManager.init(); _log.fine('PupilIdentityManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return pupilIdentityManager; }); @@ -50,7 +50,7 @@ class DiInitOnUserAuth { await schoolCalendarManager.init(); _log.fine('SchoolCalendarManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return schoolCalendarManager; }); @@ -61,7 +61,7 @@ class DiInitOnUserAuth { await supportCategoryManager.init(); _log.fine('SupportCategoryManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return supportCategoryManager; }); @@ -71,8 +71,8 @@ class DiInitOnUserAuth { await pupilManager.init(); - _log.info('PupilManager initialized'); - _log.info('########################################################'); + _log.fine('PupilManager initialized'); + _log.info('========================================================'); return pupilManager; }, dependsOn: [PupilIdentityManager, HubSessionManager]); @@ -86,7 +86,7 @@ class DiInitOnUserAuth { final bookManager = BookManager(); await bookManager.init(); _log.fine('BookManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return bookManager; }, dependsOn: []); @@ -94,7 +94,7 @@ class DiInitOnUserAuth { final schoolDataManager = SchoolDataMainManager(); await schoolDataManager.init(); _log.fine('SchoolDataMainManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return schoolDataManager; }, dependsOn: []); @@ -102,7 +102,7 @@ class DiInitOnUserAuth { final workbookManager = WorkbookManager(); await workbookManager.init(); _log.fine('WorkbookManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return workbookManager; }, dependsOn: [HubSessionManager, PupilManager]); @@ -112,7 +112,7 @@ class DiInitOnUserAuth { await competenceManager.init(); _log.fine('CompetenceManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return competenceManager; }); @@ -125,7 +125,7 @@ class DiInitOnUserAuth { final authorizationManager = AuthorizationManager(); await authorizationManager.init(); _log.fine('AuthorizationManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return authorizationManager; }, dependsOn: [HubSessionManager]); @@ -163,8 +163,11 @@ class DiInitOnUserAuth { ); di.registerSingletonWithDependencies(() { - _log.info('SchooldayEventFilterManager initializing'); - return SchooldayEventFilterManager(); + final schooldayEventFilterManager = SchooldayEventFilterManager(); + + _log.fine('SchooldayEventFilterManager initialized'); + _log.info('========================================================'); + return schooldayEventFilterManager; }, dependsOn: [PupilManager, PupilFilterManager, SchooldayEventManager]); di.registerSingletonWithDependencies( @@ -198,7 +201,7 @@ class DiInitOnUserAuth { final schoolListManager = SchoolListManager(); await schoolListManager.init(); _log.fine('SchoolListManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return schoolListManager; }, dependsOn: [HubSessionManager, PupilManager]); @@ -206,7 +209,7 @@ class DiInitOnUserAuth { final schoolListFilterManager = SchoolListFilterManager(); schoolListFilterManager.init(); _log.fine('SchoolListFilterManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return schoolListFilterManager; }, dependsOn: [PupilsFilter, SchoolListManager]); @@ -214,14 +217,14 @@ class DiInitOnUserAuth { final userManager = UserManager(); await userManager.init(); _log.fine('UserManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return userManager; }, dependsOn: [HubSessionManager]); di.registerSingletonAsync(() async { final timetableApiService = TimetableApiService(); _log.fine('TimetableApiService initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return timetableApiService; }); @@ -229,12 +232,10 @@ class DiInitOnUserAuth { final timetableManager = TimetableManager(); await timetableManager.init(); _log.fine('TimetableManager initialized'); - _log.info('########################################################'); + _log.info('========================================================'); return timetableManager; }, dependsOn: [HubSessionManager, TimetableApiService]); - _log.info( - '[Init] Managers depending on authentication are being initialized...', - ); + _log.info('Managers depending on authentication are being initialized...'); } } diff --git a/school_data_hub_flutter/lib/core/session/hub_session_manager.dart b/school_data_hub_flutter/lib/core/session/hub_session_manager.dart index 89974804..7d809c3e 100644 --- a/school_data_hub_flutter/lib/core/session/hub_session_manager.dart +++ b/school_data_hub_flutter/lib/core/session/hub_session_manager.dart @@ -8,9 +8,9 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/core/auth/hub_auth_key_manager.dart'; -import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/env/utils/env_utils.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:serverpod_auth_client/serverpod_auth_client.dart' as auth_client; import 'package:serverpod_auth_shared_flutter/serverpod_auth_shared_flutter.dart'; @@ -326,7 +326,7 @@ class HubSessionManager with ChangeNotifier { 'We have a signed user - Saving userinfo to storage with key: $_userInfoStorageKey', ); - _log.fine('User info received from auth call: ${signedInUser!.userName}'); + _log.info('User info received for user: ${signedInUser!.userName}'); await _storage.setString( _userInfoStorageKey, @@ -334,13 +334,13 @@ class HubSessionManager with ChangeNotifier { ); _log.info( - 'User auth from auth call stored in storage and set: ${signedInUser?.userName}', + 'User auth from auth call stored in storage and set for user: ${signedInUser?.userName}', ); _user = await _client.user.getCurrentUser(); if (!_isDisposed) notifyListeners(); - _log.fine('User fetched from server: ${_user?.userInfo?.userName}'); + _log.info('User fetched from server: ${_user?.userInfo?.userName}'); // We can start now the managers dependent on authentication if (!_isDisposed) { await InitManager.registerManagersDependingOnAuthedSession(); diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart index 1f0b654e..50e4a28d 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/dialogs/change_env_dialog.dart @@ -4,9 +4,9 @@ import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; -import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/env/models/env.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:watch_it/watch_it.dart'; final _envManager = di(); diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart index 05042b44..8654088f 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_session_section.dart @@ -6,8 +6,8 @@ import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; -import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/app_entry_point/login_page/login_controller.dart'; diff --git a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart index daa51d91..3fb532b0 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_helper.dart @@ -5,8 +5,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; -import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_credentials.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/models/matrix_room.dart'; @@ -31,7 +31,6 @@ class MatrixPolicyHelper { // if they are null, we will read them from secure storage final _secureStorageKey = _envManager.storageKeyForMatrixCredentials; - MatrixCredentials? storedCredentials; if (passedCredentials == null) { _log.warning( 'No matrix credentials passed, the app is initializing\nreading matrix credentials from secure storage', @@ -43,10 +42,6 @@ class MatrixPolicyHelper { if (matrixStoredValues == null) { throw Exception('Matrix stored values are null'); } - - storedCredentials = MatrixCredentials.fromJson( - jsonDecode(matrixStoredValues), - ); } else { _log.info('Matrix credentials passed, storing them in secure storage'); @@ -74,12 +69,9 @@ class MatrixPolicyHelper { } // is the passed credentials are null, we will use the stored ones - final validCredentials = passedCredentials ?? storedCredentials; + // final validCredentials = passedCredentials ?? storedCredentials; - await InitManager.registerMatrixManagers( - validCredentials!, - storageKey: _secureStorageKey, - ); + await InitManager.registerMatrixManagers(); return; } diff --git a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart index 9997954f..7fa67c43 100644 --- a/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart +++ b/school_data_hub_flutter/lib/features/matrix/domain/matrix_policy_manager.dart @@ -4,8 +4,8 @@ import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; -import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/data/matrix_api_service.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/filters/matrix_policy_filter_manager.dart'; @@ -112,16 +112,6 @@ class MatrixPolicyManager extends ChangeNotifier { return this; } - void clearMatrixCredentials() { - _matrixUrl = ''; - _corporalToken = ''; - _matrixToken = ''; - _matrixAdminId = null; - _matrixPolicy = null; - _encryptionKey = ''; - _encryptionIv = ''; - } - void pendingChangesHandler(bool newValue) { if (newValue == _policyPendingChanges.value) return; _policyPendingChanges.value = newValue; diff --git a/school_data_hub_flutter/lib/features/matrix/services/api/api_client.dart b/school_data_hub_flutter/lib/features/matrix/services/api/api_client.dart index 9caaf94c..a391c40e 100644 --- a/school_data_hub_flutter/lib/features/matrix/services/api/api_client.dart +++ b/school_data_hub_flutter/lib/features/matrix/services/api/api_client.dart @@ -101,7 +101,7 @@ class ApiClient { cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, ); - _log.fine("Response[GET] ${response.statusCode} => PATH: $uri"); + // _log.fine("Response[GET] ${response.statusCode} => PATH: $uri"); return response; } @@ -124,7 +124,7 @@ class ApiClient { cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, ); - _log.fine("Response[PATCH] ${response.statusCode} => PATH: $uri"); + // _log.fine("Response[PATCH] ${response.statusCode} => PATH: $uri"); if (response.statusCode == 401) { if (response.data['message'] == 'Token nicht (mehr) gültig!') { // locator().logout(); @@ -154,7 +154,7 @@ class ApiClient { onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); - _log.fine("Response[POST] ${response.statusCode} => PATH: $uri"); + // _log.fine("Response[POST] ${response.statusCode} => PATH: $uri"); if (response.statusCode == 401) { // if (response.data['message'] == 'Token nicht (mehr) gültig!') { // locator().logout(); @@ -184,7 +184,7 @@ class ApiClient { onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); - _log.fine("Response[PUT] ${response.statusCode} => PATH: $uri"); + // _log.fine("Response[PUT] ${response.statusCode} => PATH: $uri"); if (response.statusCode == 401) { // if (response.data['message'] == 'Token nicht (mehr) gültig!') { // locator().logout(); @@ -211,7 +211,7 @@ class ApiClient { options: options, cancelToken: cancelToken, ); - _log.fine("Response[DELETE] ${response.statusCode} => PATH: $uri"); + // _log.fine("Response[DELETE] ${response.statusCode} => PATH: $uri"); if (response.statusCode == 401) { // if (response.data['message'] == 'Token nicht (mehr) gültig!') { // locator().logout(); diff --git a/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart b/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart index cf703f9a..4a18e3c7 100644 --- a/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart +++ b/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/data/file_upload_service.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; @@ -270,10 +269,6 @@ class PupilDataApiService { } Future fetchLastIdentitiesUpdate() async { - final _log = Logger('PupilDataApiService'); - _log.warning('########################################################'); - _log.warning('Client is using server URL: ${_client.host}'); - _log.warning('########################################################'); final lastUpdate = await ClientHelper.apiCall( call: () => _client.pupilIdentity.fetchLastPupilIdentitiesUpdate(), errorMessage: 'Die letzte Aktualisierung konnte nicht geladen werden', diff --git a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart index c771844e..63ffa886 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart @@ -49,7 +49,7 @@ class PupilIdentityManager { // List get availablePupilIds => _pupilIdentities.keys.toList(); List get availablePupilIds { - _log.fine( + _log.info( 'getter returning [${_pupilIdentities.keys.length}] available pupil ids', ); return _pupilIdentities.keys.toList(); diff --git a/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart b/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart index 5e8eeae1..5367ff7a 100644 --- a/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart +++ b/school_data_hub_flutter/lib/features/school_lists/domain/filters/school_list_filter_manager.dart @@ -23,7 +23,7 @@ class SchoolListFilterManager { SchoolListFilterManager init() { _schoolListManager.addListener(_onSchoolListsChanged); resetFilters(); - _log.fine('SchoolListFilterManager initialized'); + return this; } @@ -35,9 +35,11 @@ class SchoolListFilterManager { // If we have an active filter, reapply it to the new data if (_filterState.value) { _filteredSchoolLists.value = _schoolListManager.schoolLists - .where((element) => element.name - .toLowerCase() - .contains(_filteredSchoolLists.value.first.name.toLowerCase())) + .where( + (element) => element.name.toLowerCase().contains( + _filteredSchoolLists.value.first.name.toLowerCase(), + ), + ) .toList(); } else { // Otherwise, just update with the new full list @@ -53,7 +55,9 @@ class SchoolListFilterManager { _filterState.value = false; _filteredSchoolLists.value = _schoolListManager.schoolLists; _filtersStateManager.setFilterState( - filterState: FilterState.schoolList, value: false); + filterState: FilterState.schoolList, + value: false, + ); } void onSearchEnter(String text) { @@ -63,7 +67,9 @@ class SchoolListFilterManager { } _filterState.value = true; _filtersStateManager.setFilterState( - filterState: FilterState.schoolList, value: true); + filterState: FilterState.schoolList, + value: true, + ); String lowerCaseText = text.toLowerCase(); _filteredSchoolLists.value = _schoolListManager.schoolLists .where((element) => element.name.toLowerCase().contains(lowerCaseText)) @@ -71,30 +77,31 @@ class SchoolListFilterManager { } List addPupilEntryFiltersToFilteredPupils( - List pupilEntries) { + List pupilEntries, + ) { List filteredPupilEntries = []; bool filterIsOn = false; for (PupilListEntry pupilEntry in pupilEntries) { - if (_pupilFilterManager - .pupilFilterState.value[PupilFilter.schoolListYesResponse]! && + if (_pupilFilterManager.pupilFilterState.value[PupilFilter + .schoolListYesResponse]! && pupilEntry.status != true) { filterIsOn = true; continue; } - if (_pupilFilterManager - .pupilFilterState.value[PupilFilter.schoolListNoResponse]! && + if (_pupilFilterManager.pupilFilterState.value[PupilFilter + .schoolListNoResponse]! && pupilEntry.status != false) { filterIsOn = true; continue; } - if (_pupilFilterManager - .pupilFilterState.value[PupilFilter.schoolListNullResponse]! && + if (_pupilFilterManager.pupilFilterState.value[PupilFilter + .schoolListNullResponse]! && pupilEntry.status != null) { filterIsOn = true; continue; } - if (_pupilFilterManager - .pupilFilterState.value[PupilFilter.schoolListCommentResponse]! && + if (_pupilFilterManager.pupilFilterState.value[PupilFilter + .schoolListCommentResponse]! && pupilEntry.comment == null) { filterIsOn = true; continue; diff --git a/school_data_hub_flutter/lib/features/school_lists/domain/models/school_list_pupil_entries_proxy.dart b/school_data_hub_flutter/lib/features/school_lists/domain/models/school_list_pupil_entries_proxy.dart index 2106ff5a..dc2ab75a 100644 --- a/school_data_hub_flutter/lib/features/school_lists/domain/models/school_list_pupil_entries_proxy.dart +++ b/school_data_hub_flutter/lib/features/school_lists/domain/models/school_list_pupil_entries_proxy.dart @@ -15,8 +15,9 @@ class SchoolListPupilEntriesProxyMap with ChangeNotifier { final Set newEntryIds = newEntries.map((e) => e.id!).toSet(); // Find keys to remove (in current map but not in new entries) - final List keysToRemove = - pupilEntries.keys.where((key) => !newEntryIds.contains(key)).toList(); + final List keysToRemove = pupilEntries.keys + .where((key) => !newEntryIds.contains(key)) + .toList(); // Remove entries that no longer exist for (final key in keysToRemove) { @@ -46,7 +47,7 @@ class SchoolListPupilEntriesProxyMap with ChangeNotifier { pupilEntries[entryId]!.setPupilEntry(newEntry); hasChanges = true; } else { - _log.fine('No changes for entry with ID: $entryId'); + _log.info('No changes for entry with ID: $entryId'); } } } @@ -54,9 +55,9 @@ class SchoolListPupilEntriesProxyMap with ChangeNotifier { // Only notify listeners if something actually changed if (hasChanges) { notifyListeners(); - _log.fine('${pupilEntries.length} pupil entries updated in proxy'); + _log.info('${pupilEntries.length} pupil entries updated in proxy'); } else { - _log.fine('No changes in pupil entries'); + _log.info('No changes in pupil entries'); } } diff --git a/school_data_hub_flutter/lib/features/school_lists/domain/school_list_manager.dart b/school_data_hub_flutter/lib/features/school_lists/domain/school_list_manager.dart index e42249b7..a6c1b3ca 100644 --- a/school_data_hub_flutter/lib/features/school_lists/domain/school_list_manager.dart +++ b/school_data_hub_flutter/lib/features/school_lists/domain/school_list_manager.dart @@ -80,8 +80,10 @@ class SchoolListManager with ChangeNotifier { if (pupilEntries.isEmpty) { return []; } - final pupilIdsInList = - pupilEntries.values.map((e) => e.pupilEntry.pupilId).toSet().toList(); + final pupilIdsInList = pupilEntries.values + .map((e) => e.pupilEntry.pupilId) + .toSet() + .toList(); return _pupilManager.getPupilsFromPupilIds(pupilIdsInList); } @@ -102,7 +104,7 @@ class SchoolListManager with ChangeNotifier { _schoolListIdPupilEntriesMap[schoolList.id!]!.setPupilEntries( pupilEntries, ); - _log.fine( + _log.info( 'Updated pupil entries map for school list number ${schoolList.id!}', ); } else { @@ -111,13 +113,13 @@ class SchoolListManager with ChangeNotifier { _schoolListIdPupilEntriesMap[schoolList.id!]!.setPupilEntries( pupilEntries, ); - _log.fine( + _log.info( 'Created new pupil entries map for school list number ${schoolList.id!}', ); } notifyListeners(); - _log.fine( + _log.info( 'Finished updating School list number ${schoolList.id!} with ${pupilEntries.length} pupil entries', ); } @@ -220,13 +222,12 @@ class SchoolListManager with ChangeNotifier { required bool public, }) async { final schoolList = await ClientHelper.apiCall( - call: - () => _apiSchoolListService.postSchoolListWithGroup( - name: name, - description: description, - pupilIds: pupilIds, - public: public, - ), + call: () => _apiSchoolListService.postSchoolListWithGroup( + name: name, + description: description, + pupilIds: pupilIds, + public: public, + ), errorMessage: 'Fehler beim Erstellen der Schulliste', ); if (schoolList == null) { diff --git a/school_data_hub_flutter/lib/main.dart b/school_data_hub_flutter/lib/main.dart index 6837c025..498d7752 100644 --- a/school_data_hub_flutter/lib/main.dart +++ b/school_data_hub_flutter/lib/main.dart @@ -8,8 +8,8 @@ import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:school_data_hub_flutter/app_utils/logger/logrecord_formatter.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/core/di/init_manager.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:school_data_hub_flutter/core/session/serverpod_connectivity_monitor.dart'; import 'package:school_data_hub_flutter/features/app_entry_point/entry_point/entry_point_controller.dart'; import 'package:school_data_hub_flutter/features/app_entry_point/error_page.dart'; @@ -60,7 +60,7 @@ void main() async { await di.allReady(); runApp(const MyApp()); - // TODO: INFO - This is a hack to avoid calls to firebase from the mobile_scanner package every 15 minutes + //- Hack: - This is a to avoid calls to firebase from the mobile_scanner package every 15 minutes // like described here: https://github.com/juliansteenbakker/mobile_scanner/issues/553 if (Platform.isAndroid) { final dir = await getApplicationDocumentsDirectory(); From 26692c057959f218c6361084e95fe30b4193da3f Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sat, 29 Nov 2025 17:43:36 +0100 Subject: [PATCH 17/52] [APP] Statistics: added attendance chart --- .../statistics/chart_page/chart_page.dart | 173 ++++++++++++++++++ .../chart_page/chart_page_controller.dart | 76 ++++++++ 2 files changed, 249 insertions(+) diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart index effc3fb3..d6b64115 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart @@ -30,12 +30,18 @@ class ChartPage extends StatelessWidget { }) > eventChartData; + final Map< + DateTime, + ({int excused, int unexcused, int goneHome}) + > + attendanceChartData; final List schooldays; const ChartPage({ super.key, required this.chartData, required this.eventChartData, + required this.attendanceChartData, required this.schooldays, }); @@ -276,6 +282,68 @@ class ChartPage extends StatelessWidget { ]; } + List> _createAttendanceSeries() { + // Sort schooldays by date + final sorted = sortedSchooldays; + + final excusedData = sorted.map((schoolday) { + final data = attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.excused ?? 0, + seriesId: 'excused', + ); + }).toList(); + + final unexcusedData = sorted.map((schoolday) { + final data = attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.unexcused ?? 0, + seriesId: 'unexcused', + ); + }).toList(); + + final goneHomeData = sorted.map((schoolday) { + final data = attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.goneHome ?? 0, + seriesId: 'goneHome', + ); + }).toList(); + + return [ + charts.Series( + id: 'Entschuldigt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.green), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: excusedData, + ), + charts.Series( + id: 'Unentschuldigt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: unexcusedData, + ), + charts.Series( + id: 'Nach Hause geschickt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: goneHomeData, + ), + ]; + } + @override Widget build(BuildContext context) { if (schooldays.isEmpty) { @@ -561,6 +629,111 @@ class ChartPage extends StatelessWidget { _buildLegendItem('Sonstiges', Colors.grey), ], ), + const Gap(30), + const Text( + 'Anwesenheit nach Schultag', + style: AppStyles.title, + ), + const Gap(10), + SizedBox( + height: 300, + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxHeight <= 0 || + constraints.maxWidth <= 0) { + return const Center( + child: Text('Chart wird geladen...'), + ); + } + final attendanceSeries = _createAttendanceSeries(); + return charts.OrdinalComboChart( + attendanceSeries, + animate: true, + animationDuration: const Duration(milliseconds: 500), + defaultRenderer: charts.BarRendererConfig( + groupingType: charts.BarGroupingType.stacked, + ), + layoutConfig: charts.LayoutConfig( + leftMarginSpec: charts.MarginSpec.fixedPixel(60), + topMarginSpec: charts.MarginSpec.fixedPixel(20), + rightMarginSpec: charts.MarginSpec.fixedPixel(40), + bottomMarginSpec: charts.MarginSpec.fixedPixel(60), + ), + primaryMeasureAxis: const charts.NumericAxisSpec( + tickProviderSpec: + charts.BasicNumericTickProviderSpec( + zeroBound: true, + ), + ), + domainAxis: charts.OrdinalAxisSpec( + tickProviderSpec: charts.StaticOrdinalTickProviderSpec( + sortedSchooldays.map((schoolday) { + final dateStr = _formatDateForChart( + schoolday.schoolday, + ); + final firstOfMonthDates = + _getFirstOfMonthDates(); + + // Only show label if it's the first of a month + if (firstOfMonthDates.contains(dateStr)) { + try { + final date = DateFormat( + 'dd.MM', + ).parse(dateStr); + return charts.TickSpec( + dateStr, + label: DateFormat('MMM').format(date), + ); + } catch (e) { + return charts.TickSpec(dateStr); + } + } else { + // Return tick with empty label for other dates + return charts.TickSpec( + dateStr, + label: '', + ); + } + }).toList(), + ), + ), + behaviors: [ + charts.SeriesLegend( + position: charts.BehaviorPosition.bottom, + desiredMaxRows: 3, + cellPadding: const EdgeInsets.only( + right: 4.0, + bottom: 4.0, + ), + ), + charts.ChartTitle( + 'Schultag', + behaviorPosition: charts.BehaviorPosition.bottom, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + charts.ChartTitle( + 'Anzahl', + behaviorPosition: charts.BehaviorPosition.start, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + ], + ); + }, + ), + ), + const Gap(20), + Wrap( + alignment: WrapAlignment.center, + spacing: 20, + runSpacing: 10, + children: [ + _buildLegendItem('Entschuldigt', Colors.green), + _buildLegendItem('Unentschuldigt', Colors.red), + _buildLegendItem('Nach Hause geschickt', Colors.orange), + ], + ), const Gap(20), ], ), diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart index 7d5d3ad9..badc124c 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/features/_attendance/domain/attendance_manager.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; @@ -17,6 +18,7 @@ class _ChartPageControllerState extends State { late final PupilManager _pupilManager; late final SchoolCalendarManager _schoolCalendarManager; late final SchooldayEventManager _schooldayEventManager; + late final AttendanceManager _attendanceManager; @override void initState() { @@ -24,6 +26,7 @@ class _ChartPageControllerState extends State { _pupilManager = di(); _schoolCalendarManager = di(); _schooldayEventManager = di(); + _attendanceManager = di(); } /// Gets schooldays for the current semester @@ -333,6 +336,64 @@ class _ChartPageControllerState extends State { }).length; } + /// Counts attendance stats for a given schoolday + ({int excused, int unexcused, int goneHome}) getAttendanceCountForSchoolday( + Schoolday schoolday, + ) { + int excused = 0; + int unexcused = 0; + int goneHome = 0; + + final pupils = _pupilManager.allPupils; + final schooldayDate = schoolday.schoolday.toLocal(); + final dayDate = DateTime( + schooldayDate.year, + schooldayDate.month, + schooldayDate.day, + ); + + for (final pupil in pupils) { + final missedSchooldays = + _attendanceManager + .getPupilMissedSchooldaysProxy(pupil.pupilId) + .missedSchooldays; + + for (final missedSchoolday in missedSchooldays) { + // Check if this missedSchoolday corresponds to the current schoolday + bool isSameDay = false; + + if (missedSchoolday.schooldayId == schoolday.id) { + isSameDay = true; + } else if (missedSchoolday.schoolday != null) { + final missedDate = missedSchoolday.schoolday!.schoolday.toLocal(); + final missedDayDate = DateTime( + missedDate.year, + missedDate.month, + missedDate.day, + ); + if (missedDayDate == dayDate) { + isSameDay = true; + } + } + + if (isSameDay) { + if (missedSchoolday.missedType == MissedType.missed) { + if (missedSchoolday.unexcused == true) { + unexcused++; + } else { + excused++; + } + } + if (missedSchoolday.returned == true) { + goneHome++; + } + } + } + } + + return (excused: excused, unexcused: unexcused, goneHome: goneHome); + } + /// Gets data for the chart: schooldays with their counts Map< DateTime, @@ -424,6 +485,19 @@ class _ChartPageControllerState extends State { return data; } + /// Gets attendance data for the chart + Map + getAttendanceChartData() { + final schooldays = getSchooldaysForCurrentSemester(); + final Map data = {}; + + for (final schoolday in schooldays) { + data[schoolday.schoolday] = getAttendanceCountForSchoolday(schoolday); + } + + return data; + } + @override Widget build(BuildContext context) { final chartData = getChartData(); @@ -453,10 +527,12 @@ class _ChartPageControllerState extends State { } final eventChartData = getEventChartData(); + final attendanceChartData = getAttendanceChartData(); return ChartPage( chartData: chartData, eventChartData: eventChartData, + attendanceChartData: attendanceChartData, schooldays: schooldays, ); } From d96b84783565dca48758fa9da65f13cf032eefe3 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sun, 30 Nov 2025 15:59:40 +0100 Subject: [PATCH 18/52] [APP] Added visualizations --- .../presentation/widgets/pupil_list_card.dart | 62 ++ .../statistics/chart_page/chart_page.dart | 766 ++---------------- .../chart_page/chart_page_controller.dart | 690 +++++++--------- .../widgets/chart_page_bottom_bar.dart | 88 ++ .../chart_page/widgets/event_stats_view.dart | 376 +++++++++ .../chart_page/widgets/pupil_stats_view.dart | 404 +++++++++ .../controller/statistics.dart | 17 +- .../list_tiles/enrollment_list_tiles.dart | 247 ++---- .../list_tiles/languages_list_tiles.dart | 148 +++- .../statistics_page/statistics_page.dart | 19 +- 10 files changed, 1468 insertions(+), 1349 deletions(-) create mode 100644 school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_list_card.dart create mode 100644 school_data_hub_flutter/lib/features/statistics/chart_page/widgets/chart_page_bottom_bar.dart create mode 100644 school_data_hub_flutter/lib/features/statistics/chart_page/widgets/event_stats_view.dart create mode 100644 school_data_hub_flutter/lib/features/statistics/chart_page/widgets/pupil_stats_view.dart diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_list_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_list_card.dart new file mode 100644 index 00000000..67bfbfd3 --- /dev/null +++ b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/pupil_list_card.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/avatar.dart'; + +class PupilListCard extends StatelessWidget { + final PupilProxy passedPupil; + const PupilListCard({ + required this.passedPupil, + super.key, + }); + + @override + Widget build(BuildContext context) { + final PupilProxy pupil = passedPupil; + + return Card( + color: Colors.white, + child: Row( + children: [ + AvatarWithBadges(pupil: pupil, size: 60), + Expanded( + child: InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => PupilProfilePage(pupil: pupil), + ), + ); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${pupil.firstName} ${pupil.lastName}', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + Text( + 'Klasse: ${pupil.schoolGrade.name}', + style: const TextStyle( + fontSize: 14, + color: Colors.grey, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } +} + diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart index d6b64115..8694dfc3 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page.dart @@ -1,13 +1,15 @@ -import 'package:community_charts_flutter/community_charts_flutter.dart' - as charts; import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; -import 'package:intl/intl.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/styles.dart'; - -class ChartPage extends StatelessWidget { +import 'package:school_data_hub_flutter/common/widgets/generic_components/bottom_nav_bar_no_filter_button.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/widgets/attendance_stats_view.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/widgets/chart_page_bottom_bar.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/widgets/event_stats_view.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/widgets/pupil_stats_view.dart'; +import 'package:watch_it/watch_it.dart'; + +class ChartPage extends WatchingWidget { final Map< DateTime, ({ @@ -30,10 +32,7 @@ class ChartPage extends StatelessWidget { }) > eventChartData; - final Map< - DateTime, - ({int excused, int unexcused, int goneHome}) - > + final Map attendanceChartData; final List schooldays; @@ -51,737 +50,64 @@ class ChartPage extends StatelessWidget { ..sort((a, b) => a.schoolday.compareTo(b.schoolday)); } - /// Gets the first schoolday of each month for tick labels - Set _getFirstOfMonthDates() { - final Set firstOfMonthDates = {}; - String? currentMonth; - - for (final schoolday in sortedSchooldays) { - final date = schoolday.schoolday.toLocal(); - final monthKey = '${date.year}-${date.month}'; - - // If this is a new month, add the first schoolday of that month - if (monthKey != currentMonth) { - currentMonth = monthKey; - firstOfMonthDates.add(_formatDateForChart(schoolday.schoolday)); - } - } - - return firstOfMonthDates; - } - - List> _createSeries() { - // Sort schooldays by date - final sorted = sortedSchooldays; - - final specialNeedsData = sorted.map((schoolday) { - final data = chartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.specialNeeds ?? 0, - seriesId: 'specialNeeds', - ); - }).toList(); - - final migrationSupportData = sorted.map((schoolday) { - final data = chartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.migrationSupport ?? 0, - seriesId: 'migrationSupport', - ); - }).toList(); - - final supportLevel3Data = sorted.map((schoolday) { - final data = chartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.supportLevel3 ?? 0, - seriesId: 'supportLevel3', - ); - }).toList(); - - final regularPupilsData = sorted.map((schoolday) { - final data = chartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.regularPupils ?? 0, - seriesId: 'regularPupils', - ); - }).toList(); - - final newPupilsData = sorted.map((schoolday) { - final data = chartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.newPupils ?? 0, - seriesId: 'newPupils', - ); - }).toList(); - - return [ - charts.Series( - id: 'Special Needs', - colorFn: (_, __) => - charts.ColorUtil.fromDartColor(AppColors.accentColor), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: specialNeedsData, - ), - charts.Series( - id: 'Migration Support', - colorFn: (_, __) => - charts.ColorUtil.fromDartColor(AppColors.backgroundColor), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: migrationSupportData, - ), - charts.Series( - id: 'Support Level 3', - colorFn: (_, __) => - charts.ColorUtil.fromDartColor(AppColors.warningButtonColor), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: supportLevel3Data, - ), - charts.Series( - id: 'Regular Pupils', - colorFn: (_, __) => charts.ColorUtil.fromDartColor( - const Color.fromARGB( - 255, - 200, - 200, - 200, - ), // Light gray for regular pupils - ), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: regularPupilsData, - ), - charts.Series( - id: 'New Pupils', - colorFn: (_, __) => - charts.ColorUtil.fromDartColor(AppColors.successButtonColor), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: newPupilsData, - )..setAttribute(charts.rendererIdKey, 'lineSeries'), - ]; - } - - String _formatDateForChart(DateTime date) { - final localDate = date.isUtc ? date.toLocal() : date; - return DateFormat('dd.MM').format(localDate); - } - - List> _createEventSeries() { - // Sort schooldays by date - final sorted = sortedSchooldays; - - final parentsMeetingData = sorted.map((schoolday) { - final data = eventChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.parentsMeeting ?? 0, - seriesId: 'parentsMeeting', - ); - }).toList(); - - final admonitionData = sorted.map((schoolday) { - final data = eventChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.admonition ?? 0, - seriesId: 'admonition', - ); - }).toList(); - - final afternoonCareAdmonitionData = sorted.map((schoolday) { - final data = eventChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.afternoonCareAdmonition ?? 0, - seriesId: 'afternoonCareAdmonition', - ); - }).toList(); - - final admonitionAndBannedData = sorted.map((schoolday) { - final data = eventChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.admonitionAndBanned ?? 0, - seriesId: 'admonitionAndBanned', - ); - }).toList(); - - final otherEventData = sorted.map((schoolday) { - final data = eventChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.otherEvent ?? 0, - seriesId: 'otherEvent', - ); - }).toList(); - - return [ - charts.Series( - id: 'Elterngespräch', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.blue), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: parentsMeetingData, - ), - charts.Series( - id: 'Rote Karte', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: admonitionData, - ), - charts.Series( - id: 'Rote Karte - OGS', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: afternoonCareAdmonitionData, - ), - charts.Series( - id: 'Rote Karte + Abholen', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.purple), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: admonitionAndBannedData, - ), - charts.Series( - id: 'Sonstiges', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.grey), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: otherEventData, - ), - ]; - } - - List> _createAttendanceSeries() { - // Sort schooldays by date - final sorted = sortedSchooldays; - - final excusedData = sorted.map((schoolday) { - final data = attendanceChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.excused ?? 0, - seriesId: 'excused', - ); - }).toList(); - - final unexcusedData = sorted.map((schoolday) { - final data = attendanceChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.unexcused ?? 0, - seriesId: 'unexcused', - ); - }).toList(); - - final goneHomeData = sorted.map((schoolday) { - final data = attendanceChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.goneHome ?? 0, - seriesId: 'goneHome', - ); - }).toList(); - - return [ - charts.Series( - id: 'Entschuldigt', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.green), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: excusedData, - ), - charts.Series( - id: 'Unentschuldigt', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: unexcusedData, - ), - charts.Series( - id: 'Nach Hause geschickt', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: goneHomeData, - ), - ]; - } - @override Widget build(BuildContext context) { + final selectedIndex = createOnce( + () => ValueNotifier(0), + ); + final index = watchPropertyValue((m) => m.value, target: selectedIndex); + if (schooldays.isEmpty) { return Scaffold( backgroundColor: AppColors.canvasColor, - appBar: AppBar( - automaticallyImplyLeading: false, - backgroundColor: AppColors.backgroundColor, - centerTitle: true, - title: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.bar_chart_rounded, size: 25, color: Colors.white), - Gap(10), - Text('Statistik Diagramm', style: AppStyles.appBarTextStyle), - ], - ), + appBar: const GenericAppBar( + iconData: Icons.bar_chart_rounded, + title: 'Statistik Diagramm', ), body: const Center(child: Text('Keine Daten verfügbar')), + bottomNavigationBar: const BottomNavBarNoFilterButton(), ); } - final series = _createSeries(); - return Scaffold( backgroundColor: AppColors.canvasColor, - appBar: AppBar( - automaticallyImplyLeading: false, - backgroundColor: AppColors.backgroundColor, - centerTitle: true, - title: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.bar_chart_rounded, size: 25, color: Colors.white), - Gap(10), - Text('Statistik Diagramm', style: AppStyles.appBarTextStyle), - ], - ), + appBar: const GenericAppBar( + iconData: Icons.bar_chart_rounded, + title: 'Statistik Diagramm', ), body: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 800), - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Gap(15), - const Text( - 'Schülerzahlen nach Schultag', - style: AppStyles.title, - ), - const Gap(10), - SizedBox( - height: 300, - child: LayoutBuilder( - builder: (context, constraints) { - if (constraints.maxHeight <= 0 || - constraints.maxWidth <= 0) { - return const Center( - child: Text('Chart wird geladen...'), - ); - } - return charts.OrdinalComboChart( - series, - animate: true, - animationDuration: const Duration(milliseconds: 500), - defaultRenderer: charts.LineRendererConfig( - includeArea: true, - stacked: true, - ), - customSeriesRenderers: [ - charts.LineRendererConfig( - customRendererId: 'lineSeries', - strokeWidthPx: 3.0, // Make the line thicker - includeArea: false, - stacked: false, - ), - ], - layoutConfig: charts.LayoutConfig( - leftMarginSpec: charts.MarginSpec.fixedPixel(60), - topMarginSpec: charts.MarginSpec.fixedPixel(20), - rightMarginSpec: charts.MarginSpec.fixedPixel(40), - bottomMarginSpec: charts.MarginSpec.fixedPixel(60), - ), - primaryMeasureAxis: const charts.NumericAxisSpec( - tickProviderSpec: - charts.BasicNumericTickProviderSpec( - zeroBound: true, - ), - ), - domainAxis: charts.OrdinalAxisSpec( - tickProviderSpec: charts.StaticOrdinalTickProviderSpec( - sortedSchooldays.map((schoolday) { - final dateStr = _formatDateForChart( - schoolday.schoolday, - ); - final firstOfMonthDates = - _getFirstOfMonthDates(); - - // Only show label if it's the first of a month - if (firstOfMonthDates.contains(dateStr)) { - try { - final date = DateFormat( - 'dd.MM', - ).parse(dateStr); - return charts.TickSpec( - dateStr, - label: DateFormat('MMM').format(date), - ); - } catch (e) { - return charts.TickSpec(dateStr); - } - } else { - // Return tick with empty label for other dates - return charts.TickSpec( - dateStr, - label: '', - ); - } - }).toList(), - ), - ), - behaviors: [ - charts.SeriesLegend( - position: charts.BehaviorPosition.bottom, - desiredMaxRows: 4, - cellPadding: const EdgeInsets.only( - right: 4.0, - bottom: 4.0, - ), - ), - charts.ChartTitle( - 'Schultag', - behaviorPosition: charts.BehaviorPosition.bottom, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, - ), - charts.ChartTitle( - 'Anzahl Schüler', - behaviorPosition: charts.BehaviorPosition.start, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, - ), - ], - ); - }, - ), - ), - const Gap(20), - Wrap( - alignment: WrapAlignment.center, - spacing: 20, - runSpacing: 10, - children: [ - _buildLegendItem( - 'Besonderer Förderbedarf', - AppColors.accentColor, - ), - _buildLegendItem( - 'Migrationsunterstützung', - AppColors.backgroundColor, - ), - _buildLegendItem( - 'Förderstufe 3', - AppColors.warningButtonColor, - ), - _buildLegendItem( - 'Reguläre Schüler', - const Color.fromARGB(255, 200, 200, 200), - ), - _buildLegendItem( - 'Neue Schüler', - AppColors.successButtonColor, - ), - ], - ), - const Gap(30), - const Text( - 'Schultagevents nach Schultag', - style: AppStyles.title, - ), - const Gap(10), - SizedBox( - height: 300, - child: LayoutBuilder( - builder: (context, constraints) { - if (constraints.maxHeight <= 0 || - constraints.maxWidth <= 0) { - return const Center( - child: Text('Chart wird geladen...'), - ); - } - final eventSeries = _createEventSeries(); - return charts.OrdinalComboChart( - eventSeries, - animate: true, - animationDuration: const Duration(milliseconds: 500), - defaultRenderer: charts.BarRendererConfig( - groupingType: charts.BarGroupingType.stacked, - ), - layoutConfig: charts.LayoutConfig( - leftMarginSpec: charts.MarginSpec.fixedPixel(60), - topMarginSpec: charts.MarginSpec.fixedPixel(20), - rightMarginSpec: charts.MarginSpec.fixedPixel(40), - bottomMarginSpec: charts.MarginSpec.fixedPixel(60), - ), - primaryMeasureAxis: const charts.NumericAxisSpec( - tickProviderSpec: - charts.BasicNumericTickProviderSpec( - zeroBound: true, - ), - ), - domainAxis: charts.OrdinalAxisSpec( - tickProviderSpec: charts.StaticOrdinalTickProviderSpec( - sortedSchooldays.map((schoolday) { - final dateStr = _formatDateForChart( - schoolday.schoolday, - ); - final firstOfMonthDates = - _getFirstOfMonthDates(); - - // Only show label if it's the first of a month - if (firstOfMonthDates.contains(dateStr)) { - try { - final date = DateFormat( - 'dd.MM', - ).parse(dateStr); - return charts.TickSpec( - dateStr, - label: DateFormat('MMM').format(date), - ); - } catch (e) { - return charts.TickSpec(dateStr); - } - } else { - // Return tick with empty label for other dates - return charts.TickSpec( - dateStr, - label: '', - ); - } - }).toList(), - ), - ), - behaviors: [ - charts.SeriesLegend( - position: charts.BehaviorPosition.bottom, - desiredMaxRows: 3, - cellPadding: const EdgeInsets.only( - right: 4.0, - bottom: 4.0, - ), - ), - charts.ChartTitle( - 'Schultag', - behaviorPosition: charts.BehaviorPosition.bottom, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, - ), - charts.ChartTitle( - 'Anzahl Events', - behaviorPosition: charts.BehaviorPosition.start, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, - ), - ], - ); - }, - ), - ), - const Gap(20), - Wrap( - alignment: WrapAlignment.center, - spacing: 20, - runSpacing: 10, - children: [ - _buildLegendItem('Elterngespräch', Colors.blue), - _buildLegendItem('Rote Karte', Colors.red), - _buildLegendItem('Rote Karte - OGS', Colors.orange), - _buildLegendItem('Rote Karte + Abholen', Colors.purple), - _buildLegendItem('Sonstiges', Colors.grey), - ], - ), - const Gap(30), - const Text( - 'Anwesenheit nach Schultag', - style: AppStyles.title, - ), - const Gap(10), - SizedBox( - height: 300, - child: LayoutBuilder( - builder: (context, constraints) { - if (constraints.maxHeight <= 0 || - constraints.maxWidth <= 0) { - return const Center( - child: Text('Chart wird geladen...'), - ); - } - final attendanceSeries = _createAttendanceSeries(); - return charts.OrdinalComboChart( - attendanceSeries, - animate: true, - animationDuration: const Duration(milliseconds: 500), - defaultRenderer: charts.BarRendererConfig( - groupingType: charts.BarGroupingType.stacked, - ), - layoutConfig: charts.LayoutConfig( - leftMarginSpec: charts.MarginSpec.fixedPixel(60), - topMarginSpec: charts.MarginSpec.fixedPixel(20), - rightMarginSpec: charts.MarginSpec.fixedPixel(40), - bottomMarginSpec: charts.MarginSpec.fixedPixel(60), - ), - primaryMeasureAxis: const charts.NumericAxisSpec( - tickProviderSpec: - charts.BasicNumericTickProviderSpec( - zeroBound: true, - ), - ), - domainAxis: charts.OrdinalAxisSpec( - tickProviderSpec: charts.StaticOrdinalTickProviderSpec( - sortedSchooldays.map((schoolday) { - final dateStr = _formatDateForChart( - schoolday.schoolday, - ); - final firstOfMonthDates = - _getFirstOfMonthDates(); - - // Only show label if it's the first of a month - if (firstOfMonthDates.contains(dateStr)) { - try { - final date = DateFormat( - 'dd.MM', - ).parse(dateStr); - return charts.TickSpec( - dateStr, - label: DateFormat('MMM').format(date), - ); - } catch (e) { - return charts.TickSpec(dateStr); - } - } else { - // Return tick with empty label for other dates - return charts.TickSpec( - dateStr, - label: '', - ); - } - }).toList(), - ), - ), - behaviors: [ - charts.SeriesLegend( - position: charts.BehaviorPosition.bottom, - desiredMaxRows: 3, - cellPadding: const EdgeInsets.only( - right: 4.0, - bottom: 4.0, - ), - ), - charts.ChartTitle( - 'Schultag', - behaviorPosition: charts.BehaviorPosition.bottom, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, - ), - charts.ChartTitle( - 'Anzahl', - behaviorPosition: charts.BehaviorPosition.start, - titleOutsideJustification: - charts.OutsideJustification.middleDrawArea, - ), - ], - ); - }, - ), - ), - const Gap(20), - Wrap( - alignment: WrapAlignment.center, - spacing: 20, - runSpacing: 10, - children: [ - _buildLegendItem('Entschuldigt', Colors.green), - _buildLegendItem('Unentschuldigt', Colors.red), - _buildLegendItem('Nach Hause geschickt', Colors.orange), - ], - ), - const Gap(20), - ], - ), - ), - ), + child: _buildBody(index), ), ), - bottomNavigationBar: BottomAppBar( - padding: const EdgeInsets.all(10), - shape: null, - color: AppColors.backgroundColor, - child: IconTheme( - data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 800), - child: Row( - children: [ - IconButton( - tooltip: 'zurück', - icon: const Icon(Icons.arrow_back, size: 30), - onPressed: () { - Navigator.pop(context); - }, - ), - const Spacer(), - ], - ), - ), - ), + bottomNavigationBar: ChartPageBottomBar( + selectedIndex: index, + onDestinationSelected: (value) => selectedIndex.value = value, ), ); } - Widget _buildLegendItem(String label, Color color) { - return Row( - children: [ - Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(4), - ), - ), - const Gap(8), - Text(label, style: const TextStyle(fontSize: 14)), - ], - ); + Widget _buildBody(int index) { + switch (index) { + case 0: + return PupilStatsView( + sortedSchooldays: sortedSchooldays, + chartData: chartData, + ); + case 1: + return EventStatsView( + sortedSchooldays: sortedSchooldays, + eventChartData: eventChartData, + ); + case 2: + return AttendanceStatsView( + sortedSchooldays: sortedSchooldays, + attendanceChartData: attendanceChartData, + ); + default: + return const SizedBox.shrink(); + } } } diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart index badc124c..528f425a 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/chart_page_controller.dart @@ -20,6 +20,36 @@ class _ChartPageControllerState extends State { late final SchooldayEventManager _schooldayEventManager; late final AttendanceManager _attendanceManager; + bool _isLoading = true; + List _schooldays = []; + + Map< + DateTime, + ({ + int specialNeeds, + int migrationSupport, + int supportLevel3, + int regularPupils, + int newPupils, + }) + > + _chartData = {}; + + Map< + DateTime, + ({ + int parentsMeeting, + int admonition, + int afternoonCareAdmonition, + int admonitionAndBanned, + int otherEvent, + }) + > + _eventChartData = {}; + + Map + _attendanceChartData = {}; + @override void initState() { super.initState(); @@ -27,10 +57,12 @@ class _ChartPageControllerState extends State { _schoolCalendarManager = di(); _schooldayEventManager = di(); _attendanceManager = di(); + + _loadData(); } /// Gets schooldays for the current semester - List getSchooldaysForCurrentSemester() { + List _getSchooldaysForCurrentSemester() { final currentSemester = _schoolCalendarManager.getCurrentSchoolSemester(); if (currentSemester == null) { return []; @@ -39,6 +71,8 @@ class _ChartPageControllerState extends State { final allSchooldays = _schoolCalendarManager.schooldays.value; final semesterStart = currentSemester.startDate.toLocal(); final semesterEnd = currentSemester.endDate.toLocal(); + final now = DateTime.now().toLocal(); + final today = DateTime(now.year, now.month, now.day); return allSchooldays.where((schoolday) { final schooldayDate = schoolday.schoolday.toLocal(); @@ -57,115 +91,109 @@ class _ChartPageControllerState extends State { schooldayDate.month, schooldayDate.day, ); + + // Check if day is in future + if (dayDate.isAfter(today)) { + return false; + } + return (dayDate.isAfter(startDate) || dayDate == startDate) && (dayDate.isBefore(endDate) || dayDate == endDate); }).toList()..sort((a, b) => a.schoolday.compareTo(b.schoolday)); } - /// Counts pupils with specialNeeds for a given schoolday - /// Only counts pupils where pupilSince <= schoolday date - int getSpecialNeedsCountForSchoolday(Schoolday schoolday) { - final pupils = _pupilManager.allPupils; - final schooldayDate = schoolday.schoolday.toLocal(); - final dayDate = DateTime( - schooldayDate.year, - schooldayDate.month, - schooldayDate.day, - ); + Future _loadData() async { + if (!mounted) return; + setState(() => _isLoading = true); - return pupils.where((pupil) { - final pupilSince = pupil.pupilSince.toLocal(); - final sinceDate = DateTime( - pupilSince.year, - pupilSince.month, - pupilSince.day, - ); - // Pupil must be enrolled on or before this schoolday - if (sinceDate.isAfter(dayDate)) { - return false; + // 1. Get Schooldays + final schooldays = _getSchooldaysForCurrentSemester(); + + if (schooldays.isEmpty) { + if (mounted) { + setState(() { + _schooldays = []; + _chartData = {}; + _eventChartData = {}; + _attendanceChartData = {}; + _isLoading = false; + }); } - // Pupil must have specialNeeds - return pupil.specialNeeds != null && pupil.specialNeeds!.isNotEmpty; - }).length; - } + return; + } - /// Counts pupils with migrationSupportEnds for a given schoolday - /// Only counts pupils where pupilSince <= schoolday date - /// and migrationSupportEnds is on or after the schoolday - int getMigrationSupportCountForSchoolday(Schoolday schoolday) { - final pupils = _pupilManager.allPupils; - final schooldayDate = schoolday.schoolday.toLocal(); - final dayDate = DateTime( - schooldayDate.year, - schooldayDate.month, - schooldayDate.day, - ); + // 2. Pre-process Events + final allEvents = _schooldayEventManager.schooldayEvents; + final eventsById = >{}; + final eventsByDateMap = >{}; + + for (final event in allEvents) { + if (event.schooldayId != null) { + eventsById.putIfAbsent(event.schooldayId!, () => []).add(event); + } + + final d = event.schoolday!.schoolday.toLocal(); + final dayDate = DateTime(d.year, d.month, d.day); + eventsByDateMap.putIfAbsent(dayDate, () => []).add(event); + } - return pupils.where((pupil) { + // 3. Pre-process Attendance + final allMissed = _attendanceManager.missedSchooldays.value; + final missedById = >{}; + final missedByDate = >{}; + + for (final missed in allMissed) { + if (missed.schooldayId != null) { + missedById.putIfAbsent(missed.schooldayId!, () => []).add(missed); + } + + final d = missed.schoolday!.schoolday.toLocal(); + final dayDate = DateTime(d.year, d.month, d.day); + missedByDate.putIfAbsent(dayDate, () => []).add(missed); + } + + // 4. Pre-process Pupils + final pupils = _pupilManager.allPupils; + final processedPupils = pupils.map((pupil) { final pupilSince = pupil.pupilSince.toLocal(); final sinceDate = DateTime( pupilSince.year, pupilSince.month, pupilSince.day, ); - // Pupil must be enrolled on or before this schoolday - if (sinceDate.isAfter(dayDate)) { - return false; + + DateTime? migrationEnd; + if (pupil.migrationSupportEnds != null) { + final d = pupil.migrationSupportEnds!.toLocal(); + migrationEnd = DateTime(d.year, d.month, d.day); } - // Pupil must have migrationSupportEnds - if (pupil.migrationSupportEnds == null) { - return false; + DateTime? supportCreated; + int? supportLevel; + if (pupil.latestSupportLevel != null) { + supportLevel = pupil.latestSupportLevel!.level; + final d = pupil.latestSupportLevel!.createdAt.toLocal(); + supportCreated = DateTime(d.year, d.month, d.day); } - // Check if migrationSupportEnds falls on a valid schoolday - final supportEndsDate = pupil.migrationSupportEnds!.toLocal(); - final supportDate = DateTime( - supportEndsDate.year, - supportEndsDate.month, - supportEndsDate.day, + return ( + sinceDate: sinceDate, + hasSpecialNeeds: + pupil.specialNeeds != null && pupil.specialNeeds!.isNotEmpty, + migrationEnd: migrationEnd, + supportLevel: supportLevel, + supportCreated: supportCreated, ); - return dayDate.isBefore(supportDate) || dayDate == supportDate; - }).length; - } + }).toList(); - /// Counts new pupils enrolled during the semester up to a given schoolday - /// Cumulative count starting from 0 on the first semester day - int getNewPupilsCountForSchoolday( - Schoolday schoolday, - List allSchooldays, - ) { final currentSemester = _schoolCalendarManager.getCurrentSchoolSemester(); + // currentSemester can't be null if schooldays is not empty (checked by _getSchooldaysForCurrentSemester) + // but to be safe: if (currentSemester == null) { - return 0; + if (mounted) setState(() => _isLoading = false); + return; } - // First day of semester should always be 0 - if (allSchooldays.isNotEmpty) { - final firstDay = allSchooldays.first.schoolday.toLocal(); - final currentDay = schoolday.schoolday.toLocal(); - final firstDayDate = DateTime( - firstDay.year, - firstDay.month, - firstDay.day, - ); - final currentDayDate = DateTime( - currentDay.year, - currentDay.month, - currentDay.day, - ); - if (firstDayDate == currentDayDate) { - return 0; - } - } - - final pupils = _pupilManager.allPupils; - final schooldayDate = schoolday.schoolday.toLocal(); - final dayDate = DateTime( - schooldayDate.year, - schooldayDate.month, - schooldayDate.day, - ); final semesterStart = currentSemester.startDate.toLocal(); final semesterStartDate = DateTime( semesterStart.year, @@ -173,337 +201,230 @@ class _ChartPageControllerState extends State { semesterStart.day, ); - return pupils.where((pupil) { - final pupilSince = pupil.pupilSince.toLocal(); - final sinceDate = DateTime( - pupilSince.year, - pupilSince.month, - pupilSince.day, - ); - // Pupil must be enrolled on or before this schoolday - // AND pupilSince must be within the semester (on or after semester start) - return (sinceDate.isBefore(dayDate) || sinceDate == dayDate) && - (sinceDate.isAfter(semesterStartDate) || - sinceDate == semesterStartDate); - }).length; - } + final chartData = + < + DateTime, + ({ + int specialNeeds, + int migrationSupport, + int supportLevel3, + int regularPupils, + int newPupils, + }) + >{}; + final eventChartData = + < + DateTime, + ({ + int parentsMeeting, + int admonition, + int afternoonCareAdmonition, + int admonitionAndBanned, + int otherEvent, + }) + >{}; + final attendanceChartData = + {}; + + // 5. Iterate Schooldays + for (final schoolday in schooldays) { + final sDate = schoolday.schoolday.toLocal(); + final dayDate = DateTime(sDate.year, sDate.month, sDate.day); + + // --- Pupil Stats --- + int specialNeeds = 0; + int migrationSupport = 0; + int supportLevel3 = 0; + int regularPupils = 0; + int newPupils = 0; + + for (final p in processedPupils) { + // Enrolled check + if (p.sinceDate.isAfter(dayDate)) continue; + + // New Pupils + if ((p.sinceDate.isBefore(dayDate) || p.sinceDate == dayDate) && + (p.sinceDate.isAfter(semesterStartDate) || + p.sinceDate == semesterStartDate)) { + newPupils++; + } - /// Counts pupils with support level 3 (no special needs) for a given schoolday - /// Only counts pupils where pupilSince <= schoolday date - /// and latestSupportLevel.createdAt <= schoolday date - int getSupportLevel3CountForSchoolday(Schoolday schoolday) { - final pupils = _pupilManager.allPupils; - final schooldayDate = schoolday.schoolday.toLocal(); - final dayDate = DateTime( - schooldayDate.year, - schooldayDate.month, - schooldayDate.day, - ); + // Special Needs + if (p.hasSpecialNeeds) { + specialNeeds++; + } - return pupils.where((pupil) { - final pupilSince = pupil.pupilSince.toLocal(); - final sinceDate = DateTime( - pupilSince.year, - pupilSince.month, - pupilSince.day, - ); - // Pupil must be enrolled on or before this schoolday - if (sinceDate.isAfter(dayDate)) { - return false; - } + // Migration Support + bool isMigration = false; + if (p.migrationEnd != null) { + if (dayDate.isBefore(p.migrationEnd!) || dayDate == p.migrationEnd!) { + migrationSupport++; + isMigration = true; + } + } - // Pupil must NOT have specialNeeds - if (pupil.specialNeeds != null && pupil.specialNeeds!.isNotEmpty) { - return false; - } + // Support Level 3 + bool isLevel3 = false; + if (!p.hasSpecialNeeds && + p.supportLevel == 3 && + p.supportCreated != null) { + if (p.supportCreated!.isBefore(dayDate) || + p.supportCreated == dayDate) { + supportLevel3++; + isLevel3 = true; + } + } - // Pupil must have latestSupportLevel with level == 3 - final latestSupportLevel = pupil.latestSupportLevel; - if (latestSupportLevel == null || latestSupportLevel.level != 3) { - return false; + // Regular + if (!p.hasSpecialNeeds && !isMigration && !isLevel3) { + regularPupils++; + } } - // latestSupportLevel.createdAt must be equal to or before the schoolday - final supportCreatedAt = latestSupportLevel.createdAt.toLocal(); - final supportDate = DateTime( - supportCreatedAt.year, - supportCreatedAt.month, - supportCreatedAt.day, - ); - if (supportDate.isAfter(dayDate)) { - return false; + // Fix New Pupils for first day of semester logic + if (schooldays.isNotEmpty) { + final firstDay = schooldays.first.schoolday.toLocal(); + final firstDayDate = DateTime( + firstDay.year, + firstDay.month, + firstDay.day, + ); + if (dayDate == firstDayDate) { + newPupils = 0; + } } - return true; - }).length; - } - - /// Counts pupils without specialNeeds, migrationSupport, or support level 3 for a given schoolday - /// Only counts pupils where pupilSince <= schoolday date - int getRegularPupilsCountForSchoolday(Schoolday schoolday) { - final pupils = _pupilManager.allPupils; - final schooldayDate = schoolday.schoolday.toLocal(); - final dayDate = DateTime( - schooldayDate.year, - schooldayDate.month, - schooldayDate.day, - ); - - return pupils.where((pupil) { - final pupilSince = pupil.pupilSince.toLocal(); - final sinceDate = DateTime( - pupilSince.year, - pupilSince.month, - pupilSince.day, + chartData[schoolday.schoolday] = ( + specialNeeds: specialNeeds, + migrationSupport: migrationSupport, + supportLevel3: supportLevel3, + regularPupils: regularPupils, + newPupils: newPupils, ); - // Pupil must be enrolled on or before this schoolday - if (sinceDate.isAfter(dayDate)) { - return false; - } - // Pupil must NOT have specialNeeds - if (pupil.specialNeeds != null && pupil.specialNeeds!.isNotEmpty) { - return false; + // --- Events --- + final List dayEvents = []; + if (eventsById.containsKey(schoolday.id)) { + dayEvents.addAll(eventsById[schoolday.id]!); } - - // Pupil must NOT have active migrationSupportEnds - // (migrationSupportEnds is null OR it's before this schoolday) - if (pupil.migrationSupportEnds != null) { - final supportEndsDate = pupil.migrationSupportEnds!.toLocal(); - final supportDate = DateTime( - supportEndsDate.year, - supportEndsDate.month, - supportEndsDate.day, - ); - // If migrationSupportEnds is on or after this schoolday, pupil has migration support - if (dayDate.isBefore(supportDate) || dayDate == supportDate) { - return false; + if (eventsByDateMap.containsKey(dayDate)) { + for (final e in eventsByDateMap[dayDate]!) { + if (e.schooldayId != schoolday.id) { + dayEvents.add(e); + } } } - // Pupil must NOT have support level 3 (no special needs) - final latestSupportLevel = pupil.latestSupportLevel; - if (latestSupportLevel != null && latestSupportLevel.level == 3) { - final supportCreatedAt = latestSupportLevel.createdAt.toLocal(); - final supportDate = DateTime( - supportCreatedAt.year, - supportCreatedAt.month, - supportCreatedAt.day, - ); - // If support level 3 was created on or before this schoolday, exclude from regular - if (supportDate.isBefore(dayDate) || supportDate == dayDate) { - return false; + int parentsMeeting = 0; + int admonition = 0; + int afternoonCareAdmonition = 0; + int admonitionAndBanned = 0; + int otherEvent = 0; + + for (final e in dayEvents) { + switch (e.eventType) { + case SchooldayEventType.parentsMeeting: + parentsMeeting++; + break; + case SchooldayEventType.admonition: + admonition++; + break; + case SchooldayEventType.afternoonCareAdmonition: + afternoonCareAdmonition++; + break; + case SchooldayEventType.admonitionAndBanned: + admonitionAndBanned++; + break; + case SchooldayEventType.otherEvent: + otherEvent++; + break; + default: + break; } } - return true; - }).length; - } - - /// Counts events by type for a given schoolday - int getEventCountForSchoolday( - Schoolday schoolday, - SchooldayEventType eventType, - ) { - final allEvents = _schooldayEventManager.schooldayEvents; - final schooldayDate = schoolday.schoolday.toLocal(); - final dayDate = DateTime( - schooldayDate.year, - schooldayDate.month, - schooldayDate.day, - ); + eventChartData[schoolday.schoolday] = ( + parentsMeeting: parentsMeeting, + admonition: admonition, + afternoonCareAdmonition: afternoonCareAdmonition, + admonitionAndBanned: admonitionAndBanned, + otherEvent: otherEvent, + ); - return allEvents.where((event) { - // Match by schooldayId if available, otherwise by date - if (event.schooldayId == schoolday.id) { - return event.eventType == eventType; + // --- Attendance --- + final List dayMissed = []; + if (missedById.containsKey(schoolday.id)) { + dayMissed.addAll(missedById[schoolday.id]!); } - - // Fallback: match by date if schoolday object is available - if (event.schoolday != null) { - final eventDate = event.schoolday!.schoolday.toLocal(); - final eventDayDate = DateTime( - eventDate.year, - eventDate.month, - eventDate.day, - ); - if (eventDayDate == dayDate && event.eventType == eventType) { - return true; + if (missedByDate.containsKey(dayDate)) { + for (final m in missedByDate[dayDate]!) { + if (m.schooldayId != schoolday.id) { + dayMissed.add(m); + } } } - - return false; - }).length; - } - - /// Counts attendance stats for a given schoolday - ({int excused, int unexcused, int goneHome}) getAttendanceCountForSchoolday( - Schoolday schoolday, - ) { - int excused = 0; - int unexcused = 0; - int goneHome = 0; - final pupils = _pupilManager.allPupils; - final schooldayDate = schoolday.schoolday.toLocal(); - final dayDate = DateTime( - schooldayDate.year, - schooldayDate.month, - schooldayDate.day, - ); + int excused = 0; + int unexcused = 0; + int goneHome = 0; - for (final pupil in pupils) { - final missedSchooldays = - _attendanceManager - .getPupilMissedSchooldaysProxy(pupil.pupilId) - .missedSchooldays; - - for (final missedSchoolday in missedSchooldays) { - // Check if this missedSchoolday corresponds to the current schoolday - bool isSameDay = false; - - if (missedSchoolday.schooldayId == schoolday.id) { - isSameDay = true; - } else if (missedSchoolday.schoolday != null) { - final missedDate = missedSchoolday.schoolday!.schoolday.toLocal(); - final missedDayDate = DateTime( - missedDate.year, - missedDate.month, - missedDate.day, - ); - if (missedDayDate == dayDate) { - isSameDay = true; + for (final m in dayMissed) { + if (m.missedType == MissedType.missed) { + if (m.unexcused == true) { + unexcused++; + } else { + excused++; } } - - if (isSameDay) { - if (missedSchoolday.missedType == MissedType.missed) { - if (missedSchoolday.unexcused == true) { - unexcused++; - } else { - excused++; - } - } - if (missedSchoolday.returned == true) { - goneHome++; - } + if (m.returned == true) { + goneHome++; } } - } - - return (excused: excused, unexcused: unexcused, goneHome: goneHome); - } - - /// Gets data for the chart: schooldays with their counts - Map< - DateTime, - ({ - int specialNeeds, - int migrationSupport, - int supportLevel3, - int regularPupils, - int newPupils, - }) - > - getChartData() { - final schooldays = getSchooldaysForCurrentSemester(); - final Map< - DateTime, - ({ - int specialNeeds, - int migrationSupport, - int supportLevel3, - int regularPupils, - int newPupils, - }) - > - data = {}; - - for (final schoolday in schooldays) { - data[schoolday.schoolday] = ( - specialNeeds: getSpecialNeedsCountForSchoolday(schoolday), - migrationSupport: getMigrationSupportCountForSchoolday(schoolday), - supportLevel3: getSupportLevel3CountForSchoolday(schoolday), - regularPupils: getRegularPupilsCountForSchoolday(schoolday), - newPupils: getNewPupilsCountForSchoolday(schoolday, schooldays), - ); - } - - return data; - } - /// Gets event data for the chart: schooldays with event counts by type - Map< - DateTime, - ({ - int parentsMeeting, - int admonition, - int afternoonCareAdmonition, - int admonitionAndBanned, - int otherEvent, - }) - > - getEventChartData() { - final schooldays = getSchooldaysForCurrentSemester(); - final Map< - DateTime, - ({ - int parentsMeeting, - int admonition, - int afternoonCareAdmonition, - int admonitionAndBanned, - int otherEvent, - }) - > - data = {}; - - for (final schoolday in schooldays) { - data[schoolday.schoolday] = ( - parentsMeeting: getEventCountForSchoolday( - schoolday, - SchooldayEventType.parentsMeeting, - ), - admonition: getEventCountForSchoolday( - schoolday, - SchooldayEventType.admonition, - ), - afternoonCareAdmonition: getEventCountForSchoolday( - schoolday, - SchooldayEventType.afternoonCareAdmonition, - ), - admonitionAndBanned: getEventCountForSchoolday( - schoolday, - SchooldayEventType.admonitionAndBanned, - ), - otherEvent: getEventCountForSchoolday( - schoolday, - SchooldayEventType.otherEvent, - ), + attendanceChartData[schoolday.schoolday] = ( + excused: excused, + unexcused: unexcused, + goneHome: goneHome, ); } - return data; - } - - /// Gets attendance data for the chart - Map - getAttendanceChartData() { - final schooldays = getSchooldaysForCurrentSemester(); - final Map data = {}; - - for (final schoolday in schooldays) { - data[schoolday.schoolday] = getAttendanceCountForSchoolday(schoolday); + if (mounted) { + setState(() { + _schooldays = schooldays; + _chartData = chartData; + _eventChartData = eventChartData; + _attendanceChartData = attendanceChartData; + _isLoading = false; + }); } - - return data; } @override Widget build(BuildContext context) { - final chartData = getChartData(); - final schooldays = getSchooldaysForCurrentSemester(); + if (_isLoading) { + return Scaffold( + backgroundColor: const Color(0xfff2f2f7), + appBar: AppBar( + automaticallyImplyLeading: false, + backgroundColor: const Color.fromRGBO(74, 76, 161, 1), + centerTitle: true, + title: const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.bar_chart_rounded, size: 25, color: Colors.white), + SizedBox(width: 10), + Text( + 'Statistik Diagramm', + style: TextStyle(color: Colors.white, fontSize: 20), + ), + ], + ), + ), + body: const Center(child: CircularProgressIndicator()), + ); + } - if (schooldays.isEmpty) { + if (_schooldays.isEmpty) { return Scaffold( backgroundColor: const Color(0xfff2f2f7), appBar: AppBar( @@ -526,14 +447,11 @@ class _ChartPageControllerState extends State { ); } - final eventChartData = getEventChartData(); - final attendanceChartData = getAttendanceChartData(); - return ChartPage( - chartData: chartData, - eventChartData: eventChartData, - attendanceChartData: attendanceChartData, - schooldays: schooldays, + chartData: _chartData, + eventChartData: _eventChartData, + attendanceChartData: _attendanceChartData, + schooldays: _schooldays, ); } } diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/chart_page_bottom_bar.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/chart_page_bottom_bar.dart new file mode 100644 index 00000000..23bc95f3 --- /dev/null +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/chart_page_bottom_bar.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/widgets/bottom_nav_bar_layouts.dart'; + +class ChartPageBottomBar extends StatelessWidget { + final int selectedIndex; + final ValueChanged onDestinationSelected; + + const ChartPageBottomBar({ + super.key, + required this.selectedIndex, + required this.onDestinationSelected, + }); + + @override + Widget build(BuildContext context) { + return BottomNavBarLayout( + bottomNavBar: BottomAppBar( + height: 80, + padding: const EdgeInsets.symmetric(horizontal: 10), + color: AppColors.backgroundColor, + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 800), + child: Row( + children: [ + IconButton( + tooltip: 'Zurück', + icon: const Icon( + Icons.arrow_back, + size: 30, + color: Colors.white, + ), + onPressed: () { + Navigator.pop(context); + }, + ), + const Gap(10), + Expanded( + child: NavigationBarTheme( + data: NavigationBarThemeData( + labelTextStyle: WidgetStateProperty.resolveWith((states) { + return const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + ); + }), + iconTheme: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return null; // Keep default theme for selected state (usually contrasting with indicator) + } + return const IconThemeData(color: Colors.white); + }), + ), + child: NavigationBar( + elevation: 0, + backgroundColor: Colors.transparent, + indicatorColor: + Theme.of(context).colorScheme.secondaryContainer, + selectedIndex: selectedIndex, + onDestinationSelected: onDestinationSelected, + destinations: const [ + NavigationDestination( + icon: Icon(Icons.people_outline), + selectedIcon: Icon(Icons.people), + label: 'Schüler', + ), + NavigationDestination( + icon: Icon(Icons.event_note_outlined), + selectedIcon: Icon(Icons.event_note), + label: 'Ereignisse', + ), + NavigationDestination( + icon: Icon(Icons.access_time_outlined), + selectedIcon: Icon(Icons.access_time), + label: 'Anwesenheit', + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/event_stats_view.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/event_stats_view.dart new file mode 100644 index 00000000..477beb9a --- /dev/null +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/event_stats_view.dart @@ -0,0 +1,376 @@ +import 'package:community_charts_flutter/community_charts_flutter.dart' + as charts; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:intl/intl.dart'; +import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page.dart'; + +class EventStatsView extends StatefulWidget { + final List sortedSchooldays; + final Map< + DateTime, + ({ + int parentsMeeting, + int admonition, + int afternoonCareAdmonition, + int admonitionAndBanned, + int otherEvent, + }) + > + eventChartData; + + const EventStatsView({ + super.key, + required this.sortedSchooldays, + required this.eventChartData, + }); + + @override + State createState() => _EventStatsViewState(); +} + +class _EventStatsViewState extends State { + final Set _hiddenSeries = {}; + + void _toggleSeries(String seriesId) { + setState(() { + if (_hiddenSeries.contains(seriesId)) { + _hiddenSeries.remove(seriesId); + } else { + _hiddenSeries.add(seriesId); + } + }); + } + + void _onSelectionChanged(charts.SelectionModel model) { + final selectedDatum = model.selectedDatum; + + if (selectedDatum.isNotEmpty) { + final datum = selectedDatum.first; + final chartData = datum.datum as ChartData; + + // Construct data string with all values for this date + final dateData = widget.eventChartData[chartData.date]; + if (dateData == null) return; + + final buffer = StringBuffer(); + buffer.writeln('Datum: ${chartData.dateString}'); + buffer.writeln(); + buffer.writeln('Elterngespräch: ${dateData.parentsMeeting}'); + buffer.writeln('Rote Karte: ${dateData.admonition}'); + buffer.writeln('Rote Karte - OGS: ${dateData.afternoonCareAdmonition}'); + buffer.writeln('Rote Karte + Abholen: ${dateData.admonitionAndBanned}'); + buffer.writeln('Sonstiges: ${dateData.otherEvent}'); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Details'), + content: Text(buffer.toString()), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Schließen'), + ), + ], + ), + ); + } + } + + @override + Widget build(BuildContext context) { + final eventSeries = _createEventSeries(); + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(15), + const Center( + child: Text('Ereignisse nach Schultag', style: AppStyles.title), + ), + const Gap(10), + SizedBox( + height: 400, + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxHeight <= 0 || constraints.maxWidth <= 0) { + return const Center(child: Text('Chart wird geladen...')); + } + return charts.OrdinalComboChart( + eventSeries, + animate: true, + animationDuration: const Duration(milliseconds: 500), + defaultRenderer: charts.BarRendererConfig( + groupingType: charts.BarGroupingType.stacked, + ), + layoutConfig: charts.LayoutConfig( + leftMarginSpec: charts.MarginSpec.fixedPixel(60), + topMarginSpec: charts.MarginSpec.fixedPixel(20), + rightMarginSpec: charts.MarginSpec.fixedPixel(40), + bottomMarginSpec: charts.MarginSpec.fixedPixel(60), + ), + primaryMeasureAxis: const charts.NumericAxisSpec( + tickProviderSpec: charts.BasicNumericTickProviderSpec( + zeroBound: true, + ), + ), + domainAxis: charts.OrdinalAxisSpec( + tickProviderSpec: charts.StaticOrdinalTickProviderSpec( + widget.sortedSchooldays.map((schoolday) { + final dateStr = _formatDateForChart( + schoolday.schoolday, + ); + final firstOfMonthDates = _getFirstOfMonthDates(); + + if (firstOfMonthDates.contains(dateStr)) { + try { + final date = DateFormat('dd.MM').parse(dateStr); + return charts.TickSpec( + dateStr, + label: DateFormat('MMM').format(date), + ); + } catch (e) { + return charts.TickSpec(dateStr); + } + } else { + return charts.TickSpec(dateStr, label: ''); + } + }).toList(), + ), + ), + selectionModels: [ + charts.SelectionModelConfig( + type: charts.SelectionModelType.info, + changedListener: _onSelectionChanged, + ), + ], + behaviors: [ + charts.ChartTitle( + 'Schulereignisse', + behaviorPosition: charts.BehaviorPosition.start, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + ], + ); + }, + ), + ), + const Gap(20), + Center( + child: Wrap( + alignment: WrapAlignment.center, + spacing: 20, + runSpacing: 10, + children: [ + _buildLegendItem( + 'Elterngespräch', + Colors.blue, + 'parentsMeeting', + ), + _buildLegendItem('Rote Karte', Colors.red, 'admonition'), + _buildLegendItem( + 'Rote Karte - OGS', + Colors.orange, + 'afternoonCareAdmonition', + ), + _buildLegendItem( + 'Rote Karte + Abholen', + Colors.purple, + 'admonitionAndBanned', + ), + _buildLegendItem('Sonstiges', Colors.grey, 'otherEvent'), + ], + ), + ), + const Gap(20), + ], + ), + ), + ); + } + + Widget _buildLegendItem(String label, Color color, String seriesId) { + final isHidden = _hiddenSeries.contains(seriesId); + return InkWell( + onTap: () => _toggleSeries(seriesId), + child: Opacity( + opacity: isHidden ? 0.5 : 1.0, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4), + ), + child: isHidden + ? const Icon(Icons.close, size: 16, color: Colors.white) + : null, + ), + const Gap(8), + Text( + label, + style: TextStyle( + fontSize: 16, + decoration: isHidden + ? TextDecoration.lineThrough + : TextDecoration.none, + ), + ), + ], + ), + ), + ); + } + + Set _getFirstOfMonthDates() { + final Set firstOfMonthDates = {}; + String? currentMonth; + + for (final schoolday in widget.sortedSchooldays) { + final date = schoolday.schoolday.toLocal(); + final monthKey = '${date.year}-${date.month}'; + if (monthKey != currentMonth) { + currentMonth = monthKey; + firstOfMonthDates.add(_formatDateForChart(schoolday.schoolday)); + } + } + return firstOfMonthDates; + } + + String _formatDateForChart(DateTime date) { + final localDate = date.isUtc ? date.toLocal() : date; + return DateFormat('dd.MM').format(localDate); + } + + List> _createEventSeries() { + final List> series = []; + + if (!_hiddenSeries.contains('parentsMeeting')) { + final parentsMeetingData = widget.sortedSchooldays.map((schoolday) { + final data = widget.eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.parentsMeeting ?? 0, + seriesId: 'parentsMeeting', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Elterngespräch', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.blue), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: parentsMeetingData, + ), + ); + } + + if (!_hiddenSeries.contains('admonition')) { + final admonitionData = widget.sortedSchooldays.map((schoolday) { + final data = widget.eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.admonition ?? 0, + seriesId: 'admonition', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Rote Karte', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: admonitionData, + ), + ); + } + + if (!_hiddenSeries.contains('afternoonCareAdmonition')) { + final afternoonCareAdmonitionData = widget.sortedSchooldays.map(( + schoolday, + ) { + final data = widget.eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.afternoonCareAdmonition ?? 0, + seriesId: 'afternoonCareAdmonition', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Rote Karte - OGS', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: afternoonCareAdmonitionData, + ), + ); + } + + if (!_hiddenSeries.contains('admonitionAndBanned')) { + final admonitionAndBannedData = widget.sortedSchooldays.map((schoolday) { + final data = widget.eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.admonitionAndBanned ?? 0, + seriesId: 'admonitionAndBanned', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Rote Karte + Abholen', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.purple), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: admonitionAndBannedData, + ), + ); + } + + if (!_hiddenSeries.contains('otherEvent')) { + final otherEventData = widget.sortedSchooldays.map((schoolday) { + final data = widget.eventChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.otherEvent ?? 0, + seriesId: 'otherEvent', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Sonstiges', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.grey), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: otherEventData, + ), + ); + } + + return series; + } +} diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/pupil_stats_view.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/pupil_stats_view.dart new file mode 100644 index 00000000..00cc2996 --- /dev/null +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/pupil_stats_view.dart @@ -0,0 +1,404 @@ +import 'package:community_charts_flutter/community_charts_flutter.dart' + as charts; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:intl/intl.dart'; +import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page.dart'; + +class PupilStatsView extends StatefulWidget { + final List sortedSchooldays; + final Map< + DateTime, + ({ + int specialNeeds, + int migrationSupport, + int supportLevel3, + int regularPupils, + int newPupils, + }) + > + chartData; + + const PupilStatsView({ + super.key, + required this.sortedSchooldays, + required this.chartData, + }); + + @override + State createState() => _PupilStatsViewState(); +} + +class _PupilStatsViewState extends State { + final Set _hiddenSeries = {}; + + void _toggleSeries(String seriesId) { + setState(() { + if (_hiddenSeries.contains(seriesId)) { + _hiddenSeries.remove(seriesId); + } else { + _hiddenSeries.add(seriesId); + } + }); + } + + void _onSelectionChanged(charts.SelectionModel model) { + final selectedDatum = model.selectedDatum; + + if (selectedDatum.isNotEmpty) { + final datum = selectedDatum.first; + final chartData = datum.datum as ChartData; + + // Construct data string with all values for this date + final dateData = widget.chartData[chartData.date]; + if (dateData == null) return; + + final buffer = StringBuffer(); + buffer.writeln('Datum: ${chartData.dateString}'); + buffer.writeln(); + buffer.writeln('Besonderer Förderbedarf: ${dateData.specialNeeds}'); + buffer.writeln('Migrationsunterstützung: ${dateData.migrationSupport}'); + buffer.writeln('Förderstufe 3: ${dateData.supportLevel3}'); + buffer.writeln('Reguläre Schüler: ${dateData.regularPupils}'); + buffer.writeln('Neue Schüler: ${dateData.newPupils}'); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Details'), + content: Text(buffer.toString()), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Schließen'), + ), + ], + ), + ); + } + } + + @override + Widget build(BuildContext context) { + final series = _createSeries(); + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(15), + const Center( + child: Text( + 'Schülerzahlen nach Schultag', + style: AppStyles.title, + ), + ), + const Gap(10), + SizedBox( + height: 400, + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxHeight <= 0 || constraints.maxWidth <= 0) { + return const Center(child: Text('Chart wird geladen...')); + } + return charts.OrdinalComboChart( + series, + animate: true, + animationDuration: const Duration(milliseconds: 500), + defaultRenderer: charts.LineRendererConfig( + includeArea: true, + stacked: true, + ), + customSeriesRenderers: [ + charts.LineRendererConfig( + customRendererId: 'lineSeries', + strokeWidthPx: 3.0, + includeArea: false, + stacked: false, + ), + ], + layoutConfig: charts.LayoutConfig( + leftMarginSpec: charts.MarginSpec.fixedPixel(60), + topMarginSpec: charts.MarginSpec.fixedPixel(20), + rightMarginSpec: charts.MarginSpec.fixedPixel(40), + bottomMarginSpec: charts.MarginSpec.fixedPixel(60), + ), + primaryMeasureAxis: const charts.NumericAxisSpec( + tickProviderSpec: charts.BasicNumericTickProviderSpec( + zeroBound: true, + ), + ), + domainAxis: charts.OrdinalAxisSpec( + tickProviderSpec: charts.StaticOrdinalTickProviderSpec( + widget.sortedSchooldays.map((schoolday) { + final dateStr = _formatDateForChart( + schoolday.schoolday, + ); + final firstOfMonthDates = _getFirstOfMonthDates(); + + if (firstOfMonthDates.contains(dateStr)) { + try { + final date = DateFormat('dd.MM').parse(dateStr); + return charts.TickSpec( + dateStr, + label: DateFormat('MMM').format(date), + ); + } catch (e) { + return charts.TickSpec(dateStr); + } + } else { + return charts.TickSpec(dateStr, label: ''); + } + }).toList(), + ), + ), + selectionModels: [ + charts.SelectionModelConfig( + type: charts.SelectionModelType.info, + changedListener: _onSelectionChanged, + ), + ], + behaviors: [ + charts.ChartTitle( + 'Schüler*innen', + titleStyleSpec: const charts.TextStyleSpec( + fontSize: 16, + ), + behaviorPosition: charts.BehaviorPosition.start, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + ], + ); + }, + ), + ), + const Gap(20), + Center( + child: Wrap( + alignment: WrapAlignment.center, + spacing: 20, + runSpacing: 10, + children: [ + _buildLegendItem( + 'Besonderer Förderbedarf', + AppColors.accentColor, + 'Special Needs', + ), + _buildLegendItem( + 'Migrationsunterstützung', + AppColors.backgroundColor, + 'Migration Support', + ), + _buildLegendItem( + 'Förderstufe 3', + AppColors.warningButtonColor, + 'Support Level 3', + ), + _buildLegendItem( + 'Reguläre Schüler', + const Color.fromARGB(255, 200, 200, 200), + 'Regular Pupils', + ), + _buildLegendItem( + 'Neue Schüler', + AppColors.successButtonColor, + 'New Pupils', + ), + ], + ), + ), + const Gap(20), + ], + ), + ), + ); + } + + Widget _buildLegendItem(String label, Color color, String seriesId) { + final isHidden = _hiddenSeries.contains(seriesId); + return InkWell( + onTap: () => _toggleSeries(seriesId), + child: Opacity( + opacity: isHidden ? 0.5 : 1.0, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4), + ), + child: isHidden + ? const Icon(Icons.close, size: 16, color: Colors.white) + : null, + ), + const Gap(8), + Text( + label, + style: TextStyle( + fontSize: 16, + decoration: isHidden + ? TextDecoration.lineThrough + : TextDecoration.none, + ), + ), + ], + ), + ), + ); + } + + Set _getFirstOfMonthDates() { + final Set firstOfMonthDates = {}; + String? currentMonth; + + for (final schoolday in widget.sortedSchooldays) { + final date = schoolday.schoolday.toLocal(); + final monthKey = '${date.year}-${date.month}'; + if (monthKey != currentMonth) { + currentMonth = monthKey; + firstOfMonthDates.add(_formatDateForChart(schoolday.schoolday)); + } + } + return firstOfMonthDates; + } + + String _formatDateForChart(DateTime date) { + final localDate = date.isUtc ? date.toLocal() : date; + return DateFormat('dd.MM').format(localDate); + } + + List> _createSeries() { + final List> series = []; + + if (!_hiddenSeries.contains('Special Needs')) { + final specialNeedsData = widget.sortedSchooldays.map((schoolday) { + final data = widget.chartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.specialNeeds ?? 0, + seriesId: 'Special Needs', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Special Needs', + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.accentColor), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: specialNeedsData, + ), + ); + } + + if (!_hiddenSeries.contains('Migration Support')) { + final migrationSupportData = widget.sortedSchooldays.map((schoolday) { + final data = widget.chartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.migrationSupport ?? 0, + seriesId: 'Migration Support', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Migration Support', + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.backgroundColor), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: migrationSupportData, + ), + ); + } + + if (!_hiddenSeries.contains('Support Level 3')) { + final supportLevel3Data = widget.sortedSchooldays.map((schoolday) { + final data = widget.chartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.supportLevel3 ?? 0, + seriesId: 'Support Level 3', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Support Level 3', + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.warningButtonColor), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: supportLevel3Data, + ), + ); + } + + if (!_hiddenSeries.contains('Regular Pupils')) { + final regularPupilsData = widget.sortedSchooldays.map((schoolday) { + final data = widget.chartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.regularPupils ?? 0, + seriesId: 'Regular Pupils', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Regular Pupils', + colorFn: (_, __) => charts.ColorUtil.fromDartColor( + const Color.fromARGB(255, 200, 200, 200), + ), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: regularPupilsData, + ), + ); + } + + if (!_hiddenSeries.contains('New Pupils')) { + final newPupilsData = widget.sortedSchooldays.map((schoolday) { + final data = widget.chartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.newPupils ?? 0, + seriesId: 'New Pupils', + ); + }).toList(); + + series.add( + charts.Series( + id: 'New Pupils', + colorFn: (_, __) => + charts.ColorUtil.fromDartColor(AppColors.successButtonColor), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: newPupilsData, + )..setAttribute(charts.rendererIdKey, 'lineSeries'), + ); + } + + return series; + } +} diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart index 9f3c3579..96fcdacb 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart @@ -26,7 +26,7 @@ class StatisticsController extends State { super.initState(); } - calculateLanguageOccurrences() { + void calculateLanguageOccurrences() { for (PupilProxy pupil in pupils) { languageOccurrences[pupil.language] = (languageOccurrences[pupil.language] ?? 0) + 1; @@ -38,13 +38,24 @@ class StatisticsController extends State { } List pupilsNotEnrolledOnDate(List givenPupils) { + Map pupilsWithEnrollmentDate = {}; + for (PupilProxy pupil in givenPupils) { + pupilsWithEnrollmentDate[pupil.internalId] = pupil.pupilSince + .formatDateForUser(); + } + // debugger(); return givenPupils.where((pupil) { - return !(pupil.pupilSince.month == 8 && pupil.pupilSince.day == 1); + final date = pupil.pupilSince.toLocal().add(const Duration(hours: 2)); + return !(date.month == DateTime.august && date.day == 1); }).toList(); } List pupilsEnrolledAfterDate(DateTime date) { - return pupils.where((pupil) => pupil.pupilSince.isAfter(date)).toList(); + final enrolledPupils = pupils + .where((pupil) => pupil.pupilSince.isAfter(date)) + .toList(); + enrolledPupils.sort((a, b) => a.pupilSince.compareTo(b.pupilSince)); + return enrolledPupils; } List pupilsEnrolledBetweenDates( diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart index 2bd37424..ab8872a3 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:intl/intl.dart'; +import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_list_card.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; class EnrollmentListTiles extends StatelessWidget { @@ -9,6 +12,12 @@ class EnrollmentListTiles extends StatelessWidget { @override Widget build(BuildContext context) { + final Set seenEnrollmentDates = {}; + + final currentYearPupils = controller.pupilsEnrolledAfterDate( + DateTime(2025, 08, 01), + )..sort((a, b) => b.pupilSince.compareTo(a.pupilSince)); + return ListTileTheme( contentPadding: const EdgeInsets.all(0), dense: true, @@ -46,12 +55,7 @@ class EnrollmentListTiles extends StatelessWidget { ), const Gap(10), Text( - controller - .pupilsEnrolledAfterDate( - DateFormat('yyy-MM-dd').parse('2024-08-01'), - ) - .length - .toString(), + currentYearPupils.length.toString(), style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, @@ -63,28 +67,58 @@ class EnrollmentListTiles extends StatelessWidget { children: [ ListView.builder( shrinkWrap: true, - itemCount: - controller - .pupilsEnrolledAfterDate(DateTime(2024, 08, 01)) - .length, + physics: const NeverScrollableScrollPhysics(), + itemCount: currentYearPupils.length, itemBuilder: (BuildContext context, int index) { - final pupil = - controller.pupilsEnrolledAfterDate( - DateTime(2024, 08, 01), - )[index]; - return Column( - children: [ - ListTile( - title: Text(pupil.firstName), - subtitle: Text(pupil.group), + final pupil = currentYearPupils[index]; + final bool isDatePrinted = seenEnrollmentDates.contains( + DateTime( + pupil.pupilSince.year, + pupil.pupilSince.month, + pupil.pupilSince.day, + ), + ); + + if (!isDatePrinted) { + seenEnrollmentDates.add( + DateTime( + pupil.pupilSince.year, + pupil.pupilSince.month, + pupil.pupilSince.day, ), - ], + ); + } + + return Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!isDatePrinted) + Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Row( + children: [ + const Gap(5), + Text( + '${pupil.pupilSince.asWeekdayName(context)}, ${pupil.pupilSince.formatDateForUser()}', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: AppColors.backgroundColor, + fontSize: 18, + ), + ), + ], + ), + ), + PupilListCard(passedPupil: pupil), + ], + ), ); }, ), ], ), - Row( children: [ const Text( @@ -95,8 +129,8 @@ class EnrollmentListTiles extends StatelessWidget { Text( controller .pupilsEnrolledBetweenDates( - DateFormat('yyy-MM-dd').parse('2023-08-02'), - DateFormat('yyy-MM-dd').parse('2024-07-31'), + DateFormat('yyy-MM-dd').parse('2025-08-02'), + DateFormat('yyy-MM-dd').parse('2026-07-31'), ) .length .toString(), @@ -108,175 +142,8 @@ class EnrollmentListTiles extends StatelessWidget { ), ], ), - // ListView.builder( - // padding: EdgeInsets.only(left: 10, top: 5, bottom: 15), - // shrinkWrap: true, - // physics: NeverScrollableScrollPhysics(), - // itemCount: sortedLanguageOccurrences.length, - // itemBuilder: (BuildContext context, int index) { - // final entry = sortedLanguageOccurrences[index]; - // final language = entry.key; - // final occurrences = entry.value; - // return Padding( - // padding: const EdgeInsets.all(5.0), - // child: GestureDetector( - // onTap: () { - // //- TO-DO: change missed class function - // //- like _changeMissedClassHermannpupilPage - // }, - // onLongPress: () async {}, - // child: SingleChildScrollView( - // scrollDirection: Axis.horizontal, - // child: Row( - // children: [ - // Text( - // "$language:", - // style: TextStyle( - // color: Colors.black, - // fontSize: 16, - // ), - // ), - // Gap(10), - // Text( - // "$occurrences", - // style: TextStyle( - // color: Colors.black, - // fontWeight: FontWeight.bold, - // fontSize: 18, - // ), - // ), - // ], - // ), - // ), - // ), - // ); - // }, - // ), ], ), ); } } - -enrollmentListTiles(context, StatisticsController controller) { - return ListTileTheme( - contentPadding: const EdgeInsets.all(0), - dense: true, - horizontalTitleGap: 0.0, - minLeadingWidth: 0, - child: ExpansionTile( - tilePadding: const EdgeInsets.all(0), - title: Row( - children: [ - const Text( - 'Unterjährige Anmeldungen', - style: TextStyle(color: Colors.black, fontSize: 18), - ), - const Gap(10), - Text( - controller - .pupilsNotEnrolledOnDate(controller.pupils) - .length - .toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ], - ), - children: [ - Row( - children: [ - const Text( - 'im laufenden Schulahr:', - style: TextStyle(color: Colors.black, fontSize: 18), - ), - const Gap(10), - Text( - controller - .pupilsEnrolledAfterDate( - DateFormat('yyy-MM-dd').parse('2024-08-01'), - ) - .length - .toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ], - ), - Row( - children: [ - const Text( - 'im letzten Schulahr:', - style: TextStyle(color: Colors.black, fontSize: 18), - ), - const Gap(10), - Text( - controller - .pupilsEnrolledBetweenDates( - DateFormat('yyy-MM-dd').parse('2023-08-02'), - DateFormat('yyy-MM-dd').parse('2024-07-31'), - ) - .length - .toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ], - ), - // ListView.builder( - // padding: EdgeInsets.only(left: 10, top: 5, bottom: 15), - // shrinkWrap: true, - // physics: NeverScrollableScrollPhysics(), - // itemCount: sortedLanguageOccurrences.length, - // itemBuilder: (BuildContext context, int index) { - // final entry = sortedLanguageOccurrences[index]; - // final language = entry.key; - // final occurrences = entry.value; - // return Padding( - // padding: const EdgeInsets.all(5.0), - // child: GestureDetector( - // onTap: () { - // //- TO-DO: change missed class function - // //- like _changeMissedClassHermannpupilPage - // }, - // onLongPress: () async {}, - // child: SingleChildScrollView( - // scrollDirection: Axis.horizontal, - // child: Row( - // children: [ - // Text( - // "$language:", - // style: TextStyle( - // color: Colors.black, - // fontSize: 16, - // ), - // ), - // Gap(10), - // Text( - // "$occurrences", - // style: TextStyle( - // color: Colors.black, - // fontWeight: FontWeight.bold, - // fontSize: 18, - // ), - // ), - // ], - // ), - // ), - // ), - // ); - // }, - // ), - ], - ), - ); -} diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart index f797a8bd..f8943df4 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart @@ -1,11 +1,35 @@ +import 'package:community_charts_flutter/community_charts_flutter.dart' + as charts; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:school_data_hub_flutter/common/widgets/dialogs/pupil_list_dialog.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; languagesListTiles(context, StatisticsController controller) { List> sortedLanguageOccurrences = controller.languageOccurrences.entries.toList() ..sort((a, b) => b.value.compareTo(a.value)); + + final List palette = [ + Colors.blue, + Colors.red, + Colors.green, + Colors.orange, + Colors.purple, + Colors.teal, + Colors.amber, + Colors.indigo, + Colors.brown, + Colors.pink, + Colors.cyan, + Colors.lime, + Colors.deepOrange, + Colors.lightBlue, + Colors.deepPurple, + ]; + + Color getColor(int index) => palette[index % palette.length]; + return ListTileTheme( contentPadding: const EdgeInsets.all(0), dense: true, @@ -32,46 +56,104 @@ languagesListTiles(context, StatisticsController controller) { ), children: [ Card( - child: ListView.builder( - padding: const EdgeInsets.only(left: 10, top: 5, bottom: 15), - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: sortedLanguageOccurrences.length, - itemBuilder: (BuildContext context, int index) { - final entry = sortedLanguageOccurrences[index]; - final language = entry.key; - final occurrences = entry.value; - return Padding( - padding: const EdgeInsets.all(5.0), - child: GestureDetector( - onTap: () {}, - onLongPress: () async {}, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - Text( - "$language:", - style: const TextStyle( - color: Colors.black, - fontSize: 18, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: ListView.builder( + padding: const EdgeInsets.only(left: 10, top: 5, bottom: 15), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: sortedLanguageOccurrences.length, + itemBuilder: (BuildContext context, int index) { + final entry = sortedLanguageOccurrences[index]; + final language = entry.key; + final occurrences = entry.value; + return Padding( + padding: const EdgeInsets.all(5.0), + child: GestureDetector( + onTap: () async { + final pupilsWithLanguage = controller.pupils + .where((p) => p.language == language) + .toList(); + await showDialog( + context: context, + builder: (context) => PupilListDialog( + title: 'Schüler mit Sprache: $language', + pupils: pupilsWithLanguage, + ), + ); + }, + onLongPress: () async {}, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + Container( + width: 15, + height: 15, + decoration: BoxDecoration( + color: getColor(index), + shape: BoxShape.circle, + ), + ), + const Gap(10), + Text( + "$language:", + style: const TextStyle( + color: Colors.black, + fontSize: 18, + ), + ), + const Gap(10), + Text( + "$occurrences", + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + ], ), ), - const Gap(10), - Text( - "$occurrences", - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, - ), + ), + ); + }, + ), + ), + Expanded( + child: SizedBox( + height: 300, + child: charts.PieChart( + [ + charts.Series, String>( + id: 'Languages', + domainFn: (MapEntry entry, _) => entry.key, + measureFn: (MapEntry entry, _) => + entry.value, + colorFn: (_, int? index) => + charts.ColorUtil.fromDartColor( + getColor(index ?? 0), + ), + data: sortedLanguageOccurrences, + labelAccessorFn: (MapEntry row, _) => + '${row.value}', + ), + ], + animate: true, + defaultRenderer: charts.ArcRendererConfig( + arcWidth: 60, + arcRendererDecorators: [ + charts.ArcLabelDecorator( + labelPosition: charts.ArcLabelPosition.auto, ), ], ), ), ), - ); - }, + ), + ], ), ), ], diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart index 43065b2c..1ba578d9 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart @@ -2,8 +2,6 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; -// ignore: directives_ordering -import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page_controller.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/list_tiles/group_list_tiles.dart'; @@ -38,22 +36,9 @@ class StatisticsPage extends StatelessWidget { child: Column( children: [ const Gap(15), - Row( + const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Schulzahlen', style: AppStyles.title), - IconButton( - icon: const Icon(Icons.show_chart), - tooltip: 'Diagramm anzeigen', - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => const ChartPageController(), - ), - ); - }, - ), - ], + children: [Text('Schulzahlen', style: AppStyles.title)], ), const Gap(10), Expanded( From 6628c5a905df5aeccdbaf2e750a333c5c70ef25a Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sun, 30 Nov 2025 16:00:36 +0100 Subject: [PATCH 19/52] [APP]: Relocated ressources --- .../app_main_navigation/tools_page.dart | 197 ++++++++++++------ .../widgets/pupil_lists_buttons.dart | 10 + .../settings_page/settings_page.dart | 27 ++- .../widgets/settings_account_section.dart | 12 +- .../widgets/settings_admin_section.dart | 27 +-- .../widgets/settings_tools_section.dart | 123 ----------- 6 files changed, 167 insertions(+), 229 deletions(-) delete mode 100644 school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_tools_section.dart diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart index 56a4d3ed..b8190dc2 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart @@ -12,7 +12,12 @@ import 'package:school_data_hub_flutter/common/widgets/generic_components/generi import 'package:school_data_hub_flutter/common/widgets/qr/qr_image_picker.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/birthdays_page.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page_controller.dart'; +import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_page/timetable_page.dart'; import 'package:watch_it/watch_it.dart'; @@ -95,50 +100,139 @@ class ToolsPage extends WatchingWidget { } Navigator.of(context).push( MaterialPageRoute( - builder: - (context) => PupilIdentityStreamPage( - role: PupilIdentityStreamRole.receiver, - importedChannelName: channelName, - ), + builder: (context) => PupilIdentityStreamPage( + role: PupilIdentityStreamRole.receiver, + importedChannelName: channelName, + ), ), ); }, - onLongPress: - Platform.isAndroid || Platform.isIOS - ? () async { - final channelName = await shortTextfieldDialog( - context: context, - title: 'Verbindungscode eingeben', - hintText: 'z.B. 12345678', - labelText: 'Verbindungscode', - ); - if (channelName == null || channelName.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Kein gültiger Verbindungscode.', - ), + onLongPress: Platform.isAndroid || Platform.isIOS + ? () async { + final channelName = await shortTextfieldDialog( + context: context, + title: 'Verbindungscode eingeben', + hintText: 'z.B. 12345678', + labelText: 'Verbindungscode', + ); + if (channelName == null || channelName.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Kein gültiger Verbindungscode.', ), - ); - return; - } - Navigator.of(context).push( - MaterialPageRoute( - builder: - (context) => PupilIdentityStreamPage( - role: PupilIdentityStreamRole.receiver, - importedChannelName: channelName, - ), ), ); + return; } - : null, - icon: Icons.mobile_screen_share, - title: 'Schülerdaten von einem anderen Gerät übertragen', + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => PupilIdentityStreamPage( + role: PupilIdentityStreamRole.receiver, + importedChannelName: channelName, + ), + ), + ); + } + : null, + icon: Icons.qr_code_scanner_rounded, + title: 'Schülerdaten aus einem anderen Gerät importieren', subtitle: 'QR-Code scannen oder Verbindungscode eingeben', color: Colors.blue[700]!, ), - + const Gap(16), + _buildToolButton( + onPressed: () async { + final List? + pupilIds = await Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => SelectPupilsListPage( + selectablePupils: di() + .getPupilsFromInternalIds( + di().availablePupilIds, + ), + ), + ), + ); + if (pupilIds == null || pupilIds.isEmpty) { + return; + } + final internalIds = di() + .getInternalIdsFromPupilIds(pupilIds); + final String encryptedPupilIdentities = + await di() + .generatePupilIdentitiesQrData(internalIds); + if (!context.mounted) return; + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => PupilIdentityStreamPage( + role: PupilIdentityStreamRole.sender, + encryptedData: encryptedPupilIdentities, + selectedPupilIds: pupilIds, + ), + ), + ); + }, + icon: Icons.mobile_screen_share, + title: 'Schüler-Ids mit einem anderen Gerät teilen', + subtitle: 'Daten auf ein anderes Gerät übertragen', + color: Colors.indigo[700]!, + ), + const Gap(16), + _buildToolButton( + onPressed: () async { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const ChartPageController(), + ), + ); + }, + icon: Icons.bar_chart_rounded, + title: 'Datenvisualisierungen', + subtitle: 'Schüler*inenzahlen, Fehlzeiten, Ereignisse', + color: Colors.orange[700]!, + ), + const Gap(16), + _buildToolButton( + onPressed: () async { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const Statistics(), + ), + ); + }, + icon: Icons.table_chart_rounded, + title: 'Statistik-Zahlen ansehen', + subtitle: 'Detaillierte Tabellen und Zahlenübersicht', + color: Colors.teal[700]!, + ), + const Gap(16), + _buildToolButton( + onPressed: () async { + final DateTime? selectedDate = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime.now(), + ); + if (selectedDate == null) { + return; + } + if (context.mounted) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => + BirthdaysView(selectedDate: selectedDate), + ), + ); + } + }, + icon: Icons.cake_rounded, + title: 'Geburtstage', + subtitle: + 'Vergangene Geburtstage seit einem Datum anzeigen', + color: Colors.pink[600]!, + ), const Gap(16), // Admin section @@ -161,24 +255,7 @@ class ToolsPage extends WatchingWidget { const Gap(16), - // _buildToolButton( - // onPressed: () async { - // final bool? confirm = await confirmationDialog( - // context: context, - // title: 'Datenbank aus json importieren', - // message: 'Json importieren?', - // ); - // if (confirm == true) { - // di().importSupportLevelsFromJson(); - // } - // }, - // icon: Icons.file_upload, - // title: 'Daten aus JSON importieren', - // subtitle: 'Support-Levels und andere Daten importieren', - // color: Colors.orange[700]!, - // ), - - // const Gap(16), + const Gap(16), ], // Desktop-only section @@ -206,20 +283,8 @@ class ToolsPage extends WatchingWidget { const Gap(16), _buildToolButton( - onPressed: () async { - importFromQrImage(); - }, - icon: Icons.qr_code_scanner, - title: 'Daten aus QR-Bilddatei importieren', - subtitle: 'QR-Code aus Bilddatei scannen', - color: Colors.teal[700]!, - ), - - const Gap(16), - - _buildToolButton( - onPressed: - () => importFileWithWindows('pupil_identities'), + onPressed: () => + importFileWithWindows('pupil_identities'), icon: Icons.people, title: 'ID-Liste importieren', subtitle: 'Schüler-Identitäten aus Datei importieren', diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart index 38497847..86b5348a 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/pupil_lists_buttons.dart @@ -8,6 +8,7 @@ import 'package:school_data_hub_flutter/features/app_main_navigation/widgets/mai import 'package:school_data_hub_flutter/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart'; import 'package:school_data_hub_flutter/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart'; import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart'; +import 'package:school_data_hub_flutter/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart'; import 'package:school_data_hub_flutter/features/ogs/ogs_list_page.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/_credit/credit_list_page/credit_list_page.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart'; @@ -156,6 +157,15 @@ class PupilListButtons extends WatchingWidget { ), buttonText: locale.matrixRooms, ), + const MainMenuButton( + destinationPage: PupilsMatrixContactsListPage(), + buttonIcon: Icon( + Icons.group, + size: 50, + color: AppColors.gridViewColor, + ), + buttonText: 'Matrix Kontakte', + ), ], ); } diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart index 1ebc6b4c..9c9d89fc 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart @@ -9,7 +9,6 @@ import 'package:school_data_hub_flutter/core/updater/shorebird_update_manager.da import 'package:school_data_hub_flutter/features/app_settings/settings_page/widgets/settings_account_section.dart'; import 'package:school_data_hub_flutter/features/app_settings/settings_page/widgets/settings_admin_section.dart'; import 'package:school_data_hub_flutter/features/app_settings/settings_page/widgets/settings_session_section.dart'; -import 'package:school_data_hub_flutter/features/app_settings/settings_page/widgets/settings_tools_section.dart'; import 'package:school_data_hub_flutter/l10n/app_localizations.dart'; import 'package:watch_it/watch_it.dart'; @@ -40,7 +39,6 @@ class SettingsPage extends StatelessWidget { const SettingsAccountSection(), const SettingsSessionSection(), if (isAdmin) const SettingsAdminSection(), - const SettingsToolsSection(), SettingsSection( title: const Padding( padding: EdgeInsets.all(8.0), @@ -69,20 +67,19 @@ class SettingsPage extends StatelessWidget { SettingsTile.navigation( leading: const Icon(Icons.info_rounded), title: const Text('App Infos'), - onPressed: - (context) => showAboutDialog( - context: context, - applicationIcon: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: Image.asset( - 'assets/schuldaten_hub_logo.png', - scale: 8, - ), - ), - applicationName: 'Schuldaten App', - applicationVersion: envManager.packageInfo.version, - applicationLegalese: '© 2025 Schuldaten Hub', + onPressed: (context) => showAboutDialog( + context: context, + applicationIcon: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Image.asset( + 'assets/schuldaten_hub_logo.png', + scale: 8, ), + ), + applicationName: 'Schuldaten App', + applicationVersion: envManager.packageInfo.version, + applicationLegalese: '© 2025 Schuldaten Hub', + ), ), SettingsTile.navigation( leading: const Icon(Icons.update_rounded), diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_account_section.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_account_section.dart index 20fb9cf7..749fc03a 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_account_section.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_account_section.dart @@ -14,11 +14,11 @@ class SettingsAccountSection extends AbstractSettingsSection with WatchItMixin { Widget build(BuildContext context) { di.allReady(); - final String username = - watchPropertyValue( - (HubSessionManager x) => x.user, - )!.userInfo!.userName!; + final String username = watchPropertyValue( + (HubSessionManager x) => x.user, + )!.userInfo!.userName!; final isTester = di().user!.userFlags.isTester; + final isAdmin = di().isAdmin; return SettingsSection( title: const Text( 'Konto', @@ -38,7 +38,9 @@ class SettingsAccountSection extends AbstractSettingsSection with WatchItMixin { ), SettingsTile( leading: const Icon(Icons.build_rounded), - title: Text('Ist Tester: ${isTester ? "Ja" : "Nein"}'), + title: Text( + 'Tester: ${isTester ? "Ja" : "Nein"} | Admin: ${isAdmin ? "Ja" : "Nein"}', + ), ), SettingsTile.navigation( diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_admin_section.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_admin_section.dart index a049299a..65eddefd 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_admin_section.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_admin_section.dart @@ -14,7 +14,6 @@ import 'package:school_data_hub_flutter/features/learning_support/domain/learnin import 'package:school_data_hub_flutter/features/learning_support/domain/support_category_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/presentation/set_matrix_environment_page/set_matrix_environment_controller.dart'; -import 'package:school_data_hub_flutter/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:school_data_hub_flutter/features/school/presentation/edit_school_data_page/edit_school_data_page.dart'; import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart'; @@ -134,14 +133,12 @@ class SettingsAdminSection extends AbstractSettingsSection with WatchItMixin { }, ), SettingsTile.navigation( - leading: - matrixPolicyManagerIsRegistered - ? const Icon(Icons.check_circle_rounded, color: Colors.green) - : const Icon(Icons.chat_rounded), - title: - matrixPolicyManagerIsRegistered - ? const Text('Raumverwaltung initialisiert') - : const Text('Raumverwaltung initialisieren'), + leading: matrixPolicyManagerIsRegistered + ? const Icon(Icons.check_circle_rounded, color: Colors.green) + : const Icon(Icons.chat_rounded), + title: matrixPolicyManagerIsRegistered + ? const Text('Raumverwaltung initialisiert') + : const Text('Raumverwaltung initialisieren'), onPressed: (context) async { if (matrixPolicyManagerIsRegistered) { _notificationService.showSnackBar( @@ -168,17 +165,7 @@ class SettingsAdminSection extends AbstractSettingsSection with WatchItMixin { .deleteAndDeregisterMatrixPolicyManager(); }, ), - SettingsTile.navigation( - leading: const Icon(Icons.group), - title: const Text('Kontakte bearbeiten'), - onPressed: (context) async { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => const PupilsMatrixContactsListPage(), - ), - ); - }, - ), + SettingsTile.navigation( leading: const Icon(Icons.calendar_month_rounded), title: const Text('Schultage-Kalender'), diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_tools_section.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_tools_section.dart deleted file mode 100644 index a460ad7e..00000000 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/widgets/settings_tools_section.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_settings_ui/flutter_settings_ui.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/birthdays_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart'; -import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; -import 'package:watch_it/watch_it.dart'; - -class SettingsToolsSection extends AbstractSettingsSection { - const SettingsToolsSection({super.key}); - - @override - Widget build(BuildContext context) { - return SettingsSection( - title: const Padding( - padding: EdgeInsets.all(8.0), - child: Text( - 'Tools', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), - ), - ), - tiles: [ - SettingsTile.navigation( - leading: const Icon(Icons.bar_chart_rounded), - title: const Text('Statistik-Zahlen ansehen'), - onPressed: (context) { - Navigator.of( - context, - ).push(MaterialPageRoute(builder: (ctx) => const Statistics())); - }, - ), - SettingsTile.navigation( - leading: const Icon(Icons.cake_rounded), - title: const Text('vergangene Geburtstage seit einem Datum'), - onPressed: (context) async { - final DateTime? selectedDate = await showDatePicker( - context: context, - initialDate: DateTime.now(), - firstDate: DateTime(2000), - lastDate: DateTime.now(), - ); - if (selectedDate == null) { - return; - } - if (context.mounted) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => BirthdaysView(selectedDate: selectedDate), - ), - ); - } - }, - ), - SettingsTile.navigation( - leading: const Row( - children: [ - const Icon(Icons.mobile_screen_share, size: 30), - const Icon(Icons.mobile_friendly_rounded, size: 30), - ], - ), - title: const Text('Schülerdaten übertragen'), - onPressed: (context) async { - final List? pupilIds = await Navigator.of(context).push( - MaterialPageRoute( - builder: - (ctx) => SelectPupilsListPage( - selectablePupils: di() - .getPupilsFromInternalIds( - di().availablePupilIds, - ), - ), - ), - ); - if (pupilIds == null || pupilIds.isEmpty) { - return; - } - final internalIds = di().getInternalIdsFromPupilIds( - pupilIds, - ); - final String encryptedPupilIdentities = - await di().generatePupilIdentitiesQrData( - internalIds, - ); - Navigator.of(context).push( - MaterialPageRoute( - builder: - (context) => PupilIdentityStreamPage( - role: PupilIdentityStreamRole.sender, - encryptedData: encryptedPupilIdentities, - selectedPupilIds: pupilIds, - ), - ), - ); - }, - ), - // SettingsTile.navigation( - // leading: const Icon(Icons.qr_code_rounded), - // title: const Text('Alle vorhandenen QR-Ids zeigen (autoplay)'), - // onPressed: (context) async { - // final List> qrData = _pupilIdentityManager - // .generateAllPupilIdentitiesQrData(pupilsPerCode: 8); - - // Navigator.of(context).push(MaterialPageRoute( - // builder: (ctx) => QrCodeSpeedShow(qrMaps: qrData), - // )); - // }), - // SettingsTile.navigation( - // leading: const Icon(Icons.qr_code_rounded), - // title: - // const Text('Alle vorhandenen QR-Ids zeigen (manuelle slides)'), - // onPressed: (context) async { - // final qrDataMaps = _pupilIdentityManager - // .generateAllPupilIdentitiesQrData(pupilsPerCode: 8); - // final qrData = qrDataMaps[1] as Map; - - // showQrCarouselWithController(qrData, context); - // }), - ], - ); - } -} From 06827835bfe0a66d7044c5ac7c60f7c968040281 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sun, 30 Nov 2025 16:01:38 +0100 Subject: [PATCH 20/52] [APP]: Books fuature (WIP) --- .../features/books/domain/book_manager.dart | 41 +- .../widgets/book_list_bottom_navbar.dart | 50 +- .../book_search_form_page.dart | 560 +++++++++--------- .../book_tag_management_controller.dart | 151 ++--- .../book_tag_management_page.dart | 21 +- .../book_tag_management_bottom_nav_bar.dart | 49 ++ 6 files changed, 444 insertions(+), 428 deletions(-) create mode 100644 school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/widgets/book_tag_management_bottom_nav_bar.dart diff --git a/school_data_hub_flutter/lib/features/books/domain/book_manager.dart b/school_data_hub_flutter/lib/features/books/domain/book_manager.dart index 5a686096..e89b5e3e 100644 --- a/school_data_hub_flutter/lib/features/books/domain/book_manager.dart +++ b/school_data_hub_flutter/lib/features/books/domain/book_manager.dart @@ -110,8 +110,8 @@ class BookManager { final LibraryBookProxy libraryBookProxy = LibraryBookProxy( librarybook: libraryBook, ); - final List libraryBookProxies = - _libraryBookProxies.value.toList(); + final List libraryBookProxies = _libraryBookProxies.value + .toList(); libraryBookProxies.add(libraryBookProxy); _libraryBookProxies.value = libraryBookProxies; _isbnLibraryBooksMap.value[libraryBook.book!.isbn] = libraryBookProxies; @@ -121,8 +121,8 @@ class BookManager { final LibraryBookProxy libraryBookProxy = LibraryBookProxy( librarybook: libraryBook, ); - final List libraryBookProxies = - _libraryBookProxies.value.toList(); + final List libraryBookProxies = _libraryBookProxies.value + .toList(); int index = libraryBookProxies.indexWhere( (item) => item.libraryId == libraryBookProxy.libraryId, ); @@ -141,8 +141,8 @@ class BookManager { void _removeLibraryBookProxyFromCollection( LibraryBookProxy libraryBookProxy, ) { - final List libraryBookProxies = - _libraryBookProxies.value.toList(); + final List libraryBookProxies = _libraryBookProxies.value + .toList(); libraryBookProxies.removeWhere( (p) => p.libraryId == libraryBookProxy.libraryId, ); @@ -191,6 +191,7 @@ class BookManager { if (responseTags == null) { return; } + responseTags.sort((a, b) => a.name.compareTo(b.name)); _bookTags.value = responseTags; } @@ -199,7 +200,11 @@ class BookManager { if (responseTag == null) { return; } - _bookTags.value = [..._bookTags.value, responseTag]; + + _bookTags.value = [ + ..._bookTags.value, + responseTag, + ].sorted((a, b) => a.name.compareTo(b.name)); } Future updateBookTag(BookTag tag) async { @@ -211,7 +216,7 @@ class BookManager { final index = updatedTags.indexWhere((t) => t.id == tag.id); if (index != -1) { updatedTags[index] = updatedTag; - _bookTags.value = updatedTags; + _bookTags.value = updatedTags.sorted((a, b) => a.name.compareTo(b.name)); } } @@ -226,8 +231,8 @@ class BookManager { //- BOOK LOCATIONS Future fetchLocations() async { - final List? responseLocations = - await _bookApiService.fetchBookLocations(); + final List? responseLocations = await _bookApiService + .fetchBookLocations(); if (responseLocations == null) { return; } @@ -250,10 +255,9 @@ class BookManager { return; } - _locations.value = - _locations.value - .where((loc) => loc.location != location.location) - .toList(); + _locations.value = _locations.value + .where((loc) => loc.location != location.location) + .toList(); } void setLastLocationValue(LibraryBookLocation location) { @@ -263,8 +267,8 @@ class BookManager { //- LIBRARY BOOKS Future fetchLibraryBooks() async { - final List? responseBooks = - await _bookApiService.fetchLibraryBooks(); + final List? responseBooks = await _bookApiService + .fetchLibraryBooks(); if (responseBooks == null) { return; } @@ -446,8 +450,9 @@ class BookManager { if (newPageResults.isEmpty) { _hasMorePages = false; } else { - final List searchResultsToUpdate = - _searchResults.value.toList(); // Create a copy + final List searchResultsToUpdate = _searchResults + .value + .toList(); // Create a copy for (final result in newPageResults) { final LibraryBookProxy libraryBookProxy = LibraryBookProxy( librarybook: result, diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_list_bottom_navbar.dart b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_list_bottom_navbar.dart index 3fa653b1..fbb5c577 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_list_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_list_bottom_navbar.dart @@ -1,12 +1,7 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; -import 'package:school_data_hub_flutter/app_utils/scanner.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/bottom_nav_bar_layouts.dart'; -import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/new_book_page/new_book_controller.dart'; import 'package:watch_it/watch_it.dart'; class BookListBottomNavBar extends WatchingWidget { @@ -27,52 +22,13 @@ class BookListBottomNavBar extends WatchingWidget { const Spacer(), IconButton( tooltip: 'zurück', - icon: const Icon( - Icons.arrow_back, - size: 35, - ), + icon: const Icon(Icons.arrow_back, size: 35), onPressed: () { Navigator.pop(context); }, ), - const Gap(15), - IconButton( - tooltip: 'Neues Buch', - icon: const Icon( - Icons.add, - size: 35, - ), - onPressed: () async { - if (Platform.isWindows) { - final isbn = await shortTextfieldDialog( - context: context, - title: 'ISBN eingeben', - labelText: 'ISBN', - hintText: 'Bitte geben Sie die ISBN ein'); - if (isbn != null && isbn.isNotEmpty) { - final cleanIsbn = isbn.replaceAll('-', ''); - Navigator.of(context).push(MaterialPageRoute( - builder: (ctx) => NewBook( - isEdit: false, - isbn: int.parse(cleanIsbn), - ), - )); - } else { - final String? isbn = await qrScanner( - context: context, overlayText: 'ISBN scannen'); - if (isbn != null && isbn.isNotEmpty) { - Navigator.of(context).push(MaterialPageRoute( - builder: (ctx) => NewBook( - isEdit: false, - isbn: int.parse(isbn), - ), - )); - } - } - } - }, - ), - const Gap(15) + + const Gap(5), ], ), ), diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_search_form/book_search_form_page.dart b/school_data_hub_flutter/lib/features/books/presentation/book_search_form/book_search_form_page.dart index c7cbf089..f3cacb1c 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/book_search_form/book_search_form_page.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/book_search_form/book_search_form_page.dart @@ -47,280 +47,291 @@ class _BookSearchFormPageState extends State { body: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 800), - child: SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: titleSearchController, - textInputAction: TextInputAction.next, - decoration: InputDecoration( - fillColor: Colors.white, - filled: true, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(12), + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: titleSearchController, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(12), + ), + hintText: 'Titel des Buches', + floatingLabelBehavior: FloatingLabelBehavior.never, + ), + ), ), - hintText: 'Titel des Buches', - floatingLabelBehavior: FloatingLabelBehavior.never, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: authorSearchController, - textInputAction: TextInputAction.next, - decoration: InputDecoration( - fillColor: Colors.white, - filled: true, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(12), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: authorSearchController, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(12), + ), + hintText: 'Autor', + floatingLabelBehavior: FloatingLabelBehavior.never, + ), + ), ), - hintText: 'Autor', - floatingLabelBehavior: FloatingLabelBehavior.never, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: keywordsSearchController, - textInputAction: TextInputAction.next, - decoration: InputDecoration( - fillColor: Colors.white, - filled: true, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(12), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: keywordsSearchController, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(12), + ), + hintText: 'Schlagwörter', + floatingLabelBehavior: FloatingLabelBehavior.never, + ), + ), ), - hintText: 'Schlagwörter', - floatingLabelBehavior: FloatingLabelBehavior.never, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - selectedBookTags.isEmpty - ? 'Keine Schlagwörter ausgewählt!' - : 'Ausgewählte Schlagwörter:', - style: TextStyle( - color: - selectedBookTags.isEmpty - ? Colors.grey[600] - : AppColors.interactiveColor, - fontWeight: + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( selectedBookTags.isEmpty - ? FontWeight.normal - : FontWeight.w500, - ), - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (selectedBookTags.isNotEmpty) - IconButton( - onPressed: () { - setState(() { - selectedBookTags = []; - }); - }, - icon: const Icon( - Icons.clear, - color: Colors.red, + ? 'Keine Schlagwörter ausgewählt!' + : 'Ausgewählte Schlagwörter:', + style: TextStyle( + color: selectedBookTags.isEmpty + ? Colors.grey[600] + : AppColors.interactiveColor, + fontWeight: selectedBookTags.isEmpty + ? FontWeight.normal + : FontWeight.w500, ), - tooltip: 'Alle Schlagwörter entfernen', ), - IconButton( - onPressed: () async { - final result = await Navigator.of( - context, - ).push>( - MaterialPageRoute( - builder: - (context) => SelectBookTagsPage( - initialSelectedTags: - selectedBookTags, - ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (selectedBookTags.isNotEmpty) + IconButton( + onPressed: () { + setState(() { + selectedBookTags = []; + }); + }, + icon: const Icon( + Icons.clear, + color: Colors.red, + ), + tooltip: + 'Alle Schlagwörter entfernen', + ), + IconButton( + onPressed: () async { + final result = + await Navigator.of( + context, + ).push>( + MaterialPageRoute( + builder: (context) => + SelectBookTagsPage( + initialSelectedTags: + selectedBookTags, + ), + ), + ); + if (result != null) { + setState(() { + selectedBookTags = result; + }); + } + }, + icon: const Icon( + Icons.add, + color: AppColors.interactiveColor, + ), + tooltip: 'Schlagwörter auswählen', ), - ); - if (result != null) { - setState(() { - selectedBookTags = result; - }); - } - }, - icon: const Icon( - Icons.add, - color: AppColors.interactiveColor, + ], ), - tooltip: 'Schlagwörter auswählen', + ], + ), + if (selectedBookTags.isNotEmpty) ...[ + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: selectedBookTags.map((tag) { + return FilterChip( + label: Text(tag.name), + selected: true, + onSelected: (bool selected) { + setState(() { + selectedBookTags = selectedBookTags + .where( + (selectedTag) => + selectedTag.id != tag.id, + ) + .toList(); + }); + }, + selectedColor: AppColors + .interactiveColor + .withValues(alpha: 0.2), + checkmarkColor: + AppColors.interactiveColor, + deleteIcon: const Icon( + Icons.close, + size: 18, + color: AppColors.interactiveColor, + ), + onDeleted: () { + setState(() { + selectedBookTags = selectedBookTags + .where( + (selectedTag) => + selectedTag.id != tag.id, + ) + .toList(); + }); + }, + ); + }).toList(), ), ], - ), - ], + ], + ), ), - if (selectedBookTags.isNotEmpty) ...[ - const SizedBox(height: 8), - Wrap( - spacing: 8, - runSpacing: 8, - children: - selectedBookTags.map((tag) { - return FilterChip( - label: Text(tag.name), - selected: true, - onSelected: (bool selected) { - setState(() { - selectedBookTags = - selectedBookTags - .where( - (selectedTag) => - selectedTag.id != - tag.id, - ) - .toList(); - }); - }, - selectedColor: AppColors.interactiveColor - .withValues(alpha: 0.2), - checkmarkColor: - AppColors.interactiveColor, - deleteIcon: const Icon( - Icons.close, - size: 18, - color: AppColors.interactiveColor, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: + ValueListenableBuilder>( + valueListenable: bookManager.locations, + builder: (context, locations, _) { + final List locationItems = + [ + LibraryBookLocation( + location: "Alle Räume", + ), + ...locations.where( + (loc) => loc.location != "Alle Räume", ), - onDeleted: () { - setState(() { - selectedBookTags = - selectedBookTags - .where( - (selectedTag) => - selectedTag.id != - tag.id, - ) - .toList(); - }); - }, - ); - }).toList(), + ]; + selectedLocation ??= locationItems.first; + + return DropdownButtonFormField< + LibraryBookLocation + >( + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(12), + ), + labelText: 'Ablageort', + ), + items: locationItems + .map( + (loc) => + DropdownMenuItem< + LibraryBookLocation + >( + value: loc, + child: Text(loc.location), + ), + ) + .toList(), + onChanged: (value) { + setState(() { + selectedLocation = value; + }); + }, + ); + }, ), - ], - ], ), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: ValueListenableBuilder>( - valueListenable: bookManager.locations, - builder: (context, locations, _) { - final List locationItems = [ - LibraryBookLocation(location: "Alle Räume"), - ...locations.where( - (loc) => loc.location != "Alle Räume", - ), - ]; - selectedLocation ??= locationItems.first; - - return DropdownButtonFormField( - decoration: InputDecoration( - fillColor: Colors.white, - filled: true, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(12), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: levelSearchController, + textInputAction: TextInputAction.done, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(12), + ), + hintText: 'Lesestufe', + floatingLabelBehavior: FloatingLabelBehavior.never, ), - labelText: 'Ablageort', ), - items: - locationItems - .map( - (loc) => - DropdownMenuItem( - value: loc, - child: Text(loc.location), - ), - ) - .toList(), - onChanged: (value) { - setState(() { - selectedLocation = value; - }); - }, - ); - }, - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: levelSearchController, - textInputAction: TextInputAction.done, - decoration: InputDecoration( - fillColor: Colors.white, - filled: true, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(12), ), - hintText: 'Lesestufe', - floatingLabelBehavior: FloatingLabelBehavior.never, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: DropdownButtonFormField( - value: BorrowedStatus.all, - decoration: InputDecoration( - fillColor: Colors.white, - filled: true, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(12), + Padding( + padding: const EdgeInsets.all(8.0), + child: DropdownButtonFormField( + value: BorrowedStatus.all, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(12), + ), + labelText: 'Status', + ), + items: BorrowedStatus.values + .map( + (status) => DropdownMenuItem( + value: status, + child: Text(status.value), + ), + ) + .toList(), + onChanged: (value) { + selectedBorrowStatus = value; + }, + ), ), - labelText: 'Status', - ), - items: - BorrowedStatus.values - .map( - (status) => DropdownMenuItem( - value: status, - child: Text(status.value), - ), - ) - .toList(), - onChanged: (value) { - selectedBorrowStatus = value; - }, + ], ), ), - const SizedBox(height: 10), - ElevatedButton( + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5.0), + child: ElevatedButton( style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), - backgroundColor: AppColors.backgroundColor, + backgroundColor: AppColors.successButtonColor, minimumSize: const Size.fromHeight(60), ), onPressed: () async { @@ -333,43 +344,40 @@ class _BookSearchFormPageState extends State { title: title, author: author, keywords: keywords, - location: - selectedLocation?.location == "Alle Räume" - ? null - : selectedLocation!, + location: selectedLocation?.location == "Alle Räume" + ? null + : selectedLocation!, readingLevel: readingLevel, available: selectedBorrowStatus == BorrowedStatus.available - ? true - : selectedBorrowStatus == BorrowedStatus.borrowed - ? false - : null, - tags: - selectedBookTags.isNotEmpty ? selectedBookTags : null, + ? true + : selectedBorrowStatus == BorrowedStatus.borrowed + ? false + : null, + tags: selectedBookTags.isNotEmpty + ? selectedBookTags + : null, ); Navigator.of(context).push( MaterialPageRoute( - builder: - (context) => BookSearchResultsPage( - title: title, - author: author, - keywords: keywords, - location: - selectedLocation?.location == "Alle Räume" - ? null - : selectedLocation!, - readingLevel: readingLevel, - borrowStatus: selectedBorrowStatus, - selectedTags: selectedBookTags, - ), + builder: (context) => BookSearchResultsPage( + title: title, + author: author, + keywords: keywords, + location: selectedLocation?.location == "Alle Räume" + ? null + : selectedLocation!, + readingLevel: readingLevel, + borrowStatus: selectedBorrowStatus, + selectedTags: selectedBookTags, + ), ), ); }, child: const Text('SUCHEN', style: AppStyles.buttonTextStyle), ), - const SizedBox(height: 10), - ], - ), + ), + ], ), ), ), diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart b/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart index 894520d0..e6f07e13 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; -import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; import 'package:school_data_hub_flutter/features/books/domain/book_manager.dart'; import 'package:school_data_hub_flutter/features/books/presentation/book_tag_management_page/book_tag_management_page.dart'; @@ -81,9 +80,7 @@ class BookTagManagementController extends State { ), TextButton( onPressed: () => Navigator.of(context).pop(true), - style: TextButton.styleFrom( - foregroundColor: Colors.red, - ), + style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('Löschen'), ), ], @@ -109,26 +106,16 @@ class BookTagManagementController extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.label_outline, - size: 64, - color: Colors.grey, - ), + Icon(Icons.label_outline, size: 64, color: Colors.grey), SizedBox(height: 16), Text( 'Keine Buch-Tags vorhanden', - style: TextStyle( - fontSize: 16, - color: Colors.grey, - ), + style: TextStyle(fontSize: 16, color: Colors.grey), ), SizedBox(height: 8), Text( 'Tippen Sie auf das + Symbol, um ein neues Tag zu erstellen', - style: TextStyle( - fontSize: 14, - color: Colors.grey, - ), + style: TextStyle(fontSize: 14, color: Colors.grey), textAlign: TextAlign.center, ), ], @@ -136,68 +123,84 @@ class BookTagManagementController extends State { ); } - return ListView.builder( - itemCount: bookTags.length, - itemBuilder: (context, index) { - final tag = bookTags[index]; - return Card( - margin: const EdgeInsets.only(bottom: 8.0), - child: ListTile( - leading: const Icon( - Icons.label, - color: AppColors.interactiveColor, - ), - title: Text( - tag.name, - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - trailing: PopupMenuButton( - onSelected: (value) { - switch (value) { - case 'edit': - editTag(context, tag); - break; - case 'delete': - deleteTag(context, tag); - break; - } - }, - itemBuilder: (BuildContext context) => [ - const PopupMenuItem( - value: 'edit', - child: Row( - children: [ - Icon(Icons.edit), - SizedBox(width: 8), - Text('Bearbeiten'), - ], - ), - ), - const PopupMenuItem( - value: 'delete', - child: Row( - children: [ - Icon(Icons.delete, color: Colors.red), - SizedBox(width: 8), - Text( - 'Löschen', - style: TextStyle(color: Colors.red), - ), - ], - ), - ), - ], - ), - ), - ); - }, + return SingleChildScrollView( + child: Wrap( + spacing: 8, + runSpacing: 8, + children: bookTags.map((tag) { + return Chip( + label: Text(tag.name), + deleteIcon: const Icon(Icons.more_horiz, size: 18), + onDeleted: () { + // Using onDeleted to trigger the context menu for edit/delete + // because Chip doesn't support context menu directly + _showTagOptions(context, tag); + }, + ); + }).toList(), + ), ); }, ); } + Future _showTagOptions(BuildContext context, BookTag tag) async { + final RenderBox? button = context.findRenderObject() as RenderBox?; + final overlay = + Overlay.of(context).context.findRenderObject() as RenderBox?; + + if (button == null || overlay == null) return; + + final relativePosition = RelativeRect.fromRect( + Rect.fromPoints( + button.localToGlobal(Offset.zero, ancestor: overlay), + button.localToGlobal( + button.size.bottomRight(Offset.zero), + ancestor: overlay, + ), + ), + Offset.zero & overlay.size, + ); + + final result = await showMenu( + context: context, + position: relativePosition, + items: [ + const PopupMenuItem( + value: 'edit', + child: Row( + children: [ + Icon(Icons.edit), + SizedBox(width: 8), + Text('Bearbeiten'), + ], + ), + ), + const PopupMenuItem( + value: 'delete', + child: Row( + children: [ + Icon(Icons.delete, color: Colors.red), + SizedBox(width: 8), + Text('Löschen', style: TextStyle(color: Colors.red)), + ], + ), + ), + ], + ); + + if (result != null) { + switch (result) { + case 'edit': + if (context.mounted) await editTag(context, tag); + break; + case 'delete': + if (context.mounted) await deleteTag(context, tag); + break; + } + } + } + @override Widget build(BuildContext context) { return BookTagManagementPage(this); diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_page.dart b/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_page.dart index 3cc20d70..e9677707 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_page.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/book_tag_management_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; -import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; -import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; import 'package:school_data_hub_flutter/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/book_tag_management_page/widgets/book_tag_management_bottom_nav_bar.dart'; class BookTagManagementPage extends StatelessWidget { final BookTagManagementController controller; @@ -12,17 +12,9 @@ class BookTagManagementPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - backgroundColor: AppColors.backgroundColor, - title: const Center( - child: Text('Buch-Tags verwalten', style: AppStyles.appBarTextStyle), - ), - actions: [ - IconButton( - onPressed: () => controller.createNewTag(context), - icon: const Icon(Icons.add, color: AppColors.interactiveColor), - ), - ], + appBar: const GenericAppBar( + iconData: Icons.label, + title: 'Buch-Tags verwalten', ), body: Padding( padding: const EdgeInsets.all(16.0), @@ -38,6 +30,9 @@ class BookTagManagementPage extends StatelessWidget { ], ), ), + bottomNavigationBar: BookTagManagementBottomNavBar( + onAddPressed: () => controller.createNewTag(context), + ), ); } } diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/widgets/book_tag_management_bottom_nav_bar.dart b/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/widgets/book_tag_management_bottom_nav_bar.dart new file mode 100644 index 00000000..841e3e00 --- /dev/null +++ b/school_data_hub_flutter/lib/features/books/presentation/book_tag_management_page/widgets/book_tag_management_bottom_nav_bar.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/widgets/bottom_nav_bar_layouts.dart'; + +class BookTagManagementBottomNavBar extends StatelessWidget { + final VoidCallback onAddPressed; + + const BookTagManagementBottomNavBar({ + super.key, + required this.onAddPressed, + }); + + @override + Widget build(BuildContext context) { + return BottomNavBarLayout( + bottomNavBar: BottomAppBar( + height: 60, + padding: const EdgeInsets.all(9), + shape: null, + color: AppColors.backgroundColor, + child: IconTheme( + data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Spacer(), + IconButton( + tooltip: 'Zurück', + icon: const Icon(Icons.arrow_back, size: 35), + onPressed: () { + Navigator.pop(context); + }, + ), + const Gap(15), + IconButton( + tooltip: 'Neues Tag erstellen', + icon: const Icon(Icons.add, size: 35), + onPressed: onAddPressed, + ), + const Gap(15), + ], + ), + ), + ), + ); + } +} + From e92ec5baa3f732689005212713ffcbfab1e3bc70 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sun, 30 Nov 2025 16:02:03 +0100 Subject: [PATCH 21/52] [APP] Misc WIP --- .../android/app/build.gradle | 2 +- .../widgets/dialogs/pupil_list_dialog.dart | 54 ++ .../dialogs/short_textfield_dialog.dart | 45 +- .../schoolday_event_reason_dialog.dart | 339 ++++++++++++ .../schoolday_event_type_dialog.dart | 46 +- .../widgets/pupil_schoolday_event_card.dart | 36 +- .../learn_resources_menu_page.dart | 66 +-- .../pupils_matrix_contacts_list_page.dart | 520 ++++++++---------- .../widgets/attendance_stats_view.dart | 312 +++++++++++ 9 files changed, 1056 insertions(+), 364 deletions(-) create mode 100644 school_data_hub_flutter/lib/common/widgets/dialogs/pupil_list_dialog.dart create mode 100644 school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart create mode 100644 school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart diff --git a/school_data_hub_flutter/android/app/build.gradle b/school_data_hub_flutter/android/app/build.gradle index 5da7c568..1eb4874b 100644 --- a/school_data_hub_flutter/android/app/build.gradle +++ b/school_data_hub_flutter/android/app/build.gradle @@ -25,7 +25,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. // minSdk = flutter.minSdkVersion - minSdkVersion 23 //flutter.minSdkVersion // flutter.minSdkVersion + minSdkVersion flutter.minSdkVersion //flutter.minSdkVersion // flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName diff --git a/school_data_hub_flutter/lib/common/widgets/dialogs/pupil_list_dialog.dart b/school_data_hub_flutter/lib/common/widgets/dialogs/pupil_list_dialog.dart new file mode 100644 index 00000000..7278d588 --- /dev/null +++ b/school_data_hub_flutter/lib/common/widgets/dialogs/pupil_list_dialog.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_sliver_list.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_list_card.dart'; + +class PupilListDialog extends StatelessWidget { + final String title; + final List pupils; + + const PupilListDialog({ + super.key, + required this.title, + required this.pupils, + }); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: AppColors.canvasColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 600, + maxHeight: 600, + ), + child: Column( + children: [ + GenericAppBar( + title: title, + iconData: Icons.people, + ), + Expanded( + child: CustomScrollView( + slivers: [ + GenericSliverListWithEmptyListCheck( + items: pupils, + itemBuilder: (context, pupil) => PupilListCard( + passedPupil: pupil, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + diff --git a/school_data_hub_flutter/lib/common/widgets/dialogs/short_textfield_dialog.dart b/school_data_hub_flutter/lib/common/widgets/dialogs/short_textfield_dialog.dart index bd33004c..5952df5c 100644 --- a/school_data_hub_flutter/lib/common/widgets/dialogs/short_textfield_dialog.dart +++ b/school_data_hub_flutter/lib/common/widgets/dialogs/short_textfield_dialog.dart @@ -21,37 +21,34 @@ Future shortTextfieldDialog({ textAlign: TextAlign.center, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 20), ), - content: Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: textEditingController, - style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold), + content: TextField( + controller: textEditingController, + style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold), - //maxLength: 16, - obscureText: obscureText ?? false, - decoration: InputDecoration( - //border: InputBorder.none, - border: const OutlineInputBorder( - borderSide: BorderSide( - color: AppColors.backgroundColor, - width: 2, - ), + //maxLength: 16, + obscureText: obscureText ?? false, + decoration: InputDecoration( + //border: InputBorder.none, + border: const OutlineInputBorder( + borderSide: BorderSide( + color: AppColors.backgroundColor, + width: 2, ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: AppColors.backgroundColor, - width: 2, - ), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: AppColors.backgroundColor, + width: 2, ), - labelStyle: const TextStyle(color: AppColors.backgroundColor), - labelText: labelText, - hintText: hintText, ), + labelStyle: const TextStyle(color: AppColors.backgroundColor), + labelText: labelText, + hintText: hintText, ), ), actions: [ Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.symmetric(vertical: 8.0), child: ElevatedButton( style: AppStyles.cancelButtonStyle, child: const Text('ABBRECHEN', style: AppStyles.buttonTextStyle), @@ -61,7 +58,7 @@ Future shortTextfieldDialog({ ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.symmetric(vertical: 8.0), child: ElevatedButton( style: AppStyles.successButtonStyle, child: const Text('OKAY', style: AppStyles.buttonTextStyle), diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart new file mode 100644 index 00000000..e1951223 --- /dev/null +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart @@ -0,0 +1,339 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/domain/models/schoolday_event_enums.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/new_schoolday_event_page/widgets/schoolday_event_filter_chip.dart'; +import 'package:watch_it/watch_it.dart'; + +class SchooldayEventReasonDialog extends WatchingWidget { + final SchooldayEvent schooldayEvent; + + const SchooldayEventReasonDialog({required this.schooldayEvent, super.key}); + + @override + Widget build(BuildContext context) { + final violenceAgainstPupils = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.violenceAgainstPupils.value, + ), + ), + ); + final violenceAgainstTeacher = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.violenceAgainstTeachers.value, + ), + ), + ); + final violenceAgainstThings = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.violenceAgainstThings.value, + ), + ), + ); + final insultOthers = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.insultOthers.value, + ), + ), + ); + final annoyOthers = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.annoyOthers.value, + ), + ), + ); + final imminentDanger = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.dangerousBehaviour.value, + ), + ), + ); + final ignoreTeacherInstructions = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.ignoreInstructions.value, + ), + ), + ); + final disturbLesson = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.disturbLesson.value, + ), + ), + ); + final other = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains(SchooldayEventReason.other.value), + ), + ); + final learningDevelopmentInfo = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.learningDevelopmentInfo.value, + ), + ), + ); + final learningSupportInfo = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.learningSupportInfo.value, + ), + ), + ); + final admonitionInfo = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.admonitionInfo.value, + ), + ), + ); + + return AlertDialog( + title: const Text( + 'Grund bearbeiten', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + content: SingleChildScrollView( + child: schooldayEvent.eventType == SchooldayEventType.parentsMeeting + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Wrap( + children: [ + SchooldayEventReasonFilterChip( + isReason: watch(learningDevelopmentInfo).value, + onSelected: (value) { + learningDevelopmentInfo.value = value; + }, + emojis: '💡🧠', + text: 'Lernentwicklung', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(learningSupportInfo).value, + onSelected: (value) { + learningSupportInfo.value = value; + }, + emojis: '🛟🧠', + text: 'Förderung', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(admonitionInfo).value, + onSelected: (value) { + admonitionInfo.value = value; + }, + emojis: '⚠️ℹ️', + text: 'Regelverstoß', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(other).value, + onSelected: (value) { + other.value = value; + }, + emojis: '📝', + text: 'Sonstiges', + ), + const Gap(5), + ], + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Wrap( + children: [ + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(violenceAgainstPupils).value, + onSelected: (value) { + violenceAgainstPupils.value = value; + }, + emojis: '🤜🤕', + text: 'Gewalt gegen Kinder', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(violenceAgainstTeacher).value, + onSelected: (value) { + violenceAgainstTeacher.value = value; + }, + emojis: '🤜🎓️', + text: 'Gewalt gegen Erwachsene', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(violenceAgainstThings).value, + onSelected: (value) { + violenceAgainstThings.value = value; + }, + emojis: '🤜🏫', + text: 'Gewalt gegen Sachen', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(insultOthers).value, + onSelected: (value) { + insultOthers.value = value; + }, + emojis: '🤬💔', + text: 'Beleidigen', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(annoyOthers).value, + onSelected: (value) { + annoyOthers.value = value; + }, + emojis: '😈😖', + text: 'Ärgern', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(imminentDanger).value, + onSelected: (value) { + imminentDanger.value = value; + }, + emojis: '🚨😱', + text: 'Gefahr für sich/andere', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(ignoreTeacherInstructions).value, + onSelected: (value) { + ignoreTeacherInstructions.value = value; + }, + emojis: '🎓️🙉', + text: 'Anweisungen ignorieren', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(disturbLesson).value, + onSelected: (value) { + disturbLesson.value = value; + }, + emojis: '🛑🎓️', + text: 'Unterricht stören', + ), + const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(other).value, + onSelected: (value) { + other.value = value; + }, + emojis: '📝', + text: 'Sonstiges', + ), + const Gap(5), + ], + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('ABBRECHEN', style: TextStyle(color: Colors.grey)), + ), + TextButton( + onPressed: () async { + Set schooldayEventReason = {}; + String schooldayEventReasons = ''; + if (violenceAgainstPupils.value == true) { + schooldayEventReason.add( + SchooldayEventReason.violenceAgainstPupils.value, + ); + } + + if (violenceAgainstTeacher.value == true) { + schooldayEventReason.add( + SchooldayEventReason.violenceAgainstTeachers.value, + ); + } + + if (violenceAgainstThings.value == true) { + schooldayEventReason.add( + SchooldayEventReason.violenceAgainstThings.value, + ); + } + + if (imminentDanger.value == true) { + schooldayEventReason.add( + SchooldayEventReason.dangerousBehaviour.value, + ); + } + + if (insultOthers.value == true) { + schooldayEventReason.add(SchooldayEventReason.insultOthers.value); + } + + if (annoyOthers.value == true) { + schooldayEventReason.add(SchooldayEventReason.annoyOthers.value); + } + + if (ignoreTeacherInstructions.value == true) { + schooldayEventReason.add( + SchooldayEventReason.ignoreInstructions.value, + ); + } + + if (disturbLesson.value == true) { + schooldayEventReason.add( + SchooldayEventReason.disturbLesson.value, + ); + } + + if (learningDevelopmentInfo.value == true) { + schooldayEventReason.add( + SchooldayEventReason.learningDevelopmentInfo.value, + ); + } + + if (learningSupportInfo.value == true) { + schooldayEventReason.add( + SchooldayEventReason.learningSupportInfo.value, + ); + } + + if (admonitionInfo.value == true) { + schooldayEventReason.add( + SchooldayEventReason.admonitionInfo.value, + ); + } + + if (other.value == true) { + schooldayEventReason.add(SchooldayEventReason.other.value); + } + + for (final reason in schooldayEventReason) { + schooldayEventReasons = '$schooldayEventReasons$reason*'; + } + + await di().updateSchooldayEvent( + eventToUpdate: schooldayEvent, + reason: schooldayEventReasons, + ); + + if (context.mounted) { + Navigator.pop(context); + } + }, + child: const Text('SPEICHERN', style: AppStyles.buttonTextStyle), + ), + ], + ); + } +} diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart index d38552d3..53a4a380 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart @@ -7,10 +7,7 @@ class SchooldayEventTypeDialog extends WatchingWidget { // Change HookWidget to HookConsumerWidget final SchooldayEvent schooldayEvent; - const SchooldayEventTypeDialog({ - super.key, - required this.schooldayEvent, - }); + const SchooldayEventTypeDialog({super.key, required this.schooldayEvent}); SchooldayEventManager get _schooldayEventManager => di(); @@ -35,10 +32,14 @@ class SchooldayEventTypeDialog extends WatchingWidget { @override Widget build(BuildContext context) { final selectedEventType = createOnce( - () => ValueNotifier(SchooldayEventType.notSet)); + () => ValueNotifier(SchooldayEventType.notSet), + ); return AlertDialog( - title: const Text('Select Event Type'), + title: const Text( + 'Ereignisart ändern', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), content: DropdownButton( isDense: true, underline: Container(), @@ -53,23 +54,24 @@ class SchooldayEventTypeDialog extends WatchingWidget { schoolEventType: newValue, ); }, - items: - SchooldayEventType.values.map>( - (SchooldayEventType value) { - return DropdownMenuItem( - value: value, - child: Text( - _getDropdownItemText(value), - style: TextStyle( - color: value == SchooldayEventType.notSet - ? Colors.red - : Colors.black, - fontSize: 20, + items: SchooldayEventType.values + .map>(( + SchooldayEventType value, + ) { + return DropdownMenuItem( + value: value, + child: Text( + _getDropdownItemText(value), + style: TextStyle( + color: value == SchooldayEventType.notSet + ? Colors.red + : Colors.black, + fontSize: 20, + ), ), - ), - ); - }, - ).toList(), + ); + }) + .toList(), ), actions: [ TextButton( diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart index fe753d53..c3ea9666 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart @@ -14,6 +14,7 @@ import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_type_dialog.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_reason_chips.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_type_icon.dart'; @@ -144,14 +145,33 @@ class PupilSchooldayEventCard extends StatelessWidget { ), const Gap(5), //-TODO: Add the possibility to change the admonishing reasons - Wrap( - direction: Axis.horizontal, - spacing: 5, - children: [ - ...schooldayEventReasonChips( - schooldayEvent.eventReason, - ), - ], + InkWell( + onTap: () { + if (!isAuthorized) { + _notificationService.showSnackBar( + NotificationType.error, + 'Nicht berechtigt!', + ); + return; + } + showDialog( + context: context, + builder: (BuildContext context) { + return SchooldayEventReasonDialog( + schooldayEvent: schooldayEvent, + ); + }, + ); + }, + child: Wrap( + direction: Axis.horizontal, + spacing: 5, + children: [ + ...schooldayEventReasonChips( + schooldayEvent.eventReason, + ), + ], + ), ), const Gap(10), Row( diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/learn_resources_menu_page.dart b/school_data_hub_flutter/lib/features/app_main_navigation/learn_resources_menu_page.dart index 1ac27d67..116a2274 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/learn_resources_menu_page.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/learn_resources_menu_page.dart @@ -142,43 +142,43 @@ class LearnResourcesMenuPage extends StatelessWidget { ), ), ), - if (di().user!.userFlags.isTester) - Padding( - padding: const EdgeInsets.all(4.0), - child: Card( - color: AppColors.backgroundColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15.0), - ), - child: InkWell( - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => const BooksMainMenuPage(), - ), - ); - }, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - Icons.book, - size: 50, - color: AppColors.gridViewColor, - ), - const Gap(10), - Text( - locale.books, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + + Padding( + padding: const EdgeInsets.all(4.0), + child: Card( + color: AppColors.backgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15.0), + ), + child: InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => const BooksMainMenuPage(), + ), + ); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.book, + size: 50, + color: AppColors.gridViewColor, + ), + const Gap(10), + Text( + locale.books, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, ), - ], - ), + ), + ], ), ), ), + ), ], ), ), diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart index 8c5a2b43..b19227de 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart @@ -45,306 +45,274 @@ class PupilsMatrixContactsListPage extends WatchingWidget { ), GenericSliverListWithEmptyListCheck( items: pupils, - itemBuilder: - (_, pupil) => Card( - color: - pupil.tutorInfo?.parentsContact == null || - pupil.contact == null - ? Colors.orange - : Colors.white, - child: Column( + itemBuilder: (_, pupil) => Card( + color: + pupil.tutorInfo?.parentsContact == null || + pupil.contact == null + ? Colors.orange + : Colors.white, + child: Column( + children: [ + Row( children: [ - Row( - children: [ - const Gap(5), - AvatarWithBadges(pupil: pupil, size: 80), - const Gap(10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + const Gap(5), + AvatarWithBadges(pupil: pupil, size: 80), + const Gap(10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(10), + Row( children: [ - const Gap(10), - Row( - children: [ - InkWell( - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: - (ctx) => PupilProfilePage( - pupil: pupil, - ), - ), - ); - }, - child: Text( - '${pupil.firstName} ${pupil.lastName}', - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => + PupilProfilePage(pupil: pupil), ), + ); + }, + child: Text( + '${pupil.firstName} ${pupil.lastName}', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, ), - if (pupil.family != null) - const Row( - children: [ - Gap(10), - Icon( - Icons.group, - size: 25, - color: - AppColors.backgroundColor, - ), - ], - ), - ], + ), ), - Row( - children: [ - const Text('Kontakt: '), - InkWell( - onTap: () async { - if (pupil.contact == null) { - Navigator.of(context).push( - MaterialPageRoute( - builder: - ( - ctx, - ) => NewMatrixUserPage( - pupil: pupil, - matrixId: - MatrixPolicyHelper.generateMatrixId( - isParent: false, - ), - displayName: - '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (${pupil.group})', - ), - ), - ); - return; - } - final confirm = - await confirmationDialog( - context: context, - title: 'Messenger öffnen', - message: - 'Nachricht an ${pupil.firstName} schicken?', - ); - if (confirm == true && - context.mounted) { - MatrixPolicyHelper.launchMatrixUrl( - context, - pupil.contact!, - ); - } - }, - onLongPress: () async { - final String? contact = - await longTextFieldDialog( - title: 'Kontakt', - labelText: 'Kontakt', - initialValue: pupil.contact, - parentContext: context, - ); - if (contact == null) return; - - await PupilMutator() - .updateStringProperty( - pupilId: pupil.pupilId, - property: 'contact', - value: contact, - ); - }, - child: Text( - pupil.contact ?? - 'nicht eingetragen', - style: TextStyle( - color: - pupil.contact == null - ? Colors.black - : AppColors - .backgroundColor, - fontWeight: - pupil.contact == null - ? FontWeight.normal - : FontWeight.bold, - ), + if (pupil.family != null) + const Row( + children: [ + Gap(10), + Icon( + Icons.group, + size: 25, + color: AppColors.backgroundColor, ), - ), - const Gap(10), - IconButton( - icon: const Icon(Icons.copy), - onPressed: () { - Clipboard.setData( - ClipboardData( - text: pupil.contact!, + ], + ), + ], + ), + Row( + children: [ + const Text('Kontakt: '), + InkWell( + onTap: () async { + if (pupil.contact == null) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => NewMatrixUserPage( + pupil: pupil, + matrixId: + MatrixPolicyHelper.generateMatrixId( + isParent: false, + ), + displayName: + '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (${pupil.group})', ), + ), + ); + return; + } + final confirm = await confirmationDialog( + context: context, + title: 'Messenger öffnen', + message: + 'Nachricht an ${pupil.firstName} schicken?', + ); + if (confirm == true && + context.mounted) { + MatrixPolicyHelper.launchMatrixUrl( + context, + pupil.contact!, + ); + } + }, + onLongPress: () async { + final String? contact = + await longTextFieldDialog( + title: 'Kontakt', + labelText: 'Kontakt', + initialValue: pupil.contact, + parentContext: context, ); - ScaffoldMessenger.of( - context, - ).showSnackBar( - const SnackBar( - content: Text( - 'In die Zwischenablage kopiert', - ), - ), + if (contact == null) return; + + await PupilMutator() + .updateStringProperty( + pupilId: pupil.pupilId, + property: 'contact', + value: contact, ); - }, + }, + child: Text( + pupil.contact ?? 'nicht eingetragen', + style: TextStyle( + color: pupil.contact == null + ? Colors.black + : AppColors.backgroundColor, + fontWeight: pupil.contact == null + ? FontWeight.normal + : FontWeight.bold, ), - ], + ), ), const Gap(10), - Row( - children: [ - const Text('Elternkontakt: '), - InkWell( - onTap: () async { - if (pupil - .tutorInfo - ?.parentsContact == - null) { - String? pupilSiblingsGroups; - if (pupil.family != null) { - pupilSiblingsGroups = - [ - ..._pupilManager - .getSiblings( - pupil, - ), - pupil, - ] - .map((e) => e.group) - .toList() - .join(); - } - Navigator.of(context).push( - MaterialPageRoute( - builder: - ( - ctx, - ) => NewMatrixUserPage( - pupil: pupil, - matrixId: - MatrixPolicyHelper.generateMatrixId( - isParent: true, - ), - displayName: - pupilSiblingsGroups != - null - ? 'Fa. ${pupil.lastName} (E) $pupilSiblingsGroups' - : '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (E) ${pupil.group}', - isParent: true, - ), - ), - ); - return; - } - final confirm = - await confirmationDialog( - context: context, - title: 'Messenger öffnen', - message: - 'Nachricht an ${pupil.firstName}s Erziehungsberechtigte schicken?', - ); - if (confirm == true && - context.mounted) { - MatrixPolicyHelper.launchMatrixUrl( - context, - pupil - .tutorInfo! - .parentsContact!, - ); - } - }, - onLongPress: () async { - final String? tutorContact = - await longTextFieldDialog( - title: 'Elternkontakt', - labelText: 'Elternkontakt', - initialValue: - pupil - .tutorInfo - ?.parentsContact, - parentContext: context, - ); - if (tutorContact == null) return; - final TutorInfo? tutorInfo = - pupil.tutorInfo == null - ? TutorInfo( - parentsContact: - '@$tutorContact', - createdBy: - di() - .signedInUser! - .userName!, - ) - : pupil.tutorInfo!.copyWith( - parentsContact: - '@$tutorContact', - ); - await PupilMutator() - .updateTutorInfo( - pupilId: pupil.pupilId, - tutorInfo: tutorInfo, - ); - }, - child: Text( - pupil.tutorInfo?.parentsContact ?? - 'nicht eingetragen', - style: TextStyle( - color: - pupil - .tutorInfo - ?.parentsContact == - null - ? Colors.black - : AppColors - .backgroundColor, - fontWeight: - pupil - .tutorInfo - ?.parentsContact == - null - ? FontWeight.normal - : FontWeight.bold, + IconButton( + icon: const Icon(Icons.copy), + onPressed: () { + Clipboard.setData( + ClipboardData(text: pupil.contact!), + ); + ScaffoldMessenger.of( + context, + ).showSnackBar( + const SnackBar( + content: Text( + 'In die Zwischenablage kopiert', ), ), - ), - const Gap(10), - IconButton( - icon: const Icon(Icons.copy), - onPressed: () { - Clipboard.setData( - ClipboardData( - text: - pupil - .tutorInfo - ?.parentsContact ?? - 'Kein Elternkontakt vorhanden!', - ), - ); - ScaffoldMessenger.of( - context, - ).showSnackBar( - const SnackBar( - content: Text( - 'In die Zwischenablage kopiert', - ), + ); + }, + ), + ], + ), + + Row( + children: [ + const Text('Elternkontakt: '), + InkWell( + onTap: () async { + if (pupil.tutorInfo?.parentsContact == + null) { + String? pupilSiblingsGroups; + if (pupil.family != null) { + pupilSiblingsGroups = + [ + ..._pupilManager + .getSiblings(pupil), + pupil, + ] + .map((e) => e.group) + .toList() + .join(); + } + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => NewMatrixUserPage( + pupil: pupil, + matrixId: + MatrixPolicyHelper.generateMatrixId( + isParent: true, + ), + displayName: + pupilSiblingsGroups != null + ? 'Fa. ${pupil.lastName} (E) $pupilSiblingsGroups' + : '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (E) ${pupil.group}', + isParent: true, ), + ), + ); + return; + } + final confirm = await confirmationDialog( + context: context, + title: 'Messenger öffnen', + message: + 'Nachricht an ${pupil.firstName}s Erziehungsberechtigte schicken?', + ); + if (confirm == true && + context.mounted) { + MatrixPolicyHelper.launchMatrixUrl( + context, + pupil.tutorInfo!.parentsContact!, + ); + } + }, + onLongPress: () async { + final String? tutorContact = + await longTextFieldDialog( + title: 'Elternkontakt', + labelText: 'Elternkontakt', + initialValue: pupil + .tutorInfo + ?.parentsContact, + parentContext: context, ); - }, + if (tutorContact == null) return; + final TutorInfo? tutorInfo = + pupil.tutorInfo == null + ? TutorInfo( + parentsContact: + '@$tutorContact', + createdBy: + di() + .signedInUser! + .userName!, + ) + : pupil.tutorInfo!.copyWith( + parentsContact: + '@$tutorContact', + ); + await PupilMutator().updateTutorInfo( + pupilId: pupil.pupilId, + tutorInfo: tutorInfo, + ); + }, + child: Text( + pupil.tutorInfo?.parentsContact ?? + 'nicht eingetragen', + style: TextStyle( + color: + pupil.tutorInfo?.parentsContact == + null + ? Colors.black + : AppColors.backgroundColor, + fontWeight: + pupil.tutorInfo?.parentsContact == + null + ? FontWeight.normal + : FontWeight.bold, ), - ], + ), ), const Gap(10), + IconButton( + icon: const Icon(Icons.copy), + onPressed: () { + Clipboard.setData( + ClipboardData( + text: + pupil + .tutorInfo + ?.parentsContact ?? + 'Kein Elternkontakt vorhanden!', + ), + ); + ScaffoldMessenger.of( + context, + ).showSnackBar( + const SnackBar( + content: Text( + 'In die Zwischenablage kopiert', + ), + ), + ); + }, + ), ], ), - ), - ], + const Gap(10), + ], + ), ), ], ), - ), + ], + ), + ), ), ], ), diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart new file mode 100644 index 00000000..3189e654 --- /dev/null +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart @@ -0,0 +1,312 @@ +import 'package:community_charts_flutter/community_charts_flutter.dart' + as charts; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:intl/intl.dart'; +import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page.dart'; + +class AttendanceStatsView extends StatefulWidget { + final List sortedSchooldays; + final Map + attendanceChartData; + + const AttendanceStatsView({ + super.key, + required this.sortedSchooldays, + required this.attendanceChartData, + }); + + @override + State createState() => _AttendanceStatsViewState(); +} + +class _AttendanceStatsViewState extends State { + final Set _hiddenSeries = {}; + + void _toggleSeries(String seriesId) { + setState(() { + if (_hiddenSeries.contains(seriesId)) { + _hiddenSeries.remove(seriesId); + } else { + _hiddenSeries.add(seriesId); + } + }); + } + + void _onSelectionChanged(charts.SelectionModel model) { + final selectedDatum = model.selectedDatum; + + if (selectedDatum.isNotEmpty) { + final datum = selectedDatum.first; + final chartData = datum.datum as ChartData; + + // Construct data string with all values for this date + final dateData = widget.attendanceChartData[chartData.date]; + if (dateData == null) return; + + final buffer = StringBuffer(); + buffer.writeln('Datum: ${chartData.dateString}'); + buffer.writeln(); + buffer.writeln('Entschuldigt: ${dateData.excused}'); + buffer.writeln('Unentschuldigt: ${dateData.unexcused}'); + buffer.writeln('Nach Hause geschickt: ${dateData.goneHome}'); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Details'), + content: Text(buffer.toString()), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Schließen'), + ), + ], + ), + ); + } + } + + @override + Widget build(BuildContext context) { + final attendanceSeries = _createAttendanceSeries(); + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(15), + const Center( + child: Text( + 'Fehlzeiten nach Schultag', + style: AppStyles.title, + ), + ), + const Gap(10), + SizedBox( + height: 400, + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxHeight <= 0 || constraints.maxWidth <= 0) { + return const Center(child: Text('Chart wird geladen...')); + } + return charts.OrdinalComboChart( + attendanceSeries, + animate: true, + animationDuration: const Duration(milliseconds: 500), + defaultRenderer: charts.BarRendererConfig( + groupingType: charts.BarGroupingType.stacked, + ), + layoutConfig: charts.LayoutConfig( + leftMarginSpec: charts.MarginSpec.fixedPixel(60), + topMarginSpec: charts.MarginSpec.fixedPixel(20), + rightMarginSpec: charts.MarginSpec.fixedPixel(40), + bottomMarginSpec: charts.MarginSpec.fixedPixel(60), + ), + primaryMeasureAxis: const charts.NumericAxisSpec( + tickProviderSpec: charts.BasicNumericTickProviderSpec( + zeroBound: true, + ), + ), + domainAxis: charts.OrdinalAxisSpec( + tickProviderSpec: charts.StaticOrdinalTickProviderSpec( + widget.sortedSchooldays.map((schoolday) { + final dateStr = _formatDateForChart( + schoolday.schoolday, + ); + final firstOfMonthDates = _getFirstOfMonthDates(); + + if (firstOfMonthDates.contains(dateStr)) { + try { + final date = DateFormat( + 'dd.MM', + ).parse(dateStr); + return charts.TickSpec( + dateStr, + label: DateFormat('MMM').format(date), + ); + } catch (e) { + return charts.TickSpec(dateStr); + } + } else { + return charts.TickSpec(dateStr, label: ''); + } + }).toList(), + ), + ), + selectionModels: [ + charts.SelectionModelConfig( + type: charts.SelectionModelType.info, + changedListener: _onSelectionChanged, + ), + ], + behaviors: [ + charts.ChartTitle( + 'Anzahl', + behaviorPosition: charts.BehaviorPosition.start, + titleOutsideJustification: + charts.OutsideJustification.middleDrawArea, + ), + ], + ); + }, + ), + ), + const Gap(20), + Center( + child: Wrap( + alignment: WrapAlignment.center, + spacing: 20, + runSpacing: 10, + children: [ + _buildLegendItem('Entschuldigt', Colors.green, 'excused'), + _buildLegendItem('Unentschuldigt', Colors.red, 'unexcused'), + _buildLegendItem( + 'Nach Hause geschickt', + Colors.orange, + 'goneHome', + ), + ], + ), + ), + const Gap(20), + ], + ), + ), + ); + } + + Widget _buildLegendItem(String label, Color color, String seriesId) { + final isHidden = _hiddenSeries.contains(seriesId); + return InkWell( + onTap: () => _toggleSeries(seriesId), + child: Opacity( + opacity: isHidden ? 0.5 : 1.0, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4), + ), + child: isHidden + ? const Icon(Icons.close, size: 16, color: Colors.white) + : null, + ), + const Gap(8), + Text( + label, + style: TextStyle( + fontSize: 16, + decoration: isHidden + ? TextDecoration.lineThrough + : TextDecoration.none, + ), + ), + ], + ), + ), + ); + } + + Set _getFirstOfMonthDates() { + final Set firstOfMonthDates = {}; + String? currentMonth; + + for (final schoolday in widget.sortedSchooldays) { + final date = schoolday.schoolday.toLocal(); + final monthKey = '${date.year}-${date.month}'; + if (monthKey != currentMonth) { + currentMonth = monthKey; + firstOfMonthDates.add(_formatDateForChart(schoolday.schoolday)); + } + } + return firstOfMonthDates; + } + + String _formatDateForChart(DateTime date) { + final localDate = date.isUtc ? date.toLocal() : date; + return DateFormat('dd.MM').format(localDate); + } + + List> _createAttendanceSeries() { + final List> series = []; + + if (!_hiddenSeries.contains('excused')) { + final excusedData = widget.sortedSchooldays.map((schoolday) { + final data = widget.attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.excused ?? 0, + seriesId: 'excused', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Entschuldigt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.green), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: excusedData, + ), + ); + } + + if (!_hiddenSeries.contains('unexcused')) { + final unexcusedData = widget.sortedSchooldays.map((schoolday) { + final data = widget.attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.unexcused ?? 0, + seriesId: 'unexcused', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Unentschuldigt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: unexcusedData, + ), + ); + } + + if (!_hiddenSeries.contains('goneHome')) { + final goneHomeData = widget.sortedSchooldays.map((schoolday) { + final data = widget.attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.goneHome ?? 0, + seriesId: 'goneHome', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Nach Hause geschickt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: goneHomeData, + ), + ); + } + + return series; + } +} From 0b9ae175e1a14f2e0b9a9f03e5c92daae6d7a121 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Sun, 30 Nov 2025 21:20:38 +0100 Subject: [PATCH 22/52] [APP][SERVER] books feature: add book stats --- .../books/models/book_stats_dto.dart | 141 ++ .../lib/src/protocol/client.dart | 597 +++--- .../lib/src/protocol/protocol.dart | 1457 +++++++-------- .../features/books/data/book_api_service.dart | 8 + .../features/books/domain/book_manager.dart | 11 + .../books_main_menu_page.dart | 138 +- .../books/endpoints/books/books_endpoint.dart | 24 + .../library_books/library_books_endpoint.dart | 1 + .../books/models/book_stats_dto.spy.yaml | 10 + .../books/models/book_stats_dto.dart | 155 ++ .../lib/src/generated/endpoints.dart | 9 + .../lib/src/generated/protocol.dart | 1623 +++++++++-------- .../lib/src/generated/protocol.yaml | 1 + .../test_tools/serverpod_test_tools.dart | 629 ++++--- 14 files changed, 2655 insertions(+), 2149 deletions(-) create mode 100644 school_data_hub_client/lib/src/protocol/_features/books/models/book_stats_dto.dart create mode 100644 school_data_hub_server/lib/src/_features/books/models/book_stats_dto.spy.yaml create mode 100644 school_data_hub_server/lib/src/generated/_features/books/models/book_stats_dto.dart diff --git a/school_data_hub_client/lib/src/protocol/_features/books/models/book_stats_dto.dart b/school_data_hub_client/lib/src/protocol/_features/books/models/book_stats_dto.dart new file mode 100644 index 00000000..5f044dd9 --- /dev/null +++ b/school_data_hub_client/lib/src/protocol/_features/books/models/book_stats_dto.dart @@ -0,0 +1,141 @@ +/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */ +/* To generate run: "serverpod generate" */ + +// ignore_for_file: implementation_imports +// ignore_for_file: library_private_types_in_public_api +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: public_member_api_docs +// ignore_for_file: type_literal_in_constant_pattern +// ignore_for_file: use_super_parameters + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:serverpod_client/serverpod_client.dart' as _i1; + +abstract class LibraryBookStatsDto implements _i1.SerializableModel { + LibraryBookStatsDto._({ + required this.totalCatalogedBooks, + required this.totalBooksWithReadingLevel1, + required this.booksWithReadingLevel2, + required this.booksWithReadingLevel3, + required this.totalLibraryBooks, + required this.totalReadBooks, + required this.actuallyBorrowedBooks, + }); + + factory LibraryBookStatsDto({ + required int totalCatalogedBooks, + required int totalBooksWithReadingLevel1, + required int booksWithReadingLevel2, + required int booksWithReadingLevel3, + required int totalLibraryBooks, + required int totalReadBooks, + required int actuallyBorrowedBooks, + }) = _LibraryBookStatsDtoImpl; + + factory LibraryBookStatsDto.fromJson(Map jsonSerialization) { + return LibraryBookStatsDto( + totalCatalogedBooks: jsonSerialization['totalCatalogedBooks'] as int, + totalBooksWithReadingLevel1: + jsonSerialization['totalBooksWithReadingLevel1'] as int, + booksWithReadingLevel2: + jsonSerialization['booksWithReadingLevel2'] as int, + booksWithReadingLevel3: + jsonSerialization['booksWithReadingLevel3'] as int, + totalLibraryBooks: jsonSerialization['totalLibraryBooks'] as int, + totalReadBooks: jsonSerialization['totalReadBooks'] as int, + actuallyBorrowedBooks: jsonSerialization['actuallyBorrowedBooks'] as int, + ); + } + + int totalCatalogedBooks; + + int totalBooksWithReadingLevel1; + + int booksWithReadingLevel2; + + int booksWithReadingLevel3; + + int totalLibraryBooks; + + int totalReadBooks; + + int actuallyBorrowedBooks; + + /// Returns a shallow copy of this [LibraryBookStatsDto] + /// with some or all fields replaced by the given arguments. + @_i1.useResult + LibraryBookStatsDto copyWith({ + int? totalCatalogedBooks, + int? totalBooksWithReadingLevel1, + int? booksWithReadingLevel2, + int? booksWithReadingLevel3, + int? totalLibraryBooks, + int? totalReadBooks, + int? actuallyBorrowedBooks, + }); + @override + Map toJson() { + return { + 'totalCatalogedBooks': totalCatalogedBooks, + 'totalBooksWithReadingLevel1': totalBooksWithReadingLevel1, + 'booksWithReadingLevel2': booksWithReadingLevel2, + 'booksWithReadingLevel3': booksWithReadingLevel3, + 'totalLibraryBooks': totalLibraryBooks, + 'totalReadBooks': totalReadBooks, + 'actuallyBorrowedBooks': actuallyBorrowedBooks, + }; + } + + @override + String toString() { + return _i1.SerializationManager.encode(this); + } +} + +class _LibraryBookStatsDtoImpl extends LibraryBookStatsDto { + _LibraryBookStatsDtoImpl({ + required int totalCatalogedBooks, + required int totalBooksWithReadingLevel1, + required int booksWithReadingLevel2, + required int booksWithReadingLevel3, + required int totalLibraryBooks, + required int totalReadBooks, + required int actuallyBorrowedBooks, + }) : super._( + totalCatalogedBooks: totalCatalogedBooks, + totalBooksWithReadingLevel1: totalBooksWithReadingLevel1, + booksWithReadingLevel2: booksWithReadingLevel2, + booksWithReadingLevel3: booksWithReadingLevel3, + totalLibraryBooks: totalLibraryBooks, + totalReadBooks: totalReadBooks, + actuallyBorrowedBooks: actuallyBorrowedBooks, + ); + + /// Returns a shallow copy of this [LibraryBookStatsDto] + /// with some or all fields replaced by the given arguments. + @_i1.useResult + @override + LibraryBookStatsDto copyWith({ + int? totalCatalogedBooks, + int? totalBooksWithReadingLevel1, + int? booksWithReadingLevel2, + int? booksWithReadingLevel3, + int? totalLibraryBooks, + int? totalReadBooks, + int? actuallyBorrowedBooks, + }) { + return LibraryBookStatsDto( + totalCatalogedBooks: totalCatalogedBooks ?? this.totalCatalogedBooks, + totalBooksWithReadingLevel1: + totalBooksWithReadingLevel1 ?? this.totalBooksWithReadingLevel1, + booksWithReadingLevel2: + booksWithReadingLevel2 ?? this.booksWithReadingLevel2, + booksWithReadingLevel3: + booksWithReadingLevel3 ?? this.booksWithReadingLevel3, + totalLibraryBooks: totalLibraryBooks ?? this.totalLibraryBooks, + totalReadBooks: totalReadBooks ?? this.totalReadBooks, + actuallyBorrowedBooks: + actuallyBorrowedBooks ?? this.actuallyBorrowedBooks, + ); + } +} diff --git a/school_data_hub_client/lib/src/protocol/client.dart b/school_data_hub_client/lib/src/protocol/client.dart index 0413cfc4..c45675b3 100644 --- a/school_data_hub_client/lib/src/protocol/client.dart +++ b/school_data_hub_client/lib/src/protocol/client.dart @@ -41,78 +41,80 @@ import 'package:school_data_hub_client/src/protocol/_features/books/models/book_ as _i17; import 'package:school_data_hub_client/src/protocol/_features/books/models/book.dart' as _i18; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_location.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/book_stats_dto.dart' as _i19; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_location.dart' as _i20; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_query.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book.dart' as _i21; -import 'package:school_data_hub_client/src/protocol/_features/books/models/pupil_book_lending.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_query.dart' as _i22; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/learning_support_plan.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/pupil_book_lending.dart' as _i23; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category_status.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/learning_support_plan.dart' as _i24; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category_status.dart' as _i25; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i26; -import 'package:school_data_hub_client/src/protocol/_features/matrix/compulsory_room.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' as _i27; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' +import 'package:school_data_hub_client/src/protocol/_features/matrix/compulsory_room.dart' as _i28; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level_legacy_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i29; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level_legacy_dto.dart' as _i30; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/communication_skills.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i31; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/tutor_info.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/communication_skills.dart' as _i32; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/tutor_info.dart' as _i33; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/public_media_auth.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' as _i34; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/public_media_auth.dart' as _i35; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level.dart' as _i36; -import 'package:school_data_hub_client/src/protocol/_features/school_data/models/school_data.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' as _i37; -import 'package:school_data_hub_client/src/protocol/_features/school_lists/models/school_list.dart' +import 'package:school_data_hub_client/src/protocol/_features/school_data/models/school_data.dart' as _i38; -import 'package:school_data_hub_client/src/protocol/_features/school_lists/models/pupil_entry.dart' +import 'package:school_data_hub_client/src/protocol/_features/school_lists/models/school_list.dart' as _i39; -import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/school_semester.dart' +import 'package:school_data_hub_client/src/protocol/_features/school_lists/models/pupil_entry.dart' as _i40; -import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/schoolday.dart' +import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/school_semester.dart' as _i41; -import 'package:school_data_hub_client/src/protocol/_features/schoolday_events/models/schoolday_event.dart' +import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/schoolday.dart' as _i42; -import 'package:school_data_hub_client/src/protocol/_features/schoolday_events/models/schoolday_event_type.dart' +import 'package:school_data_hub_client/src/protocol/_features/schoolday_events/models/schoolday_event.dart' as _i43; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/classroom.dart' +import 'package:school_data_hub_client/src/protocol/_features/schoolday_events/models/schoolday_event_type.dart' as _i44; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/lesson/lesson_group.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/classroom.dart' as _i45; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/lesson/lesson_group.dart' as _i46; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' as _i47; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/subject.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' as _i48; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/timetable.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/subject.dart' as _i49; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/timetable_slot.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/timetable.dart' as _i50; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/weekday_enum.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/timetable_slot.dart' as _i51; -import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/pupil_workbook.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/weekday_enum.dart' as _i52; -import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/workbook.dart' +import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/pupil_workbook.dart' as _i53; -import 'dart:typed_data' as _i54; -import 'protocol.dart' as _i55; +import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/workbook.dart' + as _i54; +import 'dart:typed_data' as _i55; +import 'protocol.dart' as _i56; /// The endpoint for admin operations. /// This endpoint requires the user to be logged in and have admin scope. @@ -495,6 +497,13 @@ class EndpointBooks extends _i1.EndpointRef { {}, ); + _i2.Future<_i19.LibraryBookStatsDto> getBookStats() => + caller.callServerEndpoint<_i19.LibraryBookStatsDto>( + 'books', + 'getBookStats', + {}, + ); + _i2.Future<_i18.Book?> fetchBookByIsbn(int isbn) => caller.callServerEndpoint<_i18.Book?>( 'books', @@ -529,31 +538,31 @@ class EndpointLibraryBookLocations extends _i1.EndpointRef { @override String get name => 'libraryBookLocations'; - _i2.Future<_i19.LibraryBookLocation> postLibraryBookLocation( - _i19.LibraryBookLocation libraryBookLocation) => - caller.callServerEndpoint<_i19.LibraryBookLocation>( + _i2.Future<_i20.LibraryBookLocation> postLibraryBookLocation( + _i20.LibraryBookLocation libraryBookLocation) => + caller.callServerEndpoint<_i20.LibraryBookLocation>( 'libraryBookLocations', 'postLibraryBookLocation', {'libraryBookLocation': libraryBookLocation}, ); - _i2.Future> fetchLibraryBookLocations() => - caller.callServerEndpoint>( + _i2.Future> fetchLibraryBookLocations() => + caller.callServerEndpoint>( 'libraryBookLocations', 'fetchLibraryBookLocations', {}, ); - _i2.Future<_i19.LibraryBookLocation> updateLibraryBookLocation( - _i19.LibraryBookLocation libraryBookLocation) => - caller.callServerEndpoint<_i19.LibraryBookLocation>( + _i2.Future<_i20.LibraryBookLocation> updateLibraryBookLocation( + _i20.LibraryBookLocation libraryBookLocation) => + caller.callServerEndpoint<_i20.LibraryBookLocation>( 'libraryBookLocations', 'updateLibraryBookLocation', {'libraryBookLocation': libraryBookLocation}, ); _i2.Future deleteLibraryBookLocation( - _i19.LibraryBookLocation location) => + _i20.LibraryBookLocation location) => caller.callServerEndpoint( 'libraryBookLocations', 'deleteLibraryBookLocation', @@ -568,12 +577,12 @@ class EndpointLibraryBooks extends _i1.EndpointRef { @override String get name => 'libraryBooks'; - _i2.Future<_i20.LibraryBook> postLibraryBook( + _i2.Future<_i21.LibraryBook> postLibraryBook( int isbn, String libraryId, - _i19.LibraryBookLocation location, + _i20.LibraryBookLocation location, ) => - caller.callServerEndpoint<_i20.LibraryBook>( + caller.callServerEndpoint<_i21.LibraryBook>( 'libraryBooks', 'postLibraryBook', { @@ -583,47 +592,47 @@ class EndpointLibraryBooks extends _i1.EndpointRef { }, ); - _i2.Future> fetchLibraryBooks() => - caller.callServerEndpoint>( + _i2.Future> fetchLibraryBooks() => + caller.callServerEndpoint>( 'libraryBooks', 'fetchLibraryBooks', {}, ); - _i2.Future<_i20.LibraryBook?> fetchLibraryBookByIsbn(int isbn) => - caller.callServerEndpoint<_i20.LibraryBook?>( + _i2.Future<_i21.LibraryBook?> fetchLibraryBookByIsbn(int isbn) => + caller.callServerEndpoint<_i21.LibraryBook?>( 'libraryBooks', 'fetchLibraryBookByIsbn', {'isbn': isbn}, ); - _i2.Future<_i20.LibraryBook?> fetchLibraryBookByLibraryId(String libraryId) => - caller.callServerEndpoint<_i20.LibraryBook?>( + _i2.Future<_i21.LibraryBook?> fetchLibraryBookByLibraryId(String libraryId) => + caller.callServerEndpoint<_i21.LibraryBook?>( 'libraryBooks', 'fetchLibraryBookByLibraryId', {'libraryId': libraryId}, ); - _i2.Future> fetchLibraryBooksMatchingQuery( - _i21.LibraryBookQuery libraryBookQuery) => - caller.callServerEndpoint>( + _i2.Future> fetchLibraryBooksMatchingQuery( + _i22.LibraryBookQuery libraryBookQuery) => + caller.callServerEndpoint>( 'libraryBooks', 'fetchLibraryBooksMatchingQuery', {'libraryBookQuery': libraryBookQuery}, ); - _i2.Future<_i20.LibraryBook> updateLibraryBookAndRelatedBook( + _i2.Future<_i21.LibraryBook> updateLibraryBookAndRelatedBook( int isbn, String libraryId, bool? available, - _i19.LibraryBookLocation? location, + _i20.LibraryBookLocation? location, String? title, String? author, String? description, String? readingLevel, List<_i17.BookTag>? tags, ) => - caller.callServerEndpoint<_i20.LibraryBook>( + caller.callServerEndpoint<_i21.LibraryBook>( 'libraryBooks', 'updateLibraryBookAndRelatedBook', { @@ -669,23 +678,23 @@ class EndpointPupilBookLending extends _i1.EndpointRef { }, ); - _i2.Future> fetchPupilBookLendings() => - caller.callServerEndpoint>( + _i2.Future> fetchPupilBookLendings() => + caller.callServerEndpoint>( 'pupilBookLending', 'fetchPupilBookLendings', {}, ); - _i2.Future<_i22.PupilBookLending?> fetchPupilBookLendingByLendingId( + _i2.Future<_i23.PupilBookLending?> fetchPupilBookLendingByLendingId( String lendingId) => - caller.callServerEndpoint<_i22.PupilBookLending?>( + caller.callServerEndpoint<_i23.PupilBookLending?>( 'pupilBookLending', 'fetchPupilBookLendingByLendingId', {'lendingId': lendingId}, ); _i2.Future<_i5.PupilData> updatePupilBookLending( - _i22.PupilBookLending pupilBookLending) => + _i23.PupilBookLending pupilBookLending) => caller.callServerEndpoint<_i5.PupilData>( 'pupilBookLending', 'updatePupilBookLending', @@ -836,29 +845,29 @@ class EndpointLearningSupportPlan extends _i1.EndpointRef { @override String get name => 'learningSupportPlan'; - _i2.Future> fetchLearningSupportPlans() => - caller.callServerEndpoint>( + _i2.Future> fetchLearningSupportPlans() => + caller.callServerEndpoint>( 'learningSupportPlan', 'fetchLearningSupportPlans', {}, ); - _i2.Future<_i23.LearningSupportPlan> createLearningSupportPlan( - _i23.LearningSupportPlan plan) => - caller.callServerEndpoint<_i23.LearningSupportPlan>( + _i2.Future<_i24.LearningSupportPlan> createLearningSupportPlan( + _i24.LearningSupportPlan plan) => + caller.callServerEndpoint<_i24.LearningSupportPlan>( 'learningSupportPlan', 'createLearningSupportPlan', {'plan': plan}, ); - _i2.Future updateLearningSupportPlan(_i23.LearningSupportPlan plan) => + _i2.Future updateLearningSupportPlan(_i24.LearningSupportPlan plan) => caller.callServerEndpoint( 'learningSupportPlan', 'updateLearningSupportPlan', {'plan': plan}, ); - _i2.Future deleteLearningSupportPlan(_i23.LearningSupportPlan plan) => + _i2.Future deleteLearningSupportPlan(_i24.LearningSupportPlan plan) => caller.callServerEndpoint( 'learningSupportPlan', 'deleteLearningSupportPlan', @@ -886,23 +895,23 @@ class EndpointLearningSupportPlan extends _i1.EndpointRef { }, ); - _i2.Future> fetchSupportCategoryStatus( + _i2.Future> fetchSupportCategoryStatus( int pupilId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'learningSupportPlan', 'fetchSupportCategoryStatus', {'pupilId': pupilId}, ); - _i2.Future> + _i2.Future> fetchSupportCategoryStatusFromPupil(int pupilId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'learningSupportPlan', 'fetchSupportCategoryStatusFromPupil', {'pupilId': pupilId}, ); - _i2.Future<_i24.SupportCategoryStatus> updateCategoryStatus( + _i2.Future<_i25.SupportCategoryStatus> updateCategoryStatus( int pupilId, int supportCategoryId, int? status, @@ -910,7 +919,7 @@ class EndpointLearningSupportPlan extends _i1.EndpointRef { String? createdBy, DateTime? createdAt, ) => - caller.callServerEndpoint<_i24.SupportCategoryStatus>( + caller.callServerEndpoint<_i25.SupportCategoryStatus>( 'learningSupportPlan', 'updateCategoryStatus', { @@ -964,12 +973,12 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { String get name => 'preSchoolMedical'; /// Create a new PreSchoolMedical record for a pupil - _i2.Future<_i25.PreSchoolMedical> createPreSchoolMedical( + _i2.Future<_i26.PreSchoolMedical> createPreSchoolMedical( int pupilId, - _i26.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i27.PreSchoolMedicalStatus? preschoolMedicalStatus, String createdBy, ) => - caller.callServerEndpoint<_i25.PreSchoolMedical>( + caller.callServerEndpoint<_i26.PreSchoolMedical>( 'preSchoolMedical', 'createPreSchoolMedical', { @@ -980,12 +989,12 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Update an existing PreSchoolMedical record - _i2.Future<_i25.PreSchoolMedical> updatePreSchoolMedical( + _i2.Future<_i26.PreSchoolMedical> updatePreSchoolMedical( int preSchoolMedicalId, - _i26.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i27.PreSchoolMedicalStatus? preschoolMedicalStatus, String updatedBy, ) => - caller.callServerEndpoint<_i25.PreSchoolMedical>( + caller.callServerEndpoint<_i26.PreSchoolMedical>( 'preSchoolMedical', 'updatePreSchoolMedical', { @@ -996,18 +1005,18 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Get a PreSchoolMedical record by ID - _i2.Future<_i25.PreSchoolMedical?> getPreSchoolMedical( + _i2.Future<_i26.PreSchoolMedical?> getPreSchoolMedical( int preSchoolMedicalId) => - caller.callServerEndpoint<_i25.PreSchoolMedical?>( + caller.callServerEndpoint<_i26.PreSchoolMedical?>( 'preSchoolMedical', 'getPreSchoolMedical', {'preSchoolMedicalId': preSchoolMedicalId}, ); /// Get PreSchoolMedical record for a specific pupil - _i2.Future<_i25.PreSchoolMedical?> getPreSchoolMedicalByPupilId( + _i2.Future<_i26.PreSchoolMedical?> getPreSchoolMedicalByPupilId( int pupilId) => - caller.callServerEndpoint<_i25.PreSchoolMedical?>( + caller.callServerEndpoint<_i26.PreSchoolMedical?>( 'preSchoolMedical', 'getPreSchoolMedicalByPupilId', {'pupilId': pupilId}, @@ -1022,12 +1031,12 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Add a file to a PreSchoolMedical record - _i2.Future<_i25.PreSchoolMedical> addFileToPreSchoolMedical( + _i2.Future<_i26.PreSchoolMedical> addFileToPreSchoolMedical( int preSchoolMedicalId, String filePath, String createdBy, ) => - caller.callServerEndpoint<_i25.PreSchoolMedical>( + caller.callServerEndpoint<_i26.PreSchoolMedical>( 'preSchoolMedical', 'addFileToPreSchoolMedical', { @@ -1052,17 +1061,17 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Get all PreSchoolMedical records (for admin purposes) - _i2.Future> getAllPreSchoolMedicalRecords() => - caller.callServerEndpoint>( + _i2.Future> getAllPreSchoolMedicalRecords() => + caller.callServerEndpoint>( 'preSchoolMedical', 'getAllPreSchoolMedicalRecords', {}, ); /// Get PreSchoolMedical records with specific status - _i2.Future> getPreSchoolMedicalByStatus( - _i26.PreSchoolMedicalStatus status) => - caller.callServerEndpoint>( + _i2.Future> getPreSchoolMedicalByStatus( + _i27.PreSchoolMedicalStatus status) => + caller.callServerEndpoint>( 'preSchoolMedical', 'getPreSchoolMedicalByStatus', {'status': status}, @@ -1120,16 +1129,16 @@ class EndpointMatrix extends _i1.EndpointRef { @override String get name => 'matrix'; - _i2.Future?> getCompulsoryRooms() => - caller.callServerEndpoint?>( + _i2.Future?> getCompulsoryRooms() => + caller.callServerEndpoint?>( 'matrix', 'getCompulsoryRooms', {}, ); - _i2.Future> setCompulsoryRooms( - List<_i27.CompulsoryRoom> compulsoryRooms) => - caller.callServerEndpoint>( + _i2.Future> setCompulsoryRooms( + List<_i28.CompulsoryRoom> compulsoryRooms) => + caller.callServerEndpoint>( 'matrix', 'setCompulsoryRooms', {'compulsoryRooms': compulsoryRooms}, @@ -1175,7 +1184,7 @@ class EndpointPupil extends _i1.EndpointRef { _i2.Future<_i5.PupilData> deletePupilDocument( int pupilId, - _i28.PupilDocumentType documentType, + _i29.PupilDocumentType documentType, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupil', @@ -1213,7 +1222,7 @@ class EndpointPupil extends _i1.EndpointRef { ); _i2.Future bulkAddSupportLevels( - List<_i29.SupportLevelLegacyDto> supportLevelData) => + List<_i30.SupportLevelLegacyDto> supportLevelData) => caller.callServerEndpoint( 'pupil', 'bulkAddSupportLevels', @@ -1228,10 +1237,10 @@ class EndpointPupilIdentity extends _i1.EndpointRef { @override String get name => 'pupilIdentity'; - _i2.Stream<_i30.PupilIdentityDto> streamEncryptedPupilIds( + _i2.Stream<_i31.PupilIdentityDto> streamEncryptedPupilIds( String channelName) => - caller.callStreamingServerEndpoint<_i2.Stream<_i30.PupilIdentityDto>, - _i30.PupilIdentityDto>( + caller.callStreamingServerEndpoint<_i2.Stream<_i31.PupilIdentityDto>, + _i31.PupilIdentityDto>( 'pupilIdentity', 'streamEncryptedPupilIds', {'channelName': channelName}, @@ -1240,7 +1249,7 @@ class EndpointPupilIdentity extends _i1.EndpointRef { _i2.Future sendPupilIdentityMessage( String pupilIdentityChannel, - _i30.PupilIdentityDto pupilIdentityMessage, + _i31.PupilIdentityDto pupilIdentityMessage, ) => caller.callServerEndpoint( 'pupilIdentity', @@ -1289,7 +1298,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updateCommunicationSkills({ required int pupilId, - required _i31.CommunicationSkills? communicationSkills, + required _i32.CommunicationSkills? communicationSkills, }) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1302,7 +1311,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updateTutorInfo( int pupilId, - _i32.TutorInfo? tutorInfo, + _i33.TutorInfo? tutorInfo, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1314,7 +1323,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { ); _i2.Future> updateSiblingsTutorInfo( - _i33.SiblingsTutorInfo siblingsTutorInfo) => + _i34.SiblingsTutorInfo siblingsTutorInfo) => caller.callServerEndpoint>( 'pupilUpdate', 'updateSiblingsTutorInfo', @@ -1325,7 +1334,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { int pupilId, String filePath, String createdBy, - _i28.PupilDocumentType documentType, + _i29.PupilDocumentType documentType, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1372,7 +1381,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updatePreSchoolMedicalStatus( int pupilId, - _i26.PreSchoolMedicalStatus preSchoolMedicalStatus, + _i27.PreSchoolMedicalStatus preSchoolMedicalStatus, String updatedBy, ) => caller.callServerEndpoint<_i5.PupilData>( @@ -1387,7 +1396,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updatePublicMediaAuth( int pupilId, - _i34.PublicMediaAuth publicMediaAuth, + _i35.PublicMediaAuth publicMediaAuth, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1399,7 +1408,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { ); _i2.Future<_i5.PupilData> updateSupportLevel( - _i35.SupportLevel supportLevel, + _i36.SupportLevel supportLevel, int pupilId, ) => caller.callServerEndpoint<_i5.PupilData>( @@ -1427,7 +1436,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updateAfterSchoolCare( int pupilId, - _i36.AfterSchoolCare afterSchoolCare, + _i37.AfterSchoolCare afterSchoolCare, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1446,16 +1455,16 @@ class EndpointSchoolData extends _i1.EndpointRef { @override String get name => 'schoolData'; - _i2.Future<_i37.SchoolData> postSchoolData(_i37.SchoolData schoolData) => - caller.callServerEndpoint<_i37.SchoolData>( + _i2.Future<_i38.SchoolData> postSchoolData(_i38.SchoolData schoolData) => + caller.callServerEndpoint<_i38.SchoolData>( 'schoolData', 'postSchoolData', {'schoolData': schoolData}, ); /// TODO: we should be specific about which school data to get - _i2.Future<_i37.SchoolData?> getSchoolData() => - caller.callServerEndpoint<_i37.SchoolData?>( + _i2.Future<_i38.SchoolData?> getSchoolData() => + caller.callServerEndpoint<_i38.SchoolData?>( 'schoolData', 'getSchoolData', {}, @@ -1469,21 +1478,21 @@ class EndpointSchoolList extends _i1.EndpointRef { @override String get name => 'schoolList'; - _i2.Future> fetchSchoolLists(String userName) => - caller.callServerEndpoint>( + _i2.Future> fetchSchoolLists(String userName) => + caller.callServerEndpoint>( 'schoolList', 'fetchSchoolLists', {'userName': userName}, ); - _i2.Future<_i38.SchoolList> postSchoolList( + _i2.Future<_i39.SchoolList> postSchoolList( String name, String description, List pupilIds, bool public, String createdBy, ) => - caller.callServerEndpoint<_i38.SchoolList>( + caller.callServerEndpoint<_i39.SchoolList>( 'schoolList', 'postSchoolList', { @@ -1495,7 +1504,7 @@ class EndpointSchoolList extends _i1.EndpointRef { }, ); - _i2.Future<_i38.SchoolList> updateSchoolList( + _i2.Future<_i39.SchoolList> updateSchoolList( int listId, String? name, String? description, @@ -1503,7 +1512,7 @@ class EndpointSchoolList extends _i1.EndpointRef { bool? public, ({_i14.MemberOperation operation, List pupilIds})? updateMembers, ) => - caller.callServerEndpoint<_i38.SchoolList>( + caller.callServerEndpoint<_i39.SchoolList>( 'schoolList', 'updateSchoolList', { @@ -1523,9 +1532,9 @@ class EndpointSchoolList extends _i1.EndpointRef { {'listId': listId}, ); - _i2.Future<_i39.PupilListEntry> updatePupilListEntry( - _i39.PupilListEntry entry) => - caller.callServerEndpoint<_i39.PupilListEntry>( + _i2.Future<_i40.PupilListEntry> updatePupilListEntry( + _i40.PupilListEntry entry) => + caller.callServerEndpoint<_i40.PupilListEntry>( 'schoolList', 'updatePupilListEntry', {'entry': entry}, @@ -1539,7 +1548,7 @@ class EndpointSchooldayAdmin extends _i1.EndpointRef { @override String get name => 'schooldayAdmin'; - _i2.Future<_i40.SchoolSemester> createSchoolSemester( + _i2.Future<_i41.SchoolSemester> createSchoolSemester( String schoolYearName, DateTime startDate, DateTime endDate, @@ -1549,7 +1558,7 @@ class EndpointSchooldayAdmin extends _i1.EndpointRef { DateTime? reportConferenceDate, DateTime? reportSignedDate, ) => - caller.callServerEndpoint<_i40.SchoolSemester>( + caller.callServerEndpoint<_i41.SchoolSemester>( 'schooldayAdmin', 'createSchoolSemester', { @@ -1564,43 +1573,43 @@ class EndpointSchooldayAdmin extends _i1.EndpointRef { }, ); - _i2.Future> getAllSchoolSemesters() => - caller.callServerEndpoint>( + _i2.Future> getAllSchoolSemesters() => + caller.callServerEndpoint>( 'schooldayAdmin', 'getAllSchoolSemesters', {}, ); - _i2.Future<_i40.SchoolSemester?> getCurrentSchoolSemester() => - caller.callServerEndpoint<_i40.SchoolSemester?>( + _i2.Future<_i41.SchoolSemester?> getCurrentSchoolSemester() => + caller.callServerEndpoint<_i41.SchoolSemester?>( 'schooldayAdmin', 'getCurrentSchoolSemester', {}, ); - _i2.Future updateSchoolSemester(_i40.SchoolSemester schoolSemester) => + _i2.Future updateSchoolSemester(_i41.SchoolSemester schoolSemester) => caller.callServerEndpoint( 'schooldayAdmin', 'updateSchoolSemester', {'schoolSemester': schoolSemester}, ); - _i2.Future deleteSchoolSemester(_i40.SchoolSemester semester) => + _i2.Future deleteSchoolSemester(_i41.SchoolSemester semester) => caller.callServerEndpoint( 'schooldayAdmin', 'deleteSchoolSemester', {'semester': semester}, ); - _i2.Future<_i41.Schoolday?> createSchoolday(DateTime date) => - caller.callServerEndpoint<_i41.Schoolday?>( + _i2.Future<_i42.Schoolday?> createSchoolday(DateTime date) => + caller.callServerEndpoint<_i42.Schoolday?>( 'schooldayAdmin', 'createSchoolday', {'date': date}, ); - _i2.Future> createSchooldays(List dates) => - caller.callServerEndpoint>( + _i2.Future> createSchooldays(List dates) => + caller.callServerEndpoint>( 'schooldayAdmin', 'createSchooldays', {'dates': dates}, @@ -1613,7 +1622,7 @@ class EndpointSchooldayAdmin extends _i1.EndpointRef { {'date': date}, ); - _i2.Future updateSchoolday(_i41.Schoolday schoolday) => + _i2.Future updateSchoolday(_i42.Schoolday schoolday) => caller.callServerEndpoint( 'schooldayAdmin', 'updateSchoolday', @@ -1628,15 +1637,15 @@ class EndpointSchoolday extends _i1.EndpointRef { @override String get name => 'schoolday'; - _i2.Future> getSchoolSemesters() => - caller.callServerEndpoint>( + _i2.Future> getSchoolSemesters() => + caller.callServerEndpoint>( 'schoolday', 'getSchoolSemesters', {}, ); - _i2.Future> getSchooldays() => - caller.callServerEndpoint>( + _i2.Future> getSchooldays() => + caller.callServerEndpoint>( 'schoolday', 'getSchooldays', {}, @@ -1650,24 +1659,24 @@ class EndpointSchooldayEvent extends _i1.EndpointRef { @override String get name => 'schooldayEvent'; - _i2.Future> fetchSchooldayEvents() => - caller.callServerEndpoint>( + _i2.Future> fetchSchooldayEvents() => + caller.callServerEndpoint>( 'schooldayEvent', 'fetchSchooldayEvents', {}, ); - _i2.Future<_i42.SchooldayEvent> createSchooldayEvent({ + _i2.Future<_i43.SchooldayEvent> createSchooldayEvent({ required int pupilId, required String pupilNameAndGroup, required String dateTimeAsString, required int schooldayId, - required _i43.SchooldayEventType type, + required _i44.SchooldayEventType type, required String reason, required String createdBy, required String tutor, }) => - caller.callServerEndpoint<_i42.SchooldayEvent>( + caller.callServerEndpoint<_i43.SchooldayEvent>( 'schooldayEvent', 'createSchooldayEvent', { @@ -1682,15 +1691,15 @@ class EndpointSchooldayEvent extends _i1.EndpointRef { }, ); - _i2.Future<_i42.SchooldayEvent> updateSchooldayEvent( - _i42.SchooldayEvent schooldayEvent, + _i2.Future<_i43.SchooldayEvent> updateSchooldayEvent( + _i43.SchooldayEvent schooldayEvent, bool changedProcessedStatus, String pupilNameAndGroup, String tutor, String modifiedBy, String dateTimeAsString, ) => - caller.callServerEndpoint<_i42.SchooldayEvent>( + caller.callServerEndpoint<_i43.SchooldayEvent>( 'schooldayEvent', 'updateSchooldayEvent', { @@ -1710,13 +1719,13 @@ class EndpointSchooldayEvent extends _i1.EndpointRef { {'schooldayEventId': schooldayEventId}, ); - _i2.Future<_i42.SchooldayEvent> updateSchooldayEventFile( + _i2.Future<_i43.SchooldayEvent> updateSchooldayEventFile( int schooldayEventId, String filePath, String createdBy, bool isprocessed, ) => - caller.callServerEndpoint<_i42.SchooldayEvent>( + caller.callServerEndpoint<_i43.SchooldayEvent>( 'schooldayEvent', 'updateSchooldayEventFile', { @@ -1727,11 +1736,11 @@ class EndpointSchooldayEvent extends _i1.EndpointRef { }, ); - _i2.Future<_i42.SchooldayEvent> deleteSchooldayEventFile( + _i2.Future<_i43.SchooldayEvent> deleteSchooldayEventFile( int schooldayEventId, bool isProcessed, ) => - caller.callServerEndpoint<_i42.SchooldayEvent>( + caller.callServerEndpoint<_i43.SchooldayEvent>( 'schooldayEvent', 'deleteSchooldayEventFile', { @@ -1748,43 +1757,43 @@ class EndpointClassroom extends _i1.EndpointRef { @override String get name => 'classroom'; - _i2.Future<_i44.Classroom> createClassroom(_i44.Classroom classroom) => - caller.callServerEndpoint<_i44.Classroom>( + _i2.Future<_i45.Classroom> createClassroom(_i45.Classroom classroom) => + caller.callServerEndpoint<_i45.Classroom>( 'classroom', 'createClassroom', {'classroom': classroom}, ); - _i2.Future> fetchClassrooms() => - caller.callServerEndpoint>( + _i2.Future> fetchClassrooms() => + caller.callServerEndpoint>( 'classroom', 'fetchClassrooms', {}, ); - _i2.Future<_i44.Classroom?> fetchClassroomById(int id) => - caller.callServerEndpoint<_i44.Classroom?>( + _i2.Future<_i45.Classroom?> fetchClassroomById(int id) => + caller.callServerEndpoint<_i45.Classroom?>( 'classroom', 'fetchClassroomById', {'id': id}, ); - _i2.Future<_i44.Classroom?> fetchClassroomByRoomCode(String roomCode) => - caller.callServerEndpoint<_i44.Classroom?>( + _i2.Future<_i45.Classroom?> fetchClassroomByRoomCode(String roomCode) => + caller.callServerEndpoint<_i45.Classroom?>( 'classroom', 'fetchClassroomByRoomCode', {'roomCode': roomCode}, ); - _i2.Future> fetchClassroomsByRoomName(String roomName) => - caller.callServerEndpoint>( + _i2.Future> fetchClassroomsByRoomName(String roomName) => + caller.callServerEndpoint>( 'classroom', 'fetchClassroomsByRoomName', {'roomName': roomName}, ); - _i2.Future<_i44.Classroom> updateClassroom(_i44.Classroom classroom) => - caller.callServerEndpoint<_i44.Classroom>( + _i2.Future<_i45.Classroom> updateClassroom(_i45.Classroom classroom) => + caller.callServerEndpoint<_i45.Classroom>( 'classroom', 'updateClassroom', {'classroom': classroom}, @@ -1804,61 +1813,61 @@ class EndpointLearningGroup extends _i1.EndpointRef { @override String get name => 'learningGroup'; - _i2.Future<_i45.LessonGroup> createLessonGroup( - _i45.LessonGroup lessonGroup) => - caller.callServerEndpoint<_i45.LessonGroup>( + _i2.Future<_i46.LessonGroup> createLessonGroup( + _i46.LessonGroup lessonGroup) => + caller.callServerEndpoint<_i46.LessonGroup>( 'learningGroup', 'createLessonGroup', {'lessonGroup': lessonGroup}, ); - _i2.Future> fetchLessonGroups() => - caller.callServerEndpoint>( + _i2.Future> fetchLessonGroups() => + caller.callServerEndpoint>( 'learningGroup', 'fetchLessonGroups', {}, ); - _i2.Future<_i45.LessonGroup?> fetchLessonGroupById(int id) => - caller.callServerEndpoint<_i45.LessonGroup?>( + _i2.Future<_i46.LessonGroup?> fetchLessonGroupById(int id) => + caller.callServerEndpoint<_i46.LessonGroup?>( 'learningGroup', 'fetchLessonGroupById', {'id': id}, ); - _i2.Future<_i45.LessonGroup?> fetchLessonGroupByPublicId(String publicId) => - caller.callServerEndpoint<_i45.LessonGroup?>( + _i2.Future<_i46.LessonGroup?> fetchLessonGroupByPublicId(String publicId) => + caller.callServerEndpoint<_i46.LessonGroup?>( 'learningGroup', 'fetchLessonGroupByPublicId', {'publicId': publicId}, ); - _i2.Future> fetchLessonGroupsByName(String name) => - caller.callServerEndpoint>( + _i2.Future> fetchLessonGroupsByName(String name) => + caller.callServerEndpoint>( 'learningGroup', 'fetchLessonGroupsByName', {'name': name}, ); - _i2.Future> fetchLessonGroupsByCreator( + _i2.Future> fetchLessonGroupsByCreator( String createdBy) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'learningGroup', 'fetchLessonGroupsByCreator', {'createdBy': createdBy}, ); - _i2.Future> fetchLessonGroupsByTimetable( + _i2.Future> fetchLessonGroupsByTimetable( int timetableId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'learningGroup', 'fetchLessonGroupsByTimetable', {'timetableId': timetableId}, ); - _i2.Future<_i45.LessonGroup> updateLessonGroup( - _i45.LessonGroup lessonGroup) => - caller.callServerEndpoint<_i45.LessonGroup>( + _i2.Future<_i46.LessonGroup> updateLessonGroup( + _i46.LessonGroup lessonGroup) => + caller.callServerEndpoint<_i46.LessonGroup>( 'learningGroup', 'updateLessonGroup', {'lessonGroup': lessonGroup}, @@ -1878,77 +1887,77 @@ class EndpointScheduledLesson extends _i1.EndpointRef { @override String get name => 'scheduledLesson'; - _i2.Future<_i46.ScheduledLesson?> createScheduledLesson( - _i46.ScheduledLesson scheduledLesson) => - caller.callServerEndpoint<_i46.ScheduledLesson?>( + _i2.Future<_i47.ScheduledLesson?> createScheduledLesson( + _i47.ScheduledLesson scheduledLesson) => + caller.callServerEndpoint<_i47.ScheduledLesson?>( 'scheduledLesson', 'createScheduledLesson', {'scheduledLesson': scheduledLesson}, ); - _i2.Future> fetchScheduledLessons() => - caller.callServerEndpoint>( + _i2.Future> fetchScheduledLessons() => + caller.callServerEndpoint>( 'scheduledLesson', 'fetchScheduledLessons', {}, ); - _i2.Future<_i46.ScheduledLesson?> fetchScheduledLessonById(int id) => - caller.callServerEndpoint<_i46.ScheduledLesson?>( + _i2.Future<_i47.ScheduledLesson?> fetchScheduledLessonById(int id) => + caller.callServerEndpoint<_i47.ScheduledLesson?>( 'scheduledLesson', 'fetchScheduledLessonById', {'id': id}, ); - _i2.Future> fetchScheduledLessonsByTimetable( + _i2.Future> fetchScheduledLessonsByTimetable( int timetableId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'scheduledLesson', 'fetchScheduledLessonsByTimetable', {'timetableId': timetableId}, ); - _i2.Future> fetchScheduledLessonsBySubject( + _i2.Future> fetchScheduledLessonsBySubject( int subjectId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'scheduledLesson', 'fetchScheduledLessonsBySubject', {'subjectId': subjectId}, ); - _i2.Future> fetchScheduledLessonsByRoom( + _i2.Future> fetchScheduledLessonsByRoom( int roomId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'scheduledLesson', 'fetchScheduledLessonsByRoom', {'roomId': roomId}, ); - _i2.Future> fetchScheduledLessonsBySlotId( + _i2.Future> fetchScheduledLessonsBySlotId( int slotId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'scheduledLesson', 'fetchScheduledLessonsBySlotId', {'slotId': slotId}, ); - _i2.Future> fetchActiveScheduledLessons() => - caller.callServerEndpoint>( + _i2.Future> fetchActiveScheduledLessons() => + caller.callServerEndpoint>( 'scheduledLesson', 'fetchActiveScheduledLessons', {}, ); - _i2.Future<_i46.ScheduledLesson?> updateScheduledLesson( - _i46.ScheduledLesson scheduledLesson) => - caller.callServerEndpoint<_i46.ScheduledLesson?>( + _i2.Future<_i47.ScheduledLesson?> updateScheduledLesson( + _i47.ScheduledLesson scheduledLesson) => + caller.callServerEndpoint<_i47.ScheduledLesson?>( 'scheduledLesson', 'updateScheduledLesson', {'scheduledLesson': scheduledLesson}, ); - _i2.Future<_i46.ScheduledLesson?> deactivateScheduledLesson(int id) => - caller.callServerEndpoint<_i46.ScheduledLesson?>( + _i2.Future<_i47.ScheduledLesson?> deactivateScheduledLesson(int id) => + caller.callServerEndpoint<_i47.ScheduledLesson?>( 'scheduledLesson', 'deactivateScheduledLesson', {'id': id}, @@ -1970,53 +1979,53 @@ class EndpointScheduledLessonGroupMembership extends _i1.EndpointRef { @override String get name => 'scheduledLessonGroupMembership'; - _i2.Future<_i47.ScheduledLessonGroupMembership> + _i2.Future<_i48.ScheduledLessonGroupMembership> createScheduledLessonGroupMembership( - _i47.ScheduledLessonGroupMembership membership) => - caller.callServerEndpoint<_i47.ScheduledLessonGroupMembership>( + _i48.ScheduledLessonGroupMembership membership) => + caller.callServerEndpoint<_i48.ScheduledLessonGroupMembership>( 'scheduledLessonGroupMembership', 'createScheduledLessonGroupMembership', {'membership': membership}, ); - _i2.Future> + _i2.Future> fetchScheduledLessonGroupMemberships() => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'scheduledLessonGroupMembership', 'fetchScheduledLessonGroupMemberships', {}, ); - _i2.Future<_i47.ScheduledLessonGroupMembership?> + _i2.Future<_i48.ScheduledLessonGroupMembership?> fetchScheduledLessonGroupMembershipById(int id) => - caller.callServerEndpoint<_i47.ScheduledLessonGroupMembership?>( + caller.callServerEndpoint<_i48.ScheduledLessonGroupMembership?>( 'scheduledLessonGroupMembership', 'fetchScheduledLessonGroupMembershipById', {'id': id}, ); - _i2.Future> + _i2.Future> fetchMembershipsByLessonGroupId(int lessonGroupId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'scheduledLessonGroupMembership', 'fetchMembershipsByLessonGroupId', {'lessonGroupId': lessonGroupId}, ); - _i2.Future> + _i2.Future> fetchMembershipsByPupilDataId(int pupilDataId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'scheduledLessonGroupMembership', 'fetchMembershipsByPupilDataId', {'pupilDataId': pupilDataId}, ); - _i2.Future<_i47.ScheduledLessonGroupMembership?> + _i2.Future<_i48.ScheduledLessonGroupMembership?> fetchMembershipByLessonGroupAndPupil( int lessonGroupId, int pupilDataId, ) => - caller.callServerEndpoint<_i47.ScheduledLessonGroupMembership?>( + caller.callServerEndpoint<_i48.ScheduledLessonGroupMembership?>( 'scheduledLessonGroupMembership', 'fetchMembershipByLessonGroupAndPupil', { @@ -2025,10 +2034,10 @@ class EndpointScheduledLessonGroupMembership extends _i1.EndpointRef { }, ); - _i2.Future<_i47.ScheduledLessonGroupMembership> + _i2.Future<_i48.ScheduledLessonGroupMembership> updateScheduledLessonGroupMembership( - _i47.ScheduledLessonGroupMembership membership) => - caller.callServerEndpoint<_i47.ScheduledLessonGroupMembership>( + _i48.ScheduledLessonGroupMembership membership) => + caller.callServerEndpoint<_i48.ScheduledLessonGroupMembership>( 'scheduledLessonGroupMembership', 'updateScheduledLessonGroupMembership', {'membership': membership}, @@ -2075,50 +2084,50 @@ class EndpointSubject extends _i1.EndpointRef { @override String get name => 'subject'; - _i2.Future<_i48.Subject> createSubject(_i48.Subject subject) => - caller.callServerEndpoint<_i48.Subject>( + _i2.Future<_i49.Subject> createSubject(_i49.Subject subject) => + caller.callServerEndpoint<_i49.Subject>( 'subject', 'createSubject', {'subject': subject}, ); - _i2.Future> fetchSubjects() => - caller.callServerEndpoint>( + _i2.Future> fetchSubjects() => + caller.callServerEndpoint>( 'subject', 'fetchSubjects', {}, ); - _i2.Future<_i48.Subject?> fetchSubjectById(int id) => - caller.callServerEndpoint<_i48.Subject?>( + _i2.Future<_i49.Subject?> fetchSubjectById(int id) => + caller.callServerEndpoint<_i49.Subject?>( 'subject', 'fetchSubjectById', {'id': id}, ); - _i2.Future<_i48.Subject?> fetchSubjectByPublicId(String publicId) => - caller.callServerEndpoint<_i48.Subject?>( + _i2.Future<_i49.Subject?> fetchSubjectByPublicId(String publicId) => + caller.callServerEndpoint<_i49.Subject?>( 'subject', 'fetchSubjectByPublicId', {'publicId': publicId}, ); - _i2.Future> fetchSubjectsByName(String name) => - caller.callServerEndpoint>( + _i2.Future> fetchSubjectsByName(String name) => + caller.callServerEndpoint>( 'subject', 'fetchSubjectsByName', {'name': name}, ); - _i2.Future> fetchSubjectsByCreator(String createdBy) => - caller.callServerEndpoint>( + _i2.Future> fetchSubjectsByCreator(String createdBy) => + caller.callServerEndpoint>( 'subject', 'fetchSubjectsByCreator', {'createdBy': createdBy}, ); - _i2.Future<_i48.Subject> updateSubject(_i48.Subject subject) => - caller.callServerEndpoint<_i48.Subject>( + _i2.Future<_i49.Subject> updateSubject(_i49.Subject subject) => + caller.callServerEndpoint<_i49.Subject>( 'subject', 'updateSubject', {'subject': subject}, @@ -2138,65 +2147,65 @@ class EndpointTimetable extends _i1.EndpointRef { @override String get name => 'timetable'; - _i2.Future<_i49.Timetable> createTimetable(_i49.Timetable timetable) => - caller.callServerEndpoint<_i49.Timetable>( + _i2.Future<_i50.Timetable> createTimetable(_i50.Timetable timetable) => + caller.callServerEndpoint<_i50.Timetable>( 'timetable', 'createTimetable', {'timetable': timetable}, ); - _i2.Future> fetchTimetables() => - caller.callServerEndpoint>( + _i2.Future> fetchTimetables() => + caller.callServerEndpoint>( 'timetable', 'fetchTimetables', {}, ); - _i2.Future<_i49.Timetable?> fetchTimetableById(int id) => - caller.callServerEndpoint<_i49.Timetable?>( + _i2.Future<_i50.Timetable?> fetchTimetableById(int id) => + caller.callServerEndpoint<_i50.Timetable?>( 'timetable', 'fetchTimetableById', {'id': id}, ); - _i2.Future<_i49.Timetable?> fetchTimetable() => - caller.callServerEndpoint<_i49.Timetable?>( + _i2.Future<_i50.Timetable?> fetchTimetable() => + caller.callServerEndpoint<_i50.Timetable?>( 'timetable', 'fetchTimetable', {}, ); - _i2.Future<_i49.Timetable?> fetchCompleteTimetableData() => - caller.callServerEndpoint<_i49.Timetable?>( + _i2.Future<_i50.Timetable?> fetchCompleteTimetableData() => + caller.callServerEndpoint<_i50.Timetable?>( 'timetable', 'fetchCompleteTimetableData', {}, ); - _i2.Future> fetchActiveTimetables() => - caller.callServerEndpoint>( + _i2.Future> fetchActiveTimetables() => + caller.callServerEndpoint>( 'timetable', 'fetchActiveTimetables', {}, ); - _i2.Future> fetchTimetablesBySemester( + _i2.Future> fetchTimetablesBySemester( int schoolSemesterId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'timetable', 'fetchTimetablesBySemester', {'schoolSemesterId': schoolSemesterId}, ); - _i2.Future<_i49.Timetable> updateTimetable(_i49.Timetable timetable) => - caller.callServerEndpoint<_i49.Timetable>( + _i2.Future<_i50.Timetable> updateTimetable(_i50.Timetable timetable) => + caller.callServerEndpoint<_i50.Timetable>( 'timetable', 'updateTimetable', {'timetable': timetable}, ); - _i2.Future<_i49.Timetable> deactivateTimetable(int id) => - caller.callServerEndpoint<_i49.Timetable>( + _i2.Future<_i50.Timetable> deactivateTimetable(int id) => + caller.callServerEndpoint<_i50.Timetable>( 'timetable', 'deactivateTimetable', {'id': id}, @@ -2216,47 +2225,47 @@ class EndpointTimetableSlot extends _i1.EndpointRef { @override String get name => 'timetableSlot'; - _i2.Future<_i50.TimetableSlot> createTimetableSlot( - _i50.TimetableSlot timetableSlot) => - caller.callServerEndpoint<_i50.TimetableSlot>( + _i2.Future<_i51.TimetableSlot> createTimetableSlot( + _i51.TimetableSlot timetableSlot) => + caller.callServerEndpoint<_i51.TimetableSlot>( 'timetableSlot', 'createTimetableSlot', {'timetableSlot': timetableSlot}, ); - _i2.Future> fetchTimetableSlots() => - caller.callServerEndpoint>( + _i2.Future> fetchTimetableSlots() => + caller.callServerEndpoint>( 'timetableSlot', 'fetchTimetableSlots', {}, ); - _i2.Future<_i50.TimetableSlot?> fetchTimetableSlotById(int id) => - caller.callServerEndpoint<_i50.TimetableSlot?>( + _i2.Future<_i51.TimetableSlot?> fetchTimetableSlotById(int id) => + caller.callServerEndpoint<_i51.TimetableSlot?>( 'timetableSlot', 'fetchTimetableSlotById', {'id': id}, ); - _i2.Future> fetchTimetableSlotsByTimetableId( + _i2.Future> fetchTimetableSlotsByTimetableId( int timetableId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'timetableSlot', 'fetchTimetableSlotsByTimetableId', {'timetableId': timetableId}, ); - _i2.Future> fetchTimetableSlotsByDay( - _i51.Weekday day) => - caller.callServerEndpoint>( + _i2.Future> fetchTimetableSlotsByDay( + _i52.Weekday day) => + caller.callServerEndpoint>( 'timetableSlot', 'fetchTimetableSlotsByDay', {'day': day}, ); - _i2.Future<_i50.TimetableSlot> updateTimetableSlot( - _i50.TimetableSlot timetableSlot) => - caller.callServerEndpoint<_i50.TimetableSlot>( + _i2.Future<_i51.TimetableSlot> updateTimetableSlot( + _i51.TimetableSlot timetableSlot) => + caller.callServerEndpoint<_i51.TimetableSlot>( 'timetableSlot', 'updateTimetableSlot', {'timetableSlot': timetableSlot}, @@ -2318,12 +2327,12 @@ class EndpointPupilWorkbooks extends _i1.EndpointRef { @override String get name => 'pupilWorkbooks'; - _i2.Future<_i52.PupilWorkbook> postPupilWorkbook( + _i2.Future<_i53.PupilWorkbook> postPupilWorkbook( int isbn, int pupilId, String createdBy, ) => - caller.callServerEndpoint<_i52.PupilWorkbook>( + caller.callServerEndpoint<_i53.PupilWorkbook>( 'pupilWorkbooks', 'postPupilWorkbook', { @@ -2333,24 +2342,24 @@ class EndpointPupilWorkbooks extends _i1.EndpointRef { }, ); - _i2.Future> fetchPupilWorkbooks() => - caller.callServerEndpoint>( + _i2.Future> fetchPupilWorkbooks() => + caller.callServerEndpoint>( 'pupilWorkbooks', 'fetchPupilWorkbooks', {}, ); - _i2.Future> fetchPupilWorkbooksFromPupil( + _i2.Future> fetchPupilWorkbooksFromPupil( int pupilId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'pupilWorkbooks', 'fetchPupilWorkbooksFromPupil', {'pupilId': pupilId}, ); - _i2.Future<_i52.PupilWorkbook> updatePupilWorkbook( - _i52.PupilWorkbook pupilWorkbook) => - caller.callServerEndpoint<_i52.PupilWorkbook>( + _i2.Future<_i53.PupilWorkbook> updatePupilWorkbook( + _i53.PupilWorkbook pupilWorkbook) => + caller.callServerEndpoint<_i53.PupilWorkbook>( 'pupilWorkbooks', 'updatePupilWorkbook', {'pupilWorkbook': pupilWorkbook}, @@ -2371,29 +2380,29 @@ class EndpointWorkbooks extends _i1.EndpointRef { @override String get name => 'workbooks'; - _i2.Future<_i53.Workbook> postWorkbook(_i53.Workbook workbook) => - caller.callServerEndpoint<_i53.Workbook>( + _i2.Future<_i54.Workbook> postWorkbook(_i54.Workbook workbook) => + caller.callServerEndpoint<_i54.Workbook>( 'workbooks', 'postWorkbook', {'workbook': workbook}, ); - _i2.Future<_i53.Workbook> fetchWorkbookByIsbn(int isbn) => - caller.callServerEndpoint<_i53.Workbook>( + _i2.Future<_i54.Workbook> fetchWorkbookByIsbn(int isbn) => + caller.callServerEndpoint<_i54.Workbook>( 'workbooks', 'fetchWorkbookByIsbn', {'isbn': isbn}, ); - _i2.Future> fetchWorkbooks() => - caller.callServerEndpoint>( + _i2.Future> fetchWorkbooks() => + caller.callServerEndpoint>( 'workbooks', 'fetchWorkbooks', {}, ); - _i2.Future<_i53.Workbook> updateWorkbook(_i53.Workbook workbook) => - caller.callServerEndpoint<_i53.Workbook>( + _i2.Future<_i54.Workbook> updateWorkbook(_i54.Workbook workbook) => + caller.callServerEndpoint<_i54.Workbook>( 'workbooks', 'updateWorkbook', {'workbook': workbook}, @@ -2444,15 +2453,15 @@ class EndpointFiles extends _i1.EndpointRef { ); /// As described in https://docs.serverpod.dev/concepts/file-uploads#client-side-code - _i2.Future<_i54.ByteData?> getImage(String documentId) => - caller.callServerEndpoint<_i54.ByteData?>( + _i2.Future<_i55.ByteData?> getImage(String documentId) => + caller.callServerEndpoint<_i55.ByteData?>( 'files', 'getImage', {'documentId': documentId}, ); - _i2.Future<_i54.ByteData?> getUnencryptedImage(String path) => - caller.callServerEndpoint<_i54.ByteData?>( + _i2.Future<_i55.ByteData?> getUnencryptedImage(String path) => + caller.callServerEndpoint<_i55.ByteData?>( 'files', 'getUnencryptedImage', {'path': path}, @@ -2483,7 +2492,7 @@ class Client extends _i1.ServerpodClientShared { bool? disconnectStreamsOnLostInternetConnection, }) : super( host, - _i55.Protocol(), + _i56.Protocol(), securityContext: securityContext, authenticationKeyManager: authenticationKeyManager, streamingConnectionTimeout: streamingConnectionTimeout, diff --git a/school_data_hub_client/lib/src/protocol/protocol.dart b/school_data_hub_client/lib/src/protocol/protocol.dart index 36b7771f..05aa048d 100644 --- a/school_data_hub_client/lib/src/protocol/protocol.dart +++ b/school_data_hub_client/lib/src/protocol/protocol.dart @@ -10,7 +10,8 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:serverpod_client/serverpod_client.dart' as _i1; -import '_features/learning_support/models/support_level.dart' as _i2; +import '_features/learning_support/models/support_goal/support_goal.dart' + as _i2; import '_features/attendance/models/missed_schoolday.dart' as _i3; import '_features/attendance/models/missed_schoolday_dto.dart' as _i4; import '_features/attendance/models/missed_type.dart' as _i5; @@ -19,156 +20,156 @@ import '_features/auth/models/user_device.dart' as _i7; import '_features/authorizations/models/authorization.dart' as _i8; import '_features/authorizations/models/pupil_authorization.dart' as _i9; import '_features/books/models/book.dart' as _i10; -import '_features/books/models/book_tagging/book_tag.dart' as _i11; -import '_features/books/models/book_tagging/book_tagging.dart' as _i12; -import '_features/books/models/library_book.dart' as _i13; -import '_features/books/models/library_book_location.dart' as _i14; -import '_features/books/models/library_book_query.dart' as _i15; -import '_features/books/models/pupil_book_lending.dart' as _i16; -import '_features/learning/models/competence.dart' as _i17; -import '_features/learning/models/competence_check.dart' as _i18; -import '_features/learning/models/competence_goal.dart' as _i19; -import '_features/learning/models/competence_report.dart' as _i20; -import '_features/learning/models/competence_report_check.dart' as _i21; -import '_features/learning/models/competence_report_item.dart' as _i22; -import '_features/learning_support/models/learning_support_plan.dart' as _i23; -import '_features/learning_support/models/support_category.dart' as _i24; -import '_features/learning_support/models/support_category_status.dart' as _i25; -import '_features/learning_support/models/support_goal/support_goal.dart' - as _i26; +import '_features/books/models/book_stats_dto.dart' as _i11; +import '_features/books/models/book_tagging/book_tag.dart' as _i12; +import '_features/books/models/book_tagging/book_tagging.dart' as _i13; +import '_features/books/models/library_book.dart' as _i14; +import '_features/books/models/library_book_location.dart' as _i15; +import '_features/books/models/library_book_query.dart' as _i16; +import '_features/books/models/pupil_book_lending.dart' as _i17; +import '_features/learning/models/competence.dart' as _i18; +import '_features/learning/models/competence_check.dart' as _i19; +import '_features/learning/models/competence_goal.dart' as _i20; +import '_features/learning/models/competence_report.dart' as _i21; +import '_features/learning/models/competence_report_check.dart' as _i22; +import '_features/learning/models/competence_report_item.dart' as _i23; +import '_features/learning_support/models/learning_support_plan.dart' as _i24; +import '_features/learning_support/models/support_category.dart' as _i25; +import '_features/learning_support/models/support_category_status.dart' as _i26; +import '_features/attendance/models/contacted_type.dart' as _i27; import '_features/learning_support/models/support_goal/support_goal_check.dart' - as _i27; -import '_features/attendance/models/contacted_type.dart' as _i28; + as _i28; +import '_features/learning_support/models/support_level.dart' as _i29; import '_features/learning_support/models/support_level_legacy_dto.dart' - as _i29; -import '_features/matrix/compulsory_room.dart' as _i30; -import '_features/matrix/matrix_room_type.dart' as _i31; + as _i30; +import '_features/matrix/compulsory_room.dart' as _i31; +import '_features/matrix/matrix_room_type.dart' as _i32; import '_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' - as _i32; -import '_features/pupil/models/pupil_data/after_school_care/after_school_pickup_times.dart' as _i33; -import '_features/pupil/models/pupil_data/after_school_care/pick_up_info.dart' +import '_features/pupil/models/pupil_data/after_school_care/after_school_pickup_times.dart' as _i34; -import '_features/pupil/models/pupil_data/communication/communication_skills.dart' +import '_features/pupil/models/pupil_data/after_school_care/pick_up_info.dart' as _i35; -import '_features/pupil/models/pupil_data/communication/public_media_auth.dart' +import '_features/pupil/models/pupil_data/communication/communication_skills.dart' as _i36; -import '_features/pupil/models/pupil_data/communication/tutor_info.dart' +import '_features/pupil/models/pupil_data/communication/public_media_auth.dart' as _i37; -import '_features/pupil/models/pupil_data/credit_transaction.dart' as _i38; -import '_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i39; +import '_features/pupil/models/pupil_data/communication/tutor_info.dart' + as _i38; +import '_features/pupil/models/pupil_data/credit_transaction.dart' as _i39; +import '_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i40; import '_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' - as _i40; -import '_features/pupil/models/pupil_data/preschool/kindergarden.dart' as _i41; + as _i41; +import '_features/pupil/models/pupil_data/preschool/kindergarden.dart' as _i42; import '_features/pupil/models/pupil_data/preschool/kindergarden_info.dart' - as _i42; -import '_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i43; -import '_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' +import '_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i44; -import '_features/pupil/models/pupil_data/preschool/pre_school_test.dart' +import '_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' as _i45; -import '_features/pupil/models/pupil_data/pupil_data.dart' as _i46; -import '_features/pupil/models/pupil_data/pupil_status.dart' as _i47; +import '_features/pupil/models/pupil_data/preschool/pre_school_test.dart' + as _i46; +import '_features/pupil/models/pupil_data/pupil_data.dart' as _i47; +import '_features/pupil/models/pupil_data/pupil_status.dart' as _i48; import '_features/pupil/models/pupil_identity/last_pupil_identities_update.dart' - as _i48; -import '_features/pupil/models/pupil_identity/pupil_identity.dart' as _i49; -import '_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i50; -import '_features/pupil/models/pupil_identity/school_grade.dart' as _i51; -import '_shared/models/member_operation.dart' as _i52; -import '_features/school_lists/models/pupil_entry.dart' as _i53; -import '_features/school_lists/models/school_list.dart' as _i54; -import '_features/schoolday/models/school_semester.dart' as _i55; -import '_features/schoolday/models/schoolday.dart' as _i56; -import '_features/schoolday_events/models/schoolday_event.dart' as _i57; -import '_features/schoolday_events/models/schoolday_event_type.dart' as _i58; -import '_features/timetable/models/classroom.dart' as _i59; -import '_features/timetable/models/junction_models/lesson_teacher.dart' as _i60; + as _i49; +import '_features/pupil/models/pupil_identity/pupil_identity.dart' as _i50; +import '_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i51; +import '_features/pupil/models/pupil_identity/school_grade.dart' as _i52; +import '_shared/models/member_operation.dart' as _i53; +import '_features/school_lists/models/pupil_entry.dart' as _i54; +import '_features/school_lists/models/school_list.dart' as _i55; +import '_features/schoolday/models/school_semester.dart' as _i56; +import '_features/schoolday/models/schoolday.dart' as _i57; +import '_features/schoolday_events/models/schoolday_event.dart' as _i58; +import '_features/schoolday_events/models/schoolday_event_type.dart' as _i59; +import '_features/timetable/models/classroom.dart' as _i60; +import '_features/timetable/models/junction_models/lesson_teacher.dart' as _i61; import '_features/timetable/models/junction_models/scheduled_lesson_teacher.dart' - as _i61; -import '_features/timetable/models/lesson/lesson.dart' as _i62; -import '_features/timetable/models/lesson/lesson_attendance.dart' as _i63; -import '_features/timetable/models/lesson/lesson_group.dart' as _i64; + as _i62; +import '_features/timetable/models/lesson/lesson.dart' as _i63; +import '_features/timetable/models/lesson/lesson_attendance.dart' as _i64; +import '_features/timetable/models/lesson/lesson_group.dart' as _i65; import '_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' - as _i65; -import '_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' as _i66; -import '_features/timetable/models/scheduled_lesson/subject.dart' as _i67; +import '_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' + as _i67; +import '_features/timetable/models/scheduled_lesson/subject.dart' as _i68; import '_features/timetable/models/scheduled_lesson/timetable_slot.dart' - as _i68; -import '_features/timetable/models/scheduled_lesson/weekday_enum.dart' as _i69; -import '_features/timetable/models/timetable.dart' as _i70; -import '_features/user/models/roles.dart' as _i71; -import '_features/user/models/staff_user.dart' as _i72; -import '_features/user/models/user_flags.dart' as _i73; -import '_features/workbooks/models/pupil_workbook.dart' as _i74; -import '_features/workbooks/models/workbook.dart' as _i75; -import '_shared/models/exceptions/test_exception.dart' as _i76; -import '_shared/models/hub_document.dart' as _i77; -import '_features/school_data/models/school_data.dart' as _i78; + as _i69; +import '_features/timetable/models/scheduled_lesson/weekday_enum.dart' as _i70; +import '_features/timetable/models/timetable.dart' as _i71; +import '_features/user/models/roles.dart' as _i72; +import '_features/user/models/staff_user.dart' as _i73; +import '_features/user/models/user_flags.dart' as _i74; +import '_features/workbooks/models/pupil_workbook.dart' as _i75; +import '_features/workbooks/models/workbook.dart' as _i76; +import '_shared/models/exceptions/test_exception.dart' as _i77; +import '_shared/models/hub_document.dart' as _i78; +import '_features/school_data/models/school_data.dart' as _i79; import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/pupil_data.dart' - as _i79; -import 'package:school_data_hub_client/src/protocol/_features/learning/models/competence.dart' as _i80; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning/models/competence.dart' as _i81; -import 'package:school_data_hub_client/src/protocol/_features/attendance/models/missed_schoolday.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category.dart' as _i82; -import 'package:serverpod_auth_client/serverpod_auth_client.dart' as _i83; +import 'package:school_data_hub_client/src/protocol/_features/attendance/models/missed_schoolday.dart' + as _i83; +import 'package:serverpod_auth_client/serverpod_auth_client.dart' as _i84; import 'package:school_data_hub_client/src/protocol/_features/auth/models/user_device.dart' - as _i84; -import 'package:school_data_hub_client/src/protocol/_features/authorizations/models/authorization.dart' as _i85; -import 'package:school_data_hub_client/src/protocol/_shared/models/member_operation.dart' +import 'package:school_data_hub_client/src/protocol/_features/authorizations/models/authorization.dart' as _i86; -import 'package:school_data_hub_client/src/protocol/_features/books/models/book_tagging/book_tag.dart' +import 'package:school_data_hub_client/src/protocol/_shared/models/member_operation.dart' as _i87; -import 'package:school_data_hub_client/src/protocol/_features/books/models/book.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/book_tagging/book_tag.dart' as _i88; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_location.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/book.dart' as _i89; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_location.dart' as _i90; -import 'package:school_data_hub_client/src/protocol/_features/books/models/pupil_book_lending.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book.dart' as _i91; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/learning_support_plan.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/pupil_book_lending.dart' as _i92; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category_status.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/learning_support_plan.dart' as _i93; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category_status.dart' as _i94; -import 'package:school_data_hub_client/src/protocol/_features/matrix/compulsory_room.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i95; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level_legacy_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/matrix/compulsory_room.dart' as _i96; -import 'package:school_data_hub_client/src/protocol/_features/school_lists/models/school_list.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level_legacy_dto.dart' as _i97; -import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/school_semester.dart' +import 'package:school_data_hub_client/src/protocol/_features/school_lists/models/school_list.dart' as _i98; -import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/schoolday.dart' +import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/school_semester.dart' as _i99; -import 'package:school_data_hub_client/src/protocol/_features/schoolday_events/models/schoolday_event.dart' +import 'package:school_data_hub_client/src/protocol/_features/schoolday/models/schoolday.dart' as _i100; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/classroom.dart' +import 'package:school_data_hub_client/src/protocol/_features/schoolday_events/models/schoolday_event.dart' as _i101; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/lesson/lesson_group.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/classroom.dart' as _i102; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/lesson/lesson_group.dart' as _i103; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' as _i104; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/subject.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' as _i105; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/timetable.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/subject.dart' as _i106; -import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/timetable_slot.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/timetable.dart' as _i107; -import 'package:school_data_hub_client/src/protocol/_features/user/models/staff_user.dart' +import 'package:school_data_hub_client/src/protocol/_features/timetable/models/scheduled_lesson/timetable_slot.dart' as _i108; -import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/pupil_workbook.dart' +import 'package:school_data_hub_client/src/protocol/_features/user/models/staff_user.dart' as _i109; -import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/workbook.dart' +import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/pupil_workbook.dart' as _i110; +import 'package:school_data_hub_client/src/protocol/_features/workbooks/models/workbook.dart' + as _i111; export '_features/attendance/models/contacted_type.dart'; export '_features/attendance/models/missed_schoolday.dart'; export '_features/attendance/models/missed_schoolday_dto.dart'; @@ -178,6 +179,7 @@ export '_features/auth/models/user_device.dart'; export '_features/authorizations/models/authorization.dart'; export '_features/authorizations/models/pupil_authorization.dart'; export '_features/books/models/book.dart'; +export '_features/books/models/book_stats_dto.dart'; export '_features/books/models/book_tagging/book_tag.dart'; export '_features/books/models/book_tagging/book_tagging.dart'; export '_features/books/models/library_book.dart'; @@ -261,8 +263,8 @@ class Protocol extends _i1.SerializationManager { Type? t, ]) { t ??= T; - if (t == _i2.SupportLevel) { - return _i2.SupportLevel.fromJson(data) as T; + if (t == _i2.SupportGoal) { + return _i2.SupportGoal.fromJson(data) as T; } if (t == _i3.MissedSchoolday) { return _i3.MissedSchoolday.fromJson(data) as T; @@ -288,212 +290,215 @@ class Protocol extends _i1.SerializationManager { if (t == _i10.Book) { return _i10.Book.fromJson(data) as T; } - if (t == _i11.BookTag) { - return _i11.BookTag.fromJson(data) as T; + if (t == _i11.LibraryBookStatsDto) { + return _i11.LibraryBookStatsDto.fromJson(data) as T; } - if (t == _i12.BookTagging) { - return _i12.BookTagging.fromJson(data) as T; + if (t == _i12.BookTag) { + return _i12.BookTag.fromJson(data) as T; } - if (t == _i13.LibraryBook) { - return _i13.LibraryBook.fromJson(data) as T; + if (t == _i13.BookTagging) { + return _i13.BookTagging.fromJson(data) as T; } - if (t == _i14.LibraryBookLocation) { - return _i14.LibraryBookLocation.fromJson(data) as T; + if (t == _i14.LibraryBook) { + return _i14.LibraryBook.fromJson(data) as T; } - if (t == _i15.LibraryBookQuery) { - return _i15.LibraryBookQuery.fromJson(data) as T; + if (t == _i15.LibraryBookLocation) { + return _i15.LibraryBookLocation.fromJson(data) as T; } - if (t == _i16.PupilBookLending) { - return _i16.PupilBookLending.fromJson(data) as T; + if (t == _i16.LibraryBookQuery) { + return _i16.LibraryBookQuery.fromJson(data) as T; } - if (t == _i17.Competence) { - return _i17.Competence.fromJson(data) as T; + if (t == _i17.PupilBookLending) { + return _i17.PupilBookLending.fromJson(data) as T; } - if (t == _i18.CompetenceCheck) { - return _i18.CompetenceCheck.fromJson(data) as T; + if (t == _i18.Competence) { + return _i18.Competence.fromJson(data) as T; } - if (t == _i19.CompetenceGoal) { - return _i19.CompetenceGoal.fromJson(data) as T; + if (t == _i19.CompetenceCheck) { + return _i19.CompetenceCheck.fromJson(data) as T; } - if (t == _i20.CompetenceReport) { - return _i20.CompetenceReport.fromJson(data) as T; + if (t == _i20.CompetenceGoal) { + return _i20.CompetenceGoal.fromJson(data) as T; } - if (t == _i21.CompetenceReportCheck) { - return _i21.CompetenceReportCheck.fromJson(data) as T; + if (t == _i21.CompetenceReport) { + return _i21.CompetenceReport.fromJson(data) as T; } - if (t == _i22.CompetenceReportItem) { - return _i22.CompetenceReportItem.fromJson(data) as T; + if (t == _i22.CompetenceReportCheck) { + return _i22.CompetenceReportCheck.fromJson(data) as T; } - if (t == _i23.LearningSupportPlan) { - return _i23.LearningSupportPlan.fromJson(data) as T; + if (t == _i23.CompetenceReportItem) { + return _i23.CompetenceReportItem.fromJson(data) as T; } - if (t == _i24.SupportCategory) { - return _i24.SupportCategory.fromJson(data) as T; + if (t == _i24.LearningSupportPlan) { + return _i24.LearningSupportPlan.fromJson(data) as T; } - if (t == _i25.SupportCategoryStatus) { - return _i25.SupportCategoryStatus.fromJson(data) as T; + if (t == _i25.SupportCategory) { + return _i25.SupportCategory.fromJson(data) as T; } - if (t == _i26.SupportGoal) { - return _i26.SupportGoal.fromJson(data) as T; + if (t == _i26.SupportCategoryStatus) { + return _i26.SupportCategoryStatus.fromJson(data) as T; } - if (t == _i27.SupportGoalCheck) { - return _i27.SupportGoalCheck.fromJson(data) as T; + if (t == _i27.ContactedType) { + return _i27.ContactedType.fromJson(data) as T; } - if (t == _i28.ContactedType) { - return _i28.ContactedType.fromJson(data) as T; + if (t == _i28.SupportGoalCheck) { + return _i28.SupportGoalCheck.fromJson(data) as T; } - if (t == _i29.SupportLevelLegacyDto) { - return _i29.SupportLevelLegacyDto.fromJson(data) as T; + if (t == _i29.SupportLevel) { + return _i29.SupportLevel.fromJson(data) as T; } - if (t == _i30.CompulsoryRoom) { - return _i30.CompulsoryRoom.fromJson(data) as T; + if (t == _i30.SupportLevelLegacyDto) { + return _i30.SupportLevelLegacyDto.fromJson(data) as T; } - if (t == _i31.MatrixRoomType) { - return _i31.MatrixRoomType.fromJson(data) as T; + if (t == _i31.CompulsoryRoom) { + return _i31.CompulsoryRoom.fromJson(data) as T; } - if (t == _i32.AfterSchoolCare) { - return _i32.AfterSchoolCare.fromJson(data) as T; + if (t == _i32.MatrixRoomType) { + return _i32.MatrixRoomType.fromJson(data) as T; } - if (t == _i33.AfterSchoolCarePickUpTimes) { - return _i33.AfterSchoolCarePickUpTimes.fromJson(data) as T; + if (t == _i33.AfterSchoolCare) { + return _i33.AfterSchoolCare.fromJson(data) as T; } - if (t == _i34.PickUpInfo) { - return _i34.PickUpInfo.fromJson(data) as T; + if (t == _i34.AfterSchoolCarePickUpTimes) { + return _i34.AfterSchoolCarePickUpTimes.fromJson(data) as T; } - if (t == _i35.CommunicationSkills) { - return _i35.CommunicationSkills.fromJson(data) as T; + if (t == _i35.PickUpInfo) { + return _i35.PickUpInfo.fromJson(data) as T; } - if (t == _i36.PublicMediaAuth) { - return _i36.PublicMediaAuth.fromJson(data) as T; + if (t == _i36.CommunicationSkills) { + return _i36.CommunicationSkills.fromJson(data) as T; } - if (t == _i37.TutorInfo) { - return _i37.TutorInfo.fromJson(data) as T; + if (t == _i37.PublicMediaAuth) { + return _i37.PublicMediaAuth.fromJson(data) as T; } - if (t == _i38.CreditTransaction) { - return _i38.CreditTransaction.fromJson(data) as T; + if (t == _i38.TutorInfo) { + return _i38.TutorInfo.fromJson(data) as T; } - if (t == _i39.PupilDocumentType) { - return _i39.PupilDocumentType.fromJson(data) as T; + if (t == _i39.CreditTransaction) { + return _i39.CreditTransaction.fromJson(data) as T; } - if (t == _i40.SiblingsTutorInfo) { - return _i40.SiblingsTutorInfo.fromJson(data) as T; + if (t == _i40.PupilDocumentType) { + return _i40.PupilDocumentType.fromJson(data) as T; } - if (t == _i41.Kindergarden) { - return _i41.Kindergarden.fromJson(data) as T; + if (t == _i41.SiblingsTutorInfo) { + return _i41.SiblingsTutorInfo.fromJson(data) as T; } - if (t == _i42.KindergardenInfo) { - return _i42.KindergardenInfo.fromJson(data) as T; + if (t == _i42.Kindergarden) { + return _i42.Kindergarden.fromJson(data) as T; } - if (t == _i43.PreSchoolMedical) { - return _i43.PreSchoolMedical.fromJson(data) as T; + if (t == _i43.KindergardenInfo) { + return _i43.KindergardenInfo.fromJson(data) as T; } - if (t == _i44.PreSchoolMedicalStatus) { - return _i44.PreSchoolMedicalStatus.fromJson(data) as T; + if (t == _i44.PreSchoolMedical) { + return _i44.PreSchoolMedical.fromJson(data) as T; } - if (t == _i45.PreSchoolTest) { - return _i45.PreSchoolTest.fromJson(data) as T; + if (t == _i45.PreSchoolMedicalStatus) { + return _i45.PreSchoolMedicalStatus.fromJson(data) as T; } - if (t == _i46.PupilData) { - return _i46.PupilData.fromJson(data) as T; + if (t == _i46.PreSchoolTest) { + return _i46.PreSchoolTest.fromJson(data) as T; } - if (t == _i47.PupilStatus) { - return _i47.PupilStatus.fromJson(data) as T; + if (t == _i47.PupilData) { + return _i47.PupilData.fromJson(data) as T; } - if (t == _i48.LastPupilIdentiesUpdate) { - return _i48.LastPupilIdentiesUpdate.fromJson(data) as T; + if (t == _i48.PupilStatus) { + return _i48.PupilStatus.fromJson(data) as T; } - if (t == _i49.PupilIdentity) { - return _i49.PupilIdentity.fromJson(data) as T; + if (t == _i49.LastPupilIdentiesUpdate) { + return _i49.LastPupilIdentiesUpdate.fromJson(data) as T; } - if (t == _i50.PupilIdentityDto) { - return _i50.PupilIdentityDto.fromJson(data) as T; + if (t == _i50.PupilIdentity) { + return _i50.PupilIdentity.fromJson(data) as T; } - if (t == _i51.SchoolGrade) { - return _i51.SchoolGrade.fromJson(data) as T; + if (t == _i51.PupilIdentityDto) { + return _i51.PupilIdentityDto.fromJson(data) as T; } - if (t == _i52.MemberOperation) { - return _i52.MemberOperation.fromJson(data) as T; + if (t == _i52.SchoolGrade) { + return _i52.SchoolGrade.fromJson(data) as T; } - if (t == _i53.PupilListEntry) { - return _i53.PupilListEntry.fromJson(data) as T; + if (t == _i53.MemberOperation) { + return _i53.MemberOperation.fromJson(data) as T; } - if (t == _i54.SchoolList) { - return _i54.SchoolList.fromJson(data) as T; + if (t == _i54.PupilListEntry) { + return _i54.PupilListEntry.fromJson(data) as T; } - if (t == _i55.SchoolSemester) { - return _i55.SchoolSemester.fromJson(data) as T; + if (t == _i55.SchoolList) { + return _i55.SchoolList.fromJson(data) as T; } - if (t == _i56.Schoolday) { - return _i56.Schoolday.fromJson(data) as T; + if (t == _i56.SchoolSemester) { + return _i56.SchoolSemester.fromJson(data) as T; } - if (t == _i57.SchooldayEvent) { - return _i57.SchooldayEvent.fromJson(data) as T; + if (t == _i57.Schoolday) { + return _i57.Schoolday.fromJson(data) as T; } - if (t == _i58.SchooldayEventType) { - return _i58.SchooldayEventType.fromJson(data) as T; + if (t == _i58.SchooldayEvent) { + return _i58.SchooldayEvent.fromJson(data) as T; } - if (t == _i59.Classroom) { - return _i59.Classroom.fromJson(data) as T; + if (t == _i59.SchooldayEventType) { + return _i59.SchooldayEventType.fromJson(data) as T; } - if (t == _i60.LessonTeacher) { - return _i60.LessonTeacher.fromJson(data) as T; + if (t == _i60.Classroom) { + return _i60.Classroom.fromJson(data) as T; } - if (t == _i61.ScheduledLessonTeacher) { - return _i61.ScheduledLessonTeacher.fromJson(data) as T; + if (t == _i61.LessonTeacher) { + return _i61.LessonTeacher.fromJson(data) as T; } - if (t == _i62.Lesson) { - return _i62.Lesson.fromJson(data) as T; + if (t == _i62.ScheduledLessonTeacher) { + return _i62.ScheduledLessonTeacher.fromJson(data) as T; } - if (t == _i63.LessonAttendance) { - return _i63.LessonAttendance.fromJson(data) as T; + if (t == _i63.Lesson) { + return _i63.Lesson.fromJson(data) as T; } - if (t == _i64.LessonGroup) { - return _i64.LessonGroup.fromJson(data) as T; + if (t == _i64.LessonAttendance) { + return _i64.LessonAttendance.fromJson(data) as T; } - if (t == _i65.ScheduledLessonGroupMembership) { - return _i65.ScheduledLessonGroupMembership.fromJson(data) as T; + if (t == _i65.LessonGroup) { + return _i65.LessonGroup.fromJson(data) as T; } - if (t == _i66.ScheduledLesson) { - return _i66.ScheduledLesson.fromJson(data) as T; + if (t == _i66.ScheduledLessonGroupMembership) { + return _i66.ScheduledLessonGroupMembership.fromJson(data) as T; } - if (t == _i67.Subject) { - return _i67.Subject.fromJson(data) as T; + if (t == _i67.ScheduledLesson) { + return _i67.ScheduledLesson.fromJson(data) as T; } - if (t == _i68.TimetableSlot) { - return _i68.TimetableSlot.fromJson(data) as T; + if (t == _i68.Subject) { + return _i68.Subject.fromJson(data) as T; } - if (t == _i69.Weekday) { - return _i69.Weekday.fromJson(data) as T; + if (t == _i69.TimetableSlot) { + return _i69.TimetableSlot.fromJson(data) as T; } - if (t == _i70.Timetable) { - return _i70.Timetable.fromJson(data) as T; + if (t == _i70.Weekday) { + return _i70.Weekday.fromJson(data) as T; } - if (t == _i71.Role) { - return _i71.Role.fromJson(data) as T; + if (t == _i71.Timetable) { + return _i71.Timetable.fromJson(data) as T; } - if (t == _i72.User) { - return _i72.User.fromJson(data) as T; + if (t == _i72.Role) { + return _i72.Role.fromJson(data) as T; } - if (t == _i73.UserFlags) { - return _i73.UserFlags.fromJson(data) as T; + if (t == _i73.User) { + return _i73.User.fromJson(data) as T; } - if (t == _i74.PupilWorkbook) { - return _i74.PupilWorkbook.fromJson(data) as T; + if (t == _i74.UserFlags) { + return _i74.UserFlags.fromJson(data) as T; } - if (t == _i75.Workbook) { - return _i75.Workbook.fromJson(data) as T; + if (t == _i75.PupilWorkbook) { + return _i75.PupilWorkbook.fromJson(data) as T; } - if (t == _i76.MyException) { - return _i76.MyException.fromJson(data) as T; + if (t == _i76.Workbook) { + return _i76.Workbook.fromJson(data) as T; } - if (t == _i77.HubDocument) { - return _i77.HubDocument.fromJson(data) as T; + if (t == _i77.MyException) { + return _i77.MyException.fromJson(data) as T; } - if (t == _i78.SchoolData) { - return _i78.SchoolData.fromJson(data) as T; + if (t == _i78.HubDocument) { + return _i78.HubDocument.fromJson(data) as T; } - if (t == _i1.getType<_i2.SupportLevel?>()) { - return (data != null ? _i2.SupportLevel.fromJson(data) : null) as T; + if (t == _i79.SchoolData) { + return _i79.SchoolData.fromJson(data) as T; + } + if (t == _i1.getType<_i2.SupportGoal?>()) { + return (data != null ? _i2.SupportGoal.fromJson(data) : null) as T; } if (t == _i1.getType<_i3.MissedSchoolday?>()) { return (data != null ? _i3.MissedSchoolday.fromJson(data) : null) as T; @@ -519,229 +524,233 @@ class Protocol extends _i1.SerializationManager { if (t == _i1.getType<_i10.Book?>()) { return (data != null ? _i10.Book.fromJson(data) : null) as T; } - if (t == _i1.getType<_i11.BookTag?>()) { - return (data != null ? _i11.BookTag.fromJson(data) : null) as T; + if (t == _i1.getType<_i11.LibraryBookStatsDto?>()) { + return (data != null ? _i11.LibraryBookStatsDto.fromJson(data) : null) + as T; } - if (t == _i1.getType<_i12.BookTagging?>()) { - return (data != null ? _i12.BookTagging.fromJson(data) : null) as T; + if (t == _i1.getType<_i12.BookTag?>()) { + return (data != null ? _i12.BookTag.fromJson(data) : null) as T; } - if (t == _i1.getType<_i13.LibraryBook?>()) { - return (data != null ? _i13.LibraryBook.fromJson(data) : null) as T; + if (t == _i1.getType<_i13.BookTagging?>()) { + return (data != null ? _i13.BookTagging.fromJson(data) : null) as T; } - if (t == _i1.getType<_i14.LibraryBookLocation?>()) { - return (data != null ? _i14.LibraryBookLocation.fromJson(data) : null) + if (t == _i1.getType<_i14.LibraryBook?>()) { + return (data != null ? _i14.LibraryBook.fromJson(data) : null) as T; + } + if (t == _i1.getType<_i15.LibraryBookLocation?>()) { + return (data != null ? _i15.LibraryBookLocation.fromJson(data) : null) as T; } - if (t == _i1.getType<_i15.LibraryBookQuery?>()) { - return (data != null ? _i15.LibraryBookQuery.fromJson(data) : null) as T; + if (t == _i1.getType<_i16.LibraryBookQuery?>()) { + return (data != null ? _i16.LibraryBookQuery.fromJson(data) : null) as T; } - if (t == _i1.getType<_i16.PupilBookLending?>()) { - return (data != null ? _i16.PupilBookLending.fromJson(data) : null) as T; + if (t == _i1.getType<_i17.PupilBookLending?>()) { + return (data != null ? _i17.PupilBookLending.fromJson(data) : null) as T; } - if (t == _i1.getType<_i17.Competence?>()) { - return (data != null ? _i17.Competence.fromJson(data) : null) as T; + if (t == _i1.getType<_i18.Competence?>()) { + return (data != null ? _i18.Competence.fromJson(data) : null) as T; } - if (t == _i1.getType<_i18.CompetenceCheck?>()) { - return (data != null ? _i18.CompetenceCheck.fromJson(data) : null) as T; + if (t == _i1.getType<_i19.CompetenceCheck?>()) { + return (data != null ? _i19.CompetenceCheck.fromJson(data) : null) as T; } - if (t == _i1.getType<_i19.CompetenceGoal?>()) { - return (data != null ? _i19.CompetenceGoal.fromJson(data) : null) as T; + if (t == _i1.getType<_i20.CompetenceGoal?>()) { + return (data != null ? _i20.CompetenceGoal.fromJson(data) : null) as T; } - if (t == _i1.getType<_i20.CompetenceReport?>()) { - return (data != null ? _i20.CompetenceReport.fromJson(data) : null) as T; + if (t == _i1.getType<_i21.CompetenceReport?>()) { + return (data != null ? _i21.CompetenceReport.fromJson(data) : null) as T; } - if (t == _i1.getType<_i21.CompetenceReportCheck?>()) { - return (data != null ? _i21.CompetenceReportCheck.fromJson(data) : null) + if (t == _i1.getType<_i22.CompetenceReportCheck?>()) { + return (data != null ? _i22.CompetenceReportCheck.fromJson(data) : null) as T; } - if (t == _i1.getType<_i22.CompetenceReportItem?>()) { - return (data != null ? _i22.CompetenceReportItem.fromJson(data) : null) + if (t == _i1.getType<_i23.CompetenceReportItem?>()) { + return (data != null ? _i23.CompetenceReportItem.fromJson(data) : null) as T; } - if (t == _i1.getType<_i23.LearningSupportPlan?>()) { - return (data != null ? _i23.LearningSupportPlan.fromJson(data) : null) + if (t == _i1.getType<_i24.LearningSupportPlan?>()) { + return (data != null ? _i24.LearningSupportPlan.fromJson(data) : null) as T; } - if (t == _i1.getType<_i24.SupportCategory?>()) { - return (data != null ? _i24.SupportCategory.fromJson(data) : null) as T; + if (t == _i1.getType<_i25.SupportCategory?>()) { + return (data != null ? _i25.SupportCategory.fromJson(data) : null) as T; } - if (t == _i1.getType<_i25.SupportCategoryStatus?>()) { - return (data != null ? _i25.SupportCategoryStatus.fromJson(data) : null) + if (t == _i1.getType<_i26.SupportCategoryStatus?>()) { + return (data != null ? _i26.SupportCategoryStatus.fromJson(data) : null) as T; } - if (t == _i1.getType<_i26.SupportGoal?>()) { - return (data != null ? _i26.SupportGoal.fromJson(data) : null) as T; + if (t == _i1.getType<_i27.ContactedType?>()) { + return (data != null ? _i27.ContactedType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i27.SupportGoalCheck?>()) { - return (data != null ? _i27.SupportGoalCheck.fromJson(data) : null) as T; + if (t == _i1.getType<_i28.SupportGoalCheck?>()) { + return (data != null ? _i28.SupportGoalCheck.fromJson(data) : null) as T; } - if (t == _i1.getType<_i28.ContactedType?>()) { - return (data != null ? _i28.ContactedType.fromJson(data) : null) as T; + if (t == _i1.getType<_i29.SupportLevel?>()) { + return (data != null ? _i29.SupportLevel.fromJson(data) : null) as T; } - if (t == _i1.getType<_i29.SupportLevelLegacyDto?>()) { - return (data != null ? _i29.SupportLevelLegacyDto.fromJson(data) : null) + if (t == _i1.getType<_i30.SupportLevelLegacyDto?>()) { + return (data != null ? _i30.SupportLevelLegacyDto.fromJson(data) : null) as T; } - if (t == _i1.getType<_i30.CompulsoryRoom?>()) { - return (data != null ? _i30.CompulsoryRoom.fromJson(data) : null) as T; + if (t == _i1.getType<_i31.CompulsoryRoom?>()) { + return (data != null ? _i31.CompulsoryRoom.fromJson(data) : null) as T; } - if (t == _i1.getType<_i31.MatrixRoomType?>()) { - return (data != null ? _i31.MatrixRoomType.fromJson(data) : null) as T; + if (t == _i1.getType<_i32.MatrixRoomType?>()) { + return (data != null ? _i32.MatrixRoomType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i32.AfterSchoolCare?>()) { - return (data != null ? _i32.AfterSchoolCare.fromJson(data) : null) as T; + if (t == _i1.getType<_i33.AfterSchoolCare?>()) { + return (data != null ? _i33.AfterSchoolCare.fromJson(data) : null) as T; } - if (t == _i1.getType<_i33.AfterSchoolCarePickUpTimes?>()) { + if (t == _i1.getType<_i34.AfterSchoolCarePickUpTimes?>()) { return (data != null - ? _i33.AfterSchoolCarePickUpTimes.fromJson(data) + ? _i34.AfterSchoolCarePickUpTimes.fromJson(data) : null) as T; } - if (t == _i1.getType<_i34.PickUpInfo?>()) { - return (data != null ? _i34.PickUpInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i35.PickUpInfo?>()) { + return (data != null ? _i35.PickUpInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i35.CommunicationSkills?>()) { - return (data != null ? _i35.CommunicationSkills.fromJson(data) : null) + if (t == _i1.getType<_i36.CommunicationSkills?>()) { + return (data != null ? _i36.CommunicationSkills.fromJson(data) : null) as T; } - if (t == _i1.getType<_i36.PublicMediaAuth?>()) { - return (data != null ? _i36.PublicMediaAuth.fromJson(data) : null) as T; + if (t == _i1.getType<_i37.PublicMediaAuth?>()) { + return (data != null ? _i37.PublicMediaAuth.fromJson(data) : null) as T; } - if (t == _i1.getType<_i37.TutorInfo?>()) { - return (data != null ? _i37.TutorInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i38.TutorInfo?>()) { + return (data != null ? _i38.TutorInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i38.CreditTransaction?>()) { - return (data != null ? _i38.CreditTransaction.fromJson(data) : null) as T; + if (t == _i1.getType<_i39.CreditTransaction?>()) { + return (data != null ? _i39.CreditTransaction.fromJson(data) : null) as T; } - if (t == _i1.getType<_i39.PupilDocumentType?>()) { - return (data != null ? _i39.PupilDocumentType.fromJson(data) : null) as T; + if (t == _i1.getType<_i40.PupilDocumentType?>()) { + return (data != null ? _i40.PupilDocumentType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i40.SiblingsTutorInfo?>()) { - return (data != null ? _i40.SiblingsTutorInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i41.SiblingsTutorInfo?>()) { + return (data != null ? _i41.SiblingsTutorInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i41.Kindergarden?>()) { - return (data != null ? _i41.Kindergarden.fromJson(data) : null) as T; + if (t == _i1.getType<_i42.Kindergarden?>()) { + return (data != null ? _i42.Kindergarden.fromJson(data) : null) as T; } - if (t == _i1.getType<_i42.KindergardenInfo?>()) { - return (data != null ? _i42.KindergardenInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i43.KindergardenInfo?>()) { + return (data != null ? _i43.KindergardenInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i43.PreSchoolMedical?>()) { - return (data != null ? _i43.PreSchoolMedical.fromJson(data) : null) as T; + if (t == _i1.getType<_i44.PreSchoolMedical?>()) { + return (data != null ? _i44.PreSchoolMedical.fromJson(data) : null) as T; } - if (t == _i1.getType<_i44.PreSchoolMedicalStatus?>()) { - return (data != null ? _i44.PreSchoolMedicalStatus.fromJson(data) : null) + if (t == _i1.getType<_i45.PreSchoolMedicalStatus?>()) { + return (data != null ? _i45.PreSchoolMedicalStatus.fromJson(data) : null) as T; } - if (t == _i1.getType<_i45.PreSchoolTest?>()) { - return (data != null ? _i45.PreSchoolTest.fromJson(data) : null) as T; + if (t == _i1.getType<_i46.PreSchoolTest?>()) { + return (data != null ? _i46.PreSchoolTest.fromJson(data) : null) as T; } - if (t == _i1.getType<_i46.PupilData?>()) { - return (data != null ? _i46.PupilData.fromJson(data) : null) as T; + if (t == _i1.getType<_i47.PupilData?>()) { + return (data != null ? _i47.PupilData.fromJson(data) : null) as T; } - if (t == _i1.getType<_i47.PupilStatus?>()) { - return (data != null ? _i47.PupilStatus.fromJson(data) : null) as T; + if (t == _i1.getType<_i48.PupilStatus?>()) { + return (data != null ? _i48.PupilStatus.fromJson(data) : null) as T; } - if (t == _i1.getType<_i48.LastPupilIdentiesUpdate?>()) { - return (data != null ? _i48.LastPupilIdentiesUpdate.fromJson(data) : null) + if (t == _i1.getType<_i49.LastPupilIdentiesUpdate?>()) { + return (data != null ? _i49.LastPupilIdentiesUpdate.fromJson(data) : null) as T; } - if (t == _i1.getType<_i49.PupilIdentity?>()) { - return (data != null ? _i49.PupilIdentity.fromJson(data) : null) as T; + if (t == _i1.getType<_i50.PupilIdentity?>()) { + return (data != null ? _i50.PupilIdentity.fromJson(data) : null) as T; } - if (t == _i1.getType<_i50.PupilIdentityDto?>()) { - return (data != null ? _i50.PupilIdentityDto.fromJson(data) : null) as T; + if (t == _i1.getType<_i51.PupilIdentityDto?>()) { + return (data != null ? _i51.PupilIdentityDto.fromJson(data) : null) as T; } - if (t == _i1.getType<_i51.SchoolGrade?>()) { - return (data != null ? _i51.SchoolGrade.fromJson(data) : null) as T; + if (t == _i1.getType<_i52.SchoolGrade?>()) { + return (data != null ? _i52.SchoolGrade.fromJson(data) : null) as T; } - if (t == _i1.getType<_i52.MemberOperation?>()) { - return (data != null ? _i52.MemberOperation.fromJson(data) : null) as T; + if (t == _i1.getType<_i53.MemberOperation?>()) { + return (data != null ? _i53.MemberOperation.fromJson(data) : null) as T; } - if (t == _i1.getType<_i53.PupilListEntry?>()) { - return (data != null ? _i53.PupilListEntry.fromJson(data) : null) as T; + if (t == _i1.getType<_i54.PupilListEntry?>()) { + return (data != null ? _i54.PupilListEntry.fromJson(data) : null) as T; } - if (t == _i1.getType<_i54.SchoolList?>()) { - return (data != null ? _i54.SchoolList.fromJson(data) : null) as T; + if (t == _i1.getType<_i55.SchoolList?>()) { + return (data != null ? _i55.SchoolList.fromJson(data) : null) as T; } - if (t == _i1.getType<_i55.SchoolSemester?>()) { - return (data != null ? _i55.SchoolSemester.fromJson(data) : null) as T; + if (t == _i1.getType<_i56.SchoolSemester?>()) { + return (data != null ? _i56.SchoolSemester.fromJson(data) : null) as T; } - if (t == _i1.getType<_i56.Schoolday?>()) { - return (data != null ? _i56.Schoolday.fromJson(data) : null) as T; + if (t == _i1.getType<_i57.Schoolday?>()) { + return (data != null ? _i57.Schoolday.fromJson(data) : null) as T; } - if (t == _i1.getType<_i57.SchooldayEvent?>()) { - return (data != null ? _i57.SchooldayEvent.fromJson(data) : null) as T; + if (t == _i1.getType<_i58.SchooldayEvent?>()) { + return (data != null ? _i58.SchooldayEvent.fromJson(data) : null) as T; } - if (t == _i1.getType<_i58.SchooldayEventType?>()) { - return (data != null ? _i58.SchooldayEventType.fromJson(data) : null) + if (t == _i1.getType<_i59.SchooldayEventType?>()) { + return (data != null ? _i59.SchooldayEventType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i59.Classroom?>()) { - return (data != null ? _i59.Classroom.fromJson(data) : null) as T; + if (t == _i1.getType<_i60.Classroom?>()) { + return (data != null ? _i60.Classroom.fromJson(data) : null) as T; } - if (t == _i1.getType<_i60.LessonTeacher?>()) { - return (data != null ? _i60.LessonTeacher.fromJson(data) : null) as T; + if (t == _i1.getType<_i61.LessonTeacher?>()) { + return (data != null ? _i61.LessonTeacher.fromJson(data) : null) as T; } - if (t == _i1.getType<_i61.ScheduledLessonTeacher?>()) { - return (data != null ? _i61.ScheduledLessonTeacher.fromJson(data) : null) + if (t == _i1.getType<_i62.ScheduledLessonTeacher?>()) { + return (data != null ? _i62.ScheduledLessonTeacher.fromJson(data) : null) as T; } - if (t == _i1.getType<_i62.Lesson?>()) { - return (data != null ? _i62.Lesson.fromJson(data) : null) as T; + if (t == _i1.getType<_i63.Lesson?>()) { + return (data != null ? _i63.Lesson.fromJson(data) : null) as T; } - if (t == _i1.getType<_i63.LessonAttendance?>()) { - return (data != null ? _i63.LessonAttendance.fromJson(data) : null) as T; + if (t == _i1.getType<_i64.LessonAttendance?>()) { + return (data != null ? _i64.LessonAttendance.fromJson(data) : null) as T; } - if (t == _i1.getType<_i64.LessonGroup?>()) { - return (data != null ? _i64.LessonGroup.fromJson(data) : null) as T; + if (t == _i1.getType<_i65.LessonGroup?>()) { + return (data != null ? _i65.LessonGroup.fromJson(data) : null) as T; } - if (t == _i1.getType<_i65.ScheduledLessonGroupMembership?>()) { + if (t == _i1.getType<_i66.ScheduledLessonGroupMembership?>()) { return (data != null - ? _i65.ScheduledLessonGroupMembership.fromJson(data) + ? _i66.ScheduledLessonGroupMembership.fromJson(data) : null) as T; } - if (t == _i1.getType<_i66.ScheduledLesson?>()) { - return (data != null ? _i66.ScheduledLesson.fromJson(data) : null) as T; + if (t == _i1.getType<_i67.ScheduledLesson?>()) { + return (data != null ? _i67.ScheduledLesson.fromJson(data) : null) as T; } - if (t == _i1.getType<_i67.Subject?>()) { - return (data != null ? _i67.Subject.fromJson(data) : null) as T; + if (t == _i1.getType<_i68.Subject?>()) { + return (data != null ? _i68.Subject.fromJson(data) : null) as T; } - if (t == _i1.getType<_i68.TimetableSlot?>()) { - return (data != null ? _i68.TimetableSlot.fromJson(data) : null) as T; + if (t == _i1.getType<_i69.TimetableSlot?>()) { + return (data != null ? _i69.TimetableSlot.fromJson(data) : null) as T; } - if (t == _i1.getType<_i69.Weekday?>()) { - return (data != null ? _i69.Weekday.fromJson(data) : null) as T; + if (t == _i1.getType<_i70.Weekday?>()) { + return (data != null ? _i70.Weekday.fromJson(data) : null) as T; } - if (t == _i1.getType<_i70.Timetable?>()) { - return (data != null ? _i70.Timetable.fromJson(data) : null) as T; + if (t == _i1.getType<_i71.Timetable?>()) { + return (data != null ? _i71.Timetable.fromJson(data) : null) as T; } - if (t == _i1.getType<_i71.Role?>()) { - return (data != null ? _i71.Role.fromJson(data) : null) as T; + if (t == _i1.getType<_i72.Role?>()) { + return (data != null ? _i72.Role.fromJson(data) : null) as T; } - if (t == _i1.getType<_i72.User?>()) { - return (data != null ? _i72.User.fromJson(data) : null) as T; + if (t == _i1.getType<_i73.User?>()) { + return (data != null ? _i73.User.fromJson(data) : null) as T; } - if (t == _i1.getType<_i73.UserFlags?>()) { - return (data != null ? _i73.UserFlags.fromJson(data) : null) as T; + if (t == _i1.getType<_i74.UserFlags?>()) { + return (data != null ? _i74.UserFlags.fromJson(data) : null) as T; } - if (t == _i1.getType<_i74.PupilWorkbook?>()) { - return (data != null ? _i74.PupilWorkbook.fromJson(data) : null) as T; + if (t == _i1.getType<_i75.PupilWorkbook?>()) { + return (data != null ? _i75.PupilWorkbook.fromJson(data) : null) as T; } - if (t == _i1.getType<_i75.Workbook?>()) { - return (data != null ? _i75.Workbook.fromJson(data) : null) as T; + if (t == _i1.getType<_i76.Workbook?>()) { + return (data != null ? _i76.Workbook.fromJson(data) : null) as T; } - if (t == _i1.getType<_i76.MyException?>()) { - return (data != null ? _i76.MyException.fromJson(data) : null) as T; + if (t == _i1.getType<_i77.MyException?>()) { + return (data != null ? _i77.MyException.fromJson(data) : null) as T; } - if (t == _i1.getType<_i77.HubDocument?>()) { - return (data != null ? _i77.HubDocument.fromJson(data) : null) as T; + if (t == _i1.getType<_i78.HubDocument?>()) { + return (data != null ? _i78.HubDocument.fromJson(data) : null) as T; } - if (t == _i1.getType<_i78.SchoolData?>()) { - return (data != null ? _i78.SchoolData.fromJson(data) : null) as T; + if (t == _i1.getType<_i79.SchoolData?>()) { + return (data != null ? _i79.SchoolData.fromJson(data) : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i23.LearningSupportPlan>(e)) + .map((e) => deserialize<_i28.SupportGoalCheck>(e)) .toList() : null) as T; } @@ -752,41 +761,41 @@ class Protocol extends _i1.SerializationManager { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i12.BookTagging>(e)).toList() + ? (data as List).map((e) => deserialize<_i13.BookTagging>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i13.LibraryBook>(e)).toList() + ? (data as List).map((e) => deserialize<_i14.LibraryBook>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i12.BookTagging>(e)).toList() + ? (data as List).map((e) => deserialize<_i13.BookTagging>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i16.PupilBookLending>(e)) + .map((e) => deserialize<_i17.PupilBookLending>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i13.LibraryBook>(e)).toList() + ? (data as List).map((e) => deserialize<_i14.LibraryBook>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i11.BookTag>(e)).toList() + ? (data as List).map((e) => deserialize<_i12.BookTag>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i77.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i78.HubDocument>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -799,23 +808,23 @@ class Protocol extends _i1.SerializationManager { ? (data as List).map((e) => deserialize(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i19.CompetenceGoal>(e)) + .map((e) => deserialize<_i20.CompetenceGoal>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i18.CompetenceCheck>(e)) + .map((e) => deserialize<_i19.CompetenceCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i77.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i78.HubDocument>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -823,15 +832,15 @@ class Protocol extends _i1.SerializationManager { ? (data as List).map((e) => deserialize(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i77.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i78.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i21.CompetenceReportCheck>(e)) + .map((e) => deserialize<_i22.CompetenceReportCheck>(e)) .toList() : null) as T; } @@ -840,70 +849,70 @@ class Protocol extends _i1.SerializationManager { ? (data as List).map((e) => deserialize(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i21.CompetenceReportCheck>(e)) + .map((e) => deserialize<_i22.CompetenceReportCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i25.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i26.SupportCategoryStatus>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i26.SupportGoal>(e)).toList() + ? (data as List).map((e) => deserialize<_i2.SupportGoal>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i26.SupportGoal>(e)).toList() + ? (data as List).map((e) => deserialize<_i2.SupportGoal>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i25.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i26.SupportCategoryStatus>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i77.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i78.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List) - .map((e) => deserialize<_i27.SupportGoalCheck>(e)) - .toList() + ? (data as List).map((e) => deserialize<_i78.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i77.HubDocument>(e)).toList() + ? (data as List) + .map((e) => deserialize<_i24.LearningSupportPlan>(e)) + .toList() : null) as T; } if (t == Set) { return (data as List).map((e) => deserialize(e)).toSet() as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i46.PupilData>(e)).toList() + ? (data as List).map((e) => deserialize<_i47.PupilData>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i77.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i78.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i77.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i78.HubDocument>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -913,90 +922,92 @@ class Protocol extends _i1.SerializationManager { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i38.CreditTransaction>(e)) + .map((e) => deserialize<_i39.CreditTransaction>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i65.ScheduledLessonGroupMembership>(e)) + .map((e) => deserialize<_i66.ScheduledLessonGroupMembership>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i63.LessonAttendance>(e)) + .map((e) => deserialize<_i64.LessonAttendance>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i19.CompetenceGoal>(e)) + .map((e) => deserialize<_i20.CompetenceGoal>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i18.CompetenceCheck>(e)) + .map((e) => deserialize<_i19.CompetenceCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i20.CompetenceReport>(e)) + .map((e) => deserialize<_i21.CompetenceReport>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i21.CompetenceReportCheck>(e)) + .map((e) => deserialize<_i22.CompetenceReportCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i74.PupilWorkbook>(e)) + .map((e) => deserialize<_i75.PupilWorkbook>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i16.PupilBookLending>(e)) + .map((e) => deserialize<_i17.PupilBookLending>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i2.SupportLevel>(e)).toList() + ? (data as List) + .map((e) => deserialize<_i29.SupportLevel>(e)) + .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i25.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i26.SupportCategoryStatus>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i26.SupportGoal>(e)).toList() + ? (data as List).map((e) => deserialize<_i2.SupportGoal>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i23.LearningSupportPlan>(e)) + .map((e) => deserialize<_i24.LearningSupportPlan>(e)) .toList() : null) as T; } @@ -1007,48 +1018,48 @@ class Protocol extends _i1.SerializationManager { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i57.SchooldayEvent>(e)) + .map((e) => deserialize<_i58.SchooldayEvent>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i53.PupilListEntry>(e)) + .map((e) => deserialize<_i54.PupilListEntry>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i53.PupilListEntry>(e)) + .map((e) => deserialize<_i54.PupilListEntry>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i70.Timetable>(e)).toList() + ? (data as List).map((e) => deserialize<_i71.Timetable>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i56.Schoolday>(e)).toList() + ? (data as List).map((e) => deserialize<_i57.Schoolday>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i20.CompetenceReport>(e)) + .map((e) => deserialize<_i21.CompetenceReport>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i23.LearningSupportPlan>(e)) + .map((e) => deserialize<_i24.LearningSupportPlan>(e)) .toList() : null) as T; } @@ -1059,52 +1070,52 @@ class Protocol extends _i1.SerializationManager { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i57.SchooldayEvent>(e)) + .map((e) => deserialize<_i58.SchooldayEvent>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i66.ScheduledLesson>(e)) + .map((e) => deserialize<_i67.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i63.LessonAttendance>(e)) + .map((e) => deserialize<_i64.LessonAttendance>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i60.LessonTeacher>(e)) + .map((e) => deserialize<_i61.LessonTeacher>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i66.ScheduledLesson>(e)) + .map((e) => deserialize<_i67.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i65.ScheduledLessonGroupMembership>(e)) + .map((e) => deserialize<_i66.ScheduledLessonGroupMembership>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i61.ScheduledLessonTeacher>(e)) + .map((e) => deserialize<_i62.ScheduledLessonTeacher>(e)) .toList() : null) as T; } @@ -1116,35 +1127,35 @@ class Protocol extends _i1.SerializationManager { testString: deserialize(data['n']['testString']), ) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i66.ScheduledLesson>(e)) + .map((e) => deserialize<_i67.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i62.Lesson>(e)).toList() + ? (data as List).map((e) => deserialize<_i63.Lesson>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i66.ScheduledLesson>(e)) + .map((e) => deserialize<_i67.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i68.TimetableSlot>(e)) + .map((e) => deserialize<_i69.TimetableSlot>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i64.LessonGroup>(e)).toList() + ? (data as List).map((e) => deserialize<_i65.LessonGroup>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -1162,17 +1173,17 @@ class Protocol extends _i1.SerializationManager { modifiedAt: deserialize(data['n']['modifiedAt']), ) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i61.ScheduledLessonTeacher>(e)) + .map((e) => deserialize<_i62.ScheduledLessonTeacher>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i60.LessonTeacher>(e)) + .map((e) => deserialize<_i61.LessonTeacher>(e)) .toList() : null) as T; } @@ -1181,51 +1192,51 @@ class Protocol extends _i1.SerializationManager { ? (data as List).map((e) => deserialize(e)).toSet() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i74.PupilWorkbook>(e)) + .map((e) => deserialize<_i75.PupilWorkbook>(e)) .toList() : null) as T; } if (t == List) { return (data as List).map((e) => deserialize(e)).toList() as T; } - if (t == Set<_i79.PupilData>) { - return (data as List).map((e) => deserialize<_i79.PupilData>(e)).toSet() + if (t == Set<_i80.PupilData>) { + return (data as List).map((e) => deserialize<_i80.PupilData>(e)).toSet() as T; } - if (t == List<_i80.Competence>) { - return (data as List).map((e) => deserialize<_i80.Competence>(e)).toList() + if (t == List<_i81.Competence>) { + return (data as List).map((e) => deserialize<_i81.Competence>(e)).toList() as T; } - if (t == List<_i81.SupportCategory>) { + if (t == List<_i82.SupportCategory>) { return (data as List) - .map((e) => deserialize<_i81.SupportCategory>(e)) + .map((e) => deserialize<_i82.SupportCategory>(e)) .toList() as T; } - if (t == List<_i82.MissedSchoolday>) { + if (t == List<_i83.MissedSchoolday>) { return (data as List) - .map((e) => deserialize<_i82.MissedSchoolday>(e)) + .map((e) => deserialize<_i83.MissedSchoolday>(e)) .toList() as T; } if (t == _i1.getType< ({ - _i83.AuthenticationResponse response, - _i84.UserDevice? userDevice + _i84.AuthenticationResponse response, + _i85.UserDevice? userDevice })>()) { return ( - response: deserialize<_i83.AuthenticationResponse>( + response: deserialize<_i84.AuthenticationResponse>( ((data as Map)['n'] as Map)['response']), userDevice: ((data)['n'] as Map)['userDevice'] == null ? null - : deserialize<_i84.UserDevice>(data['n']['userDevice']), + : deserialize<_i85.UserDevice>(data['n']['userDevice']), ) as T; } - if (t == List<_i85.Authorization>) { + if (t == List<_i86.Authorization>) { return (data as List) - .map((e) => deserialize<_i85.Authorization>(e)) + .map((e) => deserialize<_i86.Authorization>(e)) .toList() as T; } if (t == List) { @@ -1233,40 +1244,40 @@ class Protocol extends _i1.SerializationManager { } if (t == _i1.getType< - ({_i86.MemberOperation operation, List pupilIds})?>()) { + ({_i87.MemberOperation operation, List pupilIds})?>()) { return (data == null) ? null as T : ( - operation: deserialize<_i86.MemberOperation>( + operation: deserialize<_i87.MemberOperation>( ((data as Map)['n'] as Map)['operation']), pupilIds: deserialize>(data['n']['pupilIds']), ) as T; } - if (t == List<_i87.BookTag>) { - return (data as List).map((e) => deserialize<_i87.BookTag>(e)).toList() + if (t == List<_i88.BookTag>) { + return (data as List).map((e) => deserialize<_i88.BookTag>(e)).toList() as T; } - if (t == List<_i88.Book>) { - return (data as List).map((e) => deserialize<_i88.Book>(e)).toList() as T; + if (t == List<_i89.Book>) { + return (data as List).map((e) => deserialize<_i89.Book>(e)).toList() as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i87.BookTag>(e)).toList() + ? (data as List).map((e) => deserialize<_i88.BookTag>(e)).toList() : null) as T; } - if (t == List<_i89.LibraryBookLocation>) { + if (t == List<_i90.LibraryBookLocation>) { return (data as List) - .map((e) => deserialize<_i89.LibraryBookLocation>(e)) + .map((e) => deserialize<_i90.LibraryBookLocation>(e)) .toList() as T; } - if (t == List<_i90.LibraryBook>) { + if (t == List<_i91.LibraryBook>) { return (data as List) - .map((e) => deserialize<_i90.LibraryBook>(e)) + .map((e) => deserialize<_i91.LibraryBook>(e)) .toList() as T; } - if (t == List<_i91.PupilBookLending>) { + if (t == List<_i92.PupilBookLending>) { return (data as List) - .map((e) => deserialize<_i91.PupilBookLending>(e)) + .map((e) => deserialize<_i92.PupilBookLending>(e)) .toList() as T; } if (t == _i1.getType<({int value})?>()) { @@ -1296,43 +1307,43 @@ class Protocol extends _i1.SerializationManager { : deserialize(data['n']['value']), ) as T; } - if (t == List<_i92.LearningSupportPlan>) { + if (t == List<_i93.LearningSupportPlan>) { return (data as List) - .map((e) => deserialize<_i92.LearningSupportPlan>(e)) + .map((e) => deserialize<_i93.LearningSupportPlan>(e)) .toList() as T; } - if (t == List<_i93.SupportCategoryStatus>) { + if (t == List<_i94.SupportCategoryStatus>) { return (data as List) - .map((e) => deserialize<_i93.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i94.SupportCategoryStatus>(e)) .toList() as T; } - if (t == List<_i94.PreSchoolMedical>) { + if (t == List<_i95.PreSchoolMedical>) { return (data as List) - .map((e) => deserialize<_i94.PreSchoolMedical>(e)) + .map((e) => deserialize<_i95.PreSchoolMedical>(e)) .toList() as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i95.CompulsoryRoom>(e)) + .map((e) => deserialize<_i96.CompulsoryRoom>(e)) .toList() : null) as T; } - if (t == List<_i95.CompulsoryRoom>) { + if (t == List<_i96.CompulsoryRoom>) { return (data as List) - .map((e) => deserialize<_i95.CompulsoryRoom>(e)) + .map((e) => deserialize<_i96.CompulsoryRoom>(e)) .toList() as T; } - if (t == List<_i79.PupilData>) { - return (data as List).map((e) => deserialize<_i79.PupilData>(e)).toList() + if (t == List<_i80.PupilData>) { + return (data as List).map((e) => deserialize<_i80.PupilData>(e)).toList() as T; } if (t == Set) { return (data as List).map((e) => deserialize(e)).toSet() as T; } - if (t == List<_i96.SupportLevelLegacyDto>) { + if (t == List<_i97.SupportLevelLegacyDto>) { return (data as List) - .map((e) => deserialize<_i96.SupportLevelLegacyDto>(e)) + .map((e) => deserialize<_i97.SupportLevelLegacyDto>(e)) .toList() as T; } if (t == _i1.getType<({DateTime? value})>()) { @@ -1342,70 +1353,70 @@ class Protocol extends _i1.SerializationManager { : deserialize(data['n']['value']), ) as T; } - if (t == List<_i97.SchoolList>) { - return (data as List).map((e) => deserialize<_i97.SchoolList>(e)).toList() + if (t == List<_i98.SchoolList>) { + return (data as List).map((e) => deserialize<_i98.SchoolList>(e)).toList() as T; } - if (t == List<_i98.SchoolSemester>) { + if (t == List<_i99.SchoolSemester>) { return (data as List) - .map((e) => deserialize<_i98.SchoolSemester>(e)) + .map((e) => deserialize<_i99.SchoolSemester>(e)) .toList() as T; } - if (t == List<_i99.Schoolday>) { - return (data as List).map((e) => deserialize<_i99.Schoolday>(e)).toList() + if (t == List<_i100.Schoolday>) { + return (data as List).map((e) => deserialize<_i100.Schoolday>(e)).toList() as T; } if (t == List) { return (data as List).map((e) => deserialize(e)).toList() as T; } - if (t == List<_i100.SchooldayEvent>) { + if (t == List<_i101.SchooldayEvent>) { return (data as List) - .map((e) => deserialize<_i100.SchooldayEvent>(e)) + .map((e) => deserialize<_i101.SchooldayEvent>(e)) .toList() as T; } - if (t == List<_i101.Classroom>) { - return (data as List).map((e) => deserialize<_i101.Classroom>(e)).toList() + if (t == List<_i102.Classroom>) { + return (data as List).map((e) => deserialize<_i102.Classroom>(e)).toList() as T; } - if (t == List<_i102.LessonGroup>) { + if (t == List<_i103.LessonGroup>) { return (data as List) - .map((e) => deserialize<_i102.LessonGroup>(e)) + .map((e) => deserialize<_i103.LessonGroup>(e)) .toList() as T; } - if (t == List<_i103.ScheduledLesson>) { + if (t == List<_i104.ScheduledLesson>) { return (data as List) - .map((e) => deserialize<_i103.ScheduledLesson>(e)) + .map((e) => deserialize<_i104.ScheduledLesson>(e)) .toList() as T; } - if (t == List<_i104.ScheduledLessonGroupMembership>) { + if (t == List<_i105.ScheduledLessonGroupMembership>) { return (data as List) - .map((e) => deserialize<_i104.ScheduledLessonGroupMembership>(e)) + .map((e) => deserialize<_i105.ScheduledLessonGroupMembership>(e)) .toList() as T; } - if (t == List<_i105.Subject>) { - return (data as List).map((e) => deserialize<_i105.Subject>(e)).toList() + if (t == List<_i106.Subject>) { + return (data as List).map((e) => deserialize<_i106.Subject>(e)).toList() as T; } - if (t == List<_i106.Timetable>) { - return (data as List).map((e) => deserialize<_i106.Timetable>(e)).toList() + if (t == List<_i107.Timetable>) { + return (data as List).map((e) => deserialize<_i107.Timetable>(e)).toList() as T; } - if (t == List<_i107.TimetableSlot>) { + if (t == List<_i108.TimetableSlot>) { return (data as List) - .map((e) => deserialize<_i107.TimetableSlot>(e)) + .map((e) => deserialize<_i108.TimetableSlot>(e)) .toList() as T; } - if (t == List<_i108.User>) { - return (data as List).map((e) => deserialize<_i108.User>(e)).toList() + if (t == List<_i109.User>) { + return (data as List).map((e) => deserialize<_i109.User>(e)).toList() as T; } - if (t == List<_i109.PupilWorkbook>) { + if (t == List<_i110.PupilWorkbook>) { return (data as List) - .map((e) => deserialize<_i109.PupilWorkbook>(e)) + .map((e) => deserialize<_i110.PupilWorkbook>(e)) .toList() as T; } - if (t == List<_i110.Workbook>) { - return (data as List).map((e) => deserialize<_i110.Workbook>(e)).toList() + if (t == List<_i111.Workbook>) { + return (data as List).map((e) => deserialize<_i111.Workbook>(e)).toList() as T; } if (t == _i1.getType<({int testint, String testString})?>()) { @@ -1439,7 +1450,7 @@ class Protocol extends _i1.SerializationManager { ) as T; } try { - return _i83.Protocol().deserialize(data, t); + return _i84.Protocol().deserialize(data, t); } on _i1.DeserializationTypeNotFoundException catch (_) {} return super.deserialize(data, t); } @@ -1448,8 +1459,8 @@ class Protocol extends _i1.SerializationManager { String? getClassNameForObject(Object? data) { String? className = super.getClassNameForObject(data); if (className != null) return className; - if (data is _i2.SupportLevel) { - return 'SupportLevel'; + if (data is _i2.SupportGoal) { + return 'SupportGoal'; } if (data is _i3.MissedSchoolday) { return 'MissedSchoolday'; @@ -1475,215 +1486,218 @@ class Protocol extends _i1.SerializationManager { if (data is _i10.Book) { return 'Book'; } - if (data is _i11.BookTag) { + if (data is _i11.LibraryBookStatsDto) { + return 'LibraryBookStatsDto'; + } + if (data is _i12.BookTag) { return 'BookTag'; } - if (data is _i12.BookTagging) { + if (data is _i13.BookTagging) { return 'BookTagging'; } - if (data is _i13.LibraryBook) { + if (data is _i14.LibraryBook) { return 'LibraryBook'; } - if (data is _i14.LibraryBookLocation) { + if (data is _i15.LibraryBookLocation) { return 'LibraryBookLocation'; } - if (data is _i15.LibraryBookQuery) { + if (data is _i16.LibraryBookQuery) { return 'LibraryBookQuery'; } - if (data is _i16.PupilBookLending) { + if (data is _i17.PupilBookLending) { return 'PupilBookLending'; } - if (data is _i17.Competence) { + if (data is _i18.Competence) { return 'Competence'; } - if (data is _i18.CompetenceCheck) { + if (data is _i19.CompetenceCheck) { return 'CompetenceCheck'; } - if (data is _i19.CompetenceGoal) { + if (data is _i20.CompetenceGoal) { return 'CompetenceGoal'; } - if (data is _i20.CompetenceReport) { + if (data is _i21.CompetenceReport) { return 'CompetenceReport'; } - if (data is _i21.CompetenceReportCheck) { + if (data is _i22.CompetenceReportCheck) { return 'CompetenceReportCheck'; } - if (data is _i22.CompetenceReportItem) { + if (data is _i23.CompetenceReportItem) { return 'CompetenceReportItem'; } - if (data is _i23.LearningSupportPlan) { + if (data is _i24.LearningSupportPlan) { return 'LearningSupportPlan'; } - if (data is _i24.SupportCategory) { + if (data is _i25.SupportCategory) { return 'SupportCategory'; } - if (data is _i25.SupportCategoryStatus) { + if (data is _i26.SupportCategoryStatus) { return 'SupportCategoryStatus'; } - if (data is _i26.SupportGoal) { - return 'SupportGoal'; + if (data is _i27.ContactedType) { + return 'ContactedType'; } - if (data is _i27.SupportGoalCheck) { + if (data is _i28.SupportGoalCheck) { return 'SupportGoalCheck'; } - if (data is _i28.ContactedType) { - return 'ContactedType'; + if (data is _i29.SupportLevel) { + return 'SupportLevel'; } - if (data is _i29.SupportLevelLegacyDto) { + if (data is _i30.SupportLevelLegacyDto) { return 'SupportLevelLegacyDto'; } - if (data is _i30.CompulsoryRoom) { + if (data is _i31.CompulsoryRoom) { return 'CompulsoryRoom'; } - if (data is _i31.MatrixRoomType) { + if (data is _i32.MatrixRoomType) { return 'MatrixRoomType'; } - if (data is _i32.AfterSchoolCare) { + if (data is _i33.AfterSchoolCare) { return 'AfterSchoolCare'; } - if (data is _i33.AfterSchoolCarePickUpTimes) { + if (data is _i34.AfterSchoolCarePickUpTimes) { return 'AfterSchoolCarePickUpTimes'; } - if (data is _i34.PickUpInfo) { + if (data is _i35.PickUpInfo) { return 'PickUpInfo'; } - if (data is _i35.CommunicationSkills) { + if (data is _i36.CommunicationSkills) { return 'CommunicationSkills'; } - if (data is _i36.PublicMediaAuth) { + if (data is _i37.PublicMediaAuth) { return 'PublicMediaAuth'; } - if (data is _i37.TutorInfo) { + if (data is _i38.TutorInfo) { return 'TutorInfo'; } - if (data is _i38.CreditTransaction) { + if (data is _i39.CreditTransaction) { return 'CreditTransaction'; } - if (data is _i39.PupilDocumentType) { + if (data is _i40.PupilDocumentType) { return 'PupilDocumentType'; } - if (data is _i40.SiblingsTutorInfo) { + if (data is _i41.SiblingsTutorInfo) { return 'SiblingsTutorInfo'; } - if (data is _i41.Kindergarden) { + if (data is _i42.Kindergarden) { return 'Kindergarden'; } - if (data is _i42.KindergardenInfo) { + if (data is _i43.KindergardenInfo) { return 'KindergardenInfo'; } - if (data is _i43.PreSchoolMedical) { + if (data is _i44.PreSchoolMedical) { return 'PreSchoolMedical'; } - if (data is _i44.PreSchoolMedicalStatus) { + if (data is _i45.PreSchoolMedicalStatus) { return 'PreSchoolMedicalStatus'; } - if (data is _i45.PreSchoolTest) { + if (data is _i46.PreSchoolTest) { return 'PreSchoolTest'; } - if (data is _i46.PupilData) { + if (data is _i47.PupilData) { return 'PupilData'; } - if (data is _i47.PupilStatus) { + if (data is _i48.PupilStatus) { return 'PupilStatus'; } - if (data is _i48.LastPupilIdentiesUpdate) { + if (data is _i49.LastPupilIdentiesUpdate) { return 'LastPupilIdentiesUpdate'; } - if (data is _i49.PupilIdentity) { + if (data is _i50.PupilIdentity) { return 'PupilIdentity'; } - if (data is _i50.PupilIdentityDto) { + if (data is _i51.PupilIdentityDto) { return 'PupilIdentityDto'; } - if (data is _i51.SchoolGrade) { + if (data is _i52.SchoolGrade) { return 'SchoolGrade'; } - if (data is _i52.MemberOperation) { + if (data is _i53.MemberOperation) { return 'MemberOperation'; } - if (data is _i53.PupilListEntry) { + if (data is _i54.PupilListEntry) { return 'PupilListEntry'; } - if (data is _i54.SchoolList) { + if (data is _i55.SchoolList) { return 'SchoolList'; } - if (data is _i55.SchoolSemester) { + if (data is _i56.SchoolSemester) { return 'SchoolSemester'; } - if (data is _i56.Schoolday) { + if (data is _i57.Schoolday) { return 'Schoolday'; } - if (data is _i57.SchooldayEvent) { + if (data is _i58.SchooldayEvent) { return 'SchooldayEvent'; } - if (data is _i58.SchooldayEventType) { + if (data is _i59.SchooldayEventType) { return 'SchooldayEventType'; } - if (data is _i59.Classroom) { + if (data is _i60.Classroom) { return 'Classroom'; } - if (data is _i60.LessonTeacher) { + if (data is _i61.LessonTeacher) { return 'LessonTeacher'; } - if (data is _i61.ScheduledLessonTeacher) { + if (data is _i62.ScheduledLessonTeacher) { return 'ScheduledLessonTeacher'; } - if (data is _i62.Lesson) { + if (data is _i63.Lesson) { return 'Lesson'; } - if (data is _i63.LessonAttendance) { + if (data is _i64.LessonAttendance) { return 'LessonAttendance'; } - if (data is _i64.LessonGroup) { + if (data is _i65.LessonGroup) { return 'LessonGroup'; } - if (data is _i65.ScheduledLessonGroupMembership) { + if (data is _i66.ScheduledLessonGroupMembership) { return 'ScheduledLessonGroupMembership'; } - if (data is _i66.ScheduledLesson) { + if (data is _i67.ScheduledLesson) { return 'ScheduledLesson'; } - if (data is _i67.Subject) { + if (data is _i68.Subject) { return 'Subject'; } - if (data is _i68.TimetableSlot) { + if (data is _i69.TimetableSlot) { return 'TimetableSlot'; } - if (data is _i69.Weekday) { + if (data is _i70.Weekday) { return 'Weekday'; } - if (data is _i70.Timetable) { + if (data is _i71.Timetable) { return 'Timetable'; } - if (data is _i71.Role) { + if (data is _i72.Role) { return 'Role'; } - if (data is _i72.User) { + if (data is _i73.User) { return 'User'; } - if (data is _i73.UserFlags) { + if (data is _i74.UserFlags) { return 'UserFlags'; } - if (data is _i74.PupilWorkbook) { + if (data is _i75.PupilWorkbook) { return 'PupilWorkbook'; } - if (data is _i75.Workbook) { + if (data is _i76.Workbook) { return 'Workbook'; } - if (data is _i76.MyException) { + if (data is _i77.MyException) { return 'MyException'; } - if (data is _i77.HubDocument) { + if (data is _i78.HubDocument) { return 'HubDocument'; } - if (data is _i78.SchoolData) { + if (data is _i79.SchoolData) { return 'SchoolData'; } - className = _i83.Protocol().getClassNameForObject(data); + className = _i84.Protocol().getClassNameForObject(data); if (className != null) { return 'serverpod_auth.$className'; } - if (data is List<_i79.PupilData>) { + if (data is List<_i80.PupilData>) { return 'List'; } return null; @@ -1695,8 +1709,8 @@ class Protocol extends _i1.SerializationManager { if (dataClassName is! String) { return super.deserializeByClassName(data); } - if (dataClassName == 'SupportLevel') { - return deserialize<_i2.SupportLevel>(data['data']); + if (dataClassName == 'SupportGoal') { + return deserialize<_i2.SupportGoal>(data['data']); } if (dataClassName == 'MissedSchoolday') { return deserialize<_i3.MissedSchoolday>(data['data']); @@ -1722,216 +1736,219 @@ class Protocol extends _i1.SerializationManager { if (dataClassName == 'Book') { return deserialize<_i10.Book>(data['data']); } + if (dataClassName == 'LibraryBookStatsDto') { + return deserialize<_i11.LibraryBookStatsDto>(data['data']); + } if (dataClassName == 'BookTag') { - return deserialize<_i11.BookTag>(data['data']); + return deserialize<_i12.BookTag>(data['data']); } if (dataClassName == 'BookTagging') { - return deserialize<_i12.BookTagging>(data['data']); + return deserialize<_i13.BookTagging>(data['data']); } if (dataClassName == 'LibraryBook') { - return deserialize<_i13.LibraryBook>(data['data']); + return deserialize<_i14.LibraryBook>(data['data']); } if (dataClassName == 'LibraryBookLocation') { - return deserialize<_i14.LibraryBookLocation>(data['data']); + return deserialize<_i15.LibraryBookLocation>(data['data']); } if (dataClassName == 'LibraryBookQuery') { - return deserialize<_i15.LibraryBookQuery>(data['data']); + return deserialize<_i16.LibraryBookQuery>(data['data']); } if (dataClassName == 'PupilBookLending') { - return deserialize<_i16.PupilBookLending>(data['data']); + return deserialize<_i17.PupilBookLending>(data['data']); } if (dataClassName == 'Competence') { - return deserialize<_i17.Competence>(data['data']); + return deserialize<_i18.Competence>(data['data']); } if (dataClassName == 'CompetenceCheck') { - return deserialize<_i18.CompetenceCheck>(data['data']); + return deserialize<_i19.CompetenceCheck>(data['data']); } if (dataClassName == 'CompetenceGoal') { - return deserialize<_i19.CompetenceGoal>(data['data']); + return deserialize<_i20.CompetenceGoal>(data['data']); } if (dataClassName == 'CompetenceReport') { - return deserialize<_i20.CompetenceReport>(data['data']); + return deserialize<_i21.CompetenceReport>(data['data']); } if (dataClassName == 'CompetenceReportCheck') { - return deserialize<_i21.CompetenceReportCheck>(data['data']); + return deserialize<_i22.CompetenceReportCheck>(data['data']); } if (dataClassName == 'CompetenceReportItem') { - return deserialize<_i22.CompetenceReportItem>(data['data']); + return deserialize<_i23.CompetenceReportItem>(data['data']); } if (dataClassName == 'LearningSupportPlan') { - return deserialize<_i23.LearningSupportPlan>(data['data']); + return deserialize<_i24.LearningSupportPlan>(data['data']); } if (dataClassName == 'SupportCategory') { - return deserialize<_i24.SupportCategory>(data['data']); + return deserialize<_i25.SupportCategory>(data['data']); } if (dataClassName == 'SupportCategoryStatus') { - return deserialize<_i25.SupportCategoryStatus>(data['data']); + return deserialize<_i26.SupportCategoryStatus>(data['data']); } - if (dataClassName == 'SupportGoal') { - return deserialize<_i26.SupportGoal>(data['data']); + if (dataClassName == 'ContactedType') { + return deserialize<_i27.ContactedType>(data['data']); } if (dataClassName == 'SupportGoalCheck') { - return deserialize<_i27.SupportGoalCheck>(data['data']); + return deserialize<_i28.SupportGoalCheck>(data['data']); } - if (dataClassName == 'ContactedType') { - return deserialize<_i28.ContactedType>(data['data']); + if (dataClassName == 'SupportLevel') { + return deserialize<_i29.SupportLevel>(data['data']); } if (dataClassName == 'SupportLevelLegacyDto') { - return deserialize<_i29.SupportLevelLegacyDto>(data['data']); + return deserialize<_i30.SupportLevelLegacyDto>(data['data']); } if (dataClassName == 'CompulsoryRoom') { - return deserialize<_i30.CompulsoryRoom>(data['data']); + return deserialize<_i31.CompulsoryRoom>(data['data']); } if (dataClassName == 'MatrixRoomType') { - return deserialize<_i31.MatrixRoomType>(data['data']); + return deserialize<_i32.MatrixRoomType>(data['data']); } if (dataClassName == 'AfterSchoolCare') { - return deserialize<_i32.AfterSchoolCare>(data['data']); + return deserialize<_i33.AfterSchoolCare>(data['data']); } if (dataClassName == 'AfterSchoolCarePickUpTimes') { - return deserialize<_i33.AfterSchoolCarePickUpTimes>(data['data']); + return deserialize<_i34.AfterSchoolCarePickUpTimes>(data['data']); } if (dataClassName == 'PickUpInfo') { - return deserialize<_i34.PickUpInfo>(data['data']); + return deserialize<_i35.PickUpInfo>(data['data']); } if (dataClassName == 'CommunicationSkills') { - return deserialize<_i35.CommunicationSkills>(data['data']); + return deserialize<_i36.CommunicationSkills>(data['data']); } if (dataClassName == 'PublicMediaAuth') { - return deserialize<_i36.PublicMediaAuth>(data['data']); + return deserialize<_i37.PublicMediaAuth>(data['data']); } if (dataClassName == 'TutorInfo') { - return deserialize<_i37.TutorInfo>(data['data']); + return deserialize<_i38.TutorInfo>(data['data']); } if (dataClassName == 'CreditTransaction') { - return deserialize<_i38.CreditTransaction>(data['data']); + return deserialize<_i39.CreditTransaction>(data['data']); } if (dataClassName == 'PupilDocumentType') { - return deserialize<_i39.PupilDocumentType>(data['data']); + return deserialize<_i40.PupilDocumentType>(data['data']); } if (dataClassName == 'SiblingsTutorInfo') { - return deserialize<_i40.SiblingsTutorInfo>(data['data']); + return deserialize<_i41.SiblingsTutorInfo>(data['data']); } if (dataClassName == 'Kindergarden') { - return deserialize<_i41.Kindergarden>(data['data']); + return deserialize<_i42.Kindergarden>(data['data']); } if (dataClassName == 'KindergardenInfo') { - return deserialize<_i42.KindergardenInfo>(data['data']); + return deserialize<_i43.KindergardenInfo>(data['data']); } if (dataClassName == 'PreSchoolMedical') { - return deserialize<_i43.PreSchoolMedical>(data['data']); + return deserialize<_i44.PreSchoolMedical>(data['data']); } if (dataClassName == 'PreSchoolMedicalStatus') { - return deserialize<_i44.PreSchoolMedicalStatus>(data['data']); + return deserialize<_i45.PreSchoolMedicalStatus>(data['data']); } if (dataClassName == 'PreSchoolTest') { - return deserialize<_i45.PreSchoolTest>(data['data']); + return deserialize<_i46.PreSchoolTest>(data['data']); } if (dataClassName == 'PupilData') { - return deserialize<_i46.PupilData>(data['data']); + return deserialize<_i47.PupilData>(data['data']); } if (dataClassName == 'PupilStatus') { - return deserialize<_i47.PupilStatus>(data['data']); + return deserialize<_i48.PupilStatus>(data['data']); } if (dataClassName == 'LastPupilIdentiesUpdate') { - return deserialize<_i48.LastPupilIdentiesUpdate>(data['data']); + return deserialize<_i49.LastPupilIdentiesUpdate>(data['data']); } if (dataClassName == 'PupilIdentity') { - return deserialize<_i49.PupilIdentity>(data['data']); + return deserialize<_i50.PupilIdentity>(data['data']); } if (dataClassName == 'PupilIdentityDto') { - return deserialize<_i50.PupilIdentityDto>(data['data']); + return deserialize<_i51.PupilIdentityDto>(data['data']); } if (dataClassName == 'SchoolGrade') { - return deserialize<_i51.SchoolGrade>(data['data']); + return deserialize<_i52.SchoolGrade>(data['data']); } if (dataClassName == 'MemberOperation') { - return deserialize<_i52.MemberOperation>(data['data']); + return deserialize<_i53.MemberOperation>(data['data']); } if (dataClassName == 'PupilListEntry') { - return deserialize<_i53.PupilListEntry>(data['data']); + return deserialize<_i54.PupilListEntry>(data['data']); } if (dataClassName == 'SchoolList') { - return deserialize<_i54.SchoolList>(data['data']); + return deserialize<_i55.SchoolList>(data['data']); } if (dataClassName == 'SchoolSemester') { - return deserialize<_i55.SchoolSemester>(data['data']); + return deserialize<_i56.SchoolSemester>(data['data']); } if (dataClassName == 'Schoolday') { - return deserialize<_i56.Schoolday>(data['data']); + return deserialize<_i57.Schoolday>(data['data']); } if (dataClassName == 'SchooldayEvent') { - return deserialize<_i57.SchooldayEvent>(data['data']); + return deserialize<_i58.SchooldayEvent>(data['data']); } if (dataClassName == 'SchooldayEventType') { - return deserialize<_i58.SchooldayEventType>(data['data']); + return deserialize<_i59.SchooldayEventType>(data['data']); } if (dataClassName == 'Classroom') { - return deserialize<_i59.Classroom>(data['data']); + return deserialize<_i60.Classroom>(data['data']); } if (dataClassName == 'LessonTeacher') { - return deserialize<_i60.LessonTeacher>(data['data']); + return deserialize<_i61.LessonTeacher>(data['data']); } if (dataClassName == 'ScheduledLessonTeacher') { - return deserialize<_i61.ScheduledLessonTeacher>(data['data']); + return deserialize<_i62.ScheduledLessonTeacher>(data['data']); } if (dataClassName == 'Lesson') { - return deserialize<_i62.Lesson>(data['data']); + return deserialize<_i63.Lesson>(data['data']); } if (dataClassName == 'LessonAttendance') { - return deserialize<_i63.LessonAttendance>(data['data']); + return deserialize<_i64.LessonAttendance>(data['data']); } if (dataClassName == 'LessonGroup') { - return deserialize<_i64.LessonGroup>(data['data']); + return deserialize<_i65.LessonGroup>(data['data']); } if (dataClassName == 'ScheduledLessonGroupMembership') { - return deserialize<_i65.ScheduledLessonGroupMembership>(data['data']); + return deserialize<_i66.ScheduledLessonGroupMembership>(data['data']); } if (dataClassName == 'ScheduledLesson') { - return deserialize<_i66.ScheduledLesson>(data['data']); + return deserialize<_i67.ScheduledLesson>(data['data']); } if (dataClassName == 'Subject') { - return deserialize<_i67.Subject>(data['data']); + return deserialize<_i68.Subject>(data['data']); } if (dataClassName == 'TimetableSlot') { - return deserialize<_i68.TimetableSlot>(data['data']); + return deserialize<_i69.TimetableSlot>(data['data']); } if (dataClassName == 'Weekday') { - return deserialize<_i69.Weekday>(data['data']); + return deserialize<_i70.Weekday>(data['data']); } if (dataClassName == 'Timetable') { - return deserialize<_i70.Timetable>(data['data']); + return deserialize<_i71.Timetable>(data['data']); } if (dataClassName == 'Role') { - return deserialize<_i71.Role>(data['data']); + return deserialize<_i72.Role>(data['data']); } if (dataClassName == 'User') { - return deserialize<_i72.User>(data['data']); + return deserialize<_i73.User>(data['data']); } if (dataClassName == 'UserFlags') { - return deserialize<_i73.UserFlags>(data['data']); + return deserialize<_i74.UserFlags>(data['data']); } if (dataClassName == 'PupilWorkbook') { - return deserialize<_i74.PupilWorkbook>(data['data']); + return deserialize<_i75.PupilWorkbook>(data['data']); } if (dataClassName == 'Workbook') { - return deserialize<_i75.Workbook>(data['data']); + return deserialize<_i76.Workbook>(data['data']); } if (dataClassName == 'MyException') { - return deserialize<_i76.MyException>(data['data']); + return deserialize<_i77.MyException>(data['data']); } if (dataClassName == 'HubDocument') { - return deserialize<_i77.HubDocument>(data['data']); + return deserialize<_i78.HubDocument>(data['data']); } if (dataClassName == 'SchoolData') { - return deserialize<_i78.SchoolData>(data['data']); + return deserialize<_i79.SchoolData>(data['data']); } if (dataClassName.startsWith('serverpod_auth.')) { data['className'] = dataClassName.substring(15); - return _i83.Protocol().deserializeByClassName(data); + return _i84.Protocol().deserializeByClassName(data); } if (dataClassName == 'List') { - return deserialize>(data['data']); + return deserialize>(data['data']); } return super.deserializeByClassName(data); } @@ -1947,8 +1964,8 @@ Map? mapRecordToJson(Record? record) { return null; } if (record is ({ - _i83.AuthenticationResponse response, - _i84.UserDevice? userDevice + _i84.AuthenticationResponse response, + _i85.UserDevice? userDevice })) { return { "n": { @@ -1957,7 +1974,7 @@ Map? mapRecordToJson(Record? record) { }, }; } - if (record is ({_i86.MemberOperation operation, List pupilIds})) { + if (record is ({_i87.MemberOperation operation, List pupilIds})) { return { "n": { "operation": record.operation, diff --git a/school_data_hub_flutter/lib/features/books/data/book_api_service.dart b/school_data_hub_flutter/lib/features/books/data/book_api_service.dart index e64dafee..a93b783c 100644 --- a/school_data_hub_flutter/lib/features/books/data/book_api_service.dart +++ b/school_data_hub_flutter/lib/features/books/data/book_api_service.dart @@ -84,6 +84,14 @@ class BookApiService { return book; } + Future fetchBookStats() async { + final stats = await ClientHelper.apiCall( + call: () => _client.books.getBookStats(), + errorMessage: 'Fehler beim Laden der Statistiken', + ); + return stats; + } + Future updateBookTags(int isbn, List tags) async { final updatedBook = await ClientHelper.apiCall( call: () => _client.books.updateBookTags(isbn, tags: tags), diff --git a/school_data_hub_flutter/lib/features/books/domain/book_manager.dart b/school_data_hub_flutter/lib/features/books/domain/book_manager.dart index e89b5e3e..1c5b02ae 100644 --- a/school_data_hub_flutter/lib/features/books/domain/book_manager.dart +++ b/school_data_hub_flutter/lib/features/books/domain/book_manager.dart @@ -38,6 +38,9 @@ class BookManager { ValueListenable> get searchResults => _searchResults; final _searchResults = ValueNotifier>([]); + final _bookStats = ValueNotifier(null); + ValueListenable get bookStats => _bookStats; + BookManager(); // final session = di().credentials.value; @@ -56,6 +59,7 @@ class BookManager { await fetchLocations(); await fetchBookTags(); await fetchLibraryBooks(); + await fetchBookStats(); return this; } @@ -165,6 +169,13 @@ class BookManager { //- get functions + Future fetchBookStats() async { + final stats = await _bookApiService.fetchBookStats(); + if (stats != null) { + _bookStats.value = stats; + } + } + LibraryBookProxy? getLibraryBookById(int? libraryBookId) { if (libraryBookId == null) return null; return _libraryBookProxies.value.firstWhereOrNull( diff --git a/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart b/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart index e8926c80..ea6e3940 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart @@ -1,10 +1,13 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; import 'package:isbn/isbn.dart'; +import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/scanner.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; +import 'package:school_data_hub_flutter/features/books/domain/book_manager.dart'; import 'package:school_data_hub_flutter/features/books/presentation/book_search_form/book_search_form_page.dart'; import 'package:school_data_hub_flutter/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart'; import 'package:watch_it/watch_it.dart'; @@ -15,46 +18,52 @@ import '../book_list_page/book_list_page.dart'; import '../book_list_page/widgets/book_list_bottom_navbar.dart'; import '../new_book_page/new_book_controller.dart'; -class BooksMainMenuPage extends StatelessWidget { +class BooksMainMenuPage extends WatchingWidget { const BooksMainMenuPage({super.key}); @override Widget build(BuildContext context) { + final stats = watchValue((BookManager m) => m.bookStats); + return Scaffold( backgroundColor: AppColors.canvasColor, appBar: const GenericAppBar(iconData: Icons.lightbulb, title: 'Bücher'), body: Center( child: SizedBox( width: 380, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildButton(context, "Schlagwörter verwalten", () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => const BookTagManagement(), - ), - ); - }), - const SizedBox(height: 20), - _buildButton(context, "Ausgeliehene Bücher", () { - Navigator.of(context).push( - MaterialPageRoute(builder: (ctx) => const BookListPage()), - ); - }), - const SizedBox(height: 20), - _buildButton(context, "Buch erfassen", () async { - await _showNewBookDialog(context); - }), - const SizedBox(height: 20), - _buildButton(context, "Bücher suchen", () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => const BookSearchFormPage(), - ), - ); - }), - ], + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (stats != null) _buildStatsCard(stats), + const Gap(20), + _buildButton(context, "Schlagwörter verwalten", () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => const BookTagManagement(), + ), + ); + }), + const SizedBox(height: 20), + _buildButton(context, "Ausgeliehene Bücher", () { + Navigator.of(context).push( + MaterialPageRoute(builder: (ctx) => const BookListPage()), + ); + }), + const SizedBox(height: 20), + _buildButton(context, "Buch erfassen", () async { + await _showNewBookDialog(context); + }), + const SizedBox(height: 20), + _buildButton(context, "Bücher suchen", () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => const BookSearchFormPage(), + ), + ); + }), + ], + ), ), ), ), @@ -62,6 +71,71 @@ class BooksMainMenuPage extends StatelessWidget { ); } + Widget _buildStatsCard(LibraryBookStatsDto stats) { + return Card( + elevation: 4, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const Text( + 'Bibliotheks-Statistik', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const Divider(), + _buildStatRow( + 'Katalogisierte Bücher', + stats.totalCatalogedBooks.toString(), + ), + _buildStatRow( + 'Exemplare in Bibliothek', + stats.totalLibraryBooks.toString(), + ), + const Divider(), + _buildStatRow( + 'Verliehene Bücher', + stats.actuallyBorrowedBooks.toString(), + ), + _buildStatRow( + 'Gelesene Bücher (Rückgaben)', + stats.totalReadBooks.toString(), + ), + const Divider(), + _buildStatRow( + 'Lesestufe 1', + stats.totalBooksWithReadingLevel1.toString(), + ), + _buildStatRow( + 'Lesestufe 2', + stats.booksWithReadingLevel2.toString(), + ), + _buildStatRow( + 'Lesestufe 3', + stats.booksWithReadingLevel3.toString(), + ), + ], + ), + ), + ); + } + + Widget _buildStatRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: const TextStyle(fontSize: 14)), + Text( + value, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + ], + ), + ); + } + Widget _buildButton(BuildContext context, String label, VoidCallback onTap) { return Padding( padding: const EdgeInsets.all(4.0), @@ -126,8 +200,8 @@ Future _showNewBookDialog(BuildContext context) async { final cleanScannedIsbn = scannedIsbn.replaceAll('-', ''); Navigator.of(context).push( MaterialPageRoute( - builder: - (ctx) => NewBook(isEdit: false, isbn: int.parse(cleanScannedIsbn)), + builder: (ctx) => + NewBook(isEdit: false, isbn: int.parse(cleanScannedIsbn)), ), ); } diff --git a/school_data_hub_server/lib/src/_features/books/endpoints/books/books_endpoint.dart b/school_data_hub_server/lib/src/_features/books/endpoints/books/books_endpoint.dart index 7a357f53..866b5d32 100644 --- a/school_data_hub_server/lib/src/_features/books/endpoints/books/books_endpoint.dart +++ b/school_data_hub_server/lib/src/_features/books/endpoints/books/books_endpoint.dart @@ -23,6 +23,30 @@ class BooksEndpoint extends Endpoint { return books; } + Future getBookStats(Session session) async { + final totalCatalogedBooks = await Book.db.count(session); + final totalBooksWithReadingLevel1 = await Book.db + .count(session, where: (t) => t.readingLevel.equals('leicht')); + final booksWithReadingLevel2 = await Book.db + .count(session, where: (t) => t.readingLevel.equals('mittel')); + final booksWithReadingLevel3 = await Book.db + .count(session, where: (t) => t.readingLevel.equals('schwer')); + final totalLibraryBooks = await LibraryBook.db.count(session); + final totalReadBooks = await LibraryBook.db.count(session, + where: (t) => t.lending.any((l) => l.returnedAt.notEquals(null))); + final actuallyBorrowedBooks = await LibraryBook.db + .count(session, where: (t) => t.available.equals(false)); + return LibraryBookStatsDto( + totalCatalogedBooks: totalCatalogedBooks, + totalBooksWithReadingLevel1: totalBooksWithReadingLevel1, + booksWithReadingLevel2: booksWithReadingLevel2, + booksWithReadingLevel3: booksWithReadingLevel3, + totalLibraryBooks: totalLibraryBooks, + totalReadBooks: totalReadBooks, + actuallyBorrowedBooks: actuallyBorrowedBooks, + ); + } + Future fetchBookByIsbn( Session session, int isbn, diff --git a/school_data_hub_server/lib/src/_features/books/endpoints/library_books/library_books_endpoint.dart b/school_data_hub_server/lib/src/_features/books/endpoints/library_books/library_books_endpoint.dart index 80cdfc71..025dc9b5 100644 --- a/school_data_hub_server/lib/src/_features/books/endpoints/library_books/library_books_endpoint.dart +++ b/school_data_hub_server/lib/src/_features/books/endpoints/library_books/library_books_endpoint.dart @@ -63,6 +63,7 @@ class LibraryBooksEndpoint extends Endpoint { } //- read + Future> fetchLibraryBooks(Session session) async { final libraryBooks = await LibraryBook.db.find( session, diff --git a/school_data_hub_server/lib/src/_features/books/models/book_stats_dto.spy.yaml b/school_data_hub_server/lib/src/_features/books/models/book_stats_dto.spy.yaml new file mode 100644 index 00000000..64fa6e32 --- /dev/null +++ b/school_data_hub_server/lib/src/_features/books/models/book_stats_dto.spy.yaml @@ -0,0 +1,10 @@ +class: LibraryBookStatsDto +fields: + totalCatalogedBooks: int + totalBooksWithReadingLevel1: int + booksWithReadingLevel2: int + booksWithReadingLevel3: int + totalLibraryBooks: int + totalReadBooks: int + actuallyBorrowedBooks: int + diff --git a/school_data_hub_server/lib/src/generated/_features/books/models/book_stats_dto.dart b/school_data_hub_server/lib/src/generated/_features/books/models/book_stats_dto.dart new file mode 100644 index 00000000..2cda1389 --- /dev/null +++ b/school_data_hub_server/lib/src/generated/_features/books/models/book_stats_dto.dart @@ -0,0 +1,155 @@ +/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */ +/* To generate run: "serverpod generate" */ + +// ignore_for_file: implementation_imports +// ignore_for_file: library_private_types_in_public_api +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: public_member_api_docs +// ignore_for_file: type_literal_in_constant_pattern +// ignore_for_file: use_super_parameters + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:serverpod/serverpod.dart' as _i1; + +abstract class LibraryBookStatsDto + implements _i1.SerializableModel, _i1.ProtocolSerialization { + LibraryBookStatsDto._({ + required this.totalCatalogedBooks, + required this.totalBooksWithReadingLevel1, + required this.booksWithReadingLevel2, + required this.booksWithReadingLevel3, + required this.totalLibraryBooks, + required this.totalReadBooks, + required this.actuallyBorrowedBooks, + }); + + factory LibraryBookStatsDto({ + required int totalCatalogedBooks, + required int totalBooksWithReadingLevel1, + required int booksWithReadingLevel2, + required int booksWithReadingLevel3, + required int totalLibraryBooks, + required int totalReadBooks, + required int actuallyBorrowedBooks, + }) = _LibraryBookStatsDtoImpl; + + factory LibraryBookStatsDto.fromJson(Map jsonSerialization) { + return LibraryBookStatsDto( + totalCatalogedBooks: jsonSerialization['totalCatalogedBooks'] as int, + totalBooksWithReadingLevel1: + jsonSerialization['totalBooksWithReadingLevel1'] as int, + booksWithReadingLevel2: + jsonSerialization['booksWithReadingLevel2'] as int, + booksWithReadingLevel3: + jsonSerialization['booksWithReadingLevel3'] as int, + totalLibraryBooks: jsonSerialization['totalLibraryBooks'] as int, + totalReadBooks: jsonSerialization['totalReadBooks'] as int, + actuallyBorrowedBooks: jsonSerialization['actuallyBorrowedBooks'] as int, + ); + } + + int totalCatalogedBooks; + + int totalBooksWithReadingLevel1; + + int booksWithReadingLevel2; + + int booksWithReadingLevel3; + + int totalLibraryBooks; + + int totalReadBooks; + + int actuallyBorrowedBooks; + + /// Returns a shallow copy of this [LibraryBookStatsDto] + /// with some or all fields replaced by the given arguments. + @_i1.useResult + LibraryBookStatsDto copyWith({ + int? totalCatalogedBooks, + int? totalBooksWithReadingLevel1, + int? booksWithReadingLevel2, + int? booksWithReadingLevel3, + int? totalLibraryBooks, + int? totalReadBooks, + int? actuallyBorrowedBooks, + }); + @override + Map toJson() { + return { + 'totalCatalogedBooks': totalCatalogedBooks, + 'totalBooksWithReadingLevel1': totalBooksWithReadingLevel1, + 'booksWithReadingLevel2': booksWithReadingLevel2, + 'booksWithReadingLevel3': booksWithReadingLevel3, + 'totalLibraryBooks': totalLibraryBooks, + 'totalReadBooks': totalReadBooks, + 'actuallyBorrowedBooks': actuallyBorrowedBooks, + }; + } + + @override + Map toJsonForProtocol() { + return { + 'totalCatalogedBooks': totalCatalogedBooks, + 'totalBooksWithReadingLevel1': totalBooksWithReadingLevel1, + 'booksWithReadingLevel2': booksWithReadingLevel2, + 'booksWithReadingLevel3': booksWithReadingLevel3, + 'totalLibraryBooks': totalLibraryBooks, + 'totalReadBooks': totalReadBooks, + 'actuallyBorrowedBooks': actuallyBorrowedBooks, + }; + } + + @override + String toString() { + return _i1.SerializationManager.encode(this); + } +} + +class _LibraryBookStatsDtoImpl extends LibraryBookStatsDto { + _LibraryBookStatsDtoImpl({ + required int totalCatalogedBooks, + required int totalBooksWithReadingLevel1, + required int booksWithReadingLevel2, + required int booksWithReadingLevel3, + required int totalLibraryBooks, + required int totalReadBooks, + required int actuallyBorrowedBooks, + }) : super._( + totalCatalogedBooks: totalCatalogedBooks, + totalBooksWithReadingLevel1: totalBooksWithReadingLevel1, + booksWithReadingLevel2: booksWithReadingLevel2, + booksWithReadingLevel3: booksWithReadingLevel3, + totalLibraryBooks: totalLibraryBooks, + totalReadBooks: totalReadBooks, + actuallyBorrowedBooks: actuallyBorrowedBooks, + ); + + /// Returns a shallow copy of this [LibraryBookStatsDto] + /// with some or all fields replaced by the given arguments. + @_i1.useResult + @override + LibraryBookStatsDto copyWith({ + int? totalCatalogedBooks, + int? totalBooksWithReadingLevel1, + int? booksWithReadingLevel2, + int? booksWithReadingLevel3, + int? totalLibraryBooks, + int? totalReadBooks, + int? actuallyBorrowedBooks, + }) { + return LibraryBookStatsDto( + totalCatalogedBooks: totalCatalogedBooks ?? this.totalCatalogedBooks, + totalBooksWithReadingLevel1: + totalBooksWithReadingLevel1 ?? this.totalBooksWithReadingLevel1, + booksWithReadingLevel2: + booksWithReadingLevel2 ?? this.booksWithReadingLevel2, + booksWithReadingLevel3: + booksWithReadingLevel3 ?? this.booksWithReadingLevel3, + totalLibraryBooks: totalLibraryBooks ?? this.totalLibraryBooks, + totalReadBooks: totalReadBooks ?? this.totalReadBooks, + actuallyBorrowedBooks: + actuallyBorrowedBooks ?? this.actuallyBorrowedBooks, + ); + } +} diff --git a/school_data_hub_server/lib/src/generated/endpoints.dart b/school_data_hub_server/lib/src/generated/endpoints.dart index ba00aee7..73ddcb63 100644 --- a/school_data_hub_server/lib/src/generated/endpoints.dart +++ b/school_data_hub_server/lib/src/generated/endpoints.dart @@ -1106,6 +1106,15 @@ class Endpoints extends _i1.EndpointDispatch { ) async => (endpoints['books'] as _i8.BooksEndpoint).fetchBooks(session), ), + 'getBookStats': _i1.MethodConnector( + name: 'getBookStats', + params: {}, + call: ( + _i1.Session session, + Map params, + ) async => + (endpoints['books'] as _i8.BooksEndpoint).getBookStats(session), + ), 'fetchBookByIsbn': _i1.MethodConnector( name: 'fetchBookByIsbn', params: { diff --git a/school_data_hub_server/lib/src/generated/protocol.dart b/school_data_hub_server/lib/src/generated/protocol.dart index 901e52f9..881e9c5f 100644 --- a/school_data_hub_server/lib/src/generated/protocol.dart +++ b/school_data_hub_server/lib/src/generated/protocol.dart @@ -12,7 +12,8 @@ import 'package:serverpod/serverpod.dart' as _i1; import 'package:serverpod/protocol.dart' as _i2; import 'package:serverpod_auth_server/serverpod_auth_server.dart' as _i3; -import '_features/learning_support/models/support_level.dart' as _i4; +import '_features/learning_support/models/support_goal/support_goal.dart' + as _i4; import '_features/attendance/models/missed_schoolday.dart' as _i5; import '_features/attendance/models/missed_schoolday_dto.dart' as _i6; import '_features/attendance/models/missed_type.dart' as _i7; @@ -21,155 +22,155 @@ import '_features/auth/models/user_device.dart' as _i9; import '_features/authorizations/models/authorization.dart' as _i10; import '_features/authorizations/models/pupil_authorization.dart' as _i11; import '_features/books/models/book.dart' as _i12; -import '_features/books/models/book_tagging/book_tag.dart' as _i13; -import '_features/books/models/book_tagging/book_tagging.dart' as _i14; -import '_features/books/models/library_book.dart' as _i15; -import '_features/books/models/library_book_location.dart' as _i16; -import '_features/books/models/library_book_query.dart' as _i17; -import '_features/books/models/pupil_book_lending.dart' as _i18; -import '_features/learning/models/competence.dart' as _i19; -import '_features/learning/models/competence_check.dart' as _i20; -import '_features/learning/models/competence_goal.dart' as _i21; -import '_features/learning/models/competence_report.dart' as _i22; -import '_features/learning/models/competence_report_check.dart' as _i23; -import '_features/learning/models/competence_report_item.dart' as _i24; -import '_features/learning_support/models/learning_support_plan.dart' as _i25; -import '_features/learning_support/models/support_category.dart' as _i26; -import '_features/learning_support/models/support_category_status.dart' as _i27; -import '_features/learning_support/models/support_goal/support_goal.dart' - as _i28; +import '_features/books/models/book_stats_dto.dart' as _i13; +import '_features/books/models/book_tagging/book_tag.dart' as _i14; +import '_features/books/models/book_tagging/book_tagging.dart' as _i15; +import '_features/books/models/library_book.dart' as _i16; +import '_features/books/models/library_book_location.dart' as _i17; +import '_features/books/models/library_book_query.dart' as _i18; +import '_features/books/models/pupil_book_lending.dart' as _i19; +import '_features/learning/models/competence.dart' as _i20; +import '_features/learning/models/competence_check.dart' as _i21; +import '_features/learning/models/competence_goal.dart' as _i22; +import '_features/learning/models/competence_report.dart' as _i23; +import '_features/learning/models/competence_report_check.dart' as _i24; +import '_features/learning/models/competence_report_item.dart' as _i25; +import '_features/learning_support/models/learning_support_plan.dart' as _i26; +import '_features/learning_support/models/support_category.dart' as _i27; +import '_features/learning_support/models/support_category_status.dart' as _i28; +import '_features/attendance/models/contacted_type.dart' as _i29; import '_features/learning_support/models/support_goal/support_goal_check.dart' - as _i29; -import '_features/attendance/models/contacted_type.dart' as _i30; + as _i30; +import '_features/learning_support/models/support_level.dart' as _i31; import '_features/learning_support/models/support_level_legacy_dto.dart' - as _i31; -import '_features/matrix/compulsory_room.dart' as _i32; -import '_features/matrix/matrix_room_type.dart' as _i33; + as _i32; +import '_features/matrix/compulsory_room.dart' as _i33; +import '_features/matrix/matrix_room_type.dart' as _i34; import '_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' - as _i34; -import '_features/pupil/models/pupil_data/after_school_care/after_school_pickup_times.dart' as _i35; -import '_features/pupil/models/pupil_data/after_school_care/pick_up_info.dart' +import '_features/pupil/models/pupil_data/after_school_care/after_school_pickup_times.dart' as _i36; -import '_features/pupil/models/pupil_data/communication/communication_skills.dart' +import '_features/pupil/models/pupil_data/after_school_care/pick_up_info.dart' as _i37; -import '_features/pupil/models/pupil_data/communication/public_media_auth.dart' +import '_features/pupil/models/pupil_data/communication/communication_skills.dart' as _i38; -import '_features/pupil/models/pupil_data/communication/tutor_info.dart' +import '_features/pupil/models/pupil_data/communication/public_media_auth.dart' as _i39; -import '_features/pupil/models/pupil_data/credit_transaction.dart' as _i40; -import '_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i41; +import '_features/pupil/models/pupil_data/communication/tutor_info.dart' + as _i40; +import '_features/pupil/models/pupil_data/credit_transaction.dart' as _i41; +import '_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i42; import '_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' - as _i42; -import '_features/pupil/models/pupil_data/preschool/kindergarden.dart' as _i43; + as _i43; +import '_features/pupil/models/pupil_data/preschool/kindergarden.dart' as _i44; import '_features/pupil/models/pupil_data/preschool/kindergarden_info.dart' - as _i44; -import '_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i45; -import '_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' +import '_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i46; -import '_features/pupil/models/pupil_data/preschool/pre_school_test.dart' +import '_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' as _i47; -import '_features/pupil/models/pupil_data/pupil_data.dart' as _i48; -import '_features/pupil/models/pupil_data/pupil_status.dart' as _i49; +import '_features/pupil/models/pupil_data/preschool/pre_school_test.dart' + as _i48; +import '_features/pupil/models/pupil_data/pupil_data.dart' as _i49; +import '_features/pupil/models/pupil_data/pupil_status.dart' as _i50; import '_features/pupil/models/pupil_identity/last_pupil_identities_update.dart' - as _i50; -import '_features/pupil/models/pupil_identity/pupil_identity.dart' as _i51; -import '_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i52; -import '_features/pupil/models/pupil_identity/school_grade.dart' as _i53; -import '_shared/models/member_operation.dart' as _i54; -import '_features/school_lists/models/pupil_entry.dart' as _i55; -import '_features/school_lists/models/school_list.dart' as _i56; -import '_features/schoolday/models/school_semester.dart' as _i57; -import '_features/schoolday/models/schoolday.dart' as _i58; -import '_features/schoolday_events/models/schoolday_event.dart' as _i59; -import '_features/schoolday_events/models/schoolday_event_type.dart' as _i60; -import '_features/timetable/models/classroom.dart' as _i61; -import '_features/timetable/models/junction_models/lesson_teacher.dart' as _i62; + as _i51; +import '_features/pupil/models/pupil_identity/pupil_identity.dart' as _i52; +import '_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i53; +import '_features/pupil/models/pupil_identity/school_grade.dart' as _i54; +import '_shared/models/member_operation.dart' as _i55; +import '_features/school_lists/models/pupil_entry.dart' as _i56; +import '_features/school_lists/models/school_list.dart' as _i57; +import '_features/schoolday/models/school_semester.dart' as _i58; +import '_features/schoolday/models/schoolday.dart' as _i59; +import '_features/schoolday_events/models/schoolday_event.dart' as _i60; +import '_features/schoolday_events/models/schoolday_event_type.dart' as _i61; +import '_features/timetable/models/classroom.dart' as _i62; +import '_features/timetable/models/junction_models/lesson_teacher.dart' as _i63; import '_features/timetable/models/junction_models/scheduled_lesson_teacher.dart' - as _i63; -import '_features/timetable/models/lesson/lesson.dart' as _i64; -import '_features/timetable/models/lesson/lesson_attendance.dart' as _i65; -import '_features/timetable/models/lesson/lesson_group.dart' as _i66; + as _i64; +import '_features/timetable/models/lesson/lesson.dart' as _i65; +import '_features/timetable/models/lesson/lesson_attendance.dart' as _i66; +import '_features/timetable/models/lesson/lesson_group.dart' as _i67; import '_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' - as _i67; -import '_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' as _i68; -import '_features/timetable/models/scheduled_lesson/subject.dart' as _i69; +import '_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' + as _i69; +import '_features/timetable/models/scheduled_lesson/subject.dart' as _i70; import '_features/timetable/models/scheduled_lesson/timetable_slot.dart' - as _i70; -import '_features/timetable/models/scheduled_lesson/weekday_enum.dart' as _i71; -import '_features/timetable/models/timetable.dart' as _i72; -import '_features/user/models/roles.dart' as _i73; -import '_features/user/models/staff_user.dart' as _i74; -import '_features/user/models/user_flags.dart' as _i75; -import '_features/workbooks/models/pupil_workbook.dart' as _i76; -import '_features/workbooks/models/workbook.dart' as _i77; -import '_shared/models/exceptions/test_exception.dart' as _i78; -import '_shared/models/hub_document.dart' as _i79; -import '_features/school_data/models/school_data.dart' as _i80; + as _i71; +import '_features/timetable/models/scheduled_lesson/weekday_enum.dart' as _i72; +import '_features/timetable/models/timetable.dart' as _i73; +import '_features/user/models/roles.dart' as _i74; +import '_features/user/models/staff_user.dart' as _i75; +import '_features/user/models/user_flags.dart' as _i76; +import '_features/workbooks/models/pupil_workbook.dart' as _i77; +import '_features/workbooks/models/workbook.dart' as _i78; +import '_shared/models/exceptions/test_exception.dart' as _i79; +import '_shared/models/hub_document.dart' as _i80; +import '_features/school_data/models/school_data.dart' as _i81; import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/pupil_data.dart' - as _i81; -import 'package:school_data_hub_server/src/generated/_features/learning/models/competence.dart' as _i82; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category.dart' +import 'package:school_data_hub_server/src/generated/_features/learning/models/competence.dart' as _i83; -import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category.dart' as _i84; -import 'package:school_data_hub_server/src/generated/_features/auth/models/user_device.dart' +import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday.dart' as _i85; -import 'package:school_data_hub_server/src/generated/_features/authorizations/models/authorization.dart' +import 'package:school_data_hub_server/src/generated/_features/auth/models/user_device.dart' as _i86; -import 'package:school_data_hub_server/src/generated/_shared/models/member_operation.dart' +import 'package:school_data_hub_server/src/generated/_features/authorizations/models/authorization.dart' as _i87; -import 'package:school_data_hub_server/src/generated/_features/books/models/book_tagging/book_tag.dart' +import 'package:school_data_hub_server/src/generated/_shared/models/member_operation.dart' as _i88; -import 'package:school_data_hub_server/src/generated/_features/books/models/book.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book_tagging/book_tag.dart' as _i89; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book.dart' as _i90; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' as _i91; -import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book.dart' as _i92; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' as _i93; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category_status.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' as _i94; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category_status.dart' as _i95; -import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i96; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' as _i97; -import 'package:school_data_hub_server/src/generated/_features/school_lists/models/school_list.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' as _i98; -import 'package:school_data_hub_server/src/generated/_features/schoolday/models/school_semester.dart' +import 'package:school_data_hub_server/src/generated/_features/school_lists/models/school_list.dart' as _i99; -import 'package:school_data_hub_server/src/generated/_features/schoolday/models/schoolday.dart' +import 'package:school_data_hub_server/src/generated/_features/schoolday/models/school_semester.dart' as _i100; -import 'package:school_data_hub_server/src/generated/_features/schoolday_events/models/schoolday_event.dart' +import 'package:school_data_hub_server/src/generated/_features/schoolday/models/schoolday.dart' as _i101; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/classroom.dart' +import 'package:school_data_hub_server/src/generated/_features/schoolday_events/models/schoolday_event.dart' as _i102; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/lesson/lesson_group.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/classroom.dart' as _i103; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/lesson/lesson_group.dart' as _i104; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' as _i105; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/subject.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' as _i106; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/timetable.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/subject.dart' as _i107; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/timetable_slot.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/timetable.dart' as _i108; -import 'package:school_data_hub_server/src/generated/_features/user/models/staff_user.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/timetable_slot.dart' as _i109; -import 'package:school_data_hub_server/src/generated/_features/workbooks/models/pupil_workbook.dart' +import 'package:school_data_hub_server/src/generated/_features/user/models/staff_user.dart' as _i110; -import 'package:school_data_hub_server/src/generated/_features/workbooks/models/workbook.dart' +import 'package:school_data_hub_server/src/generated/_features/workbooks/models/pupil_workbook.dart' as _i111; +import 'package:school_data_hub_server/src/generated/_features/workbooks/models/workbook.dart' + as _i112; export '_features/attendance/models/contacted_type.dart'; export '_features/attendance/models/missed_schoolday.dart'; export '_features/attendance/models/missed_schoolday_dto.dart'; @@ -179,6 +180,7 @@ export '_features/auth/models/user_device.dart'; export '_features/authorizations/models/authorization.dart'; export '_features/authorizations/models/pupil_authorization.dart'; export '_features/books/models/book.dart'; +export '_features/books/models/book_stats_dto.dart'; export '_features/books/models/book_tagging/book_tag.dart'; export '_features/books/models/book_tagging/book_tagging.dart'; export '_features/books/models/library_book.dart'; @@ -4903,8 +4905,8 @@ class Protocol extends _i1.SerializationManagerServer { Type? t, ]) { t ??= T; - if (t == _i4.SupportLevel) { - return _i4.SupportLevel.fromJson(data) as T; + if (t == _i4.SupportGoal) { + return _i4.SupportGoal.fromJson(data) as T; } if (t == _i5.MissedSchoolday) { return _i5.MissedSchoolday.fromJson(data) as T; @@ -4930,212 +4932,215 @@ class Protocol extends _i1.SerializationManagerServer { if (t == _i12.Book) { return _i12.Book.fromJson(data) as T; } - if (t == _i13.BookTag) { - return _i13.BookTag.fromJson(data) as T; + if (t == _i13.LibraryBookStatsDto) { + return _i13.LibraryBookStatsDto.fromJson(data) as T; } - if (t == _i14.BookTagging) { - return _i14.BookTagging.fromJson(data) as T; + if (t == _i14.BookTag) { + return _i14.BookTag.fromJson(data) as T; } - if (t == _i15.LibraryBook) { - return _i15.LibraryBook.fromJson(data) as T; + if (t == _i15.BookTagging) { + return _i15.BookTagging.fromJson(data) as T; } - if (t == _i16.LibraryBookLocation) { - return _i16.LibraryBookLocation.fromJson(data) as T; + if (t == _i16.LibraryBook) { + return _i16.LibraryBook.fromJson(data) as T; } - if (t == _i17.LibraryBookQuery) { - return _i17.LibraryBookQuery.fromJson(data) as T; + if (t == _i17.LibraryBookLocation) { + return _i17.LibraryBookLocation.fromJson(data) as T; } - if (t == _i18.PupilBookLending) { - return _i18.PupilBookLending.fromJson(data) as T; + if (t == _i18.LibraryBookQuery) { + return _i18.LibraryBookQuery.fromJson(data) as T; } - if (t == _i19.Competence) { - return _i19.Competence.fromJson(data) as T; + if (t == _i19.PupilBookLending) { + return _i19.PupilBookLending.fromJson(data) as T; } - if (t == _i20.CompetenceCheck) { - return _i20.CompetenceCheck.fromJson(data) as T; + if (t == _i20.Competence) { + return _i20.Competence.fromJson(data) as T; } - if (t == _i21.CompetenceGoal) { - return _i21.CompetenceGoal.fromJson(data) as T; + if (t == _i21.CompetenceCheck) { + return _i21.CompetenceCheck.fromJson(data) as T; } - if (t == _i22.CompetenceReport) { - return _i22.CompetenceReport.fromJson(data) as T; + if (t == _i22.CompetenceGoal) { + return _i22.CompetenceGoal.fromJson(data) as T; } - if (t == _i23.CompetenceReportCheck) { - return _i23.CompetenceReportCheck.fromJson(data) as T; + if (t == _i23.CompetenceReport) { + return _i23.CompetenceReport.fromJson(data) as T; } - if (t == _i24.CompetenceReportItem) { - return _i24.CompetenceReportItem.fromJson(data) as T; + if (t == _i24.CompetenceReportCheck) { + return _i24.CompetenceReportCheck.fromJson(data) as T; } - if (t == _i25.LearningSupportPlan) { - return _i25.LearningSupportPlan.fromJson(data) as T; + if (t == _i25.CompetenceReportItem) { + return _i25.CompetenceReportItem.fromJson(data) as T; } - if (t == _i26.SupportCategory) { - return _i26.SupportCategory.fromJson(data) as T; + if (t == _i26.LearningSupportPlan) { + return _i26.LearningSupportPlan.fromJson(data) as T; } - if (t == _i27.SupportCategoryStatus) { - return _i27.SupportCategoryStatus.fromJson(data) as T; + if (t == _i27.SupportCategory) { + return _i27.SupportCategory.fromJson(data) as T; } - if (t == _i28.SupportGoal) { - return _i28.SupportGoal.fromJson(data) as T; + if (t == _i28.SupportCategoryStatus) { + return _i28.SupportCategoryStatus.fromJson(data) as T; } - if (t == _i29.SupportGoalCheck) { - return _i29.SupportGoalCheck.fromJson(data) as T; + if (t == _i29.ContactedType) { + return _i29.ContactedType.fromJson(data) as T; } - if (t == _i30.ContactedType) { - return _i30.ContactedType.fromJson(data) as T; + if (t == _i30.SupportGoalCheck) { + return _i30.SupportGoalCheck.fromJson(data) as T; } - if (t == _i31.SupportLevelLegacyDto) { - return _i31.SupportLevelLegacyDto.fromJson(data) as T; + if (t == _i31.SupportLevel) { + return _i31.SupportLevel.fromJson(data) as T; } - if (t == _i32.CompulsoryRoom) { - return _i32.CompulsoryRoom.fromJson(data) as T; + if (t == _i32.SupportLevelLegacyDto) { + return _i32.SupportLevelLegacyDto.fromJson(data) as T; } - if (t == _i33.MatrixRoomType) { - return _i33.MatrixRoomType.fromJson(data) as T; + if (t == _i33.CompulsoryRoom) { + return _i33.CompulsoryRoom.fromJson(data) as T; } - if (t == _i34.AfterSchoolCare) { - return _i34.AfterSchoolCare.fromJson(data) as T; + if (t == _i34.MatrixRoomType) { + return _i34.MatrixRoomType.fromJson(data) as T; } - if (t == _i35.AfterSchoolCarePickUpTimes) { - return _i35.AfterSchoolCarePickUpTimes.fromJson(data) as T; + if (t == _i35.AfterSchoolCare) { + return _i35.AfterSchoolCare.fromJson(data) as T; } - if (t == _i36.PickUpInfo) { - return _i36.PickUpInfo.fromJson(data) as T; + if (t == _i36.AfterSchoolCarePickUpTimes) { + return _i36.AfterSchoolCarePickUpTimes.fromJson(data) as T; } - if (t == _i37.CommunicationSkills) { - return _i37.CommunicationSkills.fromJson(data) as T; + if (t == _i37.PickUpInfo) { + return _i37.PickUpInfo.fromJson(data) as T; } - if (t == _i38.PublicMediaAuth) { - return _i38.PublicMediaAuth.fromJson(data) as T; + if (t == _i38.CommunicationSkills) { + return _i38.CommunicationSkills.fromJson(data) as T; } - if (t == _i39.TutorInfo) { - return _i39.TutorInfo.fromJson(data) as T; + if (t == _i39.PublicMediaAuth) { + return _i39.PublicMediaAuth.fromJson(data) as T; } - if (t == _i40.CreditTransaction) { - return _i40.CreditTransaction.fromJson(data) as T; + if (t == _i40.TutorInfo) { + return _i40.TutorInfo.fromJson(data) as T; } - if (t == _i41.PupilDocumentType) { - return _i41.PupilDocumentType.fromJson(data) as T; + if (t == _i41.CreditTransaction) { + return _i41.CreditTransaction.fromJson(data) as T; } - if (t == _i42.SiblingsTutorInfo) { - return _i42.SiblingsTutorInfo.fromJson(data) as T; + if (t == _i42.PupilDocumentType) { + return _i42.PupilDocumentType.fromJson(data) as T; } - if (t == _i43.Kindergarden) { - return _i43.Kindergarden.fromJson(data) as T; + if (t == _i43.SiblingsTutorInfo) { + return _i43.SiblingsTutorInfo.fromJson(data) as T; } - if (t == _i44.KindergardenInfo) { - return _i44.KindergardenInfo.fromJson(data) as T; + if (t == _i44.Kindergarden) { + return _i44.Kindergarden.fromJson(data) as T; } - if (t == _i45.PreSchoolMedical) { - return _i45.PreSchoolMedical.fromJson(data) as T; + if (t == _i45.KindergardenInfo) { + return _i45.KindergardenInfo.fromJson(data) as T; } - if (t == _i46.PreSchoolMedicalStatus) { - return _i46.PreSchoolMedicalStatus.fromJson(data) as T; + if (t == _i46.PreSchoolMedical) { + return _i46.PreSchoolMedical.fromJson(data) as T; } - if (t == _i47.PreSchoolTest) { - return _i47.PreSchoolTest.fromJson(data) as T; + if (t == _i47.PreSchoolMedicalStatus) { + return _i47.PreSchoolMedicalStatus.fromJson(data) as T; } - if (t == _i48.PupilData) { - return _i48.PupilData.fromJson(data) as T; + if (t == _i48.PreSchoolTest) { + return _i48.PreSchoolTest.fromJson(data) as T; } - if (t == _i49.PupilStatus) { - return _i49.PupilStatus.fromJson(data) as T; + if (t == _i49.PupilData) { + return _i49.PupilData.fromJson(data) as T; } - if (t == _i50.LastPupilIdentiesUpdate) { - return _i50.LastPupilIdentiesUpdate.fromJson(data) as T; + if (t == _i50.PupilStatus) { + return _i50.PupilStatus.fromJson(data) as T; } - if (t == _i51.PupilIdentity) { - return _i51.PupilIdentity.fromJson(data) as T; + if (t == _i51.LastPupilIdentiesUpdate) { + return _i51.LastPupilIdentiesUpdate.fromJson(data) as T; } - if (t == _i52.PupilIdentityDto) { - return _i52.PupilIdentityDto.fromJson(data) as T; + if (t == _i52.PupilIdentity) { + return _i52.PupilIdentity.fromJson(data) as T; } - if (t == _i53.SchoolGrade) { - return _i53.SchoolGrade.fromJson(data) as T; + if (t == _i53.PupilIdentityDto) { + return _i53.PupilIdentityDto.fromJson(data) as T; } - if (t == _i54.MemberOperation) { - return _i54.MemberOperation.fromJson(data) as T; + if (t == _i54.SchoolGrade) { + return _i54.SchoolGrade.fromJson(data) as T; } - if (t == _i55.PupilListEntry) { - return _i55.PupilListEntry.fromJson(data) as T; + if (t == _i55.MemberOperation) { + return _i55.MemberOperation.fromJson(data) as T; } - if (t == _i56.SchoolList) { - return _i56.SchoolList.fromJson(data) as T; + if (t == _i56.PupilListEntry) { + return _i56.PupilListEntry.fromJson(data) as T; } - if (t == _i57.SchoolSemester) { - return _i57.SchoolSemester.fromJson(data) as T; + if (t == _i57.SchoolList) { + return _i57.SchoolList.fromJson(data) as T; } - if (t == _i58.Schoolday) { - return _i58.Schoolday.fromJson(data) as T; + if (t == _i58.SchoolSemester) { + return _i58.SchoolSemester.fromJson(data) as T; } - if (t == _i59.SchooldayEvent) { - return _i59.SchooldayEvent.fromJson(data) as T; + if (t == _i59.Schoolday) { + return _i59.Schoolday.fromJson(data) as T; } - if (t == _i60.SchooldayEventType) { - return _i60.SchooldayEventType.fromJson(data) as T; + if (t == _i60.SchooldayEvent) { + return _i60.SchooldayEvent.fromJson(data) as T; } - if (t == _i61.Classroom) { - return _i61.Classroom.fromJson(data) as T; + if (t == _i61.SchooldayEventType) { + return _i61.SchooldayEventType.fromJson(data) as T; } - if (t == _i62.LessonTeacher) { - return _i62.LessonTeacher.fromJson(data) as T; + if (t == _i62.Classroom) { + return _i62.Classroom.fromJson(data) as T; } - if (t == _i63.ScheduledLessonTeacher) { - return _i63.ScheduledLessonTeacher.fromJson(data) as T; + if (t == _i63.LessonTeacher) { + return _i63.LessonTeacher.fromJson(data) as T; } - if (t == _i64.Lesson) { - return _i64.Lesson.fromJson(data) as T; + if (t == _i64.ScheduledLessonTeacher) { + return _i64.ScheduledLessonTeacher.fromJson(data) as T; } - if (t == _i65.LessonAttendance) { - return _i65.LessonAttendance.fromJson(data) as T; + if (t == _i65.Lesson) { + return _i65.Lesson.fromJson(data) as T; } - if (t == _i66.LessonGroup) { - return _i66.LessonGroup.fromJson(data) as T; + if (t == _i66.LessonAttendance) { + return _i66.LessonAttendance.fromJson(data) as T; } - if (t == _i67.ScheduledLessonGroupMembership) { - return _i67.ScheduledLessonGroupMembership.fromJson(data) as T; + if (t == _i67.LessonGroup) { + return _i67.LessonGroup.fromJson(data) as T; } - if (t == _i68.ScheduledLesson) { - return _i68.ScheduledLesson.fromJson(data) as T; + if (t == _i68.ScheduledLessonGroupMembership) { + return _i68.ScheduledLessonGroupMembership.fromJson(data) as T; } - if (t == _i69.Subject) { - return _i69.Subject.fromJson(data) as T; + if (t == _i69.ScheduledLesson) { + return _i69.ScheduledLesson.fromJson(data) as T; } - if (t == _i70.TimetableSlot) { - return _i70.TimetableSlot.fromJson(data) as T; + if (t == _i70.Subject) { + return _i70.Subject.fromJson(data) as T; } - if (t == _i71.Weekday) { - return _i71.Weekday.fromJson(data) as T; + if (t == _i71.TimetableSlot) { + return _i71.TimetableSlot.fromJson(data) as T; } - if (t == _i72.Timetable) { - return _i72.Timetable.fromJson(data) as T; + if (t == _i72.Weekday) { + return _i72.Weekday.fromJson(data) as T; } - if (t == _i73.Role) { - return _i73.Role.fromJson(data) as T; + if (t == _i73.Timetable) { + return _i73.Timetable.fromJson(data) as T; } - if (t == _i74.User) { - return _i74.User.fromJson(data) as T; + if (t == _i74.Role) { + return _i74.Role.fromJson(data) as T; } - if (t == _i75.UserFlags) { - return _i75.UserFlags.fromJson(data) as T; + if (t == _i75.User) { + return _i75.User.fromJson(data) as T; } - if (t == _i76.PupilWorkbook) { - return _i76.PupilWorkbook.fromJson(data) as T; + if (t == _i76.UserFlags) { + return _i76.UserFlags.fromJson(data) as T; } - if (t == _i77.Workbook) { - return _i77.Workbook.fromJson(data) as T; + if (t == _i77.PupilWorkbook) { + return _i77.PupilWorkbook.fromJson(data) as T; } - if (t == _i78.MyException) { - return _i78.MyException.fromJson(data) as T; + if (t == _i78.Workbook) { + return _i78.Workbook.fromJson(data) as T; } - if (t == _i79.HubDocument) { - return _i79.HubDocument.fromJson(data) as T; + if (t == _i79.MyException) { + return _i79.MyException.fromJson(data) as T; } - if (t == _i80.SchoolData) { - return _i80.SchoolData.fromJson(data) as T; + if (t == _i80.HubDocument) { + return _i80.HubDocument.fromJson(data) as T; } - if (t == _i1.getType<_i4.SupportLevel?>()) { - return (data != null ? _i4.SupportLevel.fromJson(data) : null) as T; + if (t == _i81.SchoolData) { + return _i81.SchoolData.fromJson(data) as T; + } + if (t == _i1.getType<_i4.SupportGoal?>()) { + return (data != null ? _i4.SupportGoal.fromJson(data) : null) as T; } if (t == _i1.getType<_i5.MissedSchoolday?>()) { return (data != null ? _i5.MissedSchoolday.fromJson(data) : null) as T; @@ -5162,229 +5167,233 @@ class Protocol extends _i1.SerializationManagerServer { if (t == _i1.getType<_i12.Book?>()) { return (data != null ? _i12.Book.fromJson(data) : null) as T; } - if (t == _i1.getType<_i13.BookTag?>()) { - return (data != null ? _i13.BookTag.fromJson(data) : null) as T; + if (t == _i1.getType<_i13.LibraryBookStatsDto?>()) { + return (data != null ? _i13.LibraryBookStatsDto.fromJson(data) : null) + as T; } - if (t == _i1.getType<_i14.BookTagging?>()) { - return (data != null ? _i14.BookTagging.fromJson(data) : null) as T; + if (t == _i1.getType<_i14.BookTag?>()) { + return (data != null ? _i14.BookTag.fromJson(data) : null) as T; } - if (t == _i1.getType<_i15.LibraryBook?>()) { - return (data != null ? _i15.LibraryBook.fromJson(data) : null) as T; + if (t == _i1.getType<_i15.BookTagging?>()) { + return (data != null ? _i15.BookTagging.fromJson(data) : null) as T; } - if (t == _i1.getType<_i16.LibraryBookLocation?>()) { - return (data != null ? _i16.LibraryBookLocation.fromJson(data) : null) + if (t == _i1.getType<_i16.LibraryBook?>()) { + return (data != null ? _i16.LibraryBook.fromJson(data) : null) as T; + } + if (t == _i1.getType<_i17.LibraryBookLocation?>()) { + return (data != null ? _i17.LibraryBookLocation.fromJson(data) : null) as T; } - if (t == _i1.getType<_i17.LibraryBookQuery?>()) { - return (data != null ? _i17.LibraryBookQuery.fromJson(data) : null) as T; + if (t == _i1.getType<_i18.LibraryBookQuery?>()) { + return (data != null ? _i18.LibraryBookQuery.fromJson(data) : null) as T; } - if (t == _i1.getType<_i18.PupilBookLending?>()) { - return (data != null ? _i18.PupilBookLending.fromJson(data) : null) as T; + if (t == _i1.getType<_i19.PupilBookLending?>()) { + return (data != null ? _i19.PupilBookLending.fromJson(data) : null) as T; } - if (t == _i1.getType<_i19.Competence?>()) { - return (data != null ? _i19.Competence.fromJson(data) : null) as T; + if (t == _i1.getType<_i20.Competence?>()) { + return (data != null ? _i20.Competence.fromJson(data) : null) as T; } - if (t == _i1.getType<_i20.CompetenceCheck?>()) { - return (data != null ? _i20.CompetenceCheck.fromJson(data) : null) as T; + if (t == _i1.getType<_i21.CompetenceCheck?>()) { + return (data != null ? _i21.CompetenceCheck.fromJson(data) : null) as T; } - if (t == _i1.getType<_i21.CompetenceGoal?>()) { - return (data != null ? _i21.CompetenceGoal.fromJson(data) : null) as T; + if (t == _i1.getType<_i22.CompetenceGoal?>()) { + return (data != null ? _i22.CompetenceGoal.fromJson(data) : null) as T; } - if (t == _i1.getType<_i22.CompetenceReport?>()) { - return (data != null ? _i22.CompetenceReport.fromJson(data) : null) as T; + if (t == _i1.getType<_i23.CompetenceReport?>()) { + return (data != null ? _i23.CompetenceReport.fromJson(data) : null) as T; } - if (t == _i1.getType<_i23.CompetenceReportCheck?>()) { - return (data != null ? _i23.CompetenceReportCheck.fromJson(data) : null) + if (t == _i1.getType<_i24.CompetenceReportCheck?>()) { + return (data != null ? _i24.CompetenceReportCheck.fromJson(data) : null) as T; } - if (t == _i1.getType<_i24.CompetenceReportItem?>()) { - return (data != null ? _i24.CompetenceReportItem.fromJson(data) : null) + if (t == _i1.getType<_i25.CompetenceReportItem?>()) { + return (data != null ? _i25.CompetenceReportItem.fromJson(data) : null) as T; } - if (t == _i1.getType<_i25.LearningSupportPlan?>()) { - return (data != null ? _i25.LearningSupportPlan.fromJson(data) : null) + if (t == _i1.getType<_i26.LearningSupportPlan?>()) { + return (data != null ? _i26.LearningSupportPlan.fromJson(data) : null) as T; } - if (t == _i1.getType<_i26.SupportCategory?>()) { - return (data != null ? _i26.SupportCategory.fromJson(data) : null) as T; + if (t == _i1.getType<_i27.SupportCategory?>()) { + return (data != null ? _i27.SupportCategory.fromJson(data) : null) as T; } - if (t == _i1.getType<_i27.SupportCategoryStatus?>()) { - return (data != null ? _i27.SupportCategoryStatus.fromJson(data) : null) + if (t == _i1.getType<_i28.SupportCategoryStatus?>()) { + return (data != null ? _i28.SupportCategoryStatus.fromJson(data) : null) as T; } - if (t == _i1.getType<_i28.SupportGoal?>()) { - return (data != null ? _i28.SupportGoal.fromJson(data) : null) as T; + if (t == _i1.getType<_i29.ContactedType?>()) { + return (data != null ? _i29.ContactedType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i29.SupportGoalCheck?>()) { - return (data != null ? _i29.SupportGoalCheck.fromJson(data) : null) as T; + if (t == _i1.getType<_i30.SupportGoalCheck?>()) { + return (data != null ? _i30.SupportGoalCheck.fromJson(data) : null) as T; } - if (t == _i1.getType<_i30.ContactedType?>()) { - return (data != null ? _i30.ContactedType.fromJson(data) : null) as T; + if (t == _i1.getType<_i31.SupportLevel?>()) { + return (data != null ? _i31.SupportLevel.fromJson(data) : null) as T; } - if (t == _i1.getType<_i31.SupportLevelLegacyDto?>()) { - return (data != null ? _i31.SupportLevelLegacyDto.fromJson(data) : null) + if (t == _i1.getType<_i32.SupportLevelLegacyDto?>()) { + return (data != null ? _i32.SupportLevelLegacyDto.fromJson(data) : null) as T; } - if (t == _i1.getType<_i32.CompulsoryRoom?>()) { - return (data != null ? _i32.CompulsoryRoom.fromJson(data) : null) as T; + if (t == _i1.getType<_i33.CompulsoryRoom?>()) { + return (data != null ? _i33.CompulsoryRoom.fromJson(data) : null) as T; } - if (t == _i1.getType<_i33.MatrixRoomType?>()) { - return (data != null ? _i33.MatrixRoomType.fromJson(data) : null) as T; + if (t == _i1.getType<_i34.MatrixRoomType?>()) { + return (data != null ? _i34.MatrixRoomType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i34.AfterSchoolCare?>()) { - return (data != null ? _i34.AfterSchoolCare.fromJson(data) : null) as T; + if (t == _i1.getType<_i35.AfterSchoolCare?>()) { + return (data != null ? _i35.AfterSchoolCare.fromJson(data) : null) as T; } - if (t == _i1.getType<_i35.AfterSchoolCarePickUpTimes?>()) { + if (t == _i1.getType<_i36.AfterSchoolCarePickUpTimes?>()) { return (data != null - ? _i35.AfterSchoolCarePickUpTimes.fromJson(data) + ? _i36.AfterSchoolCarePickUpTimes.fromJson(data) : null) as T; } - if (t == _i1.getType<_i36.PickUpInfo?>()) { - return (data != null ? _i36.PickUpInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i37.PickUpInfo?>()) { + return (data != null ? _i37.PickUpInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i37.CommunicationSkills?>()) { - return (data != null ? _i37.CommunicationSkills.fromJson(data) : null) + if (t == _i1.getType<_i38.CommunicationSkills?>()) { + return (data != null ? _i38.CommunicationSkills.fromJson(data) : null) as T; } - if (t == _i1.getType<_i38.PublicMediaAuth?>()) { - return (data != null ? _i38.PublicMediaAuth.fromJson(data) : null) as T; + if (t == _i1.getType<_i39.PublicMediaAuth?>()) { + return (data != null ? _i39.PublicMediaAuth.fromJson(data) : null) as T; } - if (t == _i1.getType<_i39.TutorInfo?>()) { - return (data != null ? _i39.TutorInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i40.TutorInfo?>()) { + return (data != null ? _i40.TutorInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i40.CreditTransaction?>()) { - return (data != null ? _i40.CreditTransaction.fromJson(data) : null) as T; + if (t == _i1.getType<_i41.CreditTransaction?>()) { + return (data != null ? _i41.CreditTransaction.fromJson(data) : null) as T; } - if (t == _i1.getType<_i41.PupilDocumentType?>()) { - return (data != null ? _i41.PupilDocumentType.fromJson(data) : null) as T; + if (t == _i1.getType<_i42.PupilDocumentType?>()) { + return (data != null ? _i42.PupilDocumentType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i42.SiblingsTutorInfo?>()) { - return (data != null ? _i42.SiblingsTutorInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i43.SiblingsTutorInfo?>()) { + return (data != null ? _i43.SiblingsTutorInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i43.Kindergarden?>()) { - return (data != null ? _i43.Kindergarden.fromJson(data) : null) as T; + if (t == _i1.getType<_i44.Kindergarden?>()) { + return (data != null ? _i44.Kindergarden.fromJson(data) : null) as T; } - if (t == _i1.getType<_i44.KindergardenInfo?>()) { - return (data != null ? _i44.KindergardenInfo.fromJson(data) : null) as T; + if (t == _i1.getType<_i45.KindergardenInfo?>()) { + return (data != null ? _i45.KindergardenInfo.fromJson(data) : null) as T; } - if (t == _i1.getType<_i45.PreSchoolMedical?>()) { - return (data != null ? _i45.PreSchoolMedical.fromJson(data) : null) as T; + if (t == _i1.getType<_i46.PreSchoolMedical?>()) { + return (data != null ? _i46.PreSchoolMedical.fromJson(data) : null) as T; } - if (t == _i1.getType<_i46.PreSchoolMedicalStatus?>()) { - return (data != null ? _i46.PreSchoolMedicalStatus.fromJson(data) : null) + if (t == _i1.getType<_i47.PreSchoolMedicalStatus?>()) { + return (data != null ? _i47.PreSchoolMedicalStatus.fromJson(data) : null) as T; } - if (t == _i1.getType<_i47.PreSchoolTest?>()) { - return (data != null ? _i47.PreSchoolTest.fromJson(data) : null) as T; + if (t == _i1.getType<_i48.PreSchoolTest?>()) { + return (data != null ? _i48.PreSchoolTest.fromJson(data) : null) as T; } - if (t == _i1.getType<_i48.PupilData?>()) { - return (data != null ? _i48.PupilData.fromJson(data) : null) as T; + if (t == _i1.getType<_i49.PupilData?>()) { + return (data != null ? _i49.PupilData.fromJson(data) : null) as T; } - if (t == _i1.getType<_i49.PupilStatus?>()) { - return (data != null ? _i49.PupilStatus.fromJson(data) : null) as T; + if (t == _i1.getType<_i50.PupilStatus?>()) { + return (data != null ? _i50.PupilStatus.fromJson(data) : null) as T; } - if (t == _i1.getType<_i50.LastPupilIdentiesUpdate?>()) { - return (data != null ? _i50.LastPupilIdentiesUpdate.fromJson(data) : null) + if (t == _i1.getType<_i51.LastPupilIdentiesUpdate?>()) { + return (data != null ? _i51.LastPupilIdentiesUpdate.fromJson(data) : null) as T; } - if (t == _i1.getType<_i51.PupilIdentity?>()) { - return (data != null ? _i51.PupilIdentity.fromJson(data) : null) as T; + if (t == _i1.getType<_i52.PupilIdentity?>()) { + return (data != null ? _i52.PupilIdentity.fromJson(data) : null) as T; } - if (t == _i1.getType<_i52.PupilIdentityDto?>()) { - return (data != null ? _i52.PupilIdentityDto.fromJson(data) : null) as T; + if (t == _i1.getType<_i53.PupilIdentityDto?>()) { + return (data != null ? _i53.PupilIdentityDto.fromJson(data) : null) as T; } - if (t == _i1.getType<_i53.SchoolGrade?>()) { - return (data != null ? _i53.SchoolGrade.fromJson(data) : null) as T; + if (t == _i1.getType<_i54.SchoolGrade?>()) { + return (data != null ? _i54.SchoolGrade.fromJson(data) : null) as T; } - if (t == _i1.getType<_i54.MemberOperation?>()) { - return (data != null ? _i54.MemberOperation.fromJson(data) : null) as T; + if (t == _i1.getType<_i55.MemberOperation?>()) { + return (data != null ? _i55.MemberOperation.fromJson(data) : null) as T; } - if (t == _i1.getType<_i55.PupilListEntry?>()) { - return (data != null ? _i55.PupilListEntry.fromJson(data) : null) as T; + if (t == _i1.getType<_i56.PupilListEntry?>()) { + return (data != null ? _i56.PupilListEntry.fromJson(data) : null) as T; } - if (t == _i1.getType<_i56.SchoolList?>()) { - return (data != null ? _i56.SchoolList.fromJson(data) : null) as T; + if (t == _i1.getType<_i57.SchoolList?>()) { + return (data != null ? _i57.SchoolList.fromJson(data) : null) as T; } - if (t == _i1.getType<_i57.SchoolSemester?>()) { - return (data != null ? _i57.SchoolSemester.fromJson(data) : null) as T; + if (t == _i1.getType<_i58.SchoolSemester?>()) { + return (data != null ? _i58.SchoolSemester.fromJson(data) : null) as T; } - if (t == _i1.getType<_i58.Schoolday?>()) { - return (data != null ? _i58.Schoolday.fromJson(data) : null) as T; + if (t == _i1.getType<_i59.Schoolday?>()) { + return (data != null ? _i59.Schoolday.fromJson(data) : null) as T; } - if (t == _i1.getType<_i59.SchooldayEvent?>()) { - return (data != null ? _i59.SchooldayEvent.fromJson(data) : null) as T; + if (t == _i1.getType<_i60.SchooldayEvent?>()) { + return (data != null ? _i60.SchooldayEvent.fromJson(data) : null) as T; } - if (t == _i1.getType<_i60.SchooldayEventType?>()) { - return (data != null ? _i60.SchooldayEventType.fromJson(data) : null) + if (t == _i1.getType<_i61.SchooldayEventType?>()) { + return (data != null ? _i61.SchooldayEventType.fromJson(data) : null) as T; } - if (t == _i1.getType<_i61.Classroom?>()) { - return (data != null ? _i61.Classroom.fromJson(data) : null) as T; + if (t == _i1.getType<_i62.Classroom?>()) { + return (data != null ? _i62.Classroom.fromJson(data) : null) as T; } - if (t == _i1.getType<_i62.LessonTeacher?>()) { - return (data != null ? _i62.LessonTeacher.fromJson(data) : null) as T; + if (t == _i1.getType<_i63.LessonTeacher?>()) { + return (data != null ? _i63.LessonTeacher.fromJson(data) : null) as T; } - if (t == _i1.getType<_i63.ScheduledLessonTeacher?>()) { - return (data != null ? _i63.ScheduledLessonTeacher.fromJson(data) : null) + if (t == _i1.getType<_i64.ScheduledLessonTeacher?>()) { + return (data != null ? _i64.ScheduledLessonTeacher.fromJson(data) : null) as T; } - if (t == _i1.getType<_i64.Lesson?>()) { - return (data != null ? _i64.Lesson.fromJson(data) : null) as T; + if (t == _i1.getType<_i65.Lesson?>()) { + return (data != null ? _i65.Lesson.fromJson(data) : null) as T; } - if (t == _i1.getType<_i65.LessonAttendance?>()) { - return (data != null ? _i65.LessonAttendance.fromJson(data) : null) as T; + if (t == _i1.getType<_i66.LessonAttendance?>()) { + return (data != null ? _i66.LessonAttendance.fromJson(data) : null) as T; } - if (t == _i1.getType<_i66.LessonGroup?>()) { - return (data != null ? _i66.LessonGroup.fromJson(data) : null) as T; + if (t == _i1.getType<_i67.LessonGroup?>()) { + return (data != null ? _i67.LessonGroup.fromJson(data) : null) as T; } - if (t == _i1.getType<_i67.ScheduledLessonGroupMembership?>()) { + if (t == _i1.getType<_i68.ScheduledLessonGroupMembership?>()) { return (data != null - ? _i67.ScheduledLessonGroupMembership.fromJson(data) + ? _i68.ScheduledLessonGroupMembership.fromJson(data) : null) as T; } - if (t == _i1.getType<_i68.ScheduledLesson?>()) { - return (data != null ? _i68.ScheduledLesson.fromJson(data) : null) as T; + if (t == _i1.getType<_i69.ScheduledLesson?>()) { + return (data != null ? _i69.ScheduledLesson.fromJson(data) : null) as T; } - if (t == _i1.getType<_i69.Subject?>()) { - return (data != null ? _i69.Subject.fromJson(data) : null) as T; + if (t == _i1.getType<_i70.Subject?>()) { + return (data != null ? _i70.Subject.fromJson(data) : null) as T; } - if (t == _i1.getType<_i70.TimetableSlot?>()) { - return (data != null ? _i70.TimetableSlot.fromJson(data) : null) as T; + if (t == _i1.getType<_i71.TimetableSlot?>()) { + return (data != null ? _i71.TimetableSlot.fromJson(data) : null) as T; } - if (t == _i1.getType<_i71.Weekday?>()) { - return (data != null ? _i71.Weekday.fromJson(data) : null) as T; + if (t == _i1.getType<_i72.Weekday?>()) { + return (data != null ? _i72.Weekday.fromJson(data) : null) as T; } - if (t == _i1.getType<_i72.Timetable?>()) { - return (data != null ? _i72.Timetable.fromJson(data) : null) as T; + if (t == _i1.getType<_i73.Timetable?>()) { + return (data != null ? _i73.Timetable.fromJson(data) : null) as T; } - if (t == _i1.getType<_i73.Role?>()) { - return (data != null ? _i73.Role.fromJson(data) : null) as T; + if (t == _i1.getType<_i74.Role?>()) { + return (data != null ? _i74.Role.fromJson(data) : null) as T; } - if (t == _i1.getType<_i74.User?>()) { - return (data != null ? _i74.User.fromJson(data) : null) as T; + if (t == _i1.getType<_i75.User?>()) { + return (data != null ? _i75.User.fromJson(data) : null) as T; } - if (t == _i1.getType<_i75.UserFlags?>()) { - return (data != null ? _i75.UserFlags.fromJson(data) : null) as T; + if (t == _i1.getType<_i76.UserFlags?>()) { + return (data != null ? _i76.UserFlags.fromJson(data) : null) as T; } - if (t == _i1.getType<_i76.PupilWorkbook?>()) { - return (data != null ? _i76.PupilWorkbook.fromJson(data) : null) as T; + if (t == _i1.getType<_i77.PupilWorkbook?>()) { + return (data != null ? _i77.PupilWorkbook.fromJson(data) : null) as T; } - if (t == _i1.getType<_i77.Workbook?>()) { - return (data != null ? _i77.Workbook.fromJson(data) : null) as T; + if (t == _i1.getType<_i78.Workbook?>()) { + return (data != null ? _i78.Workbook.fromJson(data) : null) as T; } - if (t == _i1.getType<_i78.MyException?>()) { - return (data != null ? _i78.MyException.fromJson(data) : null) as T; + if (t == _i1.getType<_i79.MyException?>()) { + return (data != null ? _i79.MyException.fromJson(data) : null) as T; } - if (t == _i1.getType<_i79.HubDocument?>()) { - return (data != null ? _i79.HubDocument.fromJson(data) : null) as T; + if (t == _i1.getType<_i80.HubDocument?>()) { + return (data != null ? _i80.HubDocument.fromJson(data) : null) as T; } - if (t == _i1.getType<_i80.SchoolData?>()) { - return (data != null ? _i80.SchoolData.fromJson(data) : null) as T; + if (t == _i1.getType<_i81.SchoolData?>()) { + return (data != null ? _i81.SchoolData.fromJson(data) : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i25.LearningSupportPlan>(e)) + .map((e) => deserialize<_i30.SupportGoalCheck>(e)) .toList() : null) as T; } @@ -5395,41 +5404,41 @@ class Protocol extends _i1.SerializationManagerServer { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i14.BookTagging>(e)).toList() + ? (data as List).map((e) => deserialize<_i15.BookTagging>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i15.LibraryBook>(e)).toList() + ? (data as List).map((e) => deserialize<_i16.LibraryBook>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i14.BookTagging>(e)).toList() + ? (data as List).map((e) => deserialize<_i15.BookTagging>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i18.PupilBookLending>(e)) + .map((e) => deserialize<_i19.PupilBookLending>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i15.LibraryBook>(e)).toList() + ? (data as List).map((e) => deserialize<_i16.LibraryBook>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i13.BookTag>(e)).toList() + ? (data as List).map((e) => deserialize<_i14.BookTag>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i79.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i80.HubDocument>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -5442,23 +5451,23 @@ class Protocol extends _i1.SerializationManagerServer { ? (data as List).map((e) => deserialize(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i21.CompetenceGoal>(e)) + .map((e) => deserialize<_i22.CompetenceGoal>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i20.CompetenceCheck>(e)) + .map((e) => deserialize<_i21.CompetenceCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i79.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i80.HubDocument>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -5466,15 +5475,15 @@ class Protocol extends _i1.SerializationManagerServer { ? (data as List).map((e) => deserialize(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i79.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i80.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i23.CompetenceReportCheck>(e)) + .map((e) => deserialize<_i24.CompetenceReportCheck>(e)) .toList() : null) as T; } @@ -5483,70 +5492,70 @@ class Protocol extends _i1.SerializationManagerServer { ? (data as List).map((e) => deserialize(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i23.CompetenceReportCheck>(e)) + .map((e) => deserialize<_i24.CompetenceReportCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i27.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i28.SupportCategoryStatus>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i28.SupportGoal>(e)).toList() + ? (data as List).map((e) => deserialize<_i4.SupportGoal>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i28.SupportGoal>(e)).toList() + ? (data as List).map((e) => deserialize<_i4.SupportGoal>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i27.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i28.SupportCategoryStatus>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i79.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i80.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List) - .map((e) => deserialize<_i29.SupportGoalCheck>(e)) - .toList() + ? (data as List).map((e) => deserialize<_i80.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i79.HubDocument>(e)).toList() + ? (data as List) + .map((e) => deserialize<_i26.LearningSupportPlan>(e)) + .toList() : null) as T; } if (t == Set) { return (data as List).map((e) => deserialize(e)).toSet() as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i48.PupilData>(e)).toList() + ? (data as List).map((e) => deserialize<_i49.PupilData>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i79.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i80.HubDocument>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i79.HubDocument>(e)).toList() + ? (data as List).map((e) => deserialize<_i80.HubDocument>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -5556,90 +5565,92 @@ class Protocol extends _i1.SerializationManagerServer { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i40.CreditTransaction>(e)) + .map((e) => deserialize<_i41.CreditTransaction>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i67.ScheduledLessonGroupMembership>(e)) + .map((e) => deserialize<_i68.ScheduledLessonGroupMembership>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i65.LessonAttendance>(e)) + .map((e) => deserialize<_i66.LessonAttendance>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i21.CompetenceGoal>(e)) + .map((e) => deserialize<_i22.CompetenceGoal>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i20.CompetenceCheck>(e)) + .map((e) => deserialize<_i21.CompetenceCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i22.CompetenceReport>(e)) + .map((e) => deserialize<_i23.CompetenceReport>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i23.CompetenceReportCheck>(e)) + .map((e) => deserialize<_i24.CompetenceReportCheck>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i76.PupilWorkbook>(e)) + .map((e) => deserialize<_i77.PupilWorkbook>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i18.PupilBookLending>(e)) + .map((e) => deserialize<_i19.PupilBookLending>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i4.SupportLevel>(e)).toList() + ? (data as List) + .map((e) => deserialize<_i31.SupportLevel>(e)) + .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i27.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i28.SupportCategoryStatus>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i28.SupportGoal>(e)).toList() + ? (data as List).map((e) => deserialize<_i4.SupportGoal>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i25.LearningSupportPlan>(e)) + .map((e) => deserialize<_i26.LearningSupportPlan>(e)) .toList() : null) as T; } @@ -5650,48 +5661,48 @@ class Protocol extends _i1.SerializationManagerServer { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i59.SchooldayEvent>(e)) + .map((e) => deserialize<_i60.SchooldayEvent>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i55.PupilListEntry>(e)) + .map((e) => deserialize<_i56.PupilListEntry>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i55.PupilListEntry>(e)) + .map((e) => deserialize<_i56.PupilListEntry>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i72.Timetable>(e)).toList() + ? (data as List).map((e) => deserialize<_i73.Timetable>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i58.Schoolday>(e)).toList() + ? (data as List).map((e) => deserialize<_i59.Schoolday>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i22.CompetenceReport>(e)) + .map((e) => deserialize<_i23.CompetenceReport>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i25.LearningSupportPlan>(e)) + .map((e) => deserialize<_i26.LearningSupportPlan>(e)) .toList() : null) as T; } @@ -5702,52 +5713,52 @@ class Protocol extends _i1.SerializationManagerServer { .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i59.SchooldayEvent>(e)) + .map((e) => deserialize<_i60.SchooldayEvent>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i68.ScheduledLesson>(e)) + .map((e) => deserialize<_i69.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i65.LessonAttendance>(e)) + .map((e) => deserialize<_i66.LessonAttendance>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i62.LessonTeacher>(e)) + .map((e) => deserialize<_i63.LessonTeacher>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i68.ScheduledLesson>(e)) + .map((e) => deserialize<_i69.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i67.ScheduledLessonGroupMembership>(e)) + .map((e) => deserialize<_i68.ScheduledLessonGroupMembership>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i63.ScheduledLessonTeacher>(e)) + .map((e) => deserialize<_i64.ScheduledLessonTeacher>(e)) .toList() : null) as T; } @@ -5759,35 +5770,35 @@ class Protocol extends _i1.SerializationManagerServer { testString: deserialize(data['n']['testString']), ) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i68.ScheduledLesson>(e)) + .map((e) => deserialize<_i69.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i64.Lesson>(e)).toList() + ? (data as List).map((e) => deserialize<_i65.Lesson>(e)).toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i68.ScheduledLesson>(e)) + .map((e) => deserialize<_i69.ScheduledLesson>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i70.TimetableSlot>(e)) + .map((e) => deserialize<_i71.TimetableSlot>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i66.LessonGroup>(e)).toList() + ? (data as List).map((e) => deserialize<_i67.LessonGroup>(e)).toList() : null) as T; } if (t == _i1.getType?>()) { @@ -5805,17 +5816,17 @@ class Protocol extends _i1.SerializationManagerServer { modifiedAt: deserialize(data['n']['modifiedAt']), ) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i63.ScheduledLessonTeacher>(e)) + .map((e) => deserialize<_i64.ScheduledLessonTeacher>(e)) .toList() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i62.LessonTeacher>(e)) + .map((e) => deserialize<_i63.LessonTeacher>(e)) .toList() : null) as T; } @@ -5824,51 +5835,51 @@ class Protocol extends _i1.SerializationManagerServer { ? (data as List).map((e) => deserialize(e)).toSet() : null) as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i76.PupilWorkbook>(e)) + .map((e) => deserialize<_i77.PupilWorkbook>(e)) .toList() : null) as T; } if (t == List) { return (data as List).map((e) => deserialize(e)).toList() as T; } - if (t == Set<_i81.PupilData>) { - return (data as List).map((e) => deserialize<_i81.PupilData>(e)).toSet() + if (t == Set<_i82.PupilData>) { + return (data as List).map((e) => deserialize<_i82.PupilData>(e)).toSet() as T; } - if (t == List<_i82.Competence>) { - return (data as List).map((e) => deserialize<_i82.Competence>(e)).toList() + if (t == List<_i83.Competence>) { + return (data as List).map((e) => deserialize<_i83.Competence>(e)).toList() as T; } - if (t == List<_i83.SupportCategory>) { + if (t == List<_i84.SupportCategory>) { return (data as List) - .map((e) => deserialize<_i83.SupportCategory>(e)) + .map((e) => deserialize<_i84.SupportCategory>(e)) .toList() as T; } - if (t == List<_i84.MissedSchoolday>) { + if (t == List<_i85.MissedSchoolday>) { return (data as List) - .map((e) => deserialize<_i84.MissedSchoolday>(e)) + .map((e) => deserialize<_i85.MissedSchoolday>(e)) .toList() as T; } if (t == _i1.getType< ({ _i3.AuthenticationResponse response, - _i85.UserDevice? userDevice + _i86.UserDevice? userDevice })>()) { return ( response: deserialize<_i3.AuthenticationResponse>( ((data as Map)['n'] as Map)['response']), userDevice: ((data)['n'] as Map)['userDevice'] == null ? null - : deserialize<_i85.UserDevice>(data['n']['userDevice']), + : deserialize<_i86.UserDevice>(data['n']['userDevice']), ) as T; } - if (t == List<_i86.Authorization>) { + if (t == List<_i87.Authorization>) { return (data as List) - .map((e) => deserialize<_i86.Authorization>(e)) + .map((e) => deserialize<_i87.Authorization>(e)) .toList() as T; } if (t == List) { @@ -5876,40 +5887,40 @@ class Protocol extends _i1.SerializationManagerServer { } if (t == _i1.getType< - ({_i87.MemberOperation operation, List pupilIds})?>()) { + ({_i88.MemberOperation operation, List pupilIds})?>()) { return (data == null) ? null as T : ( - operation: deserialize<_i87.MemberOperation>( + operation: deserialize<_i88.MemberOperation>( ((data as Map)['n'] as Map)['operation']), pupilIds: deserialize>(data['n']['pupilIds']), ) as T; } - if (t == List<_i88.BookTag>) { - return (data as List).map((e) => deserialize<_i88.BookTag>(e)).toList() + if (t == List<_i89.BookTag>) { + return (data as List).map((e) => deserialize<_i89.BookTag>(e)).toList() as T; } - if (t == List<_i89.Book>) { - return (data as List).map((e) => deserialize<_i89.Book>(e)).toList() as T; + if (t == List<_i90.Book>) { + return (data as List).map((e) => deserialize<_i90.Book>(e)).toList() as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null - ? (data as List).map((e) => deserialize<_i88.BookTag>(e)).toList() + ? (data as List).map((e) => deserialize<_i89.BookTag>(e)).toList() : null) as T; } - if (t == List<_i90.LibraryBookLocation>) { + if (t == List<_i91.LibraryBookLocation>) { return (data as List) - .map((e) => deserialize<_i90.LibraryBookLocation>(e)) + .map((e) => deserialize<_i91.LibraryBookLocation>(e)) .toList() as T; } - if (t == List<_i91.LibraryBook>) { + if (t == List<_i92.LibraryBook>) { return (data as List) - .map((e) => deserialize<_i91.LibraryBook>(e)) + .map((e) => deserialize<_i92.LibraryBook>(e)) .toList() as T; } - if (t == List<_i92.PupilBookLending>) { + if (t == List<_i93.PupilBookLending>) { return (data as List) - .map((e) => deserialize<_i92.PupilBookLending>(e)) + .map((e) => deserialize<_i93.PupilBookLending>(e)) .toList() as T; } if (t == _i1.getType<({int value})?>()) { @@ -5939,43 +5950,43 @@ class Protocol extends _i1.SerializationManagerServer { : deserialize(data['n']['value']), ) as T; } - if (t == List<_i93.LearningSupportPlan>) { + if (t == List<_i94.LearningSupportPlan>) { return (data as List) - .map((e) => deserialize<_i93.LearningSupportPlan>(e)) + .map((e) => deserialize<_i94.LearningSupportPlan>(e)) .toList() as T; } - if (t == List<_i94.SupportCategoryStatus>) { + if (t == List<_i95.SupportCategoryStatus>) { return (data as List) - .map((e) => deserialize<_i94.SupportCategoryStatus>(e)) + .map((e) => deserialize<_i95.SupportCategoryStatus>(e)) .toList() as T; } - if (t == List<_i95.PreSchoolMedical>) { + if (t == List<_i96.PreSchoolMedical>) { return (data as List) - .map((e) => deserialize<_i95.PreSchoolMedical>(e)) + .map((e) => deserialize<_i96.PreSchoolMedical>(e)) .toList() as T; } - if (t == _i1.getType?>()) { + if (t == _i1.getType?>()) { return (data != null ? (data as List) - .map((e) => deserialize<_i96.CompulsoryRoom>(e)) + .map((e) => deserialize<_i97.CompulsoryRoom>(e)) .toList() : null) as T; } - if (t == List<_i96.CompulsoryRoom>) { + if (t == List<_i97.CompulsoryRoom>) { return (data as List) - .map((e) => deserialize<_i96.CompulsoryRoom>(e)) + .map((e) => deserialize<_i97.CompulsoryRoom>(e)) .toList() as T; } - if (t == List<_i81.PupilData>) { - return (data as List).map((e) => deserialize<_i81.PupilData>(e)).toList() + if (t == List<_i82.PupilData>) { + return (data as List).map((e) => deserialize<_i82.PupilData>(e)).toList() as T; } if (t == Set) { return (data as List).map((e) => deserialize(e)).toSet() as T; } - if (t == List<_i97.SupportLevelLegacyDto>) { + if (t == List<_i98.SupportLevelLegacyDto>) { return (data as List) - .map((e) => deserialize<_i97.SupportLevelLegacyDto>(e)) + .map((e) => deserialize<_i98.SupportLevelLegacyDto>(e)) .toList() as T; } if (t == _i1.getType<({DateTime? value})>()) { @@ -5985,70 +5996,70 @@ class Protocol extends _i1.SerializationManagerServer { : deserialize(data['n']['value']), ) as T; } - if (t == List<_i98.SchoolList>) { - return (data as List).map((e) => deserialize<_i98.SchoolList>(e)).toList() + if (t == List<_i99.SchoolList>) { + return (data as List).map((e) => deserialize<_i99.SchoolList>(e)).toList() as T; } - if (t == List<_i99.SchoolSemester>) { + if (t == List<_i100.SchoolSemester>) { return (data as List) - .map((e) => deserialize<_i99.SchoolSemester>(e)) + .map((e) => deserialize<_i100.SchoolSemester>(e)) .toList() as T; } - if (t == List<_i100.Schoolday>) { - return (data as List).map((e) => deserialize<_i100.Schoolday>(e)).toList() + if (t == List<_i101.Schoolday>) { + return (data as List).map((e) => deserialize<_i101.Schoolday>(e)).toList() as T; } if (t == List) { return (data as List).map((e) => deserialize(e)).toList() as T; } - if (t == List<_i101.SchooldayEvent>) { + if (t == List<_i102.SchooldayEvent>) { return (data as List) - .map((e) => deserialize<_i101.SchooldayEvent>(e)) + .map((e) => deserialize<_i102.SchooldayEvent>(e)) .toList() as T; } - if (t == List<_i102.Classroom>) { - return (data as List).map((e) => deserialize<_i102.Classroom>(e)).toList() + if (t == List<_i103.Classroom>) { + return (data as List).map((e) => deserialize<_i103.Classroom>(e)).toList() as T; } - if (t == List<_i103.LessonGroup>) { + if (t == List<_i104.LessonGroup>) { return (data as List) - .map((e) => deserialize<_i103.LessonGroup>(e)) + .map((e) => deserialize<_i104.LessonGroup>(e)) .toList() as T; } - if (t == List<_i104.ScheduledLesson>) { + if (t == List<_i105.ScheduledLesson>) { return (data as List) - .map((e) => deserialize<_i104.ScheduledLesson>(e)) + .map((e) => deserialize<_i105.ScheduledLesson>(e)) .toList() as T; } - if (t == List<_i105.ScheduledLessonGroupMembership>) { + if (t == List<_i106.ScheduledLessonGroupMembership>) { return (data as List) - .map((e) => deserialize<_i105.ScheduledLessonGroupMembership>(e)) + .map((e) => deserialize<_i106.ScheduledLessonGroupMembership>(e)) .toList() as T; } - if (t == List<_i106.Subject>) { - return (data as List).map((e) => deserialize<_i106.Subject>(e)).toList() + if (t == List<_i107.Subject>) { + return (data as List).map((e) => deserialize<_i107.Subject>(e)).toList() as T; } - if (t == List<_i107.Timetable>) { - return (data as List).map((e) => deserialize<_i107.Timetable>(e)).toList() + if (t == List<_i108.Timetable>) { + return (data as List).map((e) => deserialize<_i108.Timetable>(e)).toList() as T; } - if (t == List<_i108.TimetableSlot>) { + if (t == List<_i109.TimetableSlot>) { return (data as List) - .map((e) => deserialize<_i108.TimetableSlot>(e)) + .map((e) => deserialize<_i109.TimetableSlot>(e)) .toList() as T; } - if (t == List<_i109.User>) { - return (data as List).map((e) => deserialize<_i109.User>(e)).toList() + if (t == List<_i110.User>) { + return (data as List).map((e) => deserialize<_i110.User>(e)).toList() as T; } - if (t == List<_i110.PupilWorkbook>) { + if (t == List<_i111.PupilWorkbook>) { return (data as List) - .map((e) => deserialize<_i110.PupilWorkbook>(e)) + .map((e) => deserialize<_i111.PupilWorkbook>(e)) .toList() as T; } - if (t == List<_i111.Workbook>) { - return (data as List).map((e) => deserialize<_i111.Workbook>(e)).toList() + if (t == List<_i112.Workbook>) { + return (data as List).map((e) => deserialize<_i112.Workbook>(e)).toList() as T; } if (t == _i1.getType<({int testint, String testString})?>()) { @@ -6094,8 +6105,8 @@ class Protocol extends _i1.SerializationManagerServer { String? getClassNameForObject(Object? data) { String? className = super.getClassNameForObject(data); if (className != null) return className; - if (data is _i4.SupportLevel) { - return 'SupportLevel'; + if (data is _i4.SupportGoal) { + return 'SupportGoal'; } if (data is _i5.MissedSchoolday) { return 'MissedSchoolday'; @@ -6121,208 +6132,211 @@ class Protocol extends _i1.SerializationManagerServer { if (data is _i12.Book) { return 'Book'; } - if (data is _i13.BookTag) { + if (data is _i13.LibraryBookStatsDto) { + return 'LibraryBookStatsDto'; + } + if (data is _i14.BookTag) { return 'BookTag'; } - if (data is _i14.BookTagging) { + if (data is _i15.BookTagging) { return 'BookTagging'; } - if (data is _i15.LibraryBook) { + if (data is _i16.LibraryBook) { return 'LibraryBook'; } - if (data is _i16.LibraryBookLocation) { + if (data is _i17.LibraryBookLocation) { return 'LibraryBookLocation'; } - if (data is _i17.LibraryBookQuery) { + if (data is _i18.LibraryBookQuery) { return 'LibraryBookQuery'; } - if (data is _i18.PupilBookLending) { + if (data is _i19.PupilBookLending) { return 'PupilBookLending'; } - if (data is _i19.Competence) { + if (data is _i20.Competence) { return 'Competence'; } - if (data is _i20.CompetenceCheck) { + if (data is _i21.CompetenceCheck) { return 'CompetenceCheck'; } - if (data is _i21.CompetenceGoal) { + if (data is _i22.CompetenceGoal) { return 'CompetenceGoal'; } - if (data is _i22.CompetenceReport) { + if (data is _i23.CompetenceReport) { return 'CompetenceReport'; } - if (data is _i23.CompetenceReportCheck) { + if (data is _i24.CompetenceReportCheck) { return 'CompetenceReportCheck'; } - if (data is _i24.CompetenceReportItem) { + if (data is _i25.CompetenceReportItem) { return 'CompetenceReportItem'; } - if (data is _i25.LearningSupportPlan) { + if (data is _i26.LearningSupportPlan) { return 'LearningSupportPlan'; } - if (data is _i26.SupportCategory) { + if (data is _i27.SupportCategory) { return 'SupportCategory'; } - if (data is _i27.SupportCategoryStatus) { + if (data is _i28.SupportCategoryStatus) { return 'SupportCategoryStatus'; } - if (data is _i28.SupportGoal) { - return 'SupportGoal'; + if (data is _i29.ContactedType) { + return 'ContactedType'; } - if (data is _i29.SupportGoalCheck) { + if (data is _i30.SupportGoalCheck) { return 'SupportGoalCheck'; } - if (data is _i30.ContactedType) { - return 'ContactedType'; + if (data is _i31.SupportLevel) { + return 'SupportLevel'; } - if (data is _i31.SupportLevelLegacyDto) { + if (data is _i32.SupportLevelLegacyDto) { return 'SupportLevelLegacyDto'; } - if (data is _i32.CompulsoryRoom) { + if (data is _i33.CompulsoryRoom) { return 'CompulsoryRoom'; } - if (data is _i33.MatrixRoomType) { + if (data is _i34.MatrixRoomType) { return 'MatrixRoomType'; } - if (data is _i34.AfterSchoolCare) { + if (data is _i35.AfterSchoolCare) { return 'AfterSchoolCare'; } - if (data is _i35.AfterSchoolCarePickUpTimes) { + if (data is _i36.AfterSchoolCarePickUpTimes) { return 'AfterSchoolCarePickUpTimes'; } - if (data is _i36.PickUpInfo) { + if (data is _i37.PickUpInfo) { return 'PickUpInfo'; } - if (data is _i37.CommunicationSkills) { + if (data is _i38.CommunicationSkills) { return 'CommunicationSkills'; } - if (data is _i38.PublicMediaAuth) { + if (data is _i39.PublicMediaAuth) { return 'PublicMediaAuth'; } - if (data is _i39.TutorInfo) { + if (data is _i40.TutorInfo) { return 'TutorInfo'; } - if (data is _i40.CreditTransaction) { + if (data is _i41.CreditTransaction) { return 'CreditTransaction'; } - if (data is _i41.PupilDocumentType) { + if (data is _i42.PupilDocumentType) { return 'PupilDocumentType'; } - if (data is _i42.SiblingsTutorInfo) { + if (data is _i43.SiblingsTutorInfo) { return 'SiblingsTutorInfo'; } - if (data is _i43.Kindergarden) { + if (data is _i44.Kindergarden) { return 'Kindergarden'; } - if (data is _i44.KindergardenInfo) { + if (data is _i45.KindergardenInfo) { return 'KindergardenInfo'; } - if (data is _i45.PreSchoolMedical) { + if (data is _i46.PreSchoolMedical) { return 'PreSchoolMedical'; } - if (data is _i46.PreSchoolMedicalStatus) { + if (data is _i47.PreSchoolMedicalStatus) { return 'PreSchoolMedicalStatus'; } - if (data is _i47.PreSchoolTest) { + if (data is _i48.PreSchoolTest) { return 'PreSchoolTest'; } - if (data is _i48.PupilData) { + if (data is _i49.PupilData) { return 'PupilData'; } - if (data is _i49.PupilStatus) { + if (data is _i50.PupilStatus) { return 'PupilStatus'; } - if (data is _i50.LastPupilIdentiesUpdate) { + if (data is _i51.LastPupilIdentiesUpdate) { return 'LastPupilIdentiesUpdate'; } - if (data is _i51.PupilIdentity) { + if (data is _i52.PupilIdentity) { return 'PupilIdentity'; } - if (data is _i52.PupilIdentityDto) { + if (data is _i53.PupilIdentityDto) { return 'PupilIdentityDto'; } - if (data is _i53.SchoolGrade) { + if (data is _i54.SchoolGrade) { return 'SchoolGrade'; } - if (data is _i54.MemberOperation) { + if (data is _i55.MemberOperation) { return 'MemberOperation'; } - if (data is _i55.PupilListEntry) { + if (data is _i56.PupilListEntry) { return 'PupilListEntry'; } - if (data is _i56.SchoolList) { + if (data is _i57.SchoolList) { return 'SchoolList'; } - if (data is _i57.SchoolSemester) { + if (data is _i58.SchoolSemester) { return 'SchoolSemester'; } - if (data is _i58.Schoolday) { + if (data is _i59.Schoolday) { return 'Schoolday'; } - if (data is _i59.SchooldayEvent) { + if (data is _i60.SchooldayEvent) { return 'SchooldayEvent'; } - if (data is _i60.SchooldayEventType) { + if (data is _i61.SchooldayEventType) { return 'SchooldayEventType'; } - if (data is _i61.Classroom) { + if (data is _i62.Classroom) { return 'Classroom'; } - if (data is _i62.LessonTeacher) { + if (data is _i63.LessonTeacher) { return 'LessonTeacher'; } - if (data is _i63.ScheduledLessonTeacher) { + if (data is _i64.ScheduledLessonTeacher) { return 'ScheduledLessonTeacher'; } - if (data is _i64.Lesson) { + if (data is _i65.Lesson) { return 'Lesson'; } - if (data is _i65.LessonAttendance) { + if (data is _i66.LessonAttendance) { return 'LessonAttendance'; } - if (data is _i66.LessonGroup) { + if (data is _i67.LessonGroup) { return 'LessonGroup'; } - if (data is _i67.ScheduledLessonGroupMembership) { + if (data is _i68.ScheduledLessonGroupMembership) { return 'ScheduledLessonGroupMembership'; } - if (data is _i68.ScheduledLesson) { + if (data is _i69.ScheduledLesson) { return 'ScheduledLesson'; } - if (data is _i69.Subject) { + if (data is _i70.Subject) { return 'Subject'; } - if (data is _i70.TimetableSlot) { + if (data is _i71.TimetableSlot) { return 'TimetableSlot'; } - if (data is _i71.Weekday) { + if (data is _i72.Weekday) { return 'Weekday'; } - if (data is _i72.Timetable) { + if (data is _i73.Timetable) { return 'Timetable'; } - if (data is _i73.Role) { + if (data is _i74.Role) { return 'Role'; } - if (data is _i74.User) { + if (data is _i75.User) { return 'User'; } - if (data is _i75.UserFlags) { + if (data is _i76.UserFlags) { return 'UserFlags'; } - if (data is _i76.PupilWorkbook) { + if (data is _i77.PupilWorkbook) { return 'PupilWorkbook'; } - if (data is _i77.Workbook) { + if (data is _i78.Workbook) { return 'Workbook'; } - if (data is _i78.MyException) { + if (data is _i79.MyException) { return 'MyException'; } - if (data is _i79.HubDocument) { + if (data is _i80.HubDocument) { return 'HubDocument'; } - if (data is _i80.SchoolData) { + if (data is _i81.SchoolData) { return 'SchoolData'; } className = _i2.Protocol().getClassNameForObject(data); @@ -6333,7 +6347,7 @@ class Protocol extends _i1.SerializationManagerServer { if (className != null) { return 'serverpod_auth.$className'; } - if (data is List<_i81.PupilData>) { + if (data is List<_i82.PupilData>) { return 'List'; } return null; @@ -6345,8 +6359,8 @@ class Protocol extends _i1.SerializationManagerServer { if (dataClassName is! String) { return super.deserializeByClassName(data); } - if (dataClassName == 'SupportLevel') { - return deserialize<_i4.SupportLevel>(data['data']); + if (dataClassName == 'SupportGoal') { + return deserialize<_i4.SupportGoal>(data['data']); } if (dataClassName == 'MissedSchoolday') { return deserialize<_i5.MissedSchoolday>(data['data']); @@ -6372,209 +6386,212 @@ class Protocol extends _i1.SerializationManagerServer { if (dataClassName == 'Book') { return deserialize<_i12.Book>(data['data']); } + if (dataClassName == 'LibraryBookStatsDto') { + return deserialize<_i13.LibraryBookStatsDto>(data['data']); + } if (dataClassName == 'BookTag') { - return deserialize<_i13.BookTag>(data['data']); + return deserialize<_i14.BookTag>(data['data']); } if (dataClassName == 'BookTagging') { - return deserialize<_i14.BookTagging>(data['data']); + return deserialize<_i15.BookTagging>(data['data']); } if (dataClassName == 'LibraryBook') { - return deserialize<_i15.LibraryBook>(data['data']); + return deserialize<_i16.LibraryBook>(data['data']); } if (dataClassName == 'LibraryBookLocation') { - return deserialize<_i16.LibraryBookLocation>(data['data']); + return deserialize<_i17.LibraryBookLocation>(data['data']); } if (dataClassName == 'LibraryBookQuery') { - return deserialize<_i17.LibraryBookQuery>(data['data']); + return deserialize<_i18.LibraryBookQuery>(data['data']); } if (dataClassName == 'PupilBookLending') { - return deserialize<_i18.PupilBookLending>(data['data']); + return deserialize<_i19.PupilBookLending>(data['data']); } if (dataClassName == 'Competence') { - return deserialize<_i19.Competence>(data['data']); + return deserialize<_i20.Competence>(data['data']); } if (dataClassName == 'CompetenceCheck') { - return deserialize<_i20.CompetenceCheck>(data['data']); + return deserialize<_i21.CompetenceCheck>(data['data']); } if (dataClassName == 'CompetenceGoal') { - return deserialize<_i21.CompetenceGoal>(data['data']); + return deserialize<_i22.CompetenceGoal>(data['data']); } if (dataClassName == 'CompetenceReport') { - return deserialize<_i22.CompetenceReport>(data['data']); + return deserialize<_i23.CompetenceReport>(data['data']); } if (dataClassName == 'CompetenceReportCheck') { - return deserialize<_i23.CompetenceReportCheck>(data['data']); + return deserialize<_i24.CompetenceReportCheck>(data['data']); } if (dataClassName == 'CompetenceReportItem') { - return deserialize<_i24.CompetenceReportItem>(data['data']); + return deserialize<_i25.CompetenceReportItem>(data['data']); } if (dataClassName == 'LearningSupportPlan') { - return deserialize<_i25.LearningSupportPlan>(data['data']); + return deserialize<_i26.LearningSupportPlan>(data['data']); } if (dataClassName == 'SupportCategory') { - return deserialize<_i26.SupportCategory>(data['data']); + return deserialize<_i27.SupportCategory>(data['data']); } if (dataClassName == 'SupportCategoryStatus') { - return deserialize<_i27.SupportCategoryStatus>(data['data']); + return deserialize<_i28.SupportCategoryStatus>(data['data']); } - if (dataClassName == 'SupportGoal') { - return deserialize<_i28.SupportGoal>(data['data']); + if (dataClassName == 'ContactedType') { + return deserialize<_i29.ContactedType>(data['data']); } if (dataClassName == 'SupportGoalCheck') { - return deserialize<_i29.SupportGoalCheck>(data['data']); + return deserialize<_i30.SupportGoalCheck>(data['data']); } - if (dataClassName == 'ContactedType') { - return deserialize<_i30.ContactedType>(data['data']); + if (dataClassName == 'SupportLevel') { + return deserialize<_i31.SupportLevel>(data['data']); } if (dataClassName == 'SupportLevelLegacyDto') { - return deserialize<_i31.SupportLevelLegacyDto>(data['data']); + return deserialize<_i32.SupportLevelLegacyDto>(data['data']); } if (dataClassName == 'CompulsoryRoom') { - return deserialize<_i32.CompulsoryRoom>(data['data']); + return deserialize<_i33.CompulsoryRoom>(data['data']); } if (dataClassName == 'MatrixRoomType') { - return deserialize<_i33.MatrixRoomType>(data['data']); + return deserialize<_i34.MatrixRoomType>(data['data']); } if (dataClassName == 'AfterSchoolCare') { - return deserialize<_i34.AfterSchoolCare>(data['data']); + return deserialize<_i35.AfterSchoolCare>(data['data']); } if (dataClassName == 'AfterSchoolCarePickUpTimes') { - return deserialize<_i35.AfterSchoolCarePickUpTimes>(data['data']); + return deserialize<_i36.AfterSchoolCarePickUpTimes>(data['data']); } if (dataClassName == 'PickUpInfo') { - return deserialize<_i36.PickUpInfo>(data['data']); + return deserialize<_i37.PickUpInfo>(data['data']); } if (dataClassName == 'CommunicationSkills') { - return deserialize<_i37.CommunicationSkills>(data['data']); + return deserialize<_i38.CommunicationSkills>(data['data']); } if (dataClassName == 'PublicMediaAuth') { - return deserialize<_i38.PublicMediaAuth>(data['data']); + return deserialize<_i39.PublicMediaAuth>(data['data']); } if (dataClassName == 'TutorInfo') { - return deserialize<_i39.TutorInfo>(data['data']); + return deserialize<_i40.TutorInfo>(data['data']); } if (dataClassName == 'CreditTransaction') { - return deserialize<_i40.CreditTransaction>(data['data']); + return deserialize<_i41.CreditTransaction>(data['data']); } if (dataClassName == 'PupilDocumentType') { - return deserialize<_i41.PupilDocumentType>(data['data']); + return deserialize<_i42.PupilDocumentType>(data['data']); } if (dataClassName == 'SiblingsTutorInfo') { - return deserialize<_i42.SiblingsTutorInfo>(data['data']); + return deserialize<_i43.SiblingsTutorInfo>(data['data']); } if (dataClassName == 'Kindergarden') { - return deserialize<_i43.Kindergarden>(data['data']); + return deserialize<_i44.Kindergarden>(data['data']); } if (dataClassName == 'KindergardenInfo') { - return deserialize<_i44.KindergardenInfo>(data['data']); + return deserialize<_i45.KindergardenInfo>(data['data']); } if (dataClassName == 'PreSchoolMedical') { - return deserialize<_i45.PreSchoolMedical>(data['data']); + return deserialize<_i46.PreSchoolMedical>(data['data']); } if (dataClassName == 'PreSchoolMedicalStatus') { - return deserialize<_i46.PreSchoolMedicalStatus>(data['data']); + return deserialize<_i47.PreSchoolMedicalStatus>(data['data']); } if (dataClassName == 'PreSchoolTest') { - return deserialize<_i47.PreSchoolTest>(data['data']); + return deserialize<_i48.PreSchoolTest>(data['data']); } if (dataClassName == 'PupilData') { - return deserialize<_i48.PupilData>(data['data']); + return deserialize<_i49.PupilData>(data['data']); } if (dataClassName == 'PupilStatus') { - return deserialize<_i49.PupilStatus>(data['data']); + return deserialize<_i50.PupilStatus>(data['data']); } if (dataClassName == 'LastPupilIdentiesUpdate') { - return deserialize<_i50.LastPupilIdentiesUpdate>(data['data']); + return deserialize<_i51.LastPupilIdentiesUpdate>(data['data']); } if (dataClassName == 'PupilIdentity') { - return deserialize<_i51.PupilIdentity>(data['data']); + return deserialize<_i52.PupilIdentity>(data['data']); } if (dataClassName == 'PupilIdentityDto') { - return deserialize<_i52.PupilIdentityDto>(data['data']); + return deserialize<_i53.PupilIdentityDto>(data['data']); } if (dataClassName == 'SchoolGrade') { - return deserialize<_i53.SchoolGrade>(data['data']); + return deserialize<_i54.SchoolGrade>(data['data']); } if (dataClassName == 'MemberOperation') { - return deserialize<_i54.MemberOperation>(data['data']); + return deserialize<_i55.MemberOperation>(data['data']); } if (dataClassName == 'PupilListEntry') { - return deserialize<_i55.PupilListEntry>(data['data']); + return deserialize<_i56.PupilListEntry>(data['data']); } if (dataClassName == 'SchoolList') { - return deserialize<_i56.SchoolList>(data['data']); + return deserialize<_i57.SchoolList>(data['data']); } if (dataClassName == 'SchoolSemester') { - return deserialize<_i57.SchoolSemester>(data['data']); + return deserialize<_i58.SchoolSemester>(data['data']); } if (dataClassName == 'Schoolday') { - return deserialize<_i58.Schoolday>(data['data']); + return deserialize<_i59.Schoolday>(data['data']); } if (dataClassName == 'SchooldayEvent') { - return deserialize<_i59.SchooldayEvent>(data['data']); + return deserialize<_i60.SchooldayEvent>(data['data']); } if (dataClassName == 'SchooldayEventType') { - return deserialize<_i60.SchooldayEventType>(data['data']); + return deserialize<_i61.SchooldayEventType>(data['data']); } if (dataClassName == 'Classroom') { - return deserialize<_i61.Classroom>(data['data']); + return deserialize<_i62.Classroom>(data['data']); } if (dataClassName == 'LessonTeacher') { - return deserialize<_i62.LessonTeacher>(data['data']); + return deserialize<_i63.LessonTeacher>(data['data']); } if (dataClassName == 'ScheduledLessonTeacher') { - return deserialize<_i63.ScheduledLessonTeacher>(data['data']); + return deserialize<_i64.ScheduledLessonTeacher>(data['data']); } if (dataClassName == 'Lesson') { - return deserialize<_i64.Lesson>(data['data']); + return deserialize<_i65.Lesson>(data['data']); } if (dataClassName == 'LessonAttendance') { - return deserialize<_i65.LessonAttendance>(data['data']); + return deserialize<_i66.LessonAttendance>(data['data']); } if (dataClassName == 'LessonGroup') { - return deserialize<_i66.LessonGroup>(data['data']); + return deserialize<_i67.LessonGroup>(data['data']); } if (dataClassName == 'ScheduledLessonGroupMembership') { - return deserialize<_i67.ScheduledLessonGroupMembership>(data['data']); + return deserialize<_i68.ScheduledLessonGroupMembership>(data['data']); } if (dataClassName == 'ScheduledLesson') { - return deserialize<_i68.ScheduledLesson>(data['data']); + return deserialize<_i69.ScheduledLesson>(data['data']); } if (dataClassName == 'Subject') { - return deserialize<_i69.Subject>(data['data']); + return deserialize<_i70.Subject>(data['data']); } if (dataClassName == 'TimetableSlot') { - return deserialize<_i70.TimetableSlot>(data['data']); + return deserialize<_i71.TimetableSlot>(data['data']); } if (dataClassName == 'Weekday') { - return deserialize<_i71.Weekday>(data['data']); + return deserialize<_i72.Weekday>(data['data']); } if (dataClassName == 'Timetable') { - return deserialize<_i72.Timetable>(data['data']); + return deserialize<_i73.Timetable>(data['data']); } if (dataClassName == 'Role') { - return deserialize<_i73.Role>(data['data']); + return deserialize<_i74.Role>(data['data']); } if (dataClassName == 'User') { - return deserialize<_i74.User>(data['data']); + return deserialize<_i75.User>(data['data']); } if (dataClassName == 'UserFlags') { - return deserialize<_i75.UserFlags>(data['data']); + return deserialize<_i76.UserFlags>(data['data']); } if (dataClassName == 'PupilWorkbook') { - return deserialize<_i76.PupilWorkbook>(data['data']); + return deserialize<_i77.PupilWorkbook>(data['data']); } if (dataClassName == 'Workbook') { - return deserialize<_i77.Workbook>(data['data']); + return deserialize<_i78.Workbook>(data['data']); } if (dataClassName == 'MyException') { - return deserialize<_i78.MyException>(data['data']); + return deserialize<_i79.MyException>(data['data']); } if (dataClassName == 'HubDocument') { - return deserialize<_i79.HubDocument>(data['data']); + return deserialize<_i80.HubDocument>(data['data']); } if (dataClassName == 'SchoolData') { - return deserialize<_i80.SchoolData>(data['data']); + return deserialize<_i81.SchoolData>(data['data']); } if (dataClassName.startsWith('serverpod.')) { data['className'] = dataClassName.substring(10); @@ -6585,7 +6602,7 @@ class Protocol extends _i1.SerializationManagerServer { return _i3.Protocol().deserializeByClassName(data); } if (dataClassName == 'List') { - return deserialize>(data['data']); + return deserialize>(data['data']); } return super.deserializeByClassName(data); } @@ -6615,96 +6632,96 @@ class Protocol extends _i1.SerializationManagerServer { return _i11.PupilAuthorization.t; case _i12.Book: return _i12.Book.t; - case _i13.BookTag: - return _i13.BookTag.t; - case _i14.BookTagging: - return _i14.BookTagging.t; - case _i15.LibraryBook: - return _i15.LibraryBook.t; - case _i16.LibraryBookLocation: - return _i16.LibraryBookLocation.t; - case _i18.PupilBookLending: - return _i18.PupilBookLending.t; - case _i19.Competence: - return _i19.Competence.t; - case _i20.CompetenceCheck: - return _i20.CompetenceCheck.t; - case _i21.CompetenceGoal: - return _i21.CompetenceGoal.t; - case _i22.CompetenceReport: - return _i22.CompetenceReport.t; - case _i23.CompetenceReportCheck: - return _i23.CompetenceReportCheck.t; - case _i24.CompetenceReportItem: - return _i24.CompetenceReportItem.t; - case _i25.LearningSupportPlan: - return _i25.LearningSupportPlan.t; - case _i26.SupportCategory: - return _i26.SupportCategory.t; - case _i27.SupportCategoryStatus: - return _i27.SupportCategoryStatus.t; - case _i28.SupportGoal: - return _i28.SupportGoal.t; - case _i29.SupportGoalCheck: - return _i29.SupportGoalCheck.t; - case _i4.SupportLevel: - return _i4.SupportLevel.t; - case _i32.CompulsoryRoom: - return _i32.CompulsoryRoom.t; - case _i40.CreditTransaction: - return _i40.CreditTransaction.t; - case _i43.Kindergarden: - return _i43.Kindergarden.t; - case _i45.PreSchoolMedical: - return _i45.PreSchoolMedical.t; - case _i47.PreSchoolTest: - return _i47.PreSchoolTest.t; - case _i48.PupilData: - return _i48.PupilData.t; - case _i50.LastPupilIdentiesUpdate: - return _i50.LastPupilIdentiesUpdate.t; - case _i80.SchoolData: - return _i80.SchoolData.t; - case _i55.PupilListEntry: - return _i55.PupilListEntry.t; - case _i56.SchoolList: - return _i56.SchoolList.t; - case _i57.SchoolSemester: - return _i57.SchoolSemester.t; - case _i58.Schoolday: - return _i58.Schoolday.t; - case _i59.SchooldayEvent: - return _i59.SchooldayEvent.t; - case _i61.Classroom: - return _i61.Classroom.t; - case _i62.LessonTeacher: - return _i62.LessonTeacher.t; - case _i63.ScheduledLessonTeacher: - return _i63.ScheduledLessonTeacher.t; - case _i64.Lesson: - return _i64.Lesson.t; - case _i65.LessonAttendance: - return _i65.LessonAttendance.t; - case _i66.LessonGroup: - return _i66.LessonGroup.t; - case _i67.ScheduledLessonGroupMembership: - return _i67.ScheduledLessonGroupMembership.t; - case _i68.ScheduledLesson: - return _i68.ScheduledLesson.t; - case _i69.Subject: - return _i69.Subject.t; - case _i70.TimetableSlot: - return _i70.TimetableSlot.t; - case _i72.Timetable: - return _i72.Timetable.t; - case _i74.User: - return _i74.User.t; - case _i76.PupilWorkbook: - return _i76.PupilWorkbook.t; - case _i77.Workbook: - return _i77.Workbook.t; - case _i79.HubDocument: - return _i79.HubDocument.t; + case _i14.BookTag: + return _i14.BookTag.t; + case _i15.BookTagging: + return _i15.BookTagging.t; + case _i16.LibraryBook: + return _i16.LibraryBook.t; + case _i17.LibraryBookLocation: + return _i17.LibraryBookLocation.t; + case _i19.PupilBookLending: + return _i19.PupilBookLending.t; + case _i20.Competence: + return _i20.Competence.t; + case _i21.CompetenceCheck: + return _i21.CompetenceCheck.t; + case _i22.CompetenceGoal: + return _i22.CompetenceGoal.t; + case _i23.CompetenceReport: + return _i23.CompetenceReport.t; + case _i24.CompetenceReportCheck: + return _i24.CompetenceReportCheck.t; + case _i25.CompetenceReportItem: + return _i25.CompetenceReportItem.t; + case _i26.LearningSupportPlan: + return _i26.LearningSupportPlan.t; + case _i27.SupportCategory: + return _i27.SupportCategory.t; + case _i28.SupportCategoryStatus: + return _i28.SupportCategoryStatus.t; + case _i4.SupportGoal: + return _i4.SupportGoal.t; + case _i30.SupportGoalCheck: + return _i30.SupportGoalCheck.t; + case _i31.SupportLevel: + return _i31.SupportLevel.t; + case _i33.CompulsoryRoom: + return _i33.CompulsoryRoom.t; + case _i41.CreditTransaction: + return _i41.CreditTransaction.t; + case _i44.Kindergarden: + return _i44.Kindergarden.t; + case _i46.PreSchoolMedical: + return _i46.PreSchoolMedical.t; + case _i48.PreSchoolTest: + return _i48.PreSchoolTest.t; + case _i49.PupilData: + return _i49.PupilData.t; + case _i51.LastPupilIdentiesUpdate: + return _i51.LastPupilIdentiesUpdate.t; + case _i81.SchoolData: + return _i81.SchoolData.t; + case _i56.PupilListEntry: + return _i56.PupilListEntry.t; + case _i57.SchoolList: + return _i57.SchoolList.t; + case _i58.SchoolSemester: + return _i58.SchoolSemester.t; + case _i59.Schoolday: + return _i59.Schoolday.t; + case _i60.SchooldayEvent: + return _i60.SchooldayEvent.t; + case _i62.Classroom: + return _i62.Classroom.t; + case _i63.LessonTeacher: + return _i63.LessonTeacher.t; + case _i64.ScheduledLessonTeacher: + return _i64.ScheduledLessonTeacher.t; + case _i65.Lesson: + return _i65.Lesson.t; + case _i66.LessonAttendance: + return _i66.LessonAttendance.t; + case _i67.LessonGroup: + return _i67.LessonGroup.t; + case _i68.ScheduledLessonGroupMembership: + return _i68.ScheduledLessonGroupMembership.t; + case _i69.ScheduledLesson: + return _i69.ScheduledLesson.t; + case _i70.Subject: + return _i70.Subject.t; + case _i71.TimetableSlot: + return _i71.TimetableSlot.t; + case _i73.Timetable: + return _i73.Timetable.t; + case _i75.User: + return _i75.User.t; + case _i77.PupilWorkbook: + return _i77.PupilWorkbook.t; + case _i78.Workbook: + return _i78.Workbook.t; + case _i80.HubDocument: + return _i80.HubDocument.t; } return null; } @@ -6728,7 +6745,7 @@ Map? mapRecordToJson(Record? record) { } if (record is ({ _i3.AuthenticationResponse response, - _i85.UserDevice? userDevice + _i86.UserDevice? userDevice })) { return { "n": { @@ -6737,7 +6754,7 @@ Map? mapRecordToJson(Record? record) { }, }; } - if (record is ({_i87.MemberOperation operation, List pupilIds})) { + if (record is ({_i88.MemberOperation operation, List pupilIds})) { return { "n": { "operation": record.operation, diff --git a/school_data_hub_server/lib/src/generated/protocol.yaml b/school_data_hub_server/lib/src/generated/protocol.yaml index 6c763005..7e863ffc 100644 --- a/school_data_hub_server/lib/src/generated/protocol.yaml +++ b/school_data_hub_server/lib/src/generated/protocol.yaml @@ -37,6 +37,7 @@ bookTags: books: - postBook: - fetchBooks: + - getBookStats: - fetchBookByIsbn: - updateBookTags: - deleteBook: diff --git a/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart b/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart index f2a68516..b9410deb 100644 --- a/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart +++ b/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart @@ -44,77 +44,79 @@ import 'package:school_data_hub_server/src/generated/_features/books/models/book as _i18; import 'package:school_data_hub_server/src/generated/_features/books/models/book.dart' as _i19; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book_stats_dto.dart' as _i20; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' as _i21; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_query.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book.dart' as _i22; -import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_query.dart' as _i23; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' as _i24; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category_status.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' as _i25; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category_status.dart' as _i26; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i27; -import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' as _i28; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' +import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' as _i29; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i30; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' as _i31; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/communication_skills.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i32; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/tutor_info.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/communication_skills.dart' as _i33; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/tutor_info.dart' as _i34; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/public_media_auth.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' as _i35; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/public_media_auth.dart' as _i36; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level.dart' as _i37; -import 'package:school_data_hub_server/src/generated/_features/school_data/models/school_data.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' as _i38; -import 'package:school_data_hub_server/src/generated/_features/school_lists/models/school_list.dart' +import 'package:school_data_hub_server/src/generated/_features/school_data/models/school_data.dart' as _i39; -import 'package:school_data_hub_server/src/generated/_features/school_lists/models/pupil_entry.dart' +import 'package:school_data_hub_server/src/generated/_features/school_lists/models/school_list.dart' as _i40; -import 'package:school_data_hub_server/src/generated/_features/schoolday/models/school_semester.dart' +import 'package:school_data_hub_server/src/generated/_features/school_lists/models/pupil_entry.dart' as _i41; -import 'package:school_data_hub_server/src/generated/_features/schoolday/models/schoolday.dart' +import 'package:school_data_hub_server/src/generated/_features/schoolday/models/school_semester.dart' as _i42; -import 'package:school_data_hub_server/src/generated/_features/schoolday_events/models/schoolday_event.dart' +import 'package:school_data_hub_server/src/generated/_features/schoolday/models/schoolday.dart' as _i43; -import 'package:school_data_hub_server/src/generated/_features/schoolday_events/models/schoolday_event_type.dart' +import 'package:school_data_hub_server/src/generated/_features/schoolday_events/models/schoolday_event.dart' as _i44; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/classroom.dart' +import 'package:school_data_hub_server/src/generated/_features/schoolday_events/models/schoolday_event_type.dart' as _i45; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/lesson/lesson_group.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/classroom.dart' as _i46; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/lesson/lesson_group.dart' as _i47; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/scheduled_lesson.dart' as _i48; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/subject.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/lesson_group_membership.dart' as _i49; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/timetable.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/subject.dart' as _i50; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/timetable_slot.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/timetable.dart' as _i51; -import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/weekday_enum.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/timetable_slot.dart' as _i52; -import 'package:school_data_hub_server/src/generated/_features/workbooks/models/pupil_workbook.dart' +import 'package:school_data_hub_server/src/generated/_features/timetable/models/scheduled_lesson/weekday_enum.dart' as _i53; -import 'package:school_data_hub_server/src/generated/_features/workbooks/models/workbook.dart' +import 'package:school_data_hub_server/src/generated/_features/workbooks/models/pupil_workbook.dart' as _i54; -import 'dart:typed_data' as _i55; +import 'package:school_data_hub_server/src/generated/_features/workbooks/models/workbook.dart' + as _i55; +import 'dart:typed_data' as _i56; import 'package:school_data_hub_server/src/generated/protocol.dart'; import 'package:school_data_hub_server/src/generated/endpoints.dart'; export 'package:serverpod_test/serverpod_test_public_exports.dart'; @@ -1498,6 +1500,33 @@ class _BooksEndpoint { }); } + _i3.Future<_i20.LibraryBookStatsDto> getBookStats( + _i1.TestSessionBuilder sessionBuilder) async { + return _i1.callAwaitableFunctionAndHandleExceptions(() async { + var _localUniqueSession = + (sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild( + endpoint: 'books', + method: 'getBookStats', + ); + try { + var _localCallContext = await _endpointDispatch.getMethodCallContext( + createSessionCallback: (_) => _localUniqueSession, + endpointPath: 'books', + methodName: 'getBookStats', + parameters: _i1.testObjectToJson({}), + serializationManager: _serializationManager, + ); + var _localReturnValue = await (_localCallContext.method.call( + _localUniqueSession, + _localCallContext.arguments, + ) as _i3.Future<_i20.LibraryBookStatsDto>); + return _localReturnValue; + } finally { + await _localUniqueSession.close(); + } + }); + } + _i3.Future<_i19.Book?> fetchBookByIsbn( _i1.TestSessionBuilder sessionBuilder, int isbn, @@ -1600,9 +1629,9 @@ class _LibraryBookLocationsEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i20.LibraryBookLocation> postLibraryBookLocation( + _i3.Future<_i21.LibraryBookLocation> postLibraryBookLocation( _i1.TestSessionBuilder sessionBuilder, - _i20.LibraryBookLocation libraryBookLocation, + _i21.LibraryBookLocation libraryBookLocation, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1622,7 +1651,7 @@ class _LibraryBookLocationsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i20.LibraryBookLocation>); + ) as _i3.Future<_i21.LibraryBookLocation>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1630,7 +1659,7 @@ class _LibraryBookLocationsEndpoint { }); } - _i3.Future> fetchLibraryBookLocations( + _i3.Future> fetchLibraryBookLocations( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1649,7 +1678,7 @@ class _LibraryBookLocationsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1657,9 +1686,9 @@ class _LibraryBookLocationsEndpoint { }); } - _i3.Future<_i20.LibraryBookLocation> updateLibraryBookLocation( + _i3.Future<_i21.LibraryBookLocation> updateLibraryBookLocation( _i1.TestSessionBuilder sessionBuilder, - _i20.LibraryBookLocation libraryBookLocation, + _i21.LibraryBookLocation libraryBookLocation, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1679,7 +1708,7 @@ class _LibraryBookLocationsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i20.LibraryBookLocation>); + ) as _i3.Future<_i21.LibraryBookLocation>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1689,7 +1718,7 @@ class _LibraryBookLocationsEndpoint { _i3.Future deleteLibraryBookLocation( _i1.TestSessionBuilder sessionBuilder, - _i20.LibraryBookLocation location, + _i21.LibraryBookLocation location, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1727,11 +1756,11 @@ class _LibraryBooksEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i21.LibraryBook> postLibraryBook( + _i3.Future<_i22.LibraryBook> postLibraryBook( _i1.TestSessionBuilder sessionBuilder, int isbn, String libraryId, - _i20.LibraryBookLocation location, + _i21.LibraryBookLocation location, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1754,7 +1783,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i21.LibraryBook>); + ) as _i3.Future<_i22.LibraryBook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1762,7 +1791,7 @@ class _LibraryBooksEndpoint { }); } - _i3.Future> fetchLibraryBooks( + _i3.Future> fetchLibraryBooks( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1781,7 +1810,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1789,7 +1818,7 @@ class _LibraryBooksEndpoint { }); } - _i3.Future<_i21.LibraryBook?> fetchLibraryBookByIsbn( + _i3.Future<_i22.LibraryBook?> fetchLibraryBookByIsbn( _i1.TestSessionBuilder sessionBuilder, int isbn, ) async { @@ -1810,7 +1839,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i21.LibraryBook?>); + ) as _i3.Future<_i22.LibraryBook?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1818,7 +1847,7 @@ class _LibraryBooksEndpoint { }); } - _i3.Future<_i21.LibraryBook?> fetchLibraryBookByLibraryId( + _i3.Future<_i22.LibraryBook?> fetchLibraryBookByLibraryId( _i1.TestSessionBuilder sessionBuilder, String libraryId, ) async { @@ -1839,7 +1868,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i21.LibraryBook?>); + ) as _i3.Future<_i22.LibraryBook?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1847,9 +1876,9 @@ class _LibraryBooksEndpoint { }); } - _i3.Future> fetchLibraryBooksMatchingQuery( + _i3.Future> fetchLibraryBooksMatchingQuery( _i1.TestSessionBuilder sessionBuilder, - _i22.LibraryBookQuery libraryBookQuery, + _i23.LibraryBookQuery libraryBookQuery, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1869,7 +1898,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1877,12 +1906,12 @@ class _LibraryBooksEndpoint { }); } - _i3.Future<_i21.LibraryBook> updateLibraryBookAndRelatedBook( + _i3.Future<_i22.LibraryBook> updateLibraryBookAndRelatedBook( _i1.TestSessionBuilder sessionBuilder, int isbn, String libraryId, bool? available, - _i20.LibraryBookLocation? location, + _i21.LibraryBookLocation? location, String? title, String? author, String? description, @@ -1916,7 +1945,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i21.LibraryBook>); + ) as _i3.Future<_i22.LibraryBook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1999,7 +2028,7 @@ class _PupilBookLendingEndpoint { }); } - _i3.Future> fetchPupilBookLendings( + _i3.Future> fetchPupilBookLendings( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2018,7 +2047,7 @@ class _PupilBookLendingEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2026,7 +2055,7 @@ class _PupilBookLendingEndpoint { }); } - _i3.Future<_i23.PupilBookLending?> fetchPupilBookLendingByLendingId( + _i3.Future<_i24.PupilBookLending?> fetchPupilBookLendingByLendingId( _i1.TestSessionBuilder sessionBuilder, String lendingId, ) async { @@ -2047,7 +2076,7 @@ class _PupilBookLendingEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i23.PupilBookLending?>); + ) as _i3.Future<_i24.PupilBookLending?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2057,7 +2086,7 @@ class _PupilBookLendingEndpoint { _i3.Future<_i6.PupilData> updatePupilBookLending( _i1.TestSessionBuilder sessionBuilder, - _i23.PupilBookLending pupilBookLending, + _i24.PupilBookLending pupilBookLending, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2446,7 +2475,7 @@ class _LearningSupportPlanEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future> fetchLearningSupportPlans( + _i3.Future> fetchLearningSupportPlans( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2465,7 +2494,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2473,9 +2502,9 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future<_i24.LearningSupportPlan> createLearningSupportPlan( + _i3.Future<_i25.LearningSupportPlan> createLearningSupportPlan( _i1.TestSessionBuilder sessionBuilder, - _i24.LearningSupportPlan plan, + _i25.LearningSupportPlan plan, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2494,7 +2523,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i24.LearningSupportPlan>); + ) as _i3.Future<_i25.LearningSupportPlan>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2504,7 +2533,7 @@ class _LearningSupportPlanEndpoint { _i3.Future updateLearningSupportPlan( _i1.TestSessionBuilder sessionBuilder, - _i24.LearningSupportPlan plan, + _i25.LearningSupportPlan plan, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2533,7 +2562,7 @@ class _LearningSupportPlanEndpoint { _i3.Future deleteLearningSupportPlan( _i1.TestSessionBuilder sessionBuilder, - _i24.LearningSupportPlan plan, + _i25.LearningSupportPlan plan, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2601,7 +2630,7 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future> fetchSupportCategoryStatus( + _i3.Future> fetchSupportCategoryStatus( _i1.TestSessionBuilder sessionBuilder, int pupilId, ) async { @@ -2622,7 +2651,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2630,7 +2659,7 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future> + _i3.Future> fetchSupportCategoryStatusFromPupil( _i1.TestSessionBuilder sessionBuilder, int pupilId, @@ -2652,7 +2681,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2660,7 +2689,7 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future<_i25.SupportCategoryStatus> updateCategoryStatus( + _i3.Future<_i26.SupportCategoryStatus> updateCategoryStatus( _i1.TestSessionBuilder sessionBuilder, int pupilId, int supportCategoryId, @@ -2693,7 +2722,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i25.SupportCategoryStatus>); + ) as _i3.Future<_i26.SupportCategoryStatus>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2784,10 +2813,10 @@ class _PreSchoolMedicalEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i26.PreSchoolMedical> createPreSchoolMedical( + _i3.Future<_i27.PreSchoolMedical> createPreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i27.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i28.PreSchoolMedicalStatus? preschoolMedicalStatus, String createdBy, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -2811,7 +2840,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i26.PreSchoolMedical>); + ) as _i3.Future<_i27.PreSchoolMedical>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2819,10 +2848,10 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i26.PreSchoolMedical> updatePreSchoolMedical( + _i3.Future<_i27.PreSchoolMedical> updatePreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int preSchoolMedicalId, - _i27.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i28.PreSchoolMedicalStatus? preschoolMedicalStatus, String updatedBy, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -2846,7 +2875,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i26.PreSchoolMedical>); + ) as _i3.Future<_i27.PreSchoolMedical>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2854,7 +2883,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i26.PreSchoolMedical?> getPreSchoolMedical( + _i3.Future<_i27.PreSchoolMedical?> getPreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int preSchoolMedicalId, ) async { @@ -2876,7 +2905,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i26.PreSchoolMedical?>); + ) as _i3.Future<_i27.PreSchoolMedical?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2884,7 +2913,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i26.PreSchoolMedical?> getPreSchoolMedicalByPupilId( + _i3.Future<_i27.PreSchoolMedical?> getPreSchoolMedicalByPupilId( _i1.TestSessionBuilder sessionBuilder, int pupilId, ) async { @@ -2905,7 +2934,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i26.PreSchoolMedical?>); + ) as _i3.Future<_i27.PreSchoolMedical?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2943,7 +2972,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i26.PreSchoolMedical> addFileToPreSchoolMedical( + _i3.Future<_i27.PreSchoolMedical> addFileToPreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int preSchoolMedicalId, String filePath, @@ -2970,7 +2999,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i26.PreSchoolMedical>); + ) as _i3.Future<_i27.PreSchoolMedical>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3011,7 +3040,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future> getAllPreSchoolMedicalRecords( + _i3.Future> getAllPreSchoolMedicalRecords( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3030,7 +3059,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3038,9 +3067,9 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future> getPreSchoolMedicalByStatus( + _i3.Future> getPreSchoolMedicalByStatus( _i1.TestSessionBuilder sessionBuilder, - _i27.PreSchoolMedicalStatus status, + _i28.PreSchoolMedicalStatus status, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3059,7 +3088,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3232,7 +3261,7 @@ class _MatrixEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future?> getCompulsoryRooms( + _i3.Future?> getCompulsoryRooms( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3251,7 +3280,7 @@ class _MatrixEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future?>); + ) as _i3.Future?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3259,9 +3288,9 @@ class _MatrixEndpoint { }); } - _i3.Future> setCompulsoryRooms( + _i3.Future> setCompulsoryRooms( _i1.TestSessionBuilder sessionBuilder, - List<_i28.CompulsoryRoom> compulsoryRooms, + List<_i29.CompulsoryRoom> compulsoryRooms, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3281,7 +3310,7 @@ class _MatrixEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3418,7 +3447,7 @@ class _PupilEndpoint { _i3.Future<_i6.PupilData> deletePupilDocument( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i29.PupilDocumentType documentType, + _i30.PupilDocumentType documentType, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3516,7 +3545,7 @@ class _PupilEndpoint { _i3.Future bulkAddSupportLevels( _i1.TestSessionBuilder sessionBuilder, - List<_i30.SupportLevelLegacyDto> supportLevelData, + List<_i31.SupportLevelLegacyDto> supportLevelData, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3555,12 +3584,12 @@ class _PupilIdentityEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Stream<_i31.PupilIdentityDto> streamEncryptedPupilIds( + _i3.Stream<_i32.PupilIdentityDto> streamEncryptedPupilIds( _i1.TestSessionBuilder sessionBuilder, String channelName, ) { var _localTestStreamManager = - _i1.TestStreamManager<_i31.PupilIdentityDto>(); + _i1.TestStreamManager<_i32.PupilIdentityDto>(); _i1.callStreamFunctionAndHandleExceptions( () async { var _localUniqueSession = @@ -3591,7 +3620,7 @@ class _PupilIdentityEndpoint { _i3.Future sendPupilIdentityMessage( _i1.TestSessionBuilder sessionBuilder, String pupilIdentityChannel, - _i31.PupilIdentityDto pupilIdentityMessage, + _i32.PupilIdentityDto pupilIdentityMessage, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3747,7 +3776,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateCommunicationSkills( _i1.TestSessionBuilder sessionBuilder, { required int pupilId, - required _i32.CommunicationSkills? communicationSkills, + required _i33.CommunicationSkills? communicationSkills, }) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3780,7 +3809,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateTutorInfo( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i33.TutorInfo? tutorInfo, + _i34.TutorInfo? tutorInfo, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3812,7 +3841,7 @@ class _PupilUpdateEndpoint { _i3.Future> updateSiblingsTutorInfo( _i1.TestSessionBuilder sessionBuilder, - _i34.SiblingsTutorInfo siblingsTutorInfo, + _i35.SiblingsTutorInfo siblingsTutorInfo, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3845,7 +3874,7 @@ class _PupilUpdateEndpoint { int pupilId, String filePath, String createdBy, - _i29.PupilDocumentType documentType, + _i30.PupilDocumentType documentType, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3952,7 +3981,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updatePreSchoolMedicalStatus( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i27.PreSchoolMedicalStatus preSchoolMedicalStatus, + _i28.PreSchoolMedicalStatus preSchoolMedicalStatus, String updatedBy, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -3987,7 +4016,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updatePublicMediaAuth( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i35.PublicMediaAuth publicMediaAuth, + _i36.PublicMediaAuth publicMediaAuth, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4019,7 +4048,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateSupportLevel( _i1.TestSessionBuilder sessionBuilder, - _i36.SupportLevel supportLevel, + _i37.SupportLevel supportLevel, int pupilId, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -4087,7 +4116,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateAfterSchoolCare( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i37.AfterSchoolCare afterSchoolCare, + _i38.AfterSchoolCare afterSchoolCare, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4128,9 +4157,9 @@ class _SchoolDataEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i38.SchoolData> postSchoolData( + _i3.Future<_i39.SchoolData> postSchoolData( _i1.TestSessionBuilder sessionBuilder, - _i38.SchoolData schoolData, + _i39.SchoolData schoolData, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4149,7 +4178,7 @@ class _SchoolDataEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i38.SchoolData>); + ) as _i3.Future<_i39.SchoolData>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4157,7 +4186,7 @@ class _SchoolDataEndpoint { }); } - _i3.Future<_i38.SchoolData?> getSchoolData( + _i3.Future<_i39.SchoolData?> getSchoolData( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4176,7 +4205,7 @@ class _SchoolDataEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i38.SchoolData?>); + ) as _i3.Future<_i39.SchoolData?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4195,7 +4224,7 @@ class _SchoolListEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future> fetchSchoolLists( + _i3.Future> fetchSchoolLists( _i1.TestSessionBuilder sessionBuilder, String userName, ) async { @@ -4216,7 +4245,7 @@ class _SchoolListEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4224,7 +4253,7 @@ class _SchoolListEndpoint { }); } - _i3.Future<_i39.SchoolList> postSchoolList( + _i3.Future<_i40.SchoolList> postSchoolList( _i1.TestSessionBuilder sessionBuilder, String name, String description, @@ -4255,7 +4284,7 @@ class _SchoolListEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i39.SchoolList>); + ) as _i3.Future<_i40.SchoolList>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4263,7 +4292,7 @@ class _SchoolListEndpoint { }); } - _i3.Future<_i39.SchoolList> updateSchoolList( + _i3.Future<_i40.SchoolList> updateSchoolList( _i1.TestSessionBuilder sessionBuilder, int listId, String? name, @@ -4296,7 +4325,7 @@ class _SchoolListEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i39.SchoolList>); + ) as _i3.Future<_i40.SchoolList>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4333,9 +4362,9 @@ class _SchoolListEndpoint { }); } - _i3.Future<_i40.PupilListEntry> updatePupilListEntry( + _i3.Future<_i41.PupilListEntry> updatePupilListEntry( _i1.TestSessionBuilder sessionBuilder, - _i40.PupilListEntry entry, + _i41.PupilListEntry entry, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4354,7 +4383,7 @@ class _SchoolListEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i40.PupilListEntry>); + ) as _i3.Future<_i41.PupilListEntry>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4373,7 +4402,7 @@ class _SchooldayAdminEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i41.SchoolSemester> createSchoolSemester( + _i3.Future<_i42.SchoolSemester> createSchoolSemester( _i1.TestSessionBuilder sessionBuilder, String schoolYearName, DateTime startDate, @@ -4410,7 +4439,7 @@ class _SchooldayAdminEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i41.SchoolSemester>); + ) as _i3.Future<_i42.SchoolSemester>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4418,7 +4447,7 @@ class _SchooldayAdminEndpoint { }); } - _i3.Future> getAllSchoolSemesters( + _i3.Future> getAllSchoolSemesters( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4437,7 +4466,7 @@ class _SchooldayAdminEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4445,7 +4474,7 @@ class _SchooldayAdminEndpoint { }); } - _i3.Future<_i41.SchoolSemester?> getCurrentSchoolSemester( + _i3.Future<_i42.SchoolSemester?> getCurrentSchoolSemester( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4464,7 +4493,7 @@ class _SchooldayAdminEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i41.SchoolSemester?>); + ) as _i3.Future<_i42.SchoolSemester?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4474,7 +4503,7 @@ class _SchooldayAdminEndpoint { _i3.Future updateSchoolSemester( _i1.TestSessionBuilder sessionBuilder, - _i41.SchoolSemester schoolSemester, + _i42.SchoolSemester schoolSemester, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4503,7 +4532,7 @@ class _SchooldayAdminEndpoint { _i3.Future deleteSchoolSemester( _i1.TestSessionBuilder sessionBuilder, - _i41.SchoolSemester semester, + _i42.SchoolSemester semester, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4530,7 +4559,7 @@ class _SchooldayAdminEndpoint { }); } - _i3.Future<_i42.Schoolday?> createSchoolday( + _i3.Future<_i43.Schoolday?> createSchoolday( _i1.TestSessionBuilder sessionBuilder, DateTime date, ) async { @@ -4551,7 +4580,7 @@ class _SchooldayAdminEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i42.Schoolday?>); + ) as _i3.Future<_i43.Schoolday?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4559,7 +4588,7 @@ class _SchooldayAdminEndpoint { }); } - _i3.Future> createSchooldays( + _i3.Future> createSchooldays( _i1.TestSessionBuilder sessionBuilder, List dates, ) async { @@ -4580,7 +4609,7 @@ class _SchooldayAdminEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4619,7 +4648,7 @@ class _SchooldayAdminEndpoint { _i3.Future updateSchoolday( _i1.TestSessionBuilder sessionBuilder, - _i42.Schoolday schoolday, + _i43.Schoolday schoolday, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4657,7 +4686,7 @@ class _SchooldayEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future> getSchoolSemesters( + _i3.Future> getSchoolSemesters( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4676,7 +4705,7 @@ class _SchooldayEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4684,7 +4713,7 @@ class _SchooldayEndpoint { }); } - _i3.Future> getSchooldays( + _i3.Future> getSchooldays( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4703,7 +4732,7 @@ class _SchooldayEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4722,7 +4751,7 @@ class _SchooldayEventEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future> fetchSchooldayEvents( + _i3.Future> fetchSchooldayEvents( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4741,7 +4770,7 @@ class _SchooldayEventEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4749,13 +4778,13 @@ class _SchooldayEventEndpoint { }); } - _i3.Future<_i43.SchooldayEvent> createSchooldayEvent( + _i3.Future<_i44.SchooldayEvent> createSchooldayEvent( _i1.TestSessionBuilder sessionBuilder, { required int pupilId, required String pupilNameAndGroup, required String dateTimeAsString, required int schooldayId, - required _i44.SchooldayEventType type, + required _i45.SchooldayEventType type, required String reason, required String createdBy, required String tutor, @@ -4786,7 +4815,7 @@ class _SchooldayEventEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i43.SchooldayEvent>); + ) as _i3.Future<_i44.SchooldayEvent>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4794,9 +4823,9 @@ class _SchooldayEventEndpoint { }); } - _i3.Future<_i43.SchooldayEvent> updateSchooldayEvent( + _i3.Future<_i44.SchooldayEvent> updateSchooldayEvent( _i1.TestSessionBuilder sessionBuilder, - _i43.SchooldayEvent schooldayEvent, + _i44.SchooldayEvent schooldayEvent, bool changedProcessedStatus, String pupilNameAndGroup, String tutor, @@ -4827,7 +4856,7 @@ class _SchooldayEventEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i43.SchooldayEvent>); + ) as _i3.Future<_i44.SchooldayEvent>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4865,7 +4894,7 @@ class _SchooldayEventEndpoint { }); } - _i3.Future<_i43.SchooldayEvent> updateSchooldayEventFile( + _i3.Future<_i44.SchooldayEvent> updateSchooldayEventFile( _i1.TestSessionBuilder sessionBuilder, int schooldayEventId, String filePath, @@ -4894,7 +4923,7 @@ class _SchooldayEventEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i43.SchooldayEvent>); + ) as _i3.Future<_i44.SchooldayEvent>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4902,7 +4931,7 @@ class _SchooldayEventEndpoint { }); } - _i3.Future<_i43.SchooldayEvent> deleteSchooldayEventFile( + _i3.Future<_i44.SchooldayEvent> deleteSchooldayEventFile( _i1.TestSessionBuilder sessionBuilder, int schooldayEventId, bool isProcessed, @@ -4927,7 +4956,7 @@ class _SchooldayEventEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i43.SchooldayEvent>); + ) as _i3.Future<_i44.SchooldayEvent>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4946,9 +4975,9 @@ class _ClassroomEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i45.Classroom> createClassroom( + _i3.Future<_i46.Classroom> createClassroom( _i1.TestSessionBuilder sessionBuilder, - _i45.Classroom classroom, + _i46.Classroom classroom, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4967,7 +4996,7 @@ class _ClassroomEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i45.Classroom>); + ) as _i3.Future<_i46.Classroom>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4975,7 +5004,7 @@ class _ClassroomEndpoint { }); } - _i3.Future> fetchClassrooms( + _i3.Future> fetchClassrooms( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4994,7 +5023,7 @@ class _ClassroomEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5002,7 +5031,7 @@ class _ClassroomEndpoint { }); } - _i3.Future<_i45.Classroom?> fetchClassroomById( + _i3.Future<_i46.Classroom?> fetchClassroomById( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -5023,7 +5052,7 @@ class _ClassroomEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i45.Classroom?>); + ) as _i3.Future<_i46.Classroom?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5031,7 +5060,7 @@ class _ClassroomEndpoint { }); } - _i3.Future<_i45.Classroom?> fetchClassroomByRoomCode( + _i3.Future<_i46.Classroom?> fetchClassroomByRoomCode( _i1.TestSessionBuilder sessionBuilder, String roomCode, ) async { @@ -5052,7 +5081,7 @@ class _ClassroomEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i45.Classroom?>); + ) as _i3.Future<_i46.Classroom?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5060,7 +5089,7 @@ class _ClassroomEndpoint { }); } - _i3.Future> fetchClassroomsByRoomName( + _i3.Future> fetchClassroomsByRoomName( _i1.TestSessionBuilder sessionBuilder, String roomName, ) async { @@ -5081,7 +5110,7 @@ class _ClassroomEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5089,9 +5118,9 @@ class _ClassroomEndpoint { }); } - _i3.Future<_i45.Classroom> updateClassroom( + _i3.Future<_i46.Classroom> updateClassroom( _i1.TestSessionBuilder sessionBuilder, - _i45.Classroom classroom, + _i46.Classroom classroom, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5110,7 +5139,7 @@ class _ClassroomEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i45.Classroom>); + ) as _i3.Future<_i46.Classroom>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5158,9 +5187,9 @@ class _LearningGroupEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i46.LessonGroup> createLessonGroup( + _i3.Future<_i47.LessonGroup> createLessonGroup( _i1.TestSessionBuilder sessionBuilder, - _i46.LessonGroup lessonGroup, + _i47.LessonGroup lessonGroup, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5179,7 +5208,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i46.LessonGroup>); + ) as _i3.Future<_i47.LessonGroup>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5187,7 +5216,7 @@ class _LearningGroupEndpoint { }); } - _i3.Future> fetchLessonGroups( + _i3.Future> fetchLessonGroups( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5206,7 +5235,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5214,7 +5243,7 @@ class _LearningGroupEndpoint { }); } - _i3.Future<_i46.LessonGroup?> fetchLessonGroupById( + _i3.Future<_i47.LessonGroup?> fetchLessonGroupById( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -5235,7 +5264,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i46.LessonGroup?>); + ) as _i3.Future<_i47.LessonGroup?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5243,7 +5272,7 @@ class _LearningGroupEndpoint { }); } - _i3.Future<_i46.LessonGroup?> fetchLessonGroupByPublicId( + _i3.Future<_i47.LessonGroup?> fetchLessonGroupByPublicId( _i1.TestSessionBuilder sessionBuilder, String publicId, ) async { @@ -5264,7 +5293,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i46.LessonGroup?>); + ) as _i3.Future<_i47.LessonGroup?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5272,7 +5301,7 @@ class _LearningGroupEndpoint { }); } - _i3.Future> fetchLessonGroupsByName( + _i3.Future> fetchLessonGroupsByName( _i1.TestSessionBuilder sessionBuilder, String name, ) async { @@ -5293,7 +5322,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5301,7 +5330,7 @@ class _LearningGroupEndpoint { }); } - _i3.Future> fetchLessonGroupsByCreator( + _i3.Future> fetchLessonGroupsByCreator( _i1.TestSessionBuilder sessionBuilder, String createdBy, ) async { @@ -5322,7 +5351,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5330,7 +5359,7 @@ class _LearningGroupEndpoint { }); } - _i3.Future> fetchLessonGroupsByTimetable( + _i3.Future> fetchLessonGroupsByTimetable( _i1.TestSessionBuilder sessionBuilder, int timetableId, ) async { @@ -5351,7 +5380,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5359,9 +5388,9 @@ class _LearningGroupEndpoint { }); } - _i3.Future<_i46.LessonGroup> updateLessonGroup( + _i3.Future<_i47.LessonGroup> updateLessonGroup( _i1.TestSessionBuilder sessionBuilder, - _i46.LessonGroup lessonGroup, + _i47.LessonGroup lessonGroup, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5380,7 +5409,7 @@ class _LearningGroupEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i46.LessonGroup>); + ) as _i3.Future<_i47.LessonGroup>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5428,9 +5457,9 @@ class _ScheduledLessonEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i47.ScheduledLesson?> createScheduledLesson( + _i3.Future<_i48.ScheduledLesson?> createScheduledLesson( _i1.TestSessionBuilder sessionBuilder, - _i47.ScheduledLesson scheduledLesson, + _i48.ScheduledLesson scheduledLesson, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5450,7 +5479,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i47.ScheduledLesson?>); + ) as _i3.Future<_i48.ScheduledLesson?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5458,7 +5487,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future> fetchScheduledLessons( + _i3.Future> fetchScheduledLessons( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5477,7 +5506,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5485,7 +5514,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future<_i47.ScheduledLesson?> fetchScheduledLessonById( + _i3.Future<_i48.ScheduledLesson?> fetchScheduledLessonById( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -5506,7 +5535,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i47.ScheduledLesson?>); + ) as _i3.Future<_i48.ScheduledLesson?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5514,7 +5543,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future> fetchScheduledLessonsByTimetable( + _i3.Future> fetchScheduledLessonsByTimetable( _i1.TestSessionBuilder sessionBuilder, int timetableId, ) async { @@ -5535,7 +5564,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5543,7 +5572,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future> fetchScheduledLessonsBySubject( + _i3.Future> fetchScheduledLessonsBySubject( _i1.TestSessionBuilder sessionBuilder, int subjectId, ) async { @@ -5564,7 +5593,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5572,7 +5601,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future> fetchScheduledLessonsByRoom( + _i3.Future> fetchScheduledLessonsByRoom( _i1.TestSessionBuilder sessionBuilder, int roomId, ) async { @@ -5593,7 +5622,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5601,7 +5630,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future> fetchScheduledLessonsBySlotId( + _i3.Future> fetchScheduledLessonsBySlotId( _i1.TestSessionBuilder sessionBuilder, int slotId, ) async { @@ -5622,7 +5651,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5630,7 +5659,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future> fetchActiveScheduledLessons( + _i3.Future> fetchActiveScheduledLessons( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5649,7 +5678,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5657,9 +5686,9 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future<_i47.ScheduledLesson?> updateScheduledLesson( + _i3.Future<_i48.ScheduledLesson?> updateScheduledLesson( _i1.TestSessionBuilder sessionBuilder, - _i47.ScheduledLesson scheduledLesson, + _i48.ScheduledLesson scheduledLesson, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5679,7 +5708,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i47.ScheduledLesson?>); + ) as _i3.Future<_i48.ScheduledLesson?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5687,7 +5716,7 @@ class _ScheduledLessonEndpoint { }); } - _i3.Future<_i47.ScheduledLesson?> deactivateScheduledLesson( + _i3.Future<_i48.ScheduledLesson?> deactivateScheduledLesson( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -5708,7 +5737,7 @@ class _ScheduledLessonEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i47.ScheduledLesson?>); + ) as _i3.Future<_i48.ScheduledLesson?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5756,10 +5785,10 @@ class _ScheduledLessonGroupMembershipEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i48.ScheduledLessonGroupMembership> + _i3.Future<_i49.ScheduledLessonGroupMembership> createScheduledLessonGroupMembership( _i1.TestSessionBuilder sessionBuilder, - _i48.ScheduledLessonGroupMembership membership, + _i49.ScheduledLessonGroupMembership membership, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5778,7 +5807,7 @@ class _ScheduledLessonGroupMembershipEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i48.ScheduledLessonGroupMembership>); + ) as _i3.Future<_i49.ScheduledLessonGroupMembership>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5786,7 +5815,7 @@ class _ScheduledLessonGroupMembershipEndpoint { }); } - _i3.Future> + _i3.Future> fetchScheduledLessonGroupMemberships( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -5806,7 +5835,7 @@ class _ScheduledLessonGroupMembershipEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5814,7 +5843,7 @@ class _ScheduledLessonGroupMembershipEndpoint { }); } - _i3.Future<_i48.ScheduledLessonGroupMembership?> + _i3.Future<_i49.ScheduledLessonGroupMembership?> fetchScheduledLessonGroupMembershipById( _i1.TestSessionBuilder sessionBuilder, int id, @@ -5836,7 +5865,7 @@ class _ScheduledLessonGroupMembershipEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i48.ScheduledLessonGroupMembership?>); + ) as _i3.Future<_i49.ScheduledLessonGroupMembership?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5844,7 +5873,7 @@ class _ScheduledLessonGroupMembershipEndpoint { }); } - _i3.Future> + _i3.Future> fetchMembershipsByLessonGroupId( _i1.TestSessionBuilder sessionBuilder, int lessonGroupId, @@ -5866,7 +5895,7 @@ class _ScheduledLessonGroupMembershipEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5874,7 +5903,7 @@ class _ScheduledLessonGroupMembershipEndpoint { }); } - _i3.Future> + _i3.Future> fetchMembershipsByPupilDataId( _i1.TestSessionBuilder sessionBuilder, int pupilDataId, @@ -5896,7 +5925,7 @@ class _ScheduledLessonGroupMembershipEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5904,7 +5933,7 @@ class _ScheduledLessonGroupMembershipEndpoint { }); } - _i3.Future<_i48.ScheduledLessonGroupMembership?> + _i3.Future<_i49.ScheduledLessonGroupMembership?> fetchMembershipByLessonGroupAndPupil( _i1.TestSessionBuilder sessionBuilder, int lessonGroupId, @@ -5930,7 +5959,7 @@ class _ScheduledLessonGroupMembershipEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i48.ScheduledLessonGroupMembership?>); + ) as _i3.Future<_i49.ScheduledLessonGroupMembership?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -5938,10 +5967,10 @@ class _ScheduledLessonGroupMembershipEndpoint { }); } - _i3.Future<_i48.ScheduledLessonGroupMembership> + _i3.Future<_i49.ScheduledLessonGroupMembership> updateScheduledLessonGroupMembership( _i1.TestSessionBuilder sessionBuilder, - _i48.ScheduledLessonGroupMembership membership, + _i49.ScheduledLessonGroupMembership membership, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -5960,7 +5989,7 @@ class _ScheduledLessonGroupMembershipEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i48.ScheduledLessonGroupMembership>); + ) as _i3.Future<_i49.ScheduledLessonGroupMembership>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6074,9 +6103,9 @@ class _SubjectEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i49.Subject> createSubject( + _i3.Future<_i50.Subject> createSubject( _i1.TestSessionBuilder sessionBuilder, - _i49.Subject subject, + _i50.Subject subject, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6095,7 +6124,7 @@ class _SubjectEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i49.Subject>); + ) as _i3.Future<_i50.Subject>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6103,7 +6132,7 @@ class _SubjectEndpoint { }); } - _i3.Future> fetchSubjects( + _i3.Future> fetchSubjects( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6122,7 +6151,7 @@ class _SubjectEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6130,7 +6159,7 @@ class _SubjectEndpoint { }); } - _i3.Future<_i49.Subject?> fetchSubjectById( + _i3.Future<_i50.Subject?> fetchSubjectById( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -6151,7 +6180,7 @@ class _SubjectEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i49.Subject?>); + ) as _i3.Future<_i50.Subject?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6159,7 +6188,7 @@ class _SubjectEndpoint { }); } - _i3.Future<_i49.Subject?> fetchSubjectByPublicId( + _i3.Future<_i50.Subject?> fetchSubjectByPublicId( _i1.TestSessionBuilder sessionBuilder, String publicId, ) async { @@ -6180,7 +6209,7 @@ class _SubjectEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i49.Subject?>); + ) as _i3.Future<_i50.Subject?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6188,7 +6217,7 @@ class _SubjectEndpoint { }); } - _i3.Future> fetchSubjectsByName( + _i3.Future> fetchSubjectsByName( _i1.TestSessionBuilder sessionBuilder, String name, ) async { @@ -6209,7 +6238,7 @@ class _SubjectEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6217,7 +6246,7 @@ class _SubjectEndpoint { }); } - _i3.Future> fetchSubjectsByCreator( + _i3.Future> fetchSubjectsByCreator( _i1.TestSessionBuilder sessionBuilder, String createdBy, ) async { @@ -6238,7 +6267,7 @@ class _SubjectEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6246,9 +6275,9 @@ class _SubjectEndpoint { }); } - _i3.Future<_i49.Subject> updateSubject( + _i3.Future<_i50.Subject> updateSubject( _i1.TestSessionBuilder sessionBuilder, - _i49.Subject subject, + _i50.Subject subject, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6267,7 +6296,7 @@ class _SubjectEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i49.Subject>); + ) as _i3.Future<_i50.Subject>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6315,9 +6344,9 @@ class _TimetableEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i50.Timetable> createTimetable( + _i3.Future<_i51.Timetable> createTimetable( _i1.TestSessionBuilder sessionBuilder, - _i50.Timetable timetable, + _i51.Timetable timetable, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6336,7 +6365,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i50.Timetable>); + ) as _i3.Future<_i51.Timetable>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6344,7 +6373,7 @@ class _TimetableEndpoint { }); } - _i3.Future> fetchTimetables( + _i3.Future> fetchTimetables( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6363,7 +6392,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6371,7 +6400,7 @@ class _TimetableEndpoint { }); } - _i3.Future<_i50.Timetable?> fetchTimetableById( + _i3.Future<_i51.Timetable?> fetchTimetableById( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -6392,7 +6421,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i50.Timetable?>); + ) as _i3.Future<_i51.Timetable?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6400,7 +6429,7 @@ class _TimetableEndpoint { }); } - _i3.Future<_i50.Timetable?> fetchTimetable( + _i3.Future<_i51.Timetable?> fetchTimetable( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6419,7 +6448,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i50.Timetable?>); + ) as _i3.Future<_i51.Timetable?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6427,7 +6456,7 @@ class _TimetableEndpoint { }); } - _i3.Future<_i50.Timetable?> fetchCompleteTimetableData( + _i3.Future<_i51.Timetable?> fetchCompleteTimetableData( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6446,7 +6475,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i50.Timetable?>); + ) as _i3.Future<_i51.Timetable?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6454,7 +6483,7 @@ class _TimetableEndpoint { }); } - _i3.Future> fetchActiveTimetables( + _i3.Future> fetchActiveTimetables( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6473,7 +6502,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6481,7 +6510,7 @@ class _TimetableEndpoint { }); } - _i3.Future> fetchTimetablesBySemester( + _i3.Future> fetchTimetablesBySemester( _i1.TestSessionBuilder sessionBuilder, int schoolSemesterId, ) async { @@ -6503,7 +6532,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6511,9 +6540,9 @@ class _TimetableEndpoint { }); } - _i3.Future<_i50.Timetable> updateTimetable( + _i3.Future<_i51.Timetable> updateTimetable( _i1.TestSessionBuilder sessionBuilder, - _i50.Timetable timetable, + _i51.Timetable timetable, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6532,7 +6561,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i50.Timetable>); + ) as _i3.Future<_i51.Timetable>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6540,7 +6569,7 @@ class _TimetableEndpoint { }); } - _i3.Future<_i50.Timetable> deactivateTimetable( + _i3.Future<_i51.Timetable> deactivateTimetable( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -6561,7 +6590,7 @@ class _TimetableEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i50.Timetable>); + ) as _i3.Future<_i51.Timetable>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6609,9 +6638,9 @@ class _TimetableSlotEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i51.TimetableSlot> createTimetableSlot( + _i3.Future<_i52.TimetableSlot> createTimetableSlot( _i1.TestSessionBuilder sessionBuilder, - _i51.TimetableSlot timetableSlot, + _i52.TimetableSlot timetableSlot, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6630,7 +6659,7 @@ class _TimetableSlotEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i51.TimetableSlot>); + ) as _i3.Future<_i52.TimetableSlot>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6638,7 +6667,7 @@ class _TimetableSlotEndpoint { }); } - _i3.Future> fetchTimetableSlots( + _i3.Future> fetchTimetableSlots( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6657,7 +6686,7 @@ class _TimetableSlotEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6665,7 +6694,7 @@ class _TimetableSlotEndpoint { }); } - _i3.Future<_i51.TimetableSlot?> fetchTimetableSlotById( + _i3.Future<_i52.TimetableSlot?> fetchTimetableSlotById( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -6686,7 +6715,7 @@ class _TimetableSlotEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i51.TimetableSlot?>); + ) as _i3.Future<_i52.TimetableSlot?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6694,7 +6723,7 @@ class _TimetableSlotEndpoint { }); } - _i3.Future> fetchTimetableSlotsByTimetableId( + _i3.Future> fetchTimetableSlotsByTimetableId( _i1.TestSessionBuilder sessionBuilder, int timetableId, ) async { @@ -6715,7 +6744,7 @@ class _TimetableSlotEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6723,9 +6752,9 @@ class _TimetableSlotEndpoint { }); } - _i3.Future> fetchTimetableSlotsByDay( + _i3.Future> fetchTimetableSlotsByDay( _i1.TestSessionBuilder sessionBuilder, - _i52.Weekday day, + _i53.Weekday day, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6744,7 +6773,7 @@ class _TimetableSlotEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6752,9 +6781,9 @@ class _TimetableSlotEndpoint { }); } - _i3.Future<_i51.TimetableSlot> updateTimetableSlot( + _i3.Future<_i52.TimetableSlot> updateTimetableSlot( _i1.TestSessionBuilder sessionBuilder, - _i51.TimetableSlot timetableSlot, + _i52.TimetableSlot timetableSlot, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -6773,7 +6802,7 @@ class _TimetableSlotEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i51.TimetableSlot>); + ) as _i3.Future<_i52.TimetableSlot>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6946,7 +6975,7 @@ class _PupilWorkbooksEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i53.PupilWorkbook> postPupilWorkbook( + _i3.Future<_i54.PupilWorkbook> postPupilWorkbook( _i1.TestSessionBuilder sessionBuilder, int isbn, int pupilId, @@ -6973,7 +7002,7 @@ class _PupilWorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i53.PupilWorkbook>); + ) as _i3.Future<_i54.PupilWorkbook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -6981,7 +7010,7 @@ class _PupilWorkbooksEndpoint { }); } - _i3.Future> fetchPupilWorkbooks( + _i3.Future> fetchPupilWorkbooks( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -7000,7 +7029,7 @@ class _PupilWorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7008,7 +7037,7 @@ class _PupilWorkbooksEndpoint { }); } - _i3.Future> fetchPupilWorkbooksFromPupil( + _i3.Future> fetchPupilWorkbooksFromPupil( _i1.TestSessionBuilder sessionBuilder, int pupilId, ) async { @@ -7029,7 +7058,7 @@ class _PupilWorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7037,9 +7066,9 @@ class _PupilWorkbooksEndpoint { }); } - _i3.Future<_i53.PupilWorkbook> updatePupilWorkbook( + _i3.Future<_i54.PupilWorkbook> updatePupilWorkbook( _i1.TestSessionBuilder sessionBuilder, - _i53.PupilWorkbook pupilWorkbook, + _i54.PupilWorkbook pupilWorkbook, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -7058,7 +7087,7 @@ class _PupilWorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i53.PupilWorkbook>); + ) as _i3.Future<_i54.PupilWorkbook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7107,9 +7136,9 @@ class _WorkbooksEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i54.Workbook> postWorkbook( + _i3.Future<_i55.Workbook> postWorkbook( _i1.TestSessionBuilder sessionBuilder, - _i54.Workbook workbook, + _i55.Workbook workbook, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -7128,7 +7157,7 @@ class _WorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i54.Workbook>); + ) as _i3.Future<_i55.Workbook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7136,7 +7165,7 @@ class _WorkbooksEndpoint { }); } - _i3.Future<_i54.Workbook> fetchWorkbookByIsbn( + _i3.Future<_i55.Workbook> fetchWorkbookByIsbn( _i1.TestSessionBuilder sessionBuilder, int isbn, ) async { @@ -7157,7 +7186,7 @@ class _WorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i54.Workbook>); + ) as _i3.Future<_i55.Workbook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7165,7 +7194,7 @@ class _WorkbooksEndpoint { }); } - _i3.Future> fetchWorkbooks( + _i3.Future> fetchWorkbooks( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -7184,7 +7213,7 @@ class _WorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7192,9 +7221,9 @@ class _WorkbooksEndpoint { }); } - _i3.Future<_i54.Workbook> updateWorkbook( + _i3.Future<_i55.Workbook> updateWorkbook( _i1.TestSessionBuilder sessionBuilder, - _i54.Workbook workbook, + _i55.Workbook workbook, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -7213,7 +7242,7 @@ class _WorkbooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i54.Workbook>); + ) as _i3.Future<_i55.Workbook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7327,7 +7356,7 @@ class _FilesEndpoint { }); } - _i3.Future<_i55.ByteData?> getImage( + _i3.Future<_i56.ByteData?> getImage( _i1.TestSessionBuilder sessionBuilder, String documentId, ) async { @@ -7348,7 +7377,7 @@ class _FilesEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i55.ByteData?>); + ) as _i3.Future<_i56.ByteData?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -7356,7 +7385,7 @@ class _FilesEndpoint { }); } - _i3.Future<_i55.ByteData?> getUnencryptedImage( + _i3.Future<_i56.ByteData?> getUnencryptedImage( _i1.TestSessionBuilder sessionBuilder, String path, ) async { @@ -7377,7 +7406,7 @@ class _FilesEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i55.ByteData?>); + ) as _i3.Future<_i56.ByteData?>); return _localReturnValue; } finally { await _localUniqueSession.close(); From 0c01e21dfdbdc177c491c10080ed2c89773f3946 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Mon, 1 Dec 2025 11:02:24 +0100 Subject: [PATCH 23/52] [APP] new learning support plan: return if no supportlevelhistory --- .../pupil_profile_learning_support_content_list.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart index 5d9b9ef8..f40c9148 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart @@ -387,6 +387,14 @@ class PupilProfileLearningSupportContentList extends WatchingWidget { child: ElevatedButton( style: AppStyles.actionButtonStyle, onPressed: () { + if (pupil.supportLevelHistory == null || + pupil.supportLevelHistory!.isEmpty) { + di().showSnackBar( + NotificationType.error, + 'Förderebene nicht festgelegt', + ); + return; + } Navigator.of(context).push( MaterialPageRoute( builder: (ctx) => NewLearningSupportPlan(pupil: pupil), From 5cbed37f719f2b222b8e8c4d0df4b15e44401cca Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Mon, 1 Dec 2025 11:03:56 +0100 Subject: [PATCH 24/52] [SERVER] Schoolday event notification: fix replacing 'le' everywhere in the string --- .../schoolday_event_notification_helper.dart | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart b/school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart index 1d7c5f0e..38df7f51 100644 --- a/school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart +++ b/school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart @@ -76,18 +76,18 @@ String _getEventTypeText(SchooldayEventType type) => switch (type) { }; String _getEventReasonText(String reason) => reason - .replaceAll('gm', '🤜🤕') - .replaceAll('gl', '🤜🎓️') - .replaceAll('gs', '🤜🏫') - .replaceAll('ab', '🤬💔') - .replaceAll('gv', '🚨😱') - .replaceAll('äa', '😈😖') - .replaceAll('il', '🎓️🙉') - .replaceAll('us', '🛑🎓️') - .replaceAll('ss', '📝') - .replaceAll('le', '💡🧠') - .replaceAll('fi', '🛟🧠') - .replaceAll('ki', '⚠️ℹ️'); + .replaceAll('gm*', '🤜🤕*') + .replaceAll('gl*', '🤜🎓️*') + .replaceAll('gs*', '🤜🏫*') + .replaceAll('ab*', '🤬💔*') + .replaceAll('gv*', '🚨😱*') + .replaceAll('äa*', '😈😖*') + .replaceAll('il*', '🎓️🙉*') + .replaceAll('us*', '🛑🎓️*') + .replaceAll('ss*', '📝*') + .replaceAll('le*', '💡🧠*') + .replaceAll('fi*', '🛟🧠*') + .replaceAll('ki*', '⚠️ℹ️*'); String _getSchooldayEventNotificationText( {required String eventcreator, From 4041775474ce3da439f8348b46b0713876411a40 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Mon, 1 Dec 2025 11:04:51 +0100 Subject: [PATCH 25/52] [APP] upload image: add safe area for older devices --- .../lib/common/widgets/upload_image.dart | 83 ++++++++++--------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/school_data_hub_flutter/lib/common/widgets/upload_image.dart b/school_data_hub_flutter/lib/common/widgets/upload_image.dart index b4fffe84..9e9323a5 100644 --- a/school_data_hub_flutter/lib/common/widgets/upload_image.dart +++ b/school_data_hub_flutter/lib/common/widgets/upload_image.dart @@ -10,9 +10,13 @@ import 'package:path_provider/path_provider.dart'; Future createImageFile(context) async { XFile? image = await ImagePicker().pickImage( - source: Platform.isWindows || Platform.isMacOS? ImageSource.gallery : ImageSource.camera, - preferredCameraDevice: - Platform.isWindows ? CameraDevice.front : CameraDevice.rear); + source: Platform.isWindows || Platform.isMacOS + ? ImageSource.gallery + : ImageSource.camera, + preferredCameraDevice: Platform.isWindows + ? CameraDevice.front + : CameraDevice.rear, + ); if (image == null) { return null; } @@ -49,43 +53,41 @@ class _CropAvatarState extends State { @override Widget build(BuildContext context) => Scaffold( - // appBar: AppBar( - // title: Text('Gesicht zentrieren'), - // ), - body: Center( - child: Container( - decoration: const BoxDecoration( - color: Colors.black, - ), - child: CropImage( - controller: controller, - image: Image.file(File(widget.image.path)), - paddingSize: 0, - alwaysMove: true, - ), - ), + // appBar: AppBar( + // title: Text('Gesicht zentrieren'), + // ), + body: Center( + child: Container( + decoration: const BoxDecoration(color: Colors.black), + child: CropImage( + controller: controller, + image: Image.file(File(widget.image.path)), + paddingSize: 0, + alwaysMove: true, ), - bottomNavigationBar: _buildButtons(), - ); - - Widget _buildButtons() => Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - IconButton( - icon: const Icon(Icons.rotate_90_degrees_ccw_outlined), - onPressed: _rotateLeft, - ), - IconButton( - icon: const Icon(Icons.rotate_90_degrees_cw_outlined), - onPressed: _rotateRight, - ), - TextButton( - onPressed: _finished, - child: const Text('Fertig'), - ), - ], - ); + ), + ), + bottomNavigationBar: _buildButtons(), + ); + + Widget _buildButtons() => SafeArea( + top: false, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + icon: const Icon(Icons.rotate_90_degrees_ccw_outlined), + onPressed: _rotateLeft, + ), + IconButton( + icon: const Icon(Icons.rotate_90_degrees_cw_outlined), + onPressed: _rotateRight, + ), + TextButton(onPressed: _finished, child: const Text('Fertig')), + ], + ), + ); // Future _aspectRatios() async { // final value = await showDialog( @@ -173,7 +175,8 @@ class _CropAvatarState extends State { String tempPath = (await getTemporaryDirectory()).path; File file = File(p.join(tempPath, 'temporaryProfile.jpeg')); await file.writeAsBytes( - bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes)); + bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes), + ); return file; } } From 712f73721c230d2f3760826a5e4b616b9748393d Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Mon, 1 Dec 2025 11:05:29 +0100 Subject: [PATCH 26/52] [APP] Add logs for notifications, tidy up --- .../common/services/notification_service.dart | 21 ++++++++++++++++++- .../schoolday_event_notification_text.dart | 1 - 2 files changed, 20 insertions(+), 2 deletions(-) delete mode 100644 school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart diff --git a/school_data_hub_flutter/lib/common/services/notification_service.dart b/school_data_hub_flutter/lib/common/services/notification_service.dart index 6048c864..a0457cb0 100644 --- a/school_data_hub_flutter/lib/common/services/notification_service.dart +++ b/school_data_hub_flutter/lib/common/services/notification_service.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:school_data_hub_flutter/common/models/enums.dart'; export 'package:school_data_hub_flutter/common/models/enums.dart'; @@ -10,9 +11,12 @@ class NotificationData { NotificationData(this.type, this.message); } +final _log = Logger('NotificationService'); + class NotificationService { final _snackBar = ValueNotifier( - NotificationData(NotificationType.success, '')); + NotificationData(NotificationType.success, ''), + ); ValueListenable get notification => _snackBar; final _apiRunning = ValueNotifier(false); @@ -29,11 +33,26 @@ class NotificationService { if (_loadingNewInstance.value) { return; } + _log.fine( + '=================================================== SNACK BAR START ===================================================', + ); + _log.info(message); + _log.fine( + '=================================================== SNACK BAR END ===================================================', + ); + _snackBar.value = NotificationData(type, message); } void showInformationDialog(String message) { _snackBar.value = NotificationData(NotificationType.dialog, message); + _log.fine( + '=================================================== INFORMATION DIALOG START ===================================================', + ); + _log.info(message); + _log.fine( + '=================================================== INFORMATION DIALOG END ===================================================', + ); } void apiRunning(bool value) { diff --git a/school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart b/school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart deleted file mode 100644 index baf431d4..00000000 --- a/school_data_hub_server/lib/src/utils/matrix_notifications/schoolday_event_notification_text.dart +++ /dev/null @@ -1 +0,0 @@ -import 'package:school_data_hub_server/src/generated/protocol.dart'; From ab9857746f6fc3acd516b4162e02eb26f1bdc8d0 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 13:42:39 +0100 Subject: [PATCH 27/52] [APP][SERVER]: SchoolData post moved to admin endpoint --- .../lib/src/protocol/client.dart | 368 ++++++++-------- .../school/data/school_data_api_service.dart | 2 +- .../admin/endpoints/admin_endpoint.dart | 6 + .../endpoints/school_data_endpoint.dart | 9 - .../lib/src/generated/endpoints.dart | 179 ++++---- .../lib/src/generated/protocol.yaml | 2 +- .../test_tools/serverpod_test_tools.dart | 412 +++++++++--------- 7 files changed, 487 insertions(+), 491 deletions(-) diff --git a/school_data_hub_client/lib/src/protocol/client.dart b/school_data_hub_client/lib/src/protocol/client.dart index c45675b3..57d682d6 100644 --- a/school_data_hub_client/lib/src/protocol/client.dart +++ b/school_data_hub_client/lib/src/protocol/client.dart @@ -21,65 +21,65 @@ import 'package:school_data_hub_client/src/protocol/_features/learning/models/co as _i6; import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category.dart' as _i7; -import 'package:school_data_hub_client/src/protocol/_features/attendance/models/missed_schoolday_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/school_data/models/school_data.dart' as _i8; -import 'package:school_data_hub_client/src/protocol/_features/attendance/models/missed_schoolday.dart' +import 'package:school_data_hub_client/src/protocol/_features/attendance/models/missed_schoolday_dto.dart' as _i9; -import 'package:serverpod_auth_client/serverpod_auth_client.dart' as _i10; +import 'package:school_data_hub_client/src/protocol/_features/attendance/models/missed_schoolday.dart' + as _i10; +import 'package:serverpod_auth_client/serverpod_auth_client.dart' as _i11; import 'package:school_data_hub_client/src/protocol/_features/auth/models/user_device.dart' - as _i11; -import 'package:school_data_hub_client/src/protocol/_features/auth/models/device_info.dart' as _i12; -import 'package:school_data_hub_client/src/protocol/_features/authorizations/models/authorization.dart' +import 'package:school_data_hub_client/src/protocol/_features/auth/models/device_info.dart' as _i13; -import 'package:school_data_hub_client/src/protocol/_shared/models/member_operation.dart' +import 'package:school_data_hub_client/src/protocol/_features/authorizations/models/authorization.dart' as _i14; -import 'package:school_data_hub_client/src/protocol/protocol.dart' as _i15; +import 'package:school_data_hub_client/src/protocol/_shared/models/member_operation.dart' + as _i15; +import 'package:school_data_hub_client/src/protocol/protocol.dart' as _i16; import 'package:school_data_hub_client/src/protocol/_features/authorizations/models/pupil_authorization.dart' - as _i16; -import 'package:school_data_hub_client/src/protocol/_features/books/models/book_tagging/book_tag.dart' as _i17; -import 'package:school_data_hub_client/src/protocol/_features/books/models/book.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/book_tagging/book_tag.dart' as _i18; -import 'package:school_data_hub_client/src/protocol/_features/books/models/book_stats_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/book.dart' as _i19; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_location.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/book_stats_dto.dart' as _i20; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_location.dart' as _i21; -import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_query.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book.dart' as _i22; -import 'package:school_data_hub_client/src/protocol/_features/books/models/pupil_book_lending.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/library_book_query.dart' as _i23; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/learning_support_plan.dart' +import 'package:school_data_hub_client/src/protocol/_features/books/models/pupil_book_lending.dart' as _i24; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category_status.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/learning_support_plan.dart' as _i25; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_category_status.dart' as _i26; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i27; -import 'package:school_data_hub_client/src/protocol/_features/matrix/compulsory_room.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' as _i28; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' +import 'package:school_data_hub_client/src/protocol/_features/matrix/compulsory_room.dart' as _i29; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level_legacy_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i30; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level_legacy_dto.dart' as _i31; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/communication_skills.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i32; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/tutor_info.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/communication_skills.dart' as _i33; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/tutor_info.dart' as _i34; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/public_media_auth.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' as _i35; -import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/communication/public_media_auth.dart' as _i36; -import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' +import 'package:school_data_hub_client/src/protocol/_features/learning_support/models/support_level.dart' as _i37; -import 'package:school_data_hub_client/src/protocol/_features/school_data/models/school_data.dart' +import 'package:school_data_hub_client/src/protocol/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' as _i38; import 'package:school_data_hub_client/src/protocol/_features/school_lists/models/school_list.dart' as _i39; @@ -232,6 +232,13 @@ class EndpointAdmin extends _i1.EndpointRef { 'importSupportCategoriesFromJsonFile', {'filePath': filePath}, ); + + _i2.Future<_i8.SchoolData> postSchoolData(_i8.SchoolData schoolData) => + caller.callServerEndpoint<_i8.SchoolData>( + 'admin', + 'postSchoolData', + {'schoolData': schoolData}, + ); } /// {@category Endpoint} @@ -241,41 +248,41 @@ class EndpointMissedSchoolday extends _i1.EndpointRef { @override String get name => 'missedSchoolday'; - _i2.Stream<_i8.MissedSchooldayDto> streamMissedSchooldays() => - caller.callStreamingServerEndpoint<_i2.Stream<_i8.MissedSchooldayDto>, - _i8.MissedSchooldayDto>( + _i2.Stream<_i9.MissedSchooldayDto> streamMissedSchooldays() => + caller.callStreamingServerEndpoint<_i2.Stream<_i9.MissedSchooldayDto>, + _i9.MissedSchooldayDto>( 'missedSchoolday', 'streamMissedSchooldays', {}, {}, ); - _i2.Future<_i9.MissedSchoolday> postMissedSchoolday( - _i9.MissedSchoolday missedClass) => - caller.callServerEndpoint<_i9.MissedSchoolday>( + _i2.Future<_i10.MissedSchoolday> postMissedSchoolday( + _i10.MissedSchoolday missedClass) => + caller.callServerEndpoint<_i10.MissedSchoolday>( 'missedSchoolday', 'postMissedSchoolday', {'missedClass': missedClass}, ); - _i2.Future> postMissedSchooldays( - List<_i9.MissedSchoolday> missedClasses) => - caller.callServerEndpoint>( + _i2.Future> postMissedSchooldays( + List<_i10.MissedSchoolday> missedClasses) => + caller.callServerEndpoint>( 'missedSchoolday', 'postMissedSchooldays', {'missedClasses': missedClasses}, ); - _i2.Future> fetchAllMissedSchooldays() => - caller.callServerEndpoint>( + _i2.Future> fetchAllMissedSchooldays() => + caller.callServerEndpoint>( 'missedSchoolday', 'fetchAllMissedSchooldays', {}, ); - _i2.Future> fetchMissedSchooldaysOnASchoolday( + _i2.Future> fetchMissedSchooldaysOnASchoolday( DateTime schoolday) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'missedSchoolday', 'fetchMissedSchooldaysOnASchoolday', {'schoolday': schoolday}, @@ -294,9 +301,9 @@ class EndpointMissedSchoolday extends _i1.EndpointRef { }, ); - _i2.Future<_i9.MissedSchoolday> updateMissedSchoolday( - _i9.MissedSchoolday missedSchoolday) => - caller.callServerEndpoint<_i9.MissedSchoolday>( + _i2.Future<_i10.MissedSchoolday> updateMissedSchoolday( + _i10.MissedSchoolday missedSchoolday) => + caller.callServerEndpoint<_i10.MissedSchoolday>( 'missedSchoolday', 'updateMissedSchoolday', {'missedSchoolday': missedSchoolday}, @@ -311,16 +318,16 @@ class EndpointAuth extends _i1.EndpointRef { String get name => 'auth'; _i2.Future< - ({_i10.AuthenticationResponse response, _i11.UserDevice? userDevice})> + ({_i11.AuthenticationResponse response, _i12.UserDevice? userDevice})> login( String email, String password, - _i12.DeviceInfo deviceInfo, + _i13.DeviceInfo deviceInfo, ) => caller.callServerEndpoint< ({ - _i10.AuthenticationResponse response, - _i11.UserDevice? userDevice + _i11.AuthenticationResponse response, + _i12.UserDevice? userDevice })>( 'auth', 'login', @@ -345,27 +352,27 @@ class EndpointAuthorization extends _i1.EndpointRef { @override String get name => 'authorization'; - _i2.Future> fetchAuthorizations() => - caller.callServerEndpoint>( + _i2.Future> fetchAuthorizations() => + caller.callServerEndpoint>( 'authorization', 'fetchAuthorizations', {}, ); - _i2.Future<_i13.Authorization?> fetchAuthorizationById(int id) => - caller.callServerEndpoint<_i13.Authorization?>( + _i2.Future<_i14.Authorization?> fetchAuthorizationById(int id) => + caller.callServerEndpoint<_i14.Authorization?>( 'authorization', 'fetchAuthorizationById', {'id': id}, ); - _i2.Future<_i13.Authorization> postAuthorizationWithPupils( + _i2.Future<_i14.Authorization> postAuthorizationWithPupils( String name, String description, String createdBy, List pupilIds, ) => - caller.callServerEndpoint<_i13.Authorization>( + caller.callServerEndpoint<_i14.Authorization>( 'authorization', 'postAuthorizationWithPupils', { @@ -376,20 +383,20 @@ class EndpointAuthorization extends _i1.EndpointRef { }, ); - _i2.Future<_i13.Authorization> updateAuthorization( + _i2.Future<_i14.Authorization> updateAuthorization( int authId, String? name, String? description, - ({_i14.MemberOperation operation, List pupilIds})? updateMembers, + ({_i15.MemberOperation operation, List pupilIds})? updateMembers, ) => - caller.callServerEndpoint<_i13.Authorization>( + caller.callServerEndpoint<_i14.Authorization>( 'authorization', 'updateAuthorization', { 'authId': authId, 'name': name, 'description': description, - 'updateMembers': _i15.mapRecordToJson(updateMembers), + 'updateMembers': _i16.mapRecordToJson(updateMembers), }, ); @@ -408,20 +415,20 @@ class EndpointPupilAuthorization extends _i1.EndpointRef { @override String get name => 'pupilAuthorization'; - _i2.Future<_i16.PupilAuthorization> updatePupilAuthorization( - _i16.PupilAuthorization authorization) => - caller.callServerEndpoint<_i16.PupilAuthorization>( + _i2.Future<_i17.PupilAuthorization> updatePupilAuthorization( + _i17.PupilAuthorization authorization) => + caller.callServerEndpoint<_i17.PupilAuthorization>( 'pupilAuthorization', 'updatePupilAuthorization', {'authorization': authorization}, ); - _i2.Future<_i16.PupilAuthorization> addFileToPupilAuthorization( + _i2.Future<_i17.PupilAuthorization> addFileToPupilAuthorization( int pupilAuthId, String filePath, String createdBy, ) => - caller.callServerEndpoint<_i16.PupilAuthorization>( + caller.callServerEndpoint<_i17.PupilAuthorization>( 'pupilAuthorization', 'addFileToPupilAuthorization', { @@ -431,9 +438,9 @@ class EndpointPupilAuthorization extends _i1.EndpointRef { }, ); - _i2.Future<_i16.PupilAuthorization> removeFileFromPupilAuthorization( + _i2.Future<_i17.PupilAuthorization> removeFileFromPupilAuthorization( int pupilAuthId) => - caller.callServerEndpoint<_i16.PupilAuthorization>( + caller.callServerEndpoint<_i17.PupilAuthorization>( 'pupilAuthorization', 'removeFileFromPupilAuthorization', {'pupilAuthId': pupilAuthId}, @@ -447,28 +454,28 @@ class EndpointBookTags extends _i1.EndpointRef { @override String get name => 'bookTags'; - _i2.Future<_i17.BookTag> postBookTag(_i17.BookTag bookTag) => - caller.callServerEndpoint<_i17.BookTag>( + _i2.Future<_i18.BookTag> postBookTag(_i18.BookTag bookTag) => + caller.callServerEndpoint<_i18.BookTag>( 'bookTags', 'postBookTag', {'bookTag': bookTag}, ); - _i2.Future> fetchBookTags() => - caller.callServerEndpoint>( + _i2.Future> fetchBookTags() => + caller.callServerEndpoint>( 'bookTags', 'fetchBookTags', {}, ); - _i2.Future<_i17.BookTag> updateBookTag(_i17.BookTag bookTag) => - caller.callServerEndpoint<_i17.BookTag>( + _i2.Future<_i18.BookTag> updateBookTag(_i18.BookTag bookTag) => + caller.callServerEndpoint<_i18.BookTag>( 'bookTags', 'updateBookTag', {'bookTag': bookTag}, ); - _i2.Future deleteBookTag(_i17.BookTag bookTag) => + _i2.Future deleteBookTag(_i18.BookTag bookTag) => caller.callServerEndpoint( 'bookTags', 'deleteBookTag', @@ -483,39 +490,39 @@ class EndpointBooks extends _i1.EndpointRef { @override String get name => 'books'; - _i2.Future<_i18.Book> postBook(_i18.Book book) => - caller.callServerEndpoint<_i18.Book>( + _i2.Future<_i19.Book> postBook(_i19.Book book) => + caller.callServerEndpoint<_i19.Book>( 'books', 'postBook', {'book': book}, ); - _i2.Future> fetchBooks() => - caller.callServerEndpoint>( + _i2.Future> fetchBooks() => + caller.callServerEndpoint>( 'books', 'fetchBooks', {}, ); - _i2.Future<_i19.LibraryBookStatsDto> getBookStats() => - caller.callServerEndpoint<_i19.LibraryBookStatsDto>( + _i2.Future<_i20.LibraryBookStatsDto> getBookStats() => + caller.callServerEndpoint<_i20.LibraryBookStatsDto>( 'books', 'getBookStats', {}, ); - _i2.Future<_i18.Book?> fetchBookByIsbn(int isbn) => - caller.callServerEndpoint<_i18.Book?>( + _i2.Future<_i19.Book?> fetchBookByIsbn(int isbn) => + caller.callServerEndpoint<_i19.Book?>( 'books', 'fetchBookByIsbn', {'isbn': isbn}, ); - _i2.Future<_i18.Book> updateBookTags( + _i2.Future<_i19.Book> updateBookTags( int isbn, { - List<_i17.BookTag>? tags, + List<_i18.BookTag>? tags, }) => - caller.callServerEndpoint<_i18.Book>( + caller.callServerEndpoint<_i19.Book>( 'books', 'updateBookTags', { @@ -538,31 +545,31 @@ class EndpointLibraryBookLocations extends _i1.EndpointRef { @override String get name => 'libraryBookLocations'; - _i2.Future<_i20.LibraryBookLocation> postLibraryBookLocation( - _i20.LibraryBookLocation libraryBookLocation) => - caller.callServerEndpoint<_i20.LibraryBookLocation>( + _i2.Future<_i21.LibraryBookLocation> postLibraryBookLocation( + _i21.LibraryBookLocation libraryBookLocation) => + caller.callServerEndpoint<_i21.LibraryBookLocation>( 'libraryBookLocations', 'postLibraryBookLocation', {'libraryBookLocation': libraryBookLocation}, ); - _i2.Future> fetchLibraryBookLocations() => - caller.callServerEndpoint>( + _i2.Future> fetchLibraryBookLocations() => + caller.callServerEndpoint>( 'libraryBookLocations', 'fetchLibraryBookLocations', {}, ); - _i2.Future<_i20.LibraryBookLocation> updateLibraryBookLocation( - _i20.LibraryBookLocation libraryBookLocation) => - caller.callServerEndpoint<_i20.LibraryBookLocation>( + _i2.Future<_i21.LibraryBookLocation> updateLibraryBookLocation( + _i21.LibraryBookLocation libraryBookLocation) => + caller.callServerEndpoint<_i21.LibraryBookLocation>( 'libraryBookLocations', 'updateLibraryBookLocation', {'libraryBookLocation': libraryBookLocation}, ); _i2.Future deleteLibraryBookLocation( - _i20.LibraryBookLocation location) => + _i21.LibraryBookLocation location) => caller.callServerEndpoint( 'libraryBookLocations', 'deleteLibraryBookLocation', @@ -577,12 +584,12 @@ class EndpointLibraryBooks extends _i1.EndpointRef { @override String get name => 'libraryBooks'; - _i2.Future<_i21.LibraryBook> postLibraryBook( + _i2.Future<_i22.LibraryBook> postLibraryBook( int isbn, String libraryId, - _i20.LibraryBookLocation location, + _i21.LibraryBookLocation location, ) => - caller.callServerEndpoint<_i21.LibraryBook>( + caller.callServerEndpoint<_i22.LibraryBook>( 'libraryBooks', 'postLibraryBook', { @@ -592,47 +599,47 @@ class EndpointLibraryBooks extends _i1.EndpointRef { }, ); - _i2.Future> fetchLibraryBooks() => - caller.callServerEndpoint>( + _i2.Future> fetchLibraryBooks() => + caller.callServerEndpoint>( 'libraryBooks', 'fetchLibraryBooks', {}, ); - _i2.Future<_i21.LibraryBook?> fetchLibraryBookByIsbn(int isbn) => - caller.callServerEndpoint<_i21.LibraryBook?>( + _i2.Future<_i22.LibraryBook?> fetchLibraryBookByIsbn(int isbn) => + caller.callServerEndpoint<_i22.LibraryBook?>( 'libraryBooks', 'fetchLibraryBookByIsbn', {'isbn': isbn}, ); - _i2.Future<_i21.LibraryBook?> fetchLibraryBookByLibraryId(String libraryId) => - caller.callServerEndpoint<_i21.LibraryBook?>( + _i2.Future<_i22.LibraryBook?> fetchLibraryBookByLibraryId(String libraryId) => + caller.callServerEndpoint<_i22.LibraryBook?>( 'libraryBooks', 'fetchLibraryBookByLibraryId', {'libraryId': libraryId}, ); - _i2.Future> fetchLibraryBooksMatchingQuery( - _i22.LibraryBookQuery libraryBookQuery) => - caller.callServerEndpoint>( + _i2.Future> fetchLibraryBooksMatchingQuery( + _i23.LibraryBookQuery libraryBookQuery) => + caller.callServerEndpoint>( 'libraryBooks', 'fetchLibraryBooksMatchingQuery', {'libraryBookQuery': libraryBookQuery}, ); - _i2.Future<_i21.LibraryBook> updateLibraryBookAndRelatedBook( + _i2.Future<_i22.LibraryBook> updateLibraryBookAndRelatedBook( int isbn, String libraryId, bool? available, - _i20.LibraryBookLocation? location, + _i21.LibraryBookLocation? location, String? title, String? author, String? description, String? readingLevel, - List<_i17.BookTag>? tags, + List<_i18.BookTag>? tags, ) => - caller.callServerEndpoint<_i21.LibraryBook>( + caller.callServerEndpoint<_i22.LibraryBook>( 'libraryBooks', 'updateLibraryBookAndRelatedBook', { @@ -678,23 +685,23 @@ class EndpointPupilBookLending extends _i1.EndpointRef { }, ); - _i2.Future> fetchPupilBookLendings() => - caller.callServerEndpoint>( + _i2.Future> fetchPupilBookLendings() => + caller.callServerEndpoint>( 'pupilBookLending', 'fetchPupilBookLendings', {}, ); - _i2.Future<_i23.PupilBookLending?> fetchPupilBookLendingByLendingId( + _i2.Future<_i24.PupilBookLending?> fetchPupilBookLendingByLendingId( String lendingId) => - caller.callServerEndpoint<_i23.PupilBookLending?>( + caller.callServerEndpoint<_i24.PupilBookLending?>( 'pupilBookLending', 'fetchPupilBookLendingByLendingId', {'lendingId': lendingId}, ); _i2.Future<_i5.PupilData> updatePupilBookLending( - _i23.PupilBookLending pupilBookLending) => + _i24.PupilBookLending pupilBookLending) => caller.callServerEndpoint<_i5.PupilData>( 'pupilBookLending', 'updatePupilBookLending', @@ -749,10 +756,10 @@ class EndpointCompetenceCheck extends _i1.EndpointRef { 'updateCompetenceCheck', { 'checkId': checkId, - 'score': _i15.mapRecordToJson(score), - 'valueFactor': _i15.mapRecordToJson(valueFactor), - 'createdBy': _i15.mapRecordToJson(createdBy), - 'comment': _i15.mapRecordToJson(comment), + 'score': _i16.mapRecordToJson(score), + 'valueFactor': _i16.mapRecordToJson(valueFactor), + 'createdBy': _i16.mapRecordToJson(createdBy), + 'comment': _i16.mapRecordToJson(comment), }, ); @@ -845,29 +852,29 @@ class EndpointLearningSupportPlan extends _i1.EndpointRef { @override String get name => 'learningSupportPlan'; - _i2.Future> fetchLearningSupportPlans() => - caller.callServerEndpoint>( + _i2.Future> fetchLearningSupportPlans() => + caller.callServerEndpoint>( 'learningSupportPlan', 'fetchLearningSupportPlans', {}, ); - _i2.Future<_i24.LearningSupportPlan> createLearningSupportPlan( - _i24.LearningSupportPlan plan) => - caller.callServerEndpoint<_i24.LearningSupportPlan>( + _i2.Future<_i25.LearningSupportPlan> createLearningSupportPlan( + _i25.LearningSupportPlan plan) => + caller.callServerEndpoint<_i25.LearningSupportPlan>( 'learningSupportPlan', 'createLearningSupportPlan', {'plan': plan}, ); - _i2.Future updateLearningSupportPlan(_i24.LearningSupportPlan plan) => + _i2.Future updateLearningSupportPlan(_i25.LearningSupportPlan plan) => caller.callServerEndpoint( 'learningSupportPlan', 'updateLearningSupportPlan', {'plan': plan}, ); - _i2.Future deleteLearningSupportPlan(_i24.LearningSupportPlan plan) => + _i2.Future deleteLearningSupportPlan(_i25.LearningSupportPlan plan) => caller.callServerEndpoint( 'learningSupportPlan', 'deleteLearningSupportPlan', @@ -895,23 +902,23 @@ class EndpointLearningSupportPlan extends _i1.EndpointRef { }, ); - _i2.Future> fetchSupportCategoryStatus( + _i2.Future> fetchSupportCategoryStatus( int pupilId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'learningSupportPlan', 'fetchSupportCategoryStatus', {'pupilId': pupilId}, ); - _i2.Future> + _i2.Future> fetchSupportCategoryStatusFromPupil(int pupilId) => - caller.callServerEndpoint>( + caller.callServerEndpoint>( 'learningSupportPlan', 'fetchSupportCategoryStatusFromPupil', {'pupilId': pupilId}, ); - _i2.Future<_i25.SupportCategoryStatus> updateCategoryStatus( + _i2.Future<_i26.SupportCategoryStatus> updateCategoryStatus( int pupilId, int supportCategoryId, int? status, @@ -919,7 +926,7 @@ class EndpointLearningSupportPlan extends _i1.EndpointRef { String? createdBy, DateTime? createdAt, ) => - caller.callServerEndpoint<_i25.SupportCategoryStatus>( + caller.callServerEndpoint<_i26.SupportCategoryStatus>( 'learningSupportPlan', 'updateCategoryStatus', { @@ -973,12 +980,12 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { String get name => 'preSchoolMedical'; /// Create a new PreSchoolMedical record for a pupil - _i2.Future<_i26.PreSchoolMedical> createPreSchoolMedical( + _i2.Future<_i27.PreSchoolMedical> createPreSchoolMedical( int pupilId, - _i27.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i28.PreSchoolMedicalStatus? preschoolMedicalStatus, String createdBy, ) => - caller.callServerEndpoint<_i26.PreSchoolMedical>( + caller.callServerEndpoint<_i27.PreSchoolMedical>( 'preSchoolMedical', 'createPreSchoolMedical', { @@ -989,12 +996,12 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Update an existing PreSchoolMedical record - _i2.Future<_i26.PreSchoolMedical> updatePreSchoolMedical( + _i2.Future<_i27.PreSchoolMedical> updatePreSchoolMedical( int preSchoolMedicalId, - _i27.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i28.PreSchoolMedicalStatus? preschoolMedicalStatus, String updatedBy, ) => - caller.callServerEndpoint<_i26.PreSchoolMedical>( + caller.callServerEndpoint<_i27.PreSchoolMedical>( 'preSchoolMedical', 'updatePreSchoolMedical', { @@ -1005,18 +1012,18 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Get a PreSchoolMedical record by ID - _i2.Future<_i26.PreSchoolMedical?> getPreSchoolMedical( + _i2.Future<_i27.PreSchoolMedical?> getPreSchoolMedical( int preSchoolMedicalId) => - caller.callServerEndpoint<_i26.PreSchoolMedical?>( + caller.callServerEndpoint<_i27.PreSchoolMedical?>( 'preSchoolMedical', 'getPreSchoolMedical', {'preSchoolMedicalId': preSchoolMedicalId}, ); /// Get PreSchoolMedical record for a specific pupil - _i2.Future<_i26.PreSchoolMedical?> getPreSchoolMedicalByPupilId( + _i2.Future<_i27.PreSchoolMedical?> getPreSchoolMedicalByPupilId( int pupilId) => - caller.callServerEndpoint<_i26.PreSchoolMedical?>( + caller.callServerEndpoint<_i27.PreSchoolMedical?>( 'preSchoolMedical', 'getPreSchoolMedicalByPupilId', {'pupilId': pupilId}, @@ -1031,12 +1038,12 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Add a file to a PreSchoolMedical record - _i2.Future<_i26.PreSchoolMedical> addFileToPreSchoolMedical( + _i2.Future<_i27.PreSchoolMedical> addFileToPreSchoolMedical( int preSchoolMedicalId, String filePath, String createdBy, ) => - caller.callServerEndpoint<_i26.PreSchoolMedical>( + caller.callServerEndpoint<_i27.PreSchoolMedical>( 'preSchoolMedical', 'addFileToPreSchoolMedical', { @@ -1061,17 +1068,17 @@ class EndpointPreSchoolMedical extends _i1.EndpointRef { ); /// Get all PreSchoolMedical records (for admin purposes) - _i2.Future> getAllPreSchoolMedicalRecords() => - caller.callServerEndpoint>( + _i2.Future> getAllPreSchoolMedicalRecords() => + caller.callServerEndpoint>( 'preSchoolMedical', 'getAllPreSchoolMedicalRecords', {}, ); /// Get PreSchoolMedical records with specific status - _i2.Future> getPreSchoolMedicalByStatus( - _i27.PreSchoolMedicalStatus status) => - caller.callServerEndpoint>( + _i2.Future> getPreSchoolMedicalByStatus( + _i28.PreSchoolMedicalStatus status) => + caller.callServerEndpoint>( 'preSchoolMedical', 'getPreSchoolMedicalByStatus', {'status': status}, @@ -1129,16 +1136,16 @@ class EndpointMatrix extends _i1.EndpointRef { @override String get name => 'matrix'; - _i2.Future?> getCompulsoryRooms() => - caller.callServerEndpoint?>( + _i2.Future?> getCompulsoryRooms() => + caller.callServerEndpoint?>( 'matrix', 'getCompulsoryRooms', {}, ); - _i2.Future> setCompulsoryRooms( - List<_i28.CompulsoryRoom> compulsoryRooms) => - caller.callServerEndpoint>( + _i2.Future> setCompulsoryRooms( + List<_i29.CompulsoryRoom> compulsoryRooms) => + caller.callServerEndpoint>( 'matrix', 'setCompulsoryRooms', {'compulsoryRooms': compulsoryRooms}, @@ -1184,7 +1191,7 @@ class EndpointPupil extends _i1.EndpointRef { _i2.Future<_i5.PupilData> deletePupilDocument( int pupilId, - _i29.PupilDocumentType documentType, + _i30.PupilDocumentType documentType, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupil', @@ -1222,7 +1229,7 @@ class EndpointPupil extends _i1.EndpointRef { ); _i2.Future bulkAddSupportLevels( - List<_i30.SupportLevelLegacyDto> supportLevelData) => + List<_i31.SupportLevelLegacyDto> supportLevelData) => caller.callServerEndpoint( 'pupil', 'bulkAddSupportLevels', @@ -1237,10 +1244,10 @@ class EndpointPupilIdentity extends _i1.EndpointRef { @override String get name => 'pupilIdentity'; - _i2.Stream<_i31.PupilIdentityDto> streamEncryptedPupilIds( + _i2.Stream<_i32.PupilIdentityDto> streamEncryptedPupilIds( String channelName) => - caller.callStreamingServerEndpoint<_i2.Stream<_i31.PupilIdentityDto>, - _i31.PupilIdentityDto>( + caller.callStreamingServerEndpoint<_i2.Stream<_i32.PupilIdentityDto>, + _i32.PupilIdentityDto>( 'pupilIdentity', 'streamEncryptedPupilIds', {'channelName': channelName}, @@ -1249,7 +1256,7 @@ class EndpointPupilIdentity extends _i1.EndpointRef { _i2.Future sendPupilIdentityMessage( String pupilIdentityChannel, - _i31.PupilIdentityDto pupilIdentityMessage, + _i32.PupilIdentityDto pupilIdentityMessage, ) => caller.callServerEndpoint( 'pupilIdentity', @@ -1298,7 +1305,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updateCommunicationSkills({ required int pupilId, - required _i32.CommunicationSkills? communicationSkills, + required _i33.CommunicationSkills? communicationSkills, }) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1311,7 +1318,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updateTutorInfo( int pupilId, - _i33.TutorInfo? tutorInfo, + _i34.TutorInfo? tutorInfo, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1323,7 +1330,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { ); _i2.Future> updateSiblingsTutorInfo( - _i34.SiblingsTutorInfo siblingsTutorInfo) => + _i35.SiblingsTutorInfo siblingsTutorInfo) => caller.callServerEndpoint>( 'pupilUpdate', 'updateSiblingsTutorInfo', @@ -1334,7 +1341,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { int pupilId, String filePath, String createdBy, - _i29.PupilDocumentType documentType, + _i30.PupilDocumentType documentType, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1381,7 +1388,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updatePreSchoolMedicalStatus( int pupilId, - _i27.PreSchoolMedicalStatus preSchoolMedicalStatus, + _i28.PreSchoolMedicalStatus preSchoolMedicalStatus, String updatedBy, ) => caller.callServerEndpoint<_i5.PupilData>( @@ -1396,7 +1403,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { _i2.Future<_i5.PupilData> updatePublicMediaAuth( int pupilId, - _i35.PublicMediaAuth publicMediaAuth, + _i36.PublicMediaAuth publicMediaAuth, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1408,7 +1415,7 @@ class EndpointPupilUpdate extends _i1.EndpointRef { ); _i2.Future<_i5.PupilData> updateSupportLevel( - _i36.SupportLevel supportLevel, + _i37.SupportLevel supportLevel, int pupilId, ) => caller.callServerEndpoint<_i5.PupilData>( @@ -1430,13 +1437,13 @@ class EndpointPupilUpdate extends _i1.EndpointRef { { 'pupilId': pupilId, 'schoolyearHeldBackDate': - _i15.mapRecordToJson(schoolyearHeldBackDate), + _i16.mapRecordToJson(schoolyearHeldBackDate), }, ); _i2.Future<_i5.PupilData> updateAfterSchoolCare( int pupilId, - _i37.AfterSchoolCare afterSchoolCare, + _i38.AfterSchoolCare afterSchoolCare, ) => caller.callServerEndpoint<_i5.PupilData>( 'pupilUpdate', @@ -1455,16 +1462,9 @@ class EndpointSchoolData extends _i1.EndpointRef { @override String get name => 'schoolData'; - _i2.Future<_i38.SchoolData> postSchoolData(_i38.SchoolData schoolData) => - caller.callServerEndpoint<_i38.SchoolData>( - 'schoolData', - 'postSchoolData', - {'schoolData': schoolData}, - ); - /// TODO: we should be specific about which school data to get - _i2.Future<_i38.SchoolData?> getSchoolData() => - caller.callServerEndpoint<_i38.SchoolData?>( + _i2.Future<_i8.SchoolData?> getSchoolData() => + caller.callServerEndpoint<_i8.SchoolData?>( 'schoolData', 'getSchoolData', {}, @@ -1510,7 +1510,7 @@ class EndpointSchoolList extends _i1.EndpointRef { String? description, ({String? value})? authorizedUsers, bool? public, - ({_i14.MemberOperation operation, List pupilIds})? updateMembers, + ({_i15.MemberOperation operation, List pupilIds})? updateMembers, ) => caller.callServerEndpoint<_i39.SchoolList>( 'schoolList', @@ -1519,9 +1519,9 @@ class EndpointSchoolList extends _i1.EndpointRef { 'listId': listId, 'name': name, 'description': description, - 'authorizedUsers': _i15.mapRecordToJson(authorizedUsers), + 'authorizedUsers': _i16.mapRecordToJson(authorizedUsers), 'public': public, - 'updateMembers': _i15.mapRecordToJson(updateMembers), + 'updateMembers': _i16.mapRecordToJson(updateMembers), }, ); @@ -2470,10 +2470,10 @@ class EndpointFiles extends _i1.EndpointRef { class Modules { Modules(Client client) { - auth = _i10.Caller(client); + auth = _i11.Caller(client); } - late final _i10.Caller auth; + late final _i11.Caller auth; } class Client extends _i1.ServerpodClientShared { diff --git a/school_data_hub_flutter/lib/features/school/data/school_data_api_service.dart b/school_data_hub_flutter/lib/features/school/data/school_data_api_service.dart index 4d9d8a73..a841914b 100644 --- a/school_data_hub_flutter/lib/features/school/data/school_data_api_service.dart +++ b/school_data_hub_flutter/lib/features/school/data/school_data_api_service.dart @@ -22,7 +22,7 @@ class SchoolDataApiService { /// Create or update school data Future postSchoolData(SchoolData schoolData) async { final createdSchoolData = await ClientHelper.apiCall( - call: () => _client.schoolData.postSchoolData(schoolData), + call: () => _client.admin.postSchoolData(schoolData), errorMessage: 'Fehler beim Speichern der Schulinformationen', ); return createdSchoolData; diff --git a/school_data_hub_server/lib/src/_features/admin/endpoints/admin_endpoint.dart b/school_data_hub_server/lib/src/_features/admin/endpoints/admin_endpoint.dart index e1f7865e..1ab3fe14 100644 --- a/school_data_hub_server/lib/src/_features/admin/endpoints/admin_endpoint.dart +++ b/school_data_hub_server/lib/src/_features/admin/endpoints/admin_endpoint.dart @@ -301,4 +301,10 @@ class AdminEndpoint extends Endpoint { return processedCategories; } + + Future postSchoolData( + Session session, SchoolData schoolData) async { + final schooldataInDb = await session.db.insertRow(schoolData); + return schooldataInDb; + } } diff --git a/school_data_hub_server/lib/src/_features/school_data/endpoints/school_data_endpoint.dart b/school_data_hub_server/lib/src/_features/school_data/endpoints/school_data_endpoint.dart index 55dd7e25..273dc06c 100644 --- a/school_data_hub_server/lib/src/_features/school_data/endpoints/school_data_endpoint.dart +++ b/school_data_hub_server/lib/src/_features/school_data/endpoints/school_data_endpoint.dart @@ -5,15 +5,6 @@ class SchoolDataEndpoint extends Endpoint { @override bool get requireLogin => true; - @override - Set get requiredScopes => {Scope('serverpod.admin')}; - - Future postSchoolData( - Session session, SchoolData schoolData) async { - final schooldataInDb = await session.db.insertRow(schoolData); - return schooldataInDb; - } - /// TODO: we should be specific about which school data to get Future getSchoolData(Session session) async { var schoolData = await SchoolData.db.findFirstRow(session); diff --git a/school_data_hub_server/lib/src/generated/endpoints.dart b/school_data_hub_server/lib/src/generated/endpoints.dart index 73ddcb63..3c4857a0 100644 --- a/school_data_hub_server/lib/src/generated/endpoints.dart +++ b/school_data_hub_server/lib/src/generated/endpoints.dart @@ -57,56 +57,56 @@ import '../_features/workbooks/endpoints/workbooks_endpoint.dart' as _i34; import '../_shared/endpoints/file_endpoints.dart' as _i35; import 'package:school_data_hub_server/src/generated/_features/user/models/roles.dart' as _i36; -import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday.dart' +import 'package:school_data_hub_server/src/generated/_features/school_data/models/school_data.dart' as _i37; -import 'package:school_data_hub_server/src/generated/_features/auth/models/device_info.dart' +import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday.dart' as _i38; -import 'package:school_data_hub_server/src/generated/protocol.dart' as _i39; +import 'package:school_data_hub_server/src/generated/_features/auth/models/device_info.dart' + as _i39; +import 'package:school_data_hub_server/src/generated/protocol.dart' as _i40; import 'package:school_data_hub_server/src/generated/_shared/models/member_operation.dart' - as _i40; -import 'package:school_data_hub_server/src/generated/_features/authorizations/models/pupil_authorization.dart' as _i41; -import 'package:school_data_hub_server/src/generated/_features/books/models/book_tagging/book_tag.dart' +import 'package:school_data_hub_server/src/generated/_features/authorizations/models/pupil_authorization.dart' as _i42; -import 'package:school_data_hub_server/src/generated/_features/books/models/book.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book_tagging/book_tag.dart' as _i43; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book.dart' as _i44; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_query.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' as _i45; -import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_query.dart' as _i46; -import 'package:school_data_hub_server/src/generated/_features/learning/models/competence.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' as _i47; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' +import 'package:school_data_hub_server/src/generated/_features/learning/models/competence.dart' as _i48; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' as _i49; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' as _i50; -import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category.dart' as _i51; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' +import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' as _i52; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i53; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' as _i54; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/pupil_data.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i55; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/communication_skills.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/pupil_data.dart' as _i56; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/tutor_info.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/communication_skills.dart' as _i57; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/tutor_info.dart' as _i58; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/public_media_auth.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' as _i59; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/public_media_auth.dart' as _i60; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level.dart' as _i61; -import 'package:school_data_hub_server/src/generated/_features/school_data/models/school_data.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' as _i62; import 'package:school_data_hub_server/src/generated/_features/school_lists/models/pupil_entry.dart' as _i63; @@ -610,6 +610,24 @@ class Endpoints extends _i1.EndpointDispatch { params['filePath'], ), ), + 'postSchoolData': _i1.MethodConnector( + name: 'postSchoolData', + params: { + 'schoolData': _i1.ParameterDescription( + name: 'schoolData', + type: _i1.getType<_i37.SchoolData>(), + nullable: false, + ) + }, + call: ( + _i1.Session session, + Map params, + ) async => + (endpoints['admin'] as _i2.AdminEndpoint).postSchoolData( + session, + params['schoolData'], + ), + ), }, ); connectors['missedSchoolday'] = _i1.EndpointConnector( @@ -621,7 +639,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'missedClass': _i1.ParameterDescription( name: 'missedClass', - type: _i1.getType<_i37.MissedSchoolday>(), + type: _i1.getType<_i38.MissedSchoolday>(), nullable: false, ) }, @@ -640,7 +658,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'missedClasses': _i1.ParameterDescription( name: 'missedClasses', - type: _i1.getType>(), + type: _i1.getType>(), nullable: false, ) }, @@ -713,7 +731,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'missedSchoolday': _i1.ParameterDescription( name: 'missedSchoolday', - type: _i1.getType<_i37.MissedSchoolday>(), + type: _i1.getType<_i38.MissedSchoolday>(), nullable: false, ) }, @@ -761,7 +779,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'deviceInfo': _i1.ParameterDescription( name: 'deviceInfo', - type: _i1.getType<_i38.DeviceInfo>(), + type: _i1.getType<_i39.DeviceInfo>(), nullable: false, ), }, @@ -776,7 +794,7 @@ class Endpoints extends _i1.EndpointDispatch { params['password'], params['deviceInfo'], ) - .then((record) => _i39.mapRecordToJson(record)), + .then((record) => _i40.mapRecordToJson(record)), ), 'logOut': _i1.MethodConnector( name: 'logOut', @@ -889,7 +907,7 @@ class Endpoints extends _i1.EndpointDispatch { 'updateMembers': _i1.ParameterDescription( name: 'updateMembers', type: _i1.getType< - ({_i40.MemberOperation operation, List pupilIds})?>(), + ({_i41.MemberOperation operation, List pupilIds})?>(), nullable: true, ), }, @@ -936,7 +954,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'authorization': _i1.ParameterDescription( name: 'authorization', - type: _i1.getType<_i41.PupilAuthorization>(), + type: _i1.getType<_i42.PupilAuthorization>(), nullable: false, ) }, @@ -1014,7 +1032,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'bookTag': _i1.ParameterDescription( name: 'bookTag', - type: _i1.getType<_i42.BookTag>(), + type: _i1.getType<_i43.BookTag>(), nullable: false, ) }, @@ -1042,7 +1060,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'bookTag': _i1.ParameterDescription( name: 'bookTag', - type: _i1.getType<_i42.BookTag>(), + type: _i1.getType<_i43.BookTag>(), nullable: false, ) }, @@ -1060,7 +1078,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'bookTag': _i1.ParameterDescription( name: 'bookTag', - type: _i1.getType<_i42.BookTag>(), + type: _i1.getType<_i43.BookTag>(), nullable: false, ) }, @@ -1084,7 +1102,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'book': _i1.ParameterDescription( name: 'book', - type: _i1.getType<_i43.Book>(), + type: _i1.getType<_i44.Book>(), nullable: false, ) }, @@ -1143,7 +1161,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'tags': _i1.ParameterDescription( name: 'tags', - type: _i1.getType?>(), + type: _i1.getType?>(), nullable: true, ), }, @@ -1186,7 +1204,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'libraryBookLocation': _i1.ParameterDescription( name: 'libraryBookLocation', - type: _i1.getType<_i44.LibraryBookLocation>(), + type: _i1.getType<_i45.LibraryBookLocation>(), nullable: false, ) }, @@ -1217,7 +1235,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'libraryBookLocation': _i1.ParameterDescription( name: 'libraryBookLocation', - type: _i1.getType<_i44.LibraryBookLocation>(), + type: _i1.getType<_i45.LibraryBookLocation>(), nullable: false, ) }, @@ -1237,7 +1255,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'location': _i1.ParameterDescription( name: 'location', - type: _i1.getType<_i44.LibraryBookLocation>(), + type: _i1.getType<_i45.LibraryBookLocation>(), nullable: false, ) }, @@ -1273,7 +1291,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'location': _i1.ParameterDescription( name: 'location', - type: _i1.getType<_i44.LibraryBookLocation>(), + type: _i1.getType<_i45.LibraryBookLocation>(), nullable: false, ), }, @@ -1342,7 +1360,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'libraryBookQuery': _i1.ParameterDescription( name: 'libraryBookQuery', - type: _i1.getType<_i45.LibraryBookQuery>(), + type: _i1.getType<_i46.LibraryBookQuery>(), nullable: false, ) }, @@ -1376,7 +1394,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'location': _i1.ParameterDescription( name: 'location', - type: _i1.getType<_i44.LibraryBookLocation?>(), + type: _i1.getType<_i45.LibraryBookLocation?>(), nullable: true, ), 'title': _i1.ParameterDescription( @@ -1401,7 +1419,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'tags': _i1.ParameterDescription( name: 'tags', - type: _i1.getType?>(), + type: _i1.getType?>(), nullable: true, ), }, @@ -1513,7 +1531,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'pupilBookLending': _i1.ParameterDescription( name: 'pupilBookLending', - type: _i1.getType<_i46.PupilBookLending>(), + type: _i1.getType<_i47.PupilBookLending>(), nullable: false, ) }, @@ -1777,7 +1795,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'competence': _i1.ParameterDescription( name: 'competence', - type: _i1.getType<_i47.Competence>(), + type: _i1.getType<_i48.Competence>(), nullable: false, ) }, @@ -1832,7 +1850,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'plan': _i1.ParameterDescription( name: 'plan', - type: _i1.getType<_i48.LearningSupportPlan>(), + type: _i1.getType<_i49.LearningSupportPlan>(), nullable: false, ) }, @@ -1852,7 +1870,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'plan': _i1.ParameterDescription( name: 'plan', - type: _i1.getType<_i48.LearningSupportPlan>(), + type: _i1.getType<_i49.LearningSupportPlan>(), nullable: false, ) }, @@ -1872,7 +1890,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'plan': _i1.ParameterDescription( name: 'plan', - type: _i1.getType<_i48.LearningSupportPlan>(), + type: _i1.getType<_i49.LearningSupportPlan>(), nullable: false, ) }, @@ -2113,7 +2131,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'preschoolMedicalStatus': _i1.ParameterDescription( name: 'preschoolMedicalStatus', - type: _i1.getType<_i49.PreSchoolMedicalStatus?>(), + type: _i1.getType<_i50.PreSchoolMedicalStatus?>(), nullable: true, ), 'createdBy': _i1.ParameterDescription( @@ -2144,7 +2162,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'preschoolMedicalStatus': _i1.ParameterDescription( name: 'preschoolMedicalStatus', - type: _i1.getType<_i49.PreSchoolMedicalStatus?>(), + type: _i1.getType<_i50.PreSchoolMedicalStatus?>(), nullable: true, ), 'updatedBy': _i1.ParameterDescription( @@ -2293,7 +2311,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'status': _i1.ParameterDescription( name: 'status', - type: _i1.getType<_i49.PreSchoolMedicalStatus>(), + type: _i1.getType<_i50.PreSchoolMedicalStatus>(), nullable: false, ) }, @@ -2347,7 +2365,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'category': _i1.ParameterDescription( name: 'category', - type: _i1.getType<_i50.SupportCategory>(), + type: _i1.getType<_i51.SupportCategory>(), nullable: false, ) }, @@ -2366,7 +2384,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'category': _i1.ParameterDescription( name: 'category', - type: _i1.getType<_i50.SupportCategory>(), + type: _i1.getType<_i51.SupportCategory>(), nullable: false, ) }, @@ -2385,7 +2403,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'category': _i1.ParameterDescription( name: 'category', - type: _i1.getType<_i50.SupportCategory>(), + type: _i1.getType<_i51.SupportCategory>(), nullable: false, ) }, @@ -2420,7 +2438,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'compulsoryRooms': _i1.ParameterDescription( name: 'compulsoryRooms', - type: _i1.getType>(), + type: _i1.getType>(), nullable: false, ) }, @@ -2494,7 +2512,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'documentType': _i1.ParameterDescription( name: 'documentType', - type: _i1.getType<_i52.PupilDocumentType>(), + type: _i1.getType<_i53.PupilDocumentType>(), nullable: false, ), }, @@ -2562,7 +2580,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'supportLevelData': _i1.ParameterDescription( name: 'supportLevelData', - type: _i1.getType>(), + type: _i1.getType>(), nullable: false, ) }, @@ -2604,7 +2622,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'pupilIdentityMessage': _i1.ParameterDescription( name: 'pupilIdentityMessage', - type: _i1.getType<_i54.PupilIdentityDto>(), + type: _i1.getType<_i55.PupilIdentityDto>(), nullable: false, ), }, @@ -2691,7 +2709,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'pupil': _i1.ParameterDescription( name: 'pupil', - type: _i1.getType<_i55.PupilData>(), + type: _i1.getType<_i56.PupilData>(), nullable: false, ) }, @@ -2715,7 +2733,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'communicationSkills': _i1.ParameterDescription( name: 'communicationSkills', - type: _i1.getType<_i56.CommunicationSkills?>(), + type: _i1.getType<_i57.CommunicationSkills?>(), nullable: true, ), }, @@ -2740,7 +2758,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'tutorInfo': _i1.ParameterDescription( name: 'tutorInfo', - type: _i1.getType<_i57.TutorInfo?>(), + type: _i1.getType<_i58.TutorInfo?>(), nullable: true, ), }, @@ -2760,7 +2778,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'siblingsTutorInfo': _i1.ParameterDescription( name: 'siblingsTutorInfo', - type: _i1.getType<_i58.SiblingsTutorInfo>(), + type: _i1.getType<_i59.SiblingsTutorInfo>(), nullable: false, ) }, @@ -2794,7 +2812,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'documentType': _i1.ParameterDescription( name: 'documentType', - type: _i1.getType<_i52.PupilDocumentType>(), + type: _i1.getType<_i53.PupilDocumentType>(), nullable: false, ), }, @@ -2889,7 +2907,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'preSchoolMedicalStatus': _i1.ParameterDescription( name: 'preSchoolMedicalStatus', - type: _i1.getType<_i49.PreSchoolMedicalStatus>(), + type: _i1.getType<_i50.PreSchoolMedicalStatus>(), nullable: false, ), 'updatedBy': _i1.ParameterDescription( @@ -2920,7 +2938,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'publicMediaAuth': _i1.ParameterDescription( name: 'publicMediaAuth', - type: _i1.getType<_i59.PublicMediaAuth>(), + type: _i1.getType<_i60.PublicMediaAuth>(), nullable: false, ), }, @@ -2940,7 +2958,7 @@ class Endpoints extends _i1.EndpointDispatch { params: { 'supportLevel': _i1.ParameterDescription( name: 'supportLevel', - type: _i1.getType<_i60.SupportLevel>(), + type: _i1.getType<_i61.SupportLevel>(), nullable: false, ), 'pupilId': _i1.ParameterDescription( @@ -2995,7 +3013,7 @@ class Endpoints extends _i1.EndpointDispatch { ), 'afterSchoolCare': _i1.ParameterDescription( name: 'afterSchoolCare', - type: _i1.getType<_i61.AfterSchoolCare>(), + type: _i1.getType<_i62.AfterSchoolCare>(), nullable: false, ), }, @@ -3016,25 +3034,6 @@ class Endpoints extends _i1.EndpointDispatch { name: 'schoolData', endpoint: endpoints['schoolData']!, methodConnectors: { - 'postSchoolData': _i1.MethodConnector( - name: 'postSchoolData', - params: { - 'schoolData': _i1.ParameterDescription( - name: 'schoolData', - type: _i1.getType<_i62.SchoolData>(), - nullable: false, - ) - }, - call: ( - _i1.Session session, - Map params, - ) async => - (endpoints['schoolData'] as _i21.SchoolDataEndpoint) - .postSchoolData( - session, - params['schoolData'], - ), - ), 'getSchoolData': _i1.MethodConnector( name: 'getSchoolData', params: {}, @@ -3044,7 +3043,7 @@ class Endpoints extends _i1.EndpointDispatch { ) async => (endpoints['schoolData'] as _i21.SchoolDataEndpoint) .getSchoolData(session), - ), + ) }, ); connectors['schoolList'] = _i1.EndpointConnector( @@ -3144,7 +3143,7 @@ class Endpoints extends _i1.EndpointDispatch { 'updateMembers': _i1.ParameterDescription( name: 'updateMembers', type: _i1.getType< - ({_i40.MemberOperation operation, List pupilIds})?>(), + ({_i41.MemberOperation operation, List pupilIds})?>(), nullable: true, ), }, diff --git a/school_data_hub_server/lib/src/generated/protocol.yaml b/school_data_hub_server/lib/src/generated/protocol.yaml index 7e863ffc..476d6eea 100644 --- a/school_data_hub_server/lib/src/generated/protocol.yaml +++ b/school_data_hub_server/lib/src/generated/protocol.yaml @@ -8,6 +8,7 @@ admin: - updateBackendPupilDataState: - importCompetencesFromJsonFile: - importSupportCategoriesFromJsonFile: + - postSchoolData: missedSchoolday: - streamMissedSchooldays: - postMissedSchoolday: @@ -130,7 +131,6 @@ pupilUpdate: - updateSchoolyearHeldBackDate: - updateAfterSchoolCare: schoolData: - - postSchoolData: - getSchoolData: schoolList: - fetchSchoolLists: diff --git a/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart b/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart index b9410deb..828294fe 100644 --- a/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart +++ b/school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart @@ -24,65 +24,65 @@ import 'package:school_data_hub_server/src/generated/_features/learning/models/c as _i7; import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category.dart' as _i8; -import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/school_data/models/school_data.dart' as _i9; -import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday.dart' +import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday_dto.dart' as _i10; -import 'package:serverpod_auth_server/serverpod_auth_server.dart' as _i11; +import 'package:school_data_hub_server/src/generated/_features/attendance/models/missed_schoolday.dart' + as _i11; +import 'package:serverpod_auth_server/serverpod_auth_server.dart' as _i12; import 'package:school_data_hub_server/src/generated/_features/auth/models/user_device.dart' - as _i12; -import 'package:school_data_hub_server/src/generated/_features/auth/models/device_info.dart' as _i13; -import 'package:school_data_hub_server/src/generated/protocol.dart' as _i14; +import 'package:school_data_hub_server/src/generated/_features/auth/models/device_info.dart' + as _i14; +import 'package:school_data_hub_server/src/generated/protocol.dart' as _i15; import 'package:school_data_hub_server/src/generated/_features/authorizations/models/authorization.dart' - as _i15; -import 'package:school_data_hub_server/src/generated/_shared/models/member_operation.dart' as _i16; -import 'package:school_data_hub_server/src/generated/_features/authorizations/models/pupil_authorization.dart' +import 'package:school_data_hub_server/src/generated/_shared/models/member_operation.dart' as _i17; -import 'package:school_data_hub_server/src/generated/_features/books/models/book_tagging/book_tag.dart' +import 'package:school_data_hub_server/src/generated/_features/authorizations/models/pupil_authorization.dart' as _i18; -import 'package:school_data_hub_server/src/generated/_features/books/models/book.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book_tagging/book_tag.dart' as _i19; -import 'package:school_data_hub_server/src/generated/_features/books/models/book_stats_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book.dart' as _i20; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/book_stats_dto.dart' as _i21; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_location.dart' as _i22; -import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_query.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book.dart' as _i23; -import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/library_book_query.dart' as _i24; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' +import 'package:school_data_hub_server/src/generated/_features/books/models/pupil_book_lending.dart' as _i25; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category_status.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/learning_support_plan.dart' as _i26; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_category_status.dart' as _i27; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical.dart' as _i28; -import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/preschool/pre_school_medical_status.dart' as _i29; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' +import 'package:school_data_hub_server/src/generated/_features/matrix/compulsory_room.dart' as _i30; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/pupil_document_type.dart' as _i31; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level_legacy_dto.dart' as _i32; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/communication_skills.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart' as _i33; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/tutor_info.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/communication_skills.dart' as _i34; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/tutor_info.dart' as _i35; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/public_media_auth.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/dto/siblings_tutor_info_dto.dart' as _i36; -import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/communication/public_media_auth.dart' as _i37; -import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' +import 'package:school_data_hub_server/src/generated/_features/learning_support/models/support_level.dart' as _i38; -import 'package:school_data_hub_server/src/generated/_features/school_data/models/school_data.dart' +import 'package:school_data_hub_server/src/generated/_features/pupil/models/pupil_data/after_school_care/after_school_care.dart' as _i39; import 'package:school_data_hub_server/src/generated/_features/school_lists/models/school_list.dart' as _i40; @@ -731,6 +731,35 @@ class _AdminEndpoint { } }); } + + _i3.Future<_i9.SchoolData> postSchoolData( + _i1.TestSessionBuilder sessionBuilder, + _i9.SchoolData schoolData, + ) async { + return _i1.callAwaitableFunctionAndHandleExceptions(() async { + var _localUniqueSession = + (sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild( + endpoint: 'admin', + method: 'postSchoolData', + ); + try { + var _localCallContext = await _endpointDispatch.getMethodCallContext( + createSessionCallback: (_) => _localUniqueSession, + endpointPath: 'admin', + methodName: 'postSchoolData', + parameters: _i1.testObjectToJson({'schoolData': schoolData}), + serializationManager: _serializationManager, + ); + var _localReturnValue = await (_localCallContext.method.call( + _localUniqueSession, + _localCallContext.arguments, + ) as _i3.Future<_i9.SchoolData>); + return _localReturnValue; + } finally { + await _localUniqueSession.close(); + } + }); + } } class _MissedSchooldayEndpoint { @@ -743,10 +772,10 @@ class _MissedSchooldayEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Stream<_i9.MissedSchooldayDto> streamMissedSchooldays( + _i3.Stream<_i10.MissedSchooldayDto> streamMissedSchooldays( _i1.TestSessionBuilder sessionBuilder) { var _localTestStreamManager = - _i1.TestStreamManager<_i9.MissedSchooldayDto>(); + _i1.TestStreamManager<_i10.MissedSchooldayDto>(); _i1.callStreamFunctionAndHandleExceptions( () async { var _localUniqueSession = @@ -774,9 +803,9 @@ class _MissedSchooldayEndpoint { return _localTestStreamManager.outputStreamController.stream; } - _i3.Future<_i10.MissedSchoolday> postMissedSchoolday( + _i3.Future<_i11.MissedSchoolday> postMissedSchoolday( _i1.TestSessionBuilder sessionBuilder, - _i10.MissedSchoolday missedClass, + _i11.MissedSchoolday missedClass, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -795,7 +824,7 @@ class _MissedSchooldayEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i10.MissedSchoolday>); + ) as _i3.Future<_i11.MissedSchoolday>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -803,9 +832,9 @@ class _MissedSchooldayEndpoint { }); } - _i3.Future> postMissedSchooldays( + _i3.Future> postMissedSchooldays( _i1.TestSessionBuilder sessionBuilder, - List<_i10.MissedSchoolday> missedClasses, + List<_i11.MissedSchoolday> missedClasses, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -824,7 +853,7 @@ class _MissedSchooldayEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -832,7 +861,7 @@ class _MissedSchooldayEndpoint { }); } - _i3.Future> fetchAllMissedSchooldays( + _i3.Future> fetchAllMissedSchooldays( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -851,7 +880,7 @@ class _MissedSchooldayEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -859,7 +888,7 @@ class _MissedSchooldayEndpoint { }); } - _i3.Future> fetchMissedSchooldaysOnASchoolday( + _i3.Future> fetchMissedSchooldaysOnASchoolday( _i1.TestSessionBuilder sessionBuilder, DateTime schoolday, ) async { @@ -880,7 +909,7 @@ class _MissedSchooldayEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -921,9 +950,9 @@ class _MissedSchooldayEndpoint { }); } - _i3.Future<_i10.MissedSchoolday> updateMissedSchoolday( + _i3.Future<_i11.MissedSchoolday> updateMissedSchoolday( _i1.TestSessionBuilder sessionBuilder, - _i10.MissedSchoolday missedSchoolday, + _i11.MissedSchoolday missedSchoolday, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -943,7 +972,7 @@ class _MissedSchooldayEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i10.MissedSchoolday>); + ) as _i3.Future<_i11.MissedSchoolday>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -963,12 +992,12 @@ class _AuthEndpoint { final _i2.SerializationManager _serializationManager; _i3.Future< - ({_i11.AuthenticationResponse response, _i12.UserDevice? userDevice})> + ({_i12.AuthenticationResponse response, _i13.UserDevice? userDevice})> login( _i1.TestSessionBuilder sessionBuilder, String email, String password, - _i13.DeviceInfo deviceInfo, + _i14.DeviceInfo deviceInfo, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -993,10 +1022,10 @@ class _AuthEndpoint { _localUniqueSession, _localCallContext.arguments, ) - .then((record) => _i14.Protocol().deserialize< + .then((record) => _i15.Protocol().deserialize< ({ - _i11.AuthenticationResponse response, - _i12.UserDevice? userDevice + _i12.AuthenticationResponse response, + _i13.UserDevice? userDevice })>(record)); return _localReturnValue; } finally { @@ -1045,7 +1074,7 @@ class _AuthorizationEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future> fetchAuthorizations( + _i3.Future> fetchAuthorizations( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1064,7 +1093,7 @@ class _AuthorizationEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1072,7 +1101,7 @@ class _AuthorizationEndpoint { }); } - _i3.Future<_i15.Authorization?> fetchAuthorizationById( + _i3.Future<_i16.Authorization?> fetchAuthorizationById( _i1.TestSessionBuilder sessionBuilder, int id, ) async { @@ -1093,7 +1122,7 @@ class _AuthorizationEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i15.Authorization?>); + ) as _i3.Future<_i16.Authorization?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1101,7 +1130,7 @@ class _AuthorizationEndpoint { }); } - _i3.Future<_i15.Authorization> postAuthorizationWithPupils( + _i3.Future<_i16.Authorization> postAuthorizationWithPupils( _i1.TestSessionBuilder sessionBuilder, String name, String description, @@ -1130,7 +1159,7 @@ class _AuthorizationEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i15.Authorization>); + ) as _i3.Future<_i16.Authorization>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1138,12 +1167,12 @@ class _AuthorizationEndpoint { }); } - _i3.Future<_i15.Authorization> updateAuthorization( + _i3.Future<_i16.Authorization> updateAuthorization( _i1.TestSessionBuilder sessionBuilder, int authId, String? name, String? description, - ({_i16.MemberOperation operation, List pupilIds})? updateMembers, + ({_i17.MemberOperation operation, List pupilIds})? updateMembers, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1160,14 +1189,14 @@ class _AuthorizationEndpoint { 'authId': authId, 'name': name, 'description': description, - 'updateMembers': _i14.mapRecordToJson(updateMembers), + 'updateMembers': _i15.mapRecordToJson(updateMembers), }), serializationManager: _serializationManager, ); var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i15.Authorization>); + ) as _i3.Future<_i16.Authorization>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1215,9 +1244,9 @@ class _PupilAuthorizationEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i17.PupilAuthorization> updatePupilAuthorization( + _i3.Future<_i18.PupilAuthorization> updatePupilAuthorization( _i1.TestSessionBuilder sessionBuilder, - _i17.PupilAuthorization authorization, + _i18.PupilAuthorization authorization, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1236,7 +1265,7 @@ class _PupilAuthorizationEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i17.PupilAuthorization>); + ) as _i3.Future<_i18.PupilAuthorization>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1244,7 +1273,7 @@ class _PupilAuthorizationEndpoint { }); } - _i3.Future<_i17.PupilAuthorization> addFileToPupilAuthorization( + _i3.Future<_i18.PupilAuthorization> addFileToPupilAuthorization( _i1.TestSessionBuilder sessionBuilder, int pupilAuthId, String filePath, @@ -1271,7 +1300,7 @@ class _PupilAuthorizationEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i17.PupilAuthorization>); + ) as _i3.Future<_i18.PupilAuthorization>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1279,7 +1308,7 @@ class _PupilAuthorizationEndpoint { }); } - _i3.Future<_i17.PupilAuthorization> removeFileFromPupilAuthorization( + _i3.Future<_i18.PupilAuthorization> removeFileFromPupilAuthorization( _i1.TestSessionBuilder sessionBuilder, int pupilAuthId, ) async { @@ -1300,7 +1329,7 @@ class _PupilAuthorizationEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i17.PupilAuthorization>); + ) as _i3.Future<_i18.PupilAuthorization>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1319,9 +1348,9 @@ class _BookTagsEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i18.BookTag> postBookTag( + _i3.Future<_i19.BookTag> postBookTag( _i1.TestSessionBuilder sessionBuilder, - _i18.BookTag bookTag, + _i19.BookTag bookTag, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1340,7 +1369,7 @@ class _BookTagsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i18.BookTag>); + ) as _i3.Future<_i19.BookTag>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1348,7 +1377,7 @@ class _BookTagsEndpoint { }); } - _i3.Future> fetchBookTags( + _i3.Future> fetchBookTags( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1367,7 +1396,7 @@ class _BookTagsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1375,9 +1404,9 @@ class _BookTagsEndpoint { }); } - _i3.Future<_i18.BookTag> updateBookTag( + _i3.Future<_i19.BookTag> updateBookTag( _i1.TestSessionBuilder sessionBuilder, - _i18.BookTag bookTag, + _i19.BookTag bookTag, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1396,7 +1425,7 @@ class _BookTagsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i18.BookTag>); + ) as _i3.Future<_i19.BookTag>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1406,7 +1435,7 @@ class _BookTagsEndpoint { _i3.Future deleteBookTag( _i1.TestSessionBuilder sessionBuilder, - _i18.BookTag bookTag, + _i19.BookTag bookTag, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1444,9 +1473,9 @@ class _BooksEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i19.Book> postBook( + _i3.Future<_i20.Book> postBook( _i1.TestSessionBuilder sessionBuilder, - _i19.Book book, + _i20.Book book, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1465,7 +1494,7 @@ class _BooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i19.Book>); + ) as _i3.Future<_i20.Book>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1473,7 +1502,7 @@ class _BooksEndpoint { }); } - _i3.Future> fetchBooks( + _i3.Future> fetchBooks( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1492,7 +1521,7 @@ class _BooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1500,7 +1529,7 @@ class _BooksEndpoint { }); } - _i3.Future<_i20.LibraryBookStatsDto> getBookStats( + _i3.Future<_i21.LibraryBookStatsDto> getBookStats( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1519,7 +1548,7 @@ class _BooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i20.LibraryBookStatsDto>); + ) as _i3.Future<_i21.LibraryBookStatsDto>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1527,7 +1556,7 @@ class _BooksEndpoint { }); } - _i3.Future<_i19.Book?> fetchBookByIsbn( + _i3.Future<_i20.Book?> fetchBookByIsbn( _i1.TestSessionBuilder sessionBuilder, int isbn, ) async { @@ -1548,7 +1577,7 @@ class _BooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i19.Book?>); + ) as _i3.Future<_i20.Book?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1556,10 +1585,10 @@ class _BooksEndpoint { }); } - _i3.Future<_i19.Book> updateBookTags( + _i3.Future<_i20.Book> updateBookTags( _i1.TestSessionBuilder sessionBuilder, int isbn, { - List<_i18.BookTag>? tags, + List<_i19.BookTag>? tags, }) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1581,7 +1610,7 @@ class _BooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i19.Book>); + ) as _i3.Future<_i20.Book>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1629,9 +1658,9 @@ class _LibraryBookLocationsEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i21.LibraryBookLocation> postLibraryBookLocation( + _i3.Future<_i22.LibraryBookLocation> postLibraryBookLocation( _i1.TestSessionBuilder sessionBuilder, - _i21.LibraryBookLocation libraryBookLocation, + _i22.LibraryBookLocation libraryBookLocation, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1651,7 +1680,7 @@ class _LibraryBookLocationsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i21.LibraryBookLocation>); + ) as _i3.Future<_i22.LibraryBookLocation>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1659,7 +1688,7 @@ class _LibraryBookLocationsEndpoint { }); } - _i3.Future> fetchLibraryBookLocations( + _i3.Future> fetchLibraryBookLocations( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1678,7 +1707,7 @@ class _LibraryBookLocationsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1686,9 +1715,9 @@ class _LibraryBookLocationsEndpoint { }); } - _i3.Future<_i21.LibraryBookLocation> updateLibraryBookLocation( + _i3.Future<_i22.LibraryBookLocation> updateLibraryBookLocation( _i1.TestSessionBuilder sessionBuilder, - _i21.LibraryBookLocation libraryBookLocation, + _i22.LibraryBookLocation libraryBookLocation, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1708,7 +1737,7 @@ class _LibraryBookLocationsEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i21.LibraryBookLocation>); + ) as _i3.Future<_i22.LibraryBookLocation>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1718,7 +1747,7 @@ class _LibraryBookLocationsEndpoint { _i3.Future deleteLibraryBookLocation( _i1.TestSessionBuilder sessionBuilder, - _i21.LibraryBookLocation location, + _i22.LibraryBookLocation location, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1756,11 +1785,11 @@ class _LibraryBooksEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i22.LibraryBook> postLibraryBook( + _i3.Future<_i23.LibraryBook> postLibraryBook( _i1.TestSessionBuilder sessionBuilder, int isbn, String libraryId, - _i21.LibraryBookLocation location, + _i22.LibraryBookLocation location, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1783,7 +1812,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i22.LibraryBook>); + ) as _i3.Future<_i23.LibraryBook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1791,7 +1820,7 @@ class _LibraryBooksEndpoint { }); } - _i3.Future> fetchLibraryBooks( + _i3.Future> fetchLibraryBooks( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1810,7 +1839,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1818,7 +1847,7 @@ class _LibraryBooksEndpoint { }); } - _i3.Future<_i22.LibraryBook?> fetchLibraryBookByIsbn( + _i3.Future<_i23.LibraryBook?> fetchLibraryBookByIsbn( _i1.TestSessionBuilder sessionBuilder, int isbn, ) async { @@ -1839,7 +1868,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i22.LibraryBook?>); + ) as _i3.Future<_i23.LibraryBook?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1847,7 +1876,7 @@ class _LibraryBooksEndpoint { }); } - _i3.Future<_i22.LibraryBook?> fetchLibraryBookByLibraryId( + _i3.Future<_i23.LibraryBook?> fetchLibraryBookByLibraryId( _i1.TestSessionBuilder sessionBuilder, String libraryId, ) async { @@ -1868,7 +1897,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i22.LibraryBook?>); + ) as _i3.Future<_i23.LibraryBook?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1876,9 +1905,9 @@ class _LibraryBooksEndpoint { }); } - _i3.Future> fetchLibraryBooksMatchingQuery( + _i3.Future> fetchLibraryBooksMatchingQuery( _i1.TestSessionBuilder sessionBuilder, - _i23.LibraryBookQuery libraryBookQuery, + _i24.LibraryBookQuery libraryBookQuery, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1898,7 +1927,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -1906,17 +1935,17 @@ class _LibraryBooksEndpoint { }); } - _i3.Future<_i22.LibraryBook> updateLibraryBookAndRelatedBook( + _i3.Future<_i23.LibraryBook> updateLibraryBookAndRelatedBook( _i1.TestSessionBuilder sessionBuilder, int isbn, String libraryId, bool? available, - _i21.LibraryBookLocation? location, + _i22.LibraryBookLocation? location, String? title, String? author, String? description, String? readingLevel, - List<_i18.BookTag>? tags, + List<_i19.BookTag>? tags, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -1945,7 +1974,7 @@ class _LibraryBooksEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i22.LibraryBook>); + ) as _i3.Future<_i23.LibraryBook>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2028,7 +2057,7 @@ class _PupilBookLendingEndpoint { }); } - _i3.Future> fetchPupilBookLendings( + _i3.Future> fetchPupilBookLendings( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2047,7 +2076,7 @@ class _PupilBookLendingEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2055,7 +2084,7 @@ class _PupilBookLendingEndpoint { }); } - _i3.Future<_i24.PupilBookLending?> fetchPupilBookLendingByLendingId( + _i3.Future<_i25.PupilBookLending?> fetchPupilBookLendingByLendingId( _i1.TestSessionBuilder sessionBuilder, String lendingId, ) async { @@ -2076,7 +2105,7 @@ class _PupilBookLendingEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i24.PupilBookLending?>); + ) as _i3.Future<_i25.PupilBookLending?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2086,7 +2115,7 @@ class _PupilBookLendingEndpoint { _i3.Future<_i6.PupilData> updatePupilBookLending( _i1.TestSessionBuilder sessionBuilder, - _i24.PupilBookLending pupilBookLending, + _i25.PupilBookLending pupilBookLending, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2216,10 +2245,10 @@ class _CompetenceCheckEndpoint { methodName: 'updateCompetenceCheck', parameters: _i1.testObjectToJson({ 'checkId': checkId, - 'score': _i14.mapRecordToJson(score), - 'valueFactor': _i14.mapRecordToJson(valueFactor), - 'createdBy': _i14.mapRecordToJson(createdBy), - 'comment': _i14.mapRecordToJson(comment), + 'score': _i15.mapRecordToJson(score), + 'valueFactor': _i15.mapRecordToJson(valueFactor), + 'createdBy': _i15.mapRecordToJson(createdBy), + 'comment': _i15.mapRecordToJson(comment), }), serializationManager: _serializationManager, ); @@ -2475,7 +2504,7 @@ class _LearningSupportPlanEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future> fetchLearningSupportPlans( + _i3.Future> fetchLearningSupportPlans( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2494,7 +2523,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2502,9 +2531,9 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future<_i25.LearningSupportPlan> createLearningSupportPlan( + _i3.Future<_i26.LearningSupportPlan> createLearningSupportPlan( _i1.TestSessionBuilder sessionBuilder, - _i25.LearningSupportPlan plan, + _i26.LearningSupportPlan plan, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2523,7 +2552,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i25.LearningSupportPlan>); + ) as _i3.Future<_i26.LearningSupportPlan>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2533,7 +2562,7 @@ class _LearningSupportPlanEndpoint { _i3.Future updateLearningSupportPlan( _i1.TestSessionBuilder sessionBuilder, - _i25.LearningSupportPlan plan, + _i26.LearningSupportPlan plan, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2562,7 +2591,7 @@ class _LearningSupportPlanEndpoint { _i3.Future deleteLearningSupportPlan( _i1.TestSessionBuilder sessionBuilder, - _i25.LearningSupportPlan plan, + _i26.LearningSupportPlan plan, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -2630,7 +2659,7 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future> fetchSupportCategoryStatus( + _i3.Future> fetchSupportCategoryStatus( _i1.TestSessionBuilder sessionBuilder, int pupilId, ) async { @@ -2651,7 +2680,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2659,7 +2688,7 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future> + _i3.Future> fetchSupportCategoryStatusFromPupil( _i1.TestSessionBuilder sessionBuilder, int pupilId, @@ -2681,7 +2710,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2689,7 +2718,7 @@ class _LearningSupportPlanEndpoint { }); } - _i3.Future<_i26.SupportCategoryStatus> updateCategoryStatus( + _i3.Future<_i27.SupportCategoryStatus> updateCategoryStatus( _i1.TestSessionBuilder sessionBuilder, int pupilId, int supportCategoryId, @@ -2722,7 +2751,7 @@ class _LearningSupportPlanEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i26.SupportCategoryStatus>); + ) as _i3.Future<_i27.SupportCategoryStatus>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2813,10 +2842,10 @@ class _PreSchoolMedicalEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i27.PreSchoolMedical> createPreSchoolMedical( + _i3.Future<_i28.PreSchoolMedical> createPreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i28.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i29.PreSchoolMedicalStatus? preschoolMedicalStatus, String createdBy, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -2840,7 +2869,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i27.PreSchoolMedical>); + ) as _i3.Future<_i28.PreSchoolMedical>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2848,10 +2877,10 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i27.PreSchoolMedical> updatePreSchoolMedical( + _i3.Future<_i28.PreSchoolMedical> updatePreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int preSchoolMedicalId, - _i28.PreSchoolMedicalStatus? preschoolMedicalStatus, + _i29.PreSchoolMedicalStatus? preschoolMedicalStatus, String updatedBy, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -2875,7 +2904,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i27.PreSchoolMedical>); + ) as _i3.Future<_i28.PreSchoolMedical>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2883,7 +2912,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i27.PreSchoolMedical?> getPreSchoolMedical( + _i3.Future<_i28.PreSchoolMedical?> getPreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int preSchoolMedicalId, ) async { @@ -2905,7 +2934,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i27.PreSchoolMedical?>); + ) as _i3.Future<_i28.PreSchoolMedical?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2913,7 +2942,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i27.PreSchoolMedical?> getPreSchoolMedicalByPupilId( + _i3.Future<_i28.PreSchoolMedical?> getPreSchoolMedicalByPupilId( _i1.TestSessionBuilder sessionBuilder, int pupilId, ) async { @@ -2934,7 +2963,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i27.PreSchoolMedical?>); + ) as _i3.Future<_i28.PreSchoolMedical?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -2972,7 +3001,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future<_i27.PreSchoolMedical> addFileToPreSchoolMedical( + _i3.Future<_i28.PreSchoolMedical> addFileToPreSchoolMedical( _i1.TestSessionBuilder sessionBuilder, int preSchoolMedicalId, String filePath, @@ -2999,7 +3028,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i27.PreSchoolMedical>); + ) as _i3.Future<_i28.PreSchoolMedical>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3040,7 +3069,7 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future> getAllPreSchoolMedicalRecords( + _i3.Future> getAllPreSchoolMedicalRecords( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3059,7 +3088,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3067,9 +3096,9 @@ class _PreSchoolMedicalEndpoint { }); } - _i3.Future> getPreSchoolMedicalByStatus( + _i3.Future> getPreSchoolMedicalByStatus( _i1.TestSessionBuilder sessionBuilder, - _i28.PreSchoolMedicalStatus status, + _i29.PreSchoolMedicalStatus status, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3088,7 +3117,7 @@ class _PreSchoolMedicalEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3261,7 +3290,7 @@ class _MatrixEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future?> getCompulsoryRooms( + _i3.Future?> getCompulsoryRooms( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3280,7 +3309,7 @@ class _MatrixEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future?>); + ) as _i3.Future?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3288,9 +3317,9 @@ class _MatrixEndpoint { }); } - _i3.Future> setCompulsoryRooms( + _i3.Future> setCompulsoryRooms( _i1.TestSessionBuilder sessionBuilder, - List<_i29.CompulsoryRoom> compulsoryRooms, + List<_i30.CompulsoryRoom> compulsoryRooms, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3310,7 +3339,7 @@ class _MatrixEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future>); + ) as _i3.Future>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -3447,7 +3476,7 @@ class _PupilEndpoint { _i3.Future<_i6.PupilData> deletePupilDocument( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i30.PupilDocumentType documentType, + _i31.PupilDocumentType documentType, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3545,7 +3574,7 @@ class _PupilEndpoint { _i3.Future bulkAddSupportLevels( _i1.TestSessionBuilder sessionBuilder, - List<_i31.SupportLevelLegacyDto> supportLevelData, + List<_i32.SupportLevelLegacyDto> supportLevelData, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3584,12 +3613,12 @@ class _PupilIdentityEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Stream<_i32.PupilIdentityDto> streamEncryptedPupilIds( + _i3.Stream<_i33.PupilIdentityDto> streamEncryptedPupilIds( _i1.TestSessionBuilder sessionBuilder, String channelName, ) { var _localTestStreamManager = - _i1.TestStreamManager<_i32.PupilIdentityDto>(); + _i1.TestStreamManager<_i33.PupilIdentityDto>(); _i1.callStreamFunctionAndHandleExceptions( () async { var _localUniqueSession = @@ -3620,7 +3649,7 @@ class _PupilIdentityEndpoint { _i3.Future sendPupilIdentityMessage( _i1.TestSessionBuilder sessionBuilder, String pupilIdentityChannel, - _i32.PupilIdentityDto pupilIdentityMessage, + _i33.PupilIdentityDto pupilIdentityMessage, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3776,7 +3805,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateCommunicationSkills( _i1.TestSessionBuilder sessionBuilder, { required int pupilId, - required _i33.CommunicationSkills? communicationSkills, + required _i34.CommunicationSkills? communicationSkills, }) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3809,7 +3838,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateTutorInfo( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i34.TutorInfo? tutorInfo, + _i35.TutorInfo? tutorInfo, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3841,7 +3870,7 @@ class _PupilUpdateEndpoint { _i3.Future> updateSiblingsTutorInfo( _i1.TestSessionBuilder sessionBuilder, - _i35.SiblingsTutorInfo siblingsTutorInfo, + _i36.SiblingsTutorInfo siblingsTutorInfo, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3874,7 +3903,7 @@ class _PupilUpdateEndpoint { int pupilId, String filePath, String createdBy, - _i30.PupilDocumentType documentType, + _i31.PupilDocumentType documentType, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -3981,7 +4010,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updatePreSchoolMedicalStatus( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i28.PreSchoolMedicalStatus preSchoolMedicalStatus, + _i29.PreSchoolMedicalStatus preSchoolMedicalStatus, String updatedBy, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -4016,7 +4045,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updatePublicMediaAuth( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i36.PublicMediaAuth publicMediaAuth, + _i37.PublicMediaAuth publicMediaAuth, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4048,7 +4077,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateSupportLevel( _i1.TestSessionBuilder sessionBuilder, - _i37.SupportLevel supportLevel, + _i38.SupportLevel supportLevel, int pupilId, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { @@ -4098,7 +4127,7 @@ class _PupilUpdateEndpoint { parameters: _i1.testObjectToJson({ 'pupilId': pupilId, 'schoolyearHeldBackDate': - _i14.mapRecordToJson(schoolyearHeldBackDate), + _i15.mapRecordToJson(schoolyearHeldBackDate), }), serializationManager: _serializationManager, ); @@ -4116,7 +4145,7 @@ class _PupilUpdateEndpoint { _i3.Future<_i6.PupilData> updateAfterSchoolCare( _i1.TestSessionBuilder sessionBuilder, int pupilId, - _i38.AfterSchoolCare afterSchoolCare, + _i39.AfterSchoolCare afterSchoolCare, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4157,36 +4186,7 @@ class _SchoolDataEndpoint { final _i2.SerializationManager _serializationManager; - _i3.Future<_i39.SchoolData> postSchoolData( - _i1.TestSessionBuilder sessionBuilder, - _i39.SchoolData schoolData, - ) async { - return _i1.callAwaitableFunctionAndHandleExceptions(() async { - var _localUniqueSession = - (sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild( - endpoint: 'schoolData', - method: 'postSchoolData', - ); - try { - var _localCallContext = await _endpointDispatch.getMethodCallContext( - createSessionCallback: (_) => _localUniqueSession, - endpointPath: 'schoolData', - methodName: 'postSchoolData', - parameters: _i1.testObjectToJson({'schoolData': schoolData}), - serializationManager: _serializationManager, - ); - var _localReturnValue = await (_localCallContext.method.call( - _localUniqueSession, - _localCallContext.arguments, - ) as _i3.Future<_i39.SchoolData>); - return _localReturnValue; - } finally { - await _localUniqueSession.close(); - } - }); - } - - _i3.Future<_i39.SchoolData?> getSchoolData( + _i3.Future<_i9.SchoolData?> getSchoolData( _i1.TestSessionBuilder sessionBuilder) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4205,7 +4205,7 @@ class _SchoolDataEndpoint { var _localReturnValue = await (_localCallContext.method.call( _localUniqueSession, _localCallContext.arguments, - ) as _i3.Future<_i39.SchoolData?>); + ) as _i3.Future<_i9.SchoolData?>); return _localReturnValue; } finally { await _localUniqueSession.close(); @@ -4299,7 +4299,7 @@ class _SchoolListEndpoint { String? description, ({String? value})? authorizedUsers, bool? public, - ({_i16.MemberOperation operation, List pupilIds})? updateMembers, + ({_i17.MemberOperation operation, List pupilIds})? updateMembers, ) async { return _i1.callAwaitableFunctionAndHandleExceptions(() async { var _localUniqueSession = @@ -4316,9 +4316,9 @@ class _SchoolListEndpoint { 'listId': listId, 'name': name, 'description': description, - 'authorizedUsers': _i14.mapRecordToJson(authorizedUsers), + 'authorizedUsers': _i15.mapRecordToJson(authorizedUsers), 'public': public, - 'updateMembers': _i14.mapRecordToJson(updateMembers), + 'updateMembers': _i15.mapRecordToJson(updateMembers), }), serializationManager: _serializationManager, ); From fb63bdb64dfffe5a9cfd578cef110b99aa88306e Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 13:43:16 +0100 Subject: [PATCH 28/52] [APP] fix notification service logs --- .../lib/common/services/notification_service.dart | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/school_data_hub_flutter/lib/common/services/notification_service.dart b/school_data_hub_flutter/lib/common/services/notification_service.dart index a0457cb0..e11bf8c4 100644 --- a/school_data_hub_flutter/lib/common/services/notification_service.dart +++ b/school_data_hub_flutter/lib/common/services/notification_service.dart @@ -33,12 +33,9 @@ class NotificationService { if (_loadingNewInstance.value) { return; } + _log.fine( - '=================================================== SNACK BAR START ===================================================', - ); - _log.info(message); - _log.fine( - '=================================================== SNACK BAR END ===================================================', + '====================================================================================== SNACK BAR MESSAGE: ${type.name} $message', ); _snackBar.value = NotificationData(type, message); @@ -46,12 +43,9 @@ class NotificationService { void showInformationDialog(String message) { _snackBar.value = NotificationData(NotificationType.dialog, message); + _log.fine( - '=================================================== INFORMATION DIALOG START ===================================================', - ); - _log.info(message); - _log.fine( - '=================================================== INFORMATION DIALOG END ===================================================', + '====================================================================================== INFORMATION DIALOG: $message', ); } From 1503c1fa027a8935fe29af639ef5b9c90ef15722 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 13:56:35 +0100 Subject: [PATCH 29/52] WIP --- .../lib/core/router/app_router.dart | 549 ++++++++++++++++++ school_data_hub_flutter/pubspec.lock | 8 + school_data_hub_flutter/pubspec.yaml | 1 + 3 files changed, 558 insertions(+) create mode 100644 school_data_hub_flutter/lib/core/router/app_router.dart diff --git a/school_data_hub_flutter/lib/core/router/app_router.dart b/school_data_hub_flutter/lib/core/router/app_router.dart new file mode 100644 index 00000000..e39bf468 --- /dev/null +++ b/school_data_hub_flutter/lib/core/router/app_router.dart @@ -0,0 +1,549 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/app_utils/scanner.dart'; +import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart'; +import 'package:school_data_hub_flutter/core/session/serverpod_connectivity_monitor.dart'; +import 'package:school_data_hub_flutter/features/_attendance/presentation/attendance_page/attendance_list_page.dart'; +import 'package:school_data_hub_flutter/features/_attendance/presentation/missed_classes_pupil_list_page/missed_classes_pupil_list_page.dart'; +import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart'; +import 'package:school_data_hub_flutter/features/app_entry_point/entry_point/entry_point_controller.dart'; +import 'package:school_data_hub_flutter/features/app_entry_point/error_page.dart'; +import 'package:school_data_hub_flutter/features/app_entry_point/loading_page.dart'; +import 'package:school_data_hub_flutter/features/app_entry_point/login_page/login_controller.dart'; +import 'package:school_data_hub_flutter/features/app_entry_point/no_connection_page.dart'; +import 'package:school_data_hub_flutter/features/app_main_navigation/learn_resources_menu_page.dart'; +import 'package:school_data_hub_flutter/features/app_main_navigation/pupil_lists_menu_page.dart'; +import 'package:school_data_hub_flutter/features/app_main_navigation/school_lists_page.dart'; +import 'package:school_data_hub_flutter/features/app_main_navigation/tools_page.dart'; +import 'package:school_data_hub_flutter/features/app_settings/settings_page/settings_page.dart'; +import 'package:school_data_hub_flutter/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart'; +import 'package:school_data_hub_flutter/features/books/domain/models/enums.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/book_list_page/book_list_page.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/book_search_form/book_search_form_page.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/book_search_form/select_book_tags_page.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/book_search_page/book_search_results_page.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/books_main_menu_page/books_main_menu_page.dart'; +import 'package:school_data_hub_flutter/features/books/presentation/new_book_page/new_book_controller.dart'; +import 'package:school_data_hub_flutter/features/learning/presentation/competence_list_page/competence_list_page.dart'; +import 'package:school_data_hub_flutter/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart'; +import 'package:school_data_hub_flutter/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart'; +import 'package:school_data_hub_flutter/features/learning_support/presentation/new_learning_support_plan/controller/new_learning_support_plan_controller.dart'; +import 'package:school_data_hub_flutter/features/learning_support/presentation/new_support_category_status_page/controller/new_support_category_status_controller.dart'; +import 'package:school_data_hub_flutter/features/learning_support/presentation/support_category_list_page/controller/category_list_controller.dart'; +import 'package:school_data_hub_flutter/features/learning_support/services/learning_support_plan_pdf_generator.dart'; +import 'package:school_data_hub_flutter/features/matrix/presentation/set_matrix_environment_page/set_matrix_environment_controller.dart'; +import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart'; +import 'package:school_data_hub_flutter/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart'; +import 'package:school_data_hub_flutter/features/ogs/ogs_list_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/_credit/credit_list_page/credit_list_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/birthdays_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/religion_page/religion_list_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/special_info_page/special_info_list_page.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_set_avatar.dart'; +import 'package:school_data_hub_flutter/features/school/presentation/edit_school_data_page/edit_school_data_page.dart'; +import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart'; +import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/schooldays_calendar_page/schooldays_calendar_page.dart'; +import 'package:school_data_hub_flutter/features/school_lists/presentation/school_lists_page/school_lists_page.dart'; +import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page_controller.dart'; +import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; +import 'package:school_data_hub_flutter/features/timetable/domain/timetable_manager.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/classroom_list_page/classroom_list_page.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/learning_group_list_page/learning_group_list_page.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/new_lesson_group_page/new_lesson_group_page.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/new_scheduled_lesson_page/new_scheduled_lesson_page.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/new_timetable_page/new_timetable_page.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/subject_list_page/subject_list_page.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_page/timetable_page.dart'; +import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_slot_list_page/timetable_slot_list_page.dart'; +import 'package:school_data_hub_flutter/features/user/presentation/change_password/change_password_page.dart'; +import 'package:school_data_hub_flutter/features/user/presentation/create_user/create_user_page.dart'; +import 'package:school_data_hub_flutter/features/user/presentation/reset_password/reset_user_password_page.dart'; +import 'package:school_data_hub_flutter/features/user/presentation/user_list/user_list_page.dart'; +import 'package:school_data_hub_flutter/features/workbooks/presentation/workbook_list_page/controller/workbook_list_view_model.dart'; +import 'package:watch_it/watch_it.dart'; + +class AppRouter { + static final GlobalKey rootNavigatorKey = + GlobalKey(debugLabel: 'root'); + + late final GoRouter router; + + AppRouter() { + router = GoRouter( + navigatorKey: rootNavigatorKey, + initialLocation: '/', + refreshListenable: Listenable.merge([ + di().isAuthenticated, + di().envIsReady, + di().isConnected, + ]), + redirect: (context, state) { + final envManager = di(); + final connectivityMonitor = di(); + + final isConnected = connectivityMonitor.isConnected.value; + final envIsReady = envManager.envIsReady.value; + final isAuthenticated = envManager.isAuthenticated.value; + + final path = state.uri.path; + + final isLoginLoc = path == '/login'; + final isLoadingLoc = path == '/loading'; + final isNoConnectionLoc = path == '/no-connection'; + final isEntryPointLoc = path == '/'; + + // 1. Connection Check + if (!isConnected) { + return isNoConnectionLoc ? null : '/no-connection'; + } + if (isNoConnectionLoc && isConnected) { + // If connection restored, verify where to go based on other states + if (envIsReady && isAuthenticated) return '/home'; + if (envIsReady && !isAuthenticated) return '/login'; + return '/'; + } + + // 2. Environment Ready Check + if (!envIsReady) { + // If we are trying to load an active environment + if (envManager.activeEnv != null) { + return isLoadingLoc ? null : '/loading'; + } + + // Handle deep link when env is not ready - redirect to login with preserved parameters + if (isEntryPointLoc && + state.uri.queryParameters.containsKey('channelName')) { + final from = state.uri.toString(); + return '/login?from=${Uri.encodeComponent(from)}'; + } + + // Otherwise we are at entry point selection + return isEntryPointLoc ? null : '/'; + } + + // 3. Auth Check + if (!isAuthenticated) { + if (isLoginLoc) return null; + + // Don't redirect system pages to login with return param + if (isEntryPointLoc || isLoadingLoc || isNoConnectionLoc) { + // If it's entry point with deep link params, preserve them for login redirect + if (isEntryPointLoc && + state.uri.queryParameters.containsKey('channelName')) { + final from = state.uri.toString(); + return '/login?from=${Uri.encodeComponent(from)}'; + } + return '/login'; + } + + // Save intended location for deep links + final from = state.uri.toString(); + return '/login?from=${Uri.encodeComponent(from)}'; + } + + // 4. Authenticated User + if (isAuthenticated) { + if (isLoginLoc || isEntryPointLoc || isLoadingLoc) { + final from = state.uri.queryParameters['from']; + if (from != null && from.isNotEmpty) return from; + return '/home'; + } + } + + // 5. Deep Link Handling + // If authenticated, we allow pass-through to feature routes + + // Handle redirect for root path with stream parameters + if (path == '/' && + state.uri.queryParameters.containsKey('channelName')) { + final channelName = state.uri.queryParameters['channelName']; + final url = state.uri.queryParameters['url']; + + // Force redirect to the stream page + return '/pupil-identity-stream?code=$channelName&instance=$url'; + } + + return null; + }, + routes: [ + GoRoute(path: '/', builder: (context, state) => const EntryPoint()), + GoRoute(path: '/login', builder: (context, state) => const Login()), + GoRoute( + path: '/loading', + builder: (context, state) => const LoadingPage(), + ), + GoRoute( + path: '/no-connection', + builder: (context, state) => const NoConnectionPage(), + ), + // Deep Link Route for Pupil Identity Stream Receiver + GoRoute( + path: '/pupil-identity-stream', + builder: (context, state) { + final code = state.uri.queryParameters['code']; + final instanceUrl = state.uri.queryParameters['instance']; + final extra = state.extra as Map?; + + if (extra != null && + extra['role'] == PupilIdentityStreamRole.sender) { + return PupilIdentityStreamPage( + role: PupilIdentityStreamRole.sender, + encryptedData: extra['encryptedData'], + selectedPupilIds: extra['selectedPupilIds'], + ); + } + + return PupilIdentityStreamPage( + role: PupilIdentityStreamRole.receiver, + importedChannelName: code ?? extra?['importedChannelName'], + ); + }, + ), + GoRoute( + path: '/crop-avatar', + builder: (context, state) { + final image = state.extra as XFile; + return CropAvatarView(image: image); + }, + ), + GoRoute( + path: '/scanner', + builder: (context, state) { + final overlayText = state.extra as String? ?? 'Scanner'; + return ScannerPage(overlayText: overlayText); + }, + ), + GoRoute( + path: '/timetable', + builder: (context, state) => const TimetablePage(), + ), + GoRoute( + path: '/timetable/new', + builder: (context, state) => const NewTimetablePage(), + ), + GoRoute( + path: '/timetable/slots', + builder: (context, state) => const TimetableSlotListPage(), + ), + GoRoute( + path: '/timetable/groups', + builder: (context, state) => const LearningGroupListPage(), + ), + GoRoute( + path: '/timetable/subjects', + builder: (context, state) => const SubjectListPage(), + ), + GoRoute( + path: '/timetable/lesson', + builder: (context, state) { + final extra = state.extra as Map?; + final manager = di(); + + return NewScheduledLessonPage( + timetableManager: manager, + editingLessonId: extra?['editingLessonId'], + preselectedSlotId: extra?['preselectedSlotId'], + ); + }, + ), + GoRoute( + path: '/timetable/lesson-group', + builder: (context, state) => const NewLessonGroupPage(), + ), + GoRoute( + path: '/classrooms', + builder: (context, state) => const ClassroomListPage(), + ), + GoRoute( + path: '/charts', + builder: (context, state) => const ChartPageController(), + ), + GoRoute( + path: '/statistics', + builder: (context, state) => const Statistics(), + ), + GoRoute( + path: '/select-pupils', + builder: (context, state) { + final selectablePupils = state.extra as List? ?? []; + return SelectPupilsListPage(selectablePupils: selectablePupils); + }, + ), + GoRoute( + path: '/birthdays', + builder: (context, state) { + final selectedDate = state.extra as DateTime; + return BirthdaysView(selectedDate: selectedDate); + }, + ), + GoRoute( + path: '/crop-document', + builder: (context, state) { + final image = state.extra as XFile; + return CropDocumentView(image: image); + }, + ), + GoRoute( + path: '/admin/edit-school-data', + builder: (context, state) => const EditSchoolDataPage(), + ), + GoRoute( + path: '/admin/create-user', + builder: (context, state) => const CreateUserPage(), + ), + GoRoute( + path: '/admin/users', + builder: (context, state) => const UserListPage(), + ), + GoRoute( + path: '/admin/reset-password', + builder: (context, state) => const ResetUserPasswordPage(), + ), + GoRoute( + path: '/admin/matrix/set-environment', + builder: (context, state) => const SetMatrixEnvironment(), + ), + GoRoute( + path: '/admin/calendar', + builder: (context, state) => const SchooldaysCalendarPage(), + ), + GoRoute( + path: '/admin/new-semester', + builder: (context, state) => const NewSchoolSemesterPage(), + ), + // Learning Resources + GoRoute( + path: '/learning/competences', + builder: (context, state) => const CompetenceListPage(), + ), + GoRoute( + path: '/learning/categories', + builder: (context, state) => const CategoryList(), + ), + GoRoute( + path: '/learning/workbooks', + builder: (context, state) => const WorkbookList(), + ), + GoRoute( + path: '/learning/books', + builder: (context, state) => const BooksMainMenuPage(), + ), + GoRoute( + path: '/learning/books/tags', + builder: (context, state) => const BookTagManagement(), + ), + GoRoute( + path: '/learning/books/list', + builder: (context, state) => const BookListPage(), + ), + GoRoute( + path: '/learning/books/new', + builder: (context, state) { + final extra = state.extra as Map?; + return NewBook( + isEdit: extra?['isEdit'] ?? false, + isbn: extra?['isbn'] ?? 0, + ); + }, + ), + GoRoute( + path: '/learning/books/search', + builder: (context, state) => const BookSearchFormPage(), + ), + GoRoute( + path: '/learning/books/select-tags', + builder: (context, state) { + final extra = state.extra as List?; + return SelectBookTagsPage(initialSelectedTags: extra ?? []); + }, + ), + GoRoute( + path: '/learning/books/search-results', + builder: (context, state) { + final extra = state.extra as Map?; + return BookSearchResultsPage( + title: extra?['title'] as String?, + author: extra?['author'] as String?, + keywords: extra?['keywords'] as String?, + location: extra?['location'] as LibraryBookLocation?, + readingLevel: extra?['readingLevel'] as String?, + borrowStatus: extra?['borrowStatus'] as BorrowedStatus?, + selectedTags: extra?['selectedTags'] as List? ?? [], + ); + }, + ), + GoRoute( + path: '/pupil/new-support-category-status', + builder: (context, state) { + final extra = state.extra as Map?; + return NewSupportCategoryStatus( + appBarTitle: extra?['appBarTitle'] as String, + pupilId: extra?['pupilId'] as int, + goalCategoryId: extra?['goalCategoryId'] as int, + elementType: extra?['elementType'] as String, + ); + }, + ), + GoRoute( + path: '/pupil/new-learning-support-plan', + builder: (context, state) { + final pupil = state.extra as PupilProxy; + return NewLearningSupportPlan(pupil: pupil); + }, + ), + GoRoute( + path: '/pupil/learning-support-plan-pdf', + builder: (context, state) { + final file = state.extra as File; + return LearningSupportPlanPdfViewPage(pdfFile: file); + }, + ), + GoRoute( + path: '/pupil/schoolday-events', + builder: (context, state) => const SchooldayEventListPage(), + ), + GoRoute( + path: '/pupil/missed-schooldays', + builder: (context, state) => const MissedSchooldayesPupilListPage(), + ), + GoRoute( + path: '/pupil/attendance', + builder: (context, state) => const AttendanceListPage(), + ), + GoRoute( + path: '/pupil/credit', + builder: (context, state) => const CreditListPage(), + ), + GoRoute( + path: '/pupil/learning-lists', + builder: (context, state) => const LearningPupilListPage(), + ), + GoRoute( + path: '/pupil/support-lists', + builder: (context, state) => const LearningSupportListPage(), + ), + GoRoute( + path: '/pupil/special-info', + builder: (context, state) => const SpecialInfoListPage(), + ), + GoRoute( + path: '/pupil/religion', + builder: (context, state) => const ReligionListPage(), + ), + GoRoute( + path: '/pupil/family-language', + builder: (context, state) => const FamilyLanguageLessonsListPage(), + ), + GoRoute( + path: '/pupil/ogs', + builder: (context, state) => const OgsListPage(), + ), + GoRoute( + path: '/pupil/matrix-users', + builder: (context, state) => const MatrixUsersListPage(), + ), + GoRoute( + path: '/pupil/matrix-contacts', + builder: (context, state) => const PupilsMatrixContactsListPage(), + ), + GoRoute( + path: '/school/lists', + builder: (context, state) => const SchoolListsPage(), + ), + GoRoute( + path: '/school/authorizations', + builder: (context, state) => const AuthorizationsListPage(), + ), + GoRoute( + path: '/settings/change-password', + builder: (context, state) => const UserChangePasswordPage(), + ), + GoRoute( + path: '/pupil/:id', + builder: (context, state) { + final idStr = state.pathParameters['id']; + if (idStr == null) + return const ErrorPage(error: "Keine ID übergeben"); + final id = int.tryParse(idStr); + if (id == null) return const ErrorPage(error: "Ungültige ID"); + + final pupilManager = di(); + final pupil = pupilManager.getPupilByPupilId(id); + + if (pupil == null) { + return const ErrorPage(error: "Schüler nicht gefunden"); + } + return PupilProfilePage(pupil: pupil); + }, + ), + StatefulShellRoute( + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/home', // Default tab (Pupil Lists) + builder: (context, state) => const PupilListsMenuPage(), + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/school', + builder: (context, state) => const SchoolListsMenuPage(), + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/learning', + builder: (context, state) => const LearnResourcesMenuPage(), + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/tools', + builder: (context, state) => const ToolsPage(), + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/settings', + builder: (context, state) => const SettingsPage(), + ), + ], + ), + ], + navigatorContainerBuilder: (context, navigationShell, children) { + return MainMenuBottomNavigation( + navigationShell: navigationShell, + children: children, + ); + }, + builder: (context, state, navigationShell) { + return navigationShell; // Just return the shell, the container builder handles the UI logic + }, + ), + ], + ); + } +} diff --git a/school_data_hub_flutter/pubspec.lock b/school_data_hub_flutter/pubspec.lock index 642b162a..c76bd091 100644 --- a/school_data_hub_flutter/pubspec.lock +++ b/school_data_hub_flutter/pubspec.lock @@ -685,6 +685,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: c92d18e1fe994cb06d48aa786c46b142a5633067e8297cff6b5a3ac742620104 + url: "https://pub.dev" + source: hosted + version: "17.0.0" graphs: dependency: transitive description: diff --git a/school_data_hub_flutter/pubspec.yaml b/school_data_hub_flutter/pubspec.yaml index fc517aa4..0a7711e4 100644 --- a/school_data_hub_flutter/pubspec.yaml +++ b/school_data_hub_flutter/pubspec.yaml @@ -78,6 +78,7 @@ dependencies: msix: ^3.16.12 uml_for_serverpod: ^0.0.5 community_charts_flutter: ^1.0.4 + go_router: ^17.0.0 dev_dependencies: From eb34c410fc8df142610b3c1e828fddd19d7c6cef Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 18:49:57 +0100 Subject: [PATCH 30/52] [APP] refactor --- .../app_utils/create_and_crop_image_file.dart | 100 ++++++++++++++++++ .../lib/app_utils/scanner.dart | 91 ++++++++++------ .../widgets/pupil_schoolday_event_card.dart | 10 +- .../widgets/authorization_pupil_card.dart | 42 ++++---- .../book_list_page/widgets/book_card.dart | 6 +- .../widgets/pupil_book_card.dart | 6 +- .../multi_pupil_competence_check_card.dart | 72 ++++++------- .../competence_check_card.dart | 6 +- ...upil_content_authorization_entry_card.dart | 46 ++++---- .../widgets/avatar_auth_values.dart | 57 +++++----- .../widgets/pupil_media_auth_values.dart | 83 +++++++-------- ...profile_learning_support_content_list.dart | 6 +- .../widgets/pupil_workbook_card.dart | 6 +- .../widgets/workbook_card.dart | 6 +- 14 files changed, 331 insertions(+), 206 deletions(-) create mode 100644 school_data_hub_flutter/lib/app_utils/create_and_crop_image_file.dart diff --git a/school_data_hub_flutter/lib/app_utils/create_and_crop_image_file.dart b/school_data_hub_flutter/lib/app_utils/create_and_crop_image_file.dart new file mode 100644 index 00000000..4e9608e1 --- /dev/null +++ b/school_data_hub_flutter/lib/app_utils/create_and_crop_image_file.dart @@ -0,0 +1,100 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:crop_image/crop_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; +import 'package:watch_it/watch_it.dart'; + +Future createAndCropImageFile(context) async { + XFile? image = await ImagePicker().pickImage( + source: Platform.isWindows || Platform.isMacOS + ? ImageSource.gallery + : ImageSource.camera, + preferredCameraDevice: Platform.isWindows + ? CameraDevice.front + : CameraDevice.rear, + ); + if (image == null) { + return null; + } + + File imageFile = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => CropAvatarView(image: image)), + ); + return imageFile; +} + +class CropAvatarView extends WatchingWidget { + final XFile image; + const CropAvatarView({super.key, required this.image}); + + @override + Widget build(BuildContext context) { + final controller = createOnce( + () => CropController( + aspectRatio: 21.0 / 29.7, + defaultCrop: const Rect.fromLTRB(0, 0, 1, 1), + ), + dispose: (controller) => controller.dispose(), + ); + + Future finished() async { + final bitmap = await controller.croppedBitmap( + maxSize: 800, + quality: FilterQuality.medium, + ); + final imageBytes = await bitmap.toByteData(format: ImageByteFormat.png); + if (imageBytes == null) return; + final file = await _imageToFile(bytes: imageBytes); + if (context.mounted) { + Navigator.pop(context, file); + } + } + + return Scaffold( + body: Center( + child: Container( + decoration: const BoxDecoration(color: Colors.black), + child: CropImage( + controller: controller, + image: Image.file(File(image.path)), + paddingSize: 0, + alwaysMove: true, + ), + ), + ), + bottomNavigationBar: SafeArea( + top: false, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + icon: const Icon(Icons.rotate_90_degrees_ccw_outlined), + onPressed: controller.rotateLeft, + ), + IconButton( + icon: const Icon(Icons.rotate_90_degrees_cw_outlined), + onPressed: controller.rotateRight, + ), + TextButton(onPressed: finished, child: const Text('Fertig')), + ], + ), + ), + ); + } + + static Future _imageToFile({required ByteData bytes}) async { + String tempPath = (await getTemporaryDirectory()).path; + File file = File(p.join(tempPath, 'temporaryProfile.jpeg')); + await file.writeAsBytes( + bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes), + ); + return file; + } +} diff --git a/school_data_hub_flutter/lib/app_utils/scanner.dart b/school_data_hub_flutter/lib/app_utils/scanner.dart index c03bdbc2..085a5f87 100644 --- a/school_data_hub_flutter/lib/app_utils/scanner.dart +++ b/school_data_hub_flutter/lib/app_utils/scanner.dart @@ -1,42 +1,65 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:watch_it/watch_it.dart'; final _notificationService = di(); -Future qrScanner( - {required BuildContext context, required String overlayText}) async { +Future qrScanner({ + required BuildContext context, + required String overlayText, +}) async { if (Platform.isWindows) { - _notificationService.showSnackBar(NotificationType.error, - 'Scannen mit Windows ist zur Zeit nicht möglich.'); + _notificationService.showSnackBar( + NotificationType.error, + 'Scannen mit Windows ist zur Zeit nicht möglich.', + ); return null; } - final controller = MobileScannerController( - detectionSpeed: DetectionSpeed.normal, - formats: [BarcodeFormat.qrCode, BarcodeFormat.ean13], - facing: CameraFacing.back, - torchEnabled: false, - ); - final result = await Navigator.push( + return await Navigator.push( context, MaterialPageRoute( - fullscreenDialog: true, - builder: (context) => Stack( + builder: (context) => ScannerPage(overlayText: overlayText), + ), + ); + //return await context.push('/scanner', extra: overlayText); +} + +class ScannerPage extends WatchingWidget { + final String overlayText; + const ScannerPage({required this.overlayText, super.key}); + + @override + Widget build(BuildContext context) { + final controller = createOnce( + () => MobileScannerController( + detectionSpeed: DetectionSpeed.normal, + formats: [BarcodeFormat.qrCode, BarcodeFormat.ean13], + facing: CameraFacing.back, + torchEnabled: false, + ), + dispose: (controller) => controller.dispose(), + ); + + return Scaffold( + body: Stack( children: [ MobileScanner( - controller: controller, - // fit: BoxFit.contain, - onDetect: (capture) { - final Barcode barcode; - barcode = capture.barcodes[0]; - - Navigator.pop(context, barcode.displayValue); - controller.dispose(); - }), + controller: controller, + // fit: BoxFit.contain, + onDetect: (capture) { + final Barcode barcode; + barcode = capture.barcodes[0]; + + if (context.mounted) { + context.pop(barcode.displayValue); + } + }, + ), Positioned( bottom: 50, left: 0, @@ -44,20 +67,22 @@ Future qrScanner( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(overlayText, - textAlign: TextAlign.center, - style: const TextStyle( - decoration: TextDecoration.none, - backgroundColor: Colors.transparent, - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white)), + Text( + overlayText, + textAlign: TextAlign.center, + style: const TextStyle( + decoration: TextDecoration.none, + backgroundColor: Colors.transparent, + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), ], ), ), ], ), - ), - ); - return result; + ); + } } diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart index c3ea9666..6b222fed 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart @@ -10,7 +10,7 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dial import 'package:school_data_hub_flutter/common/widgets/dialogs/schoolday_date_picker.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; @@ -233,7 +233,9 @@ class PupilSchooldayEventCard extends StatelessWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; await _schooldayEventManager .updateSchooldayEventFile( @@ -295,7 +297,9 @@ class PupilSchooldayEventCard extends StatelessWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; await _schooldayEventManager.updateSchooldayEventFile( imageFile: file, diff --git a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart index f07a1186..aa08b22e 100644 --- a/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart +++ b/school_data_hub_flutter/lib/features/authorizations/presentation/authorization_pupils_page/widgets/authorization_pupil_card.dart @@ -8,7 +8,7 @@ import 'package:school_data_hub_flutter/common/widgets/custom_checkbox_either_or import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/long_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/features/app_main_navigation/domain/main_menu_bottom_nav_manager.dart'; import 'package:school_data_hub_flutter/features/authorizations/domain/authorization_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; @@ -59,8 +59,8 @@ class AuthorizationPupilCard extends WatchingWidget { di().setPupilProfileNavPage(7); Navigator.of(context).push( MaterialPageRoute( - builder: - (ctx) => PupilProfilePage(pupil: pupil), + builder: (ctx) => + PupilProfilePage(pupil: pupil), ), ); }, @@ -161,7 +161,9 @@ class AuthorizationPupilCard extends WatchingWidget { const Gap(15), InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; await _authorizationManager.addFileToPupilAuthorization( file, @@ -184,21 +186,20 @@ class AuthorizationPupilCard extends WatchingWidget { pupilAuthorization.file!.documentId, ); }, - child: - pupilAuthorization.fileId != null - ? DocumentImage( - documentId: pupilAuthorization.file!.documentId, - size: 70, - ) - : SizedBox( - height: 70, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: Image.asset( - 'assets/document_camera.png', - ), + child: pupilAuthorization.fileId != null + ? DocumentImage( + documentId: pupilAuthorization.file!.documentId, + size: 70, + ) + : SizedBox( + height: 70, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Image.asset( + 'assets/document_camera.png', ), ), + ), ), ], ), @@ -236,10 +237,9 @@ class AuthorizationPupilCard extends WatchingWidget { pupilId: pupil.pupilId, authorizationId: authorization.id!, status: null, - comment: - authorizationComment == '' - ? null - : authorizationComment, + comment: authorizationComment == '' + ? null + : authorizationComment, ); }, child: Text( diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_card.dart b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_card.dart index 5491e4e8..c943c5ad 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_card.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/book_card.dart @@ -10,7 +10,7 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dial import 'package:school_data_hub_flutter/common/widgets/dialogs/information_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/long_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/unencrypted_image_in_card.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/books/domain/book_helper.dart'; import 'package:school_data_hub_flutter/features/books/domain/book_manager.dart'; @@ -140,7 +140,9 @@ class BookCard extends WatchingWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; // TODO: Uncomment this when the API is ready ? // await di() diff --git a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart index 0405d99e..2cc7a95c 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/book_list_page/widgets/pupil_book_card.dart @@ -6,7 +6,7 @@ import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/information_dialog.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/books/domain/book_manager.dart'; import 'package:school_data_hub_flutter/features/books/domain/models/enums.dart'; @@ -72,7 +72,9 @@ class PupilBookCard extends WatchingWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; // TODO: Uncomment this when the API is ready ? // await di() diff --git a/school_data_hub_flutter/lib/features/learning/presentation/multi_pupil_competence_check_page/widgets/multi_pupil_competence_check_card.dart b/school_data_hub_flutter/lib/features/learning/presentation/multi_pupil_competence_check_page/widgets/multi_pupil_competence_check_card.dart index 181c2002..97dae436 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/multi_pupil_competence_check_page/widgets/multi_pupil_competence_check_card.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/multi_pupil_competence_check_page/widgets/multi_pupil_competence_check_card.dart @@ -8,7 +8,7 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dial import 'package:school_data_hub_flutter/common/widgets/dialogs/long_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; import 'package:school_data_hub_flutter/common/widgets/growth_dropdown.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_helper.dart'; import 'package:school_data_hub_flutter/features/app_main_navigation/domain/main_menu_bottom_nav_manager.dart'; import 'package:school_data_hub_flutter/features/learning/domain/competence_helper.dart'; @@ -75,8 +75,8 @@ class MultiPupilCompetenceCheckCard extends WatchingWidget { ); Navigator.of(context).push( MaterialPageRoute( - builder: - (ctx) => PupilProfilePage(pupil: pupil), + builder: (ctx) => + PupilProfilePage(pupil: pupil), ), ); }, @@ -120,35 +120,35 @@ class MultiPupilCompetenceCheckCard extends WatchingWidget { const Gap(5), competenceCheck != null ? GrowthDropdown( - dropdownValue: competenceCheck.score, - onChangedFunction: (int value) async { - if (value == competenceCheck.score) { - return; - } - await di() - .updateCompetenceCheck( - competenceCheckId: - competenceCheck.checkId, - score: (value: value), - ); - }, - ) + dropdownValue: competenceCheck.score, + onChangedFunction: (int value) async { + if (value == competenceCheck.score) { + return; + } + await di() + .updateCompetenceCheck( + competenceCheckId: + competenceCheck.checkId, + score: (value: value), + ); + }, + ) : GrowthDropdown( - dropdownValue: 0, - onChangedFunction: (int? value) async { - if (value == 0) { - return; - } - await di() - .postCompetenceCheck( - pupilId: pupil.internalId, - competenceId: competenceId, - competenceComment: '', - groupId: groupId, - score: value!, - ); - }, - ), + dropdownValue: 0, + onChangedFunction: (int? value) async { + if (value == 0) { + return; + } + await di() + .postCompetenceCheck( + pupilId: pupil.internalId, + competenceId: competenceId, + competenceComment: '', + groupId: groupId, + score: value!, + ); + }, + ), const Spacer(), //- Take picture button only visible if there are less than 4 pictures if (competenceCheck != null && @@ -158,9 +158,8 @@ class MultiPupilCompetenceCheckCard extends WatchingWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile( - context, - ); + final File? file = + await createAndCropImageFile(context); if (file == null) return; await di() .addFileToCompetenceCheck( @@ -245,9 +244,8 @@ class MultiPupilCompetenceCheckCard extends WatchingWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile( - context, - ); + final File? file = + await createAndCropImageFile(context); if (file == null) return; // TODO: Uncomment this when postCompetenceCheckWithFile is implemented diff --git a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/competence_check_card.dart b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/competence_check_card.dart index 1b281a38..2c1295e4 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/competence_check_card.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/competence_check_card.dart @@ -13,7 +13,7 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/schoolday_date_pi import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; import 'package:school_data_hub_flutter/common/widgets/growth_dropdown.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_helper.dart'; import 'package:school_data_hub_flutter/features/learning/domain/competence_helper.dart'; import 'package:school_data_hub_flutter/features/learning/domain/competence_manager.dart'; @@ -294,7 +294,9 @@ class CompetenceCheckCard extends StatelessWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; await di() .addFileToCompetenceCheck( diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart index be1f6ab0..0b6acde9 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/authorization_content/pupil_content_authorization_entry_card.dart @@ -8,7 +8,7 @@ import 'package:school_data_hub_flutter/common/widgets/custom_checkbox_either_or import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/long_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/features/authorizations/domain/authorization_manager.dart'; import 'package:school_data_hub_flutter/features/authorizations/presentation/authorization_pupils_page/authorization_pupils_page.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; @@ -54,10 +54,8 @@ class PupilContentAuthorizationEntryCard extends WatchingWidget { onTap: () { Navigator.of(context).push( MaterialPageRoute( - builder: - (ctx) => AuthorizationPupilsPage( - authorization, - ), + builder: (ctx) => + AuthorizationPupilsPage(authorization), ), ); }, @@ -118,7 +116,9 @@ class PupilContentAuthorizationEntryCard extends WatchingWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; await _authorizationManager .addFileToPupilAuthorization( @@ -141,22 +141,21 @@ class PupilContentAuthorizationEntryCard extends WatchingWidget { pupilAuthorization.file!.documentId, ); }, - child: - pupilAuthorization.fileId != null - ? DocumentImage( - documentId: - pupilAuthorization.file!.documentId, - size: 70, - ) - : SizedBox( - height: 70, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: Image.asset( - 'assets/document_camera.png', - ), + child: pupilAuthorization.fileId != null + ? DocumentImage( + documentId: + pupilAuthorization.file!.documentId, + size: 70, + ) + : SizedBox( + height: 70, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Image.asset( + 'assets/document_camera.png', ), ), + ), ), ], ), @@ -234,10 +233,9 @@ class PupilContentAuthorizationEntryCard extends WatchingWidget { .updatePupilAuthorization( pupilId: pupil.pupilId, authorizationId: authorization.id!, - comment: - authorizationComment == '' - ? null - : authorizationComment, + comment: authorizationComment == '' + ? null + : authorizationComment, ); }, child: Text( diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart index 9a00269f..ee53b901 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/avatar_auth_values.dart @@ -7,7 +7,7 @@ import 'package:school_data_hub_flutter/common/services/notification_service.dar import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; @@ -51,10 +51,9 @@ class AvatarAuthValues extends WatchingWidget { Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( - color: - avatarAuth != null - ? Colors.green.withValues(alpha: 0.1) - : Colors.red.withValues(alpha: 0.1), + color: avatarAuth != null + ? Colors.green.withValues(alpha: 0.1) + : Colors.red.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(6), ), child: Icon( @@ -85,10 +84,9 @@ class AvatarAuthValues extends WatchingWidget { : 'Keine Einwilligung', style: TextStyle( fontSize: 12, - color: - avatarAuth != null - ? Colors.green.withValues(alpha: 0.8) - : Colors.red.withValues(alpha: 0.8), + color: avatarAuth != null + ? Colors.green.withValues(alpha: 0.8) + : Colors.red.withValues(alpha: 0.8), fontWeight: FontWeight.w500, ), ), @@ -99,7 +97,7 @@ class AvatarAuthValues extends WatchingWidget { // Document Image InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile(context); if (file == null) return; await PupilMutator().updatePupilDocument( imageFile: file, @@ -138,29 +136,24 @@ class AvatarAuthValues extends WatchingWidget { width: 1, ), ), - child: - avatarAuthId != null - ? DocumentImage( - documentId: avatarAuth!.documentId, - size: 50, // Reduced from 70 to 50 - ) - : Container( - height: 50, - width: 50, - decoration: BoxDecoration( - color: AppColors.backgroundColor.withValues( - alpha: 0.1, - ), - borderRadius: BorderRadius.circular(6), - ), - child: Icon( - Icons.add_a_photo, - color: AppColors.backgroundColor.withValues( - alpha: 0.6, - ), - size: 24, - ), + child: avatarAuthId != null + ? DocumentImage( + documentId: avatarAuth!.documentId, + size: 50, // Reduced from 70 to 50 + ) + : Container( + height: 50, + width: 50, + decoration: BoxDecoration( + color: AppColors.backgroundColor.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(6), + ), + child: Icon( + Icons.add_a_photo, + color: AppColors.backgroundColor.withValues(alpha: 0.6), + size: 24, ), + ), ), ), ], diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart index e367fe36..9a0412e2 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/widgets/pupil_media_auth_values.dart @@ -7,7 +7,7 @@ import 'package:school_data_hub_flutter/common/services/notification_service.dar import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_mutator.dart'; @@ -83,7 +83,7 @@ class PublicMediaAuthValues extends WatchingWidget { ), InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile(context); if (file == null) return; await PupilMutator().updatePupilDocument( imageFile: file, @@ -118,29 +118,28 @@ class PublicMediaAuthValues extends WatchingWidget { width: 1, ), ), - child: - publicMediaAuthDocumentId != null - ? DocumentImage( - documentId: publicMediaAuthDocument!.documentId, - size: 40, // Smaller than before - ) - : Container( - height: 40, - width: 40, - decoration: BoxDecoration( - color: AppColors.backgroundColor.withValues( - alpha: 0.1, - ), - borderRadius: BorderRadius.circular(6), + child: publicMediaAuthDocumentId != null + ? DocumentImage( + documentId: publicMediaAuthDocument!.documentId, + size: 40, // Smaller than before + ) + : Container( + height: 40, + width: 40, + decoration: BoxDecoration( + color: AppColors.backgroundColor.withValues( + alpha: 0.1, ), - child: Icon( - Icons.add_a_photo, - color: AppColors.backgroundColor.withValues( - alpha: 0.6, - ), - size: 20, + borderRadius: BorderRadius.circular(6), + ), + child: Icon( + Icons.add_a_photo, + color: AppColors.backgroundColor.withValues( + alpha: 0.6, ), + size: 20, ), + ), ), ), ], @@ -250,10 +249,9 @@ class PublicMediaAuthValues extends WatchingWidget { // Deny checkbox Container( decoration: BoxDecoration( - color: - !isAllowed - ? Colors.red.withValues(alpha: 0.1) - : Colors.transparent, + color: !isAllowed + ? Colors.red.withValues(alpha: 0.1) + : Colors.transparent, borderRadius: BorderRadius.circular(4), ), child: Transform.scale( @@ -262,14 +260,13 @@ class PublicMediaAuthValues extends WatchingWidget { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: !isAllowed, activeColor: Colors.red, - onChanged: - hasDocument - ? (newValue) async { - if (newValue == true) { - await onChanged(false); - } + onChanged: hasDocument + ? (newValue) async { + if (newValue == true) { + await onChanged(false); } - : null, + } + : null, ), ), ), @@ -282,10 +279,9 @@ class PublicMediaAuthValues extends WatchingWidget { // Allow checkbox Container( decoration: BoxDecoration( - color: - isAllowed - ? Colors.green.withValues(alpha: 0.1) - : Colors.transparent, + color: isAllowed + ? Colors.green.withValues(alpha: 0.1) + : Colors.transparent, borderRadius: BorderRadius.circular(4), ), child: Transform.scale( @@ -294,14 +290,13 @@ class PublicMediaAuthValues extends WatchingWidget { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: isAllowed, activeColor: Colors.green, - onChanged: - hasDocument - ? (newValue) async { - if (newValue == true && !isAllowed) { - await onChanged(true); - } + onChanged: hasDocument + ? (newValue) async { + if (newValue == true && !isAllowed) { + await onChanged(true); } - : null, + } + : null, ), ), ), diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart index f40c9148..8dffa27b 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart @@ -12,7 +12,7 @@ import 'package:school_data_hub_flutter/common/widgets/custom_expansion_tile/cus import 'package:school_data_hub_flutter/common/widgets/custom_expansion_tile/custom_expansion_tile_switch.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/learning_support/domain/support_category_manager.dart'; @@ -142,7 +142,9 @@ class PupilProfileLearningSupportContentList extends WatchingWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; await _uploadFileToPreSchoolMedical(file, pupil); diff --git a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart index a1a7a4b9..78985df9 100644 --- a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart +++ b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/pupil_workbook_card.dart @@ -11,7 +11,7 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/information_dialo import 'package:school_data_hub_flutter/common/widgets/dialogs/long_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/grades_widget.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/learning/domain/enums.dart'; import 'package:school_data_hub_flutter/features/learning/presentation/widgets/competence_check_dropdown.dart'; @@ -80,7 +80,9 @@ class PupilWorkbookCard extends WatchingWidget { children: [ InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; // TODO: Uncomment when API is ready di().showSnackBar( diff --git a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart index 2495d499..5b56fb2c 100644 --- a/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart +++ b/school_data_hub_flutter/lib/features/workbooks/presentation/workbook_list_page/widgets/workbook_card.dart @@ -11,7 +11,7 @@ import 'package:school_data_hub_flutter/common/widgets/custom_expansion_tile/cus import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/grades_widget.dart'; import 'package:school_data_hub_flutter/common/widgets/unencrypted_image_in_card.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/workbooks/domain/workbook_manager.dart'; import 'package:school_data_hub_flutter/features/workbooks/presentation/new_workbook_page/new_workbook_page.dart'; @@ -66,7 +66,9 @@ class WorkbookCard extends WatchingWidget { const Gap(10), InkWell( onTap: () async { - final File? file = await createImageFile(context); + final File? file = await createAndCropImageFile( + context, + ); if (file == null) return; // TODO: implement when ready di().showSnackBar( From 36cb2ac8a65370e739a6a8831450c209cc1814df Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 18:59:25 +0100 Subject: [PATCH 31/52] [APP] Notification logs --- .../lib/common/services/notification_service.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/school_data_hub_flutter/lib/common/services/notification_service.dart b/school_data_hub_flutter/lib/common/services/notification_service.dart index e11bf8c4..299588db 100644 --- a/school_data_hub_flutter/lib/common/services/notification_service.dart +++ b/school_data_hub_flutter/lib/common/services/notification_service.dart @@ -35,7 +35,8 @@ class NotificationService { } _log.fine( - '====================================================================================== SNACK BAR MESSAGE: ${type.name} $message', + '''====================================================================================== SNACK BAR MESSAGE: + ${type} $message''', ); _snackBar.value = NotificationData(type, message); @@ -45,7 +46,8 @@ class NotificationService { _snackBar.value = NotificationData(NotificationType.dialog, message); _log.fine( - '====================================================================================== INFORMATION DIALOG: $message', + '''====================================================================================== INFORMATION DIALOG: + $message''', ); } From 25772b72f705197afbf545ac2521bdc03403367d Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 18:59:51 +0100 Subject: [PATCH 32/52] [APP] Notification logs --- .../lib/common/services/notification_service.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/school_data_hub_flutter/lib/common/services/notification_service.dart b/school_data_hub_flutter/lib/common/services/notification_service.dart index 299588db..5fdc0da1 100644 --- a/school_data_hub_flutter/lib/common/services/notification_service.dart +++ b/school_data_hub_flutter/lib/common/services/notification_service.dart @@ -36,7 +36,8 @@ class NotificationService { _log.fine( '''====================================================================================== SNACK BAR MESSAGE: - ${type} $message''', + ${type} $message + ''', ); _snackBar.value = NotificationData(type, message); @@ -47,7 +48,8 @@ class NotificationService { _log.fine( '''====================================================================================== INFORMATION DIALOG: - $message''', + $message + ''', ); } From 1c89b5985e5434384be1a6cb5e185ed803ad901a Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 19:00:06 +0100 Subject: [PATCH 33/52] tidy up --- .../lib/common/widgets/upload_image.dart | 201 ------------------ 1 file changed, 201 deletions(-) delete mode 100644 school_data_hub_flutter/lib/common/widgets/upload_image.dart diff --git a/school_data_hub_flutter/lib/common/widgets/upload_image.dart b/school_data_hub_flutter/lib/common/widgets/upload_image.dart deleted file mode 100644 index 9e9323a5..00000000 --- a/school_data_hub_flutter/lib/common/widgets/upload_image.dart +++ /dev/null @@ -1,201 +0,0 @@ -import 'dart:io'; -import 'dart:ui'; - -import 'package:crop_image/crop_image.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:path/path.dart' as p; -import 'package:path_provider/path_provider.dart'; - -Future createImageFile(context) async { - XFile? image = await ImagePicker().pickImage( - source: Platform.isWindows || Platform.isMacOS - ? ImageSource.gallery - : ImageSource.camera, - preferredCameraDevice: Platform.isWindows - ? CameraDevice.front - : CameraDevice.rear, - ); - if (image == null) { - return null; - } - - // final MemoryImage? image = await Navigator.push( - // context, - // MaterialPageRoute(builder: (context) => CropImagePage(image: xFile)), - // ); - // if (image == null) { - // return null; - // } - // final imageFile = memoryImageToFile(image); - // return imageFile; - - File imageFile = await Navigator.push( - context, - MaterialPageRoute(builder: (context) => CropAvatarView(image: image)), - ); - return imageFile; -} - -class CropAvatarView extends StatefulWidget { - final XFile image; - const CropAvatarView({super.key, required this.image}); - @override - State createState() => _CropAvatarState(); -} - -class _CropAvatarState extends State { - final controller = CropController( - aspectRatio: 21.0 / 29.7, - defaultCrop: const Rect.fromLTRB(0, 0, 1, 1), - ); - - @override - Widget build(BuildContext context) => Scaffold( - // appBar: AppBar( - // title: Text('Gesicht zentrieren'), - // ), - body: Center( - child: Container( - decoration: const BoxDecoration(color: Colors.black), - child: CropImage( - controller: controller, - image: Image.file(File(widget.image.path)), - paddingSize: 0, - alwaysMove: true, - ), - ), - ), - bottomNavigationBar: _buildButtons(), - ); - - Widget _buildButtons() => SafeArea( - top: false, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - IconButton( - icon: const Icon(Icons.rotate_90_degrees_ccw_outlined), - onPressed: _rotateLeft, - ), - IconButton( - icon: const Icon(Icons.rotate_90_degrees_cw_outlined), - onPressed: _rotateRight, - ), - TextButton(onPressed: _finished, child: const Text('Fertig')), - ], - ), - ); - - // Future _aspectRatios() async { - // final value = await showDialog( - // context: context, - // builder: (context) { - // return SimpleDialog( - // title: const Text('Select aspect ratio'), - // children: [ - // // special case: no aspect ratio - // SimpleDialogOption( - // onPressed: () => Navigator.pop(context, -1.0), - // child: const Text('free'), - // ), - // SimpleDialogOption( - // onPressed: () => Navigator.pop(context, 1.0), - // child: const Text('square'), - // ), - // SimpleDialogOption( - // onPressed: () => Navigator.pop(context, 2.0), - // child: const Text('2:1'), - // ), - // SimpleDialogOption( - // onPressed: () => Navigator.pop(context, 1 / 2), - // child: const Text('1:2'), - // ), - // SimpleDialogOption( - // onPressed: () => Navigator.pop(context, 4.0 / 3.0), - // child: const Text('4:3'), - // ), - // SimpleDialogOption( - // onPressed: () => Navigator.pop(context, 16.0 / 9.0), - // child: const Text('16:9'), - // ), - // ], - // ); - // }, - // ); - // if (value != null) { - // controller.aspectRatio = value == -1 ? null : value; - // controller.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9); - // } - // } - - Future _rotateLeft() async => controller.rotateLeft(); - - Future _rotateRight() async => controller.rotateRight(); - - Future _finished() async { - //final image = await controller.croppedImage(); - final bitmap = await controller.croppedBitmap( - maxSize: 800, - quality: FilterQuality.medium, - ); - final imageBytes = await bitmap.toByteData(format: ImageByteFormat.png); - final file = await imageToFile(bytes: imageBytes!); - if (mounted) { - Navigator.pop(context, file); - controller.dispose(); - } - - // ignore: use_build_context_synchronously - // await showDialog( - // context: context, - // builder: (context) { - // return SimpleDialog( - // contentPadding: const EdgeInsets.all(6.0), - // titlePadding: const EdgeInsets.all(8.0), - // title: const Text('Cropped image'), - // children: [ - // Text('relative: ${controller.crop}'), - // Text('pixels: ${controller.cropSize}'), - // const SizedBox(height: 5), - // image, - // TextButton( - // onPressed: () => Navigator.pop(context, true), - // child: const Text('OK'), - // ), - // ], - // ); - // }, - // ); - } - - static Future imageToFile({required ByteData bytes}) async { - String tempPath = (await getTemporaryDirectory()).path; - File file = File(p.join(tempPath, 'temporaryProfile.jpeg')); - await file.writeAsBytes( - bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes), - ); - return file; - } -} - -Future memoryImageToFile(MemoryImage memoryImage) async { - // Get the bytes from the MemoryImage - - Uint8List bytes = memoryImage.bytes; - - // Get the temporary directory - Directory tempDir = await getTemporaryDirectory(); - - // Create a unique file path - String filePath = - '${tempDir.path}/image_${DateTime.now().millisecondsSinceEpoch}.png'; - - // Write the bytes to the file - File file = File(filePath); - await file.writeAsBytes(bytes); - - return file; -} From de0142dd06f55c684234737bdc2984c131c83fdc Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 19:00:44 +0100 Subject: [PATCH 34/52] [APP] Init process --- .../lib/core/env/env_manager.dart | 86 ++++++++----------- .../lib/core/init/init_on_user_auth.dart | 18 ++++ 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/school_data_hub_flutter/lib/core/env/env_manager.dart b/school_data_hub_flutter/lib/core/env/env_manager.dart index a537ce80..f732cf34 100644 --- a/school_data_hub_flutter/lib/core/env/env_manager.dart +++ b/school_data_hub_flutter/lib/core/env/env_manager.dart @@ -159,18 +159,6 @@ class EnvManager with ChangeNotifier { _envIsReady.value = true; await InitManager.pushActiveEnvScopeAndRegisterDependentManagers(); - if (await HubSecureStorage().containsKey(storageKeyForMatrixCredentials)) { - // Only register matrix managers if they're not already registered - if (!di.hasScope(DiScope.onMatrixEnvScope.name)) { - await InitManager.registerMatrixManagers(); - } else { - _log.info( - '[DI] Matrix managers already registered, skipping registration', - ); - // Ensure session configured flag is set even if skipping registration - di().setIsMatrixSessionConfigured(true); - } - } return; } @@ -338,42 +326,42 @@ class EnvManager with ChangeNotifier { } _log.info( - '[DI] Environment-dependent managers are ready for: ${_activeEnv!.serverName}', + 'Environment-dependent managers are ready for: ${_activeEnv!.serverName}', ); - // Handle matrix credentials if they exist - if (await HubSecureStorage().containsKey(storageKeyForMatrixCredentials)) { - _log.info( - '[DI] Found matrix credentials for environment ${_activeEnv!.serverName}', - ); - - // Only register matrix managers if they're not already registered - final hasMatrixScope = di.hasScope(DiScope.onMatrixEnvScope.name); - _log.info( - '[DI] Environment ${_activeEnv!.serverName}: hasMatrixScope = $hasMatrixScope', - ); - - if (!hasMatrixScope) { - _log.info( - '[DI] Registering matrix managers for environment ${_activeEnv!.serverName}', - ); - await InitManager.registerMatrixManagers(); - } else { - _log.info( - '[DI] Matrix managers already registered, skipping registration', - ); - // Ensure session configured flag is set even if skipping registration - di().setIsMatrixSessionConfigured(true); - } - } else { - _log.info( - '[DI] No matrix credentials found for environment ${_activeEnv!.serverName}', - ); - } + // // Handle matrix credentials if they exist + // if (await HubSecureStorage().containsKey(storageKeyForMatrixCredentials)) { + // _log.info( + // '[DI] Found matrix credentials for environment ${_activeEnv!.serverName}', + // ); + + // // Only register matrix managers if they're not already registered + // final hasMatrixScope = di.hasScope(DiScope.onMatrixEnvScope.name); + // _log.info( + // '[DI] Environment ${_activeEnv!.serverName}: hasMatrixScope = $hasMatrixScope', + // ); + + // if (!hasMatrixScope) { + // _log.info( + // '[DI] Registering matrix managers for environment ${_activeEnv!.serverName}', + // ); + // await InitManager.registerMatrixManagers(); + // } else { + // _log.info( + // '[DI] Matrix managers already registered, skipping registration', + // ); + // // Ensure session configured flag is set even if skipping registration + // di().setIsMatrixSessionConfigured(true); + // } + // } else { + // _log.info( + // '[DI] No matrix credentials found for environment ${_activeEnv!.serverName}', + // ); + // } // Wait for all DI registrations to be ready - _log.info('[DI] Waiting for all managers to be ready...'); - await di.allReady(); + // _log.info('[DI] Waiting for all managers to be ready...'); + // await di.allReady(); // Set environment as ready _envIsReady.value = true; @@ -441,12 +429,12 @@ class EnvManager with ChangeNotifier { return; } - // Try to get the session manager - this will throw if not ready - final sessionManager = di(); + // // Try to get the session manager - this will throw if not ready + // final sessionManager = di(); - // The session manager should handle the auto-login - // This will set the authentication status appropriately - await sessionManager.initialize(); + // // The session manager should handle the auto-login + // // This will set the authentication status appropriately + // await sessionManager.initialize(); _log.info('Auto-login completed successfully'); } catch (sessionError) { diff --git a/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart b/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart index 893ac25e..7ce8c905 100644 --- a/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart +++ b/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart @@ -1,5 +1,8 @@ import 'package:logging/logging.dart'; +import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/domain/filters/filters_state_manager.dart'; +import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/core/init/init_manager.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/_attendance/domain/attendance_manager.dart'; import 'package:school_data_hub_flutter/features/_attendance/domain/filters/attendance_pupil_filter.dart'; @@ -33,6 +36,21 @@ final _log = Logger('[Init][OnUserAuth]'); class InitOnUserAuth { static Future registerManagers() async { + if (await HubSecureStorage().containsKey( + di().storageKeyForMatrixCredentials, + )) { + _log.info(' Matrix credentials found'); + // Only register matrix managers if they're not already registered + if (!di.hasScope(DiScope.onMatrixEnvScope.name)) { + await InitManager.registerMatrixManagers(); + } else { + _log.info(' Matrix managers already registered, skipping registration'); + // Ensure session configured flag is set even if skipping registration + di().setIsMatrixSessionConfigured(true); + } + } else { + _log.info(' No matrix credentials found'); + } di.registerSingletonAsync(() async { final pupilIdentityManager = PupilIdentityManager(); From 54e5069dac7b6addd5adc7b218d87554697f7b4e Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 19:45:29 +0100 Subject: [PATCH 35/52] [APP] Relabel main nav tools --- .../lib/core/router/app_router.dart | 1050 ++++++++--------- .../widgets/landing_bottom_nav_bar.dart | 2 +- school_data_hub_flutter/lib/l10n/app_de.arb | 2 +- school_data_hub_flutter/lib/l10n/app_en.arb | 2 +- school_data_hub_flutter/lib/l10n/app_es.arb | 2 +- .../lib/l10n/app_localizations.dart | 6 +- .../lib/l10n/app_localizations_de.dart | 2 +- .../lib/l10n/app_localizations_en.dart | 2 +- .../lib/l10n/app_localizations_es.dart | 2 +- 9 files changed, 535 insertions(+), 535 deletions(-) diff --git a/school_data_hub_flutter/lib/core/router/app_router.dart b/school_data_hub_flutter/lib/core/router/app_router.dart index e39bf468..195ff7a0 100644 --- a/school_data_hub_flutter/lib/core/router/app_router.dart +++ b/school_data_hub_flutter/lib/core/router/app_router.dart @@ -1,549 +1,549 @@ -import 'dart:io'; +// import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:school_data_hub_client/school_data_hub_client.dart'; -import 'package:school_data_hub_flutter/app_utils/scanner.dart'; -import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; -import 'package:school_data_hub_flutter/core/env/env_manager.dart'; -import 'package:school_data_hub_flutter/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart'; -import 'package:school_data_hub_flutter/core/session/serverpod_connectivity_monitor.dart'; -import 'package:school_data_hub_flutter/features/_attendance/presentation/attendance_page/attendance_list_page.dart'; -import 'package:school_data_hub_flutter/features/_attendance/presentation/missed_classes_pupil_list_page/missed_classes_pupil_list_page.dart'; -import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart'; -import 'package:school_data_hub_flutter/features/app_entry_point/entry_point/entry_point_controller.dart'; -import 'package:school_data_hub_flutter/features/app_entry_point/error_page.dart'; -import 'package:school_data_hub_flutter/features/app_entry_point/loading_page.dart'; -import 'package:school_data_hub_flutter/features/app_entry_point/login_page/login_controller.dart'; -import 'package:school_data_hub_flutter/features/app_entry_point/no_connection_page.dart'; -import 'package:school_data_hub_flutter/features/app_main_navigation/learn_resources_menu_page.dart'; -import 'package:school_data_hub_flutter/features/app_main_navigation/pupil_lists_menu_page.dart'; -import 'package:school_data_hub_flutter/features/app_main_navigation/school_lists_page.dart'; -import 'package:school_data_hub_flutter/features/app_main_navigation/tools_page.dart'; -import 'package:school_data_hub_flutter/features/app_settings/settings_page/settings_page.dart'; -import 'package:school_data_hub_flutter/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart'; -import 'package:school_data_hub_flutter/features/books/domain/models/enums.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/book_list_page/book_list_page.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/book_search_form/book_search_form_page.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/book_search_form/select_book_tags_page.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/book_search_page/book_search_results_page.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/books_main_menu_page/books_main_menu_page.dart'; -import 'package:school_data_hub_flutter/features/books/presentation/new_book_page/new_book_controller.dart'; -import 'package:school_data_hub_flutter/features/learning/presentation/competence_list_page/competence_list_page.dart'; -import 'package:school_data_hub_flutter/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart'; -import 'package:school_data_hub_flutter/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart'; -import 'package:school_data_hub_flutter/features/learning_support/presentation/new_learning_support_plan/controller/new_learning_support_plan_controller.dart'; -import 'package:school_data_hub_flutter/features/learning_support/presentation/new_support_category_status_page/controller/new_support_category_status_controller.dart'; -import 'package:school_data_hub_flutter/features/learning_support/presentation/support_category_list_page/controller/category_list_controller.dart'; -import 'package:school_data_hub_flutter/features/learning_support/services/learning_support_plan_pdf_generator.dart'; -import 'package:school_data_hub_flutter/features/matrix/presentation/set_matrix_environment_page/set_matrix_environment_controller.dart'; -import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart'; -import 'package:school_data_hub_flutter/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart'; -import 'package:school_data_hub_flutter/features/ogs/ogs_list_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; -import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/_credit/credit_list_page/credit_list_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/birthdays_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/religion_page/religion_list_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/special_info_page/special_info_list_page.dart'; -import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_set_avatar.dart'; -import 'package:school_data_hub_flutter/features/school/presentation/edit_school_data_page/edit_school_data_page.dart'; -import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart'; -import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/schooldays_calendar_page/schooldays_calendar_page.dart'; -import 'package:school_data_hub_flutter/features/school_lists/presentation/school_lists_page/school_lists_page.dart'; -import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page_controller.dart'; -import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; -import 'package:school_data_hub_flutter/features/timetable/domain/timetable_manager.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/classroom_list_page/classroom_list_page.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/learning_group_list_page/learning_group_list_page.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/new_lesson_group_page/new_lesson_group_page.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/new_scheduled_lesson_page/new_scheduled_lesson_page.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/new_timetable_page/new_timetable_page.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/subject_list_page/subject_list_page.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_page/timetable_page.dart'; -import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_slot_list_page/timetable_slot_list_page.dart'; -import 'package:school_data_hub_flutter/features/user/presentation/change_password/change_password_page.dart'; -import 'package:school_data_hub_flutter/features/user/presentation/create_user/create_user_page.dart'; -import 'package:school_data_hub_flutter/features/user/presentation/reset_password/reset_user_password_page.dart'; -import 'package:school_data_hub_flutter/features/user/presentation/user_list/user_list_page.dart'; -import 'package:school_data_hub_flutter/features/workbooks/presentation/workbook_list_page/controller/workbook_list_view_model.dart'; -import 'package:watch_it/watch_it.dart'; +// import 'package:flutter/material.dart'; +// import 'package:go_router/go_router.dart'; +// import 'package:image_picker/image_picker.dart'; +// import 'package:school_data_hub_client/school_data_hub_client.dart'; +// import 'package:school_data_hub_flutter/app_utils/scanner.dart'; +// import 'package:school_data_hub_flutter/common/widgets/upload_image.dart'; +// import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +// import 'package:school_data_hub_flutter/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart'; +// import 'package:school_data_hub_flutter/core/session/serverpod_connectivity_monitor.dart'; +// import 'package:school_data_hub_flutter/features/_attendance/presentation/attendance_page/attendance_list_page.dart'; +// import 'package:school_data_hub_flutter/features/_attendance/presentation/missed_classes_pupil_list_page/missed_classes_pupil_list_page.dart'; +// import 'package:school_data_hub_flutter/features/_schoolday_events/presentation/schoolday_event_list_page/schoolday_event_list_page.dart'; +// import 'package:school_data_hub_flutter/features/app_entry_point/entry_point/entry_point_controller.dart'; +// import 'package:school_data_hub_flutter/features/app_entry_point/error_page.dart'; +// import 'package:school_data_hub_flutter/features/app_entry_point/loading_page.dart'; +// import 'package:school_data_hub_flutter/features/app_entry_point/login_page/login_controller.dart'; +// import 'package:school_data_hub_flutter/features/app_entry_point/no_connection_page.dart'; +// import 'package:school_data_hub_flutter/features/app_main_navigation/learn_resources_menu_page.dart'; +// import 'package:school_data_hub_flutter/features/app_main_navigation/pupil_lists_menu_page.dart'; +// import 'package:school_data_hub_flutter/features/app_main_navigation/school_lists_page.dart'; +// import 'package:school_data_hub_flutter/features/app_main_navigation/tools_page.dart'; +// import 'package:school_data_hub_flutter/features/app_settings/settings_page/settings_page.dart'; +// import 'package:school_data_hub_flutter/features/authorizations/presentation/authorizations_list_page/authorizations_list_page.dart'; +// import 'package:school_data_hub_flutter/features/books/domain/models/enums.dart'; +// import 'package:school_data_hub_flutter/features/books/presentation/book_list_page/book_list_page.dart'; +// import 'package:school_data_hub_flutter/features/books/presentation/book_search_form/book_search_form_page.dart'; +// import 'package:school_data_hub_flutter/features/books/presentation/book_search_form/select_book_tags_page.dart'; +// import 'package:school_data_hub_flutter/features/books/presentation/book_search_page/book_search_results_page.dart'; +// import 'package:school_data_hub_flutter/features/books/presentation/book_tag_management_page/book_tag_management_controller.dart'; +// import 'package:school_data_hub_flutter/features/books/presentation/books_main_menu_page/books_main_menu_page.dart'; +// import 'package:school_data_hub_flutter/features/books/presentation/new_book_page/new_book_controller.dart'; +// import 'package:school_data_hub_flutter/features/learning/presentation/competence_list_page/competence_list_page.dart'; +// import 'package:school_data_hub_flutter/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart'; +// import 'package:school_data_hub_flutter/features/learning_support/presentation/learning_support_list_page/learning_support_list_page.dart'; +// import 'package:school_data_hub_flutter/features/learning_support/presentation/new_learning_support_plan/controller/new_learning_support_plan_controller.dart'; +// import 'package:school_data_hub_flutter/features/learning_support/presentation/new_support_category_status_page/controller/new_support_category_status_controller.dart'; +// import 'package:school_data_hub_flutter/features/learning_support/presentation/support_category_list_page/controller/category_list_controller.dart'; +// import 'package:school_data_hub_flutter/features/learning_support/services/learning_support_plan_pdf_generator.dart'; +// import 'package:school_data_hub_flutter/features/matrix/presentation/set_matrix_environment_page/set_matrix_environment_controller.dart'; +// import 'package:school_data_hub_flutter/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart'; +// import 'package:school_data_hub_flutter/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart'; +// import 'package:school_data_hub_flutter/features/ogs/ogs_list_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +// import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; +// import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/_credit/credit_list_page/credit_list_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/birthdays_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_identity_stream_page/pupil_identity_stream_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_profile_page/pupil_profile_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/religion_page/religion_list_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/select_pupils_list_page/select_pupils_list_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/special_info_page/special_info_list_page.dart'; +// import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_set_avatar.dart'; +// import 'package:school_data_hub_flutter/features/school/presentation/edit_school_data_page/edit_school_data_page.dart'; +// import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/new_school_semester_page.dart'; +// import 'package:school_data_hub_flutter/features/school_calendar/presentation/new_school_semester_page/schooldays_calendar_page/schooldays_calendar_page.dart'; +// import 'package:school_data_hub_flutter/features/school_lists/presentation/school_lists_page/school_lists_page.dart'; +// import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page_controller.dart'; +// import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; +// import 'package:school_data_hub_flutter/features/timetable/domain/timetable_manager.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/classroom_list_page/classroom_list_page.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/learning_group_list_page/learning_group_list_page.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/new_lesson_group_page/new_lesson_group_page.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/new_scheduled_lesson_page/new_scheduled_lesson_page.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/new_timetable_page/new_timetable_page.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/subject_list_page/subject_list_page.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_page/timetable_page.dart'; +// import 'package:school_data_hub_flutter/features/timetable/presentation/timetable_slot_list_page/timetable_slot_list_page.dart'; +// import 'package:school_data_hub_flutter/features/user/presentation/change_password/change_password_page.dart'; +// import 'package:school_data_hub_flutter/features/user/presentation/create_user/create_user_page.dart'; +// import 'package:school_data_hub_flutter/features/user/presentation/reset_password/reset_user_password_page.dart'; +// import 'package:school_data_hub_flutter/features/user/presentation/user_list/user_list_page.dart'; +// import 'package:school_data_hub_flutter/features/workbooks/presentation/workbook_list_page/controller/workbook_list_view_model.dart'; +// import 'package:watch_it/watch_it.dart'; -class AppRouter { - static final GlobalKey rootNavigatorKey = - GlobalKey(debugLabel: 'root'); +// class AppRouter { +// static final GlobalKey rootNavigatorKey = +// GlobalKey(debugLabel: 'root'); - late final GoRouter router; +// late final GoRouter router; - AppRouter() { - router = GoRouter( - navigatorKey: rootNavigatorKey, - initialLocation: '/', - refreshListenable: Listenable.merge([ - di().isAuthenticated, - di().envIsReady, - di().isConnected, - ]), - redirect: (context, state) { - final envManager = di(); - final connectivityMonitor = di(); +// AppRouter() { +// router = GoRouter( +// navigatorKey: rootNavigatorKey, +// initialLocation: '/', +// refreshListenable: Listenable.merge([ +// di().isAuthenticated, +// di().envIsReady, +// di().isConnected, +// ]), +// redirect: (context, state) { +// final envManager = di(); +// final connectivityMonitor = di(); - final isConnected = connectivityMonitor.isConnected.value; - final envIsReady = envManager.envIsReady.value; - final isAuthenticated = envManager.isAuthenticated.value; +// final isConnected = connectivityMonitor.isConnected.value; +// final envIsReady = envManager.envIsReady.value; +// final isAuthenticated = envManager.isAuthenticated.value; - final path = state.uri.path; +// final path = state.uri.path; - final isLoginLoc = path == '/login'; - final isLoadingLoc = path == '/loading'; - final isNoConnectionLoc = path == '/no-connection'; - final isEntryPointLoc = path == '/'; +// final isLoginLoc = path == '/login'; +// final isLoadingLoc = path == '/loading'; +// final isNoConnectionLoc = path == '/no-connection'; +// final isEntryPointLoc = path == '/'; - // 1. Connection Check - if (!isConnected) { - return isNoConnectionLoc ? null : '/no-connection'; - } - if (isNoConnectionLoc && isConnected) { - // If connection restored, verify where to go based on other states - if (envIsReady && isAuthenticated) return '/home'; - if (envIsReady && !isAuthenticated) return '/login'; - return '/'; - } +// // 1. Connection Check +// if (!isConnected) { +// return isNoConnectionLoc ? null : '/no-connection'; +// } +// if (isNoConnectionLoc && isConnected) { +// // If connection restored, verify where to go based on other states +// if (envIsReady && isAuthenticated) return '/home'; +// if (envIsReady && !isAuthenticated) return '/login'; +// return '/'; +// } - // 2. Environment Ready Check - if (!envIsReady) { - // If we are trying to load an active environment - if (envManager.activeEnv != null) { - return isLoadingLoc ? null : '/loading'; - } +// // 2. Environment Ready Check +// if (!envIsReady) { +// // If we are trying to load an active environment +// if (envManager.activeEnv != null) { +// return isLoadingLoc ? null : '/loading'; +// } - // Handle deep link when env is not ready - redirect to login with preserved parameters - if (isEntryPointLoc && - state.uri.queryParameters.containsKey('channelName')) { - final from = state.uri.toString(); - return '/login?from=${Uri.encodeComponent(from)}'; - } +// // Handle deep link when env is not ready - redirect to login with preserved parameters +// if (isEntryPointLoc && +// state.uri.queryParameters.containsKey('channelName')) { +// final from = state.uri.toString(); +// return '/login?from=${Uri.encodeComponent(from)}'; +// } - // Otherwise we are at entry point selection - return isEntryPointLoc ? null : '/'; - } +// // Otherwise we are at entry point selection +// return isEntryPointLoc ? null : '/'; +// } - // 3. Auth Check - if (!isAuthenticated) { - if (isLoginLoc) return null; +// // 3. Auth Check +// if (!isAuthenticated) { +// if (isLoginLoc) return null; - // Don't redirect system pages to login with return param - if (isEntryPointLoc || isLoadingLoc || isNoConnectionLoc) { - // If it's entry point with deep link params, preserve them for login redirect - if (isEntryPointLoc && - state.uri.queryParameters.containsKey('channelName')) { - final from = state.uri.toString(); - return '/login?from=${Uri.encodeComponent(from)}'; - } - return '/login'; - } +// // Don't redirect system pages to login with return param +// if (isEntryPointLoc || isLoadingLoc || isNoConnectionLoc) { +// // If it's entry point with deep link params, preserve them for login redirect +// if (isEntryPointLoc && +// state.uri.queryParameters.containsKey('channelName')) { +// final from = state.uri.toString(); +// return '/login?from=${Uri.encodeComponent(from)}'; +// } +// return '/login'; +// } - // Save intended location for deep links - final from = state.uri.toString(); - return '/login?from=${Uri.encodeComponent(from)}'; - } +// // Save intended location for deep links +// final from = state.uri.toString(); +// return '/login?from=${Uri.encodeComponent(from)}'; +// } - // 4. Authenticated User - if (isAuthenticated) { - if (isLoginLoc || isEntryPointLoc || isLoadingLoc) { - final from = state.uri.queryParameters['from']; - if (from != null && from.isNotEmpty) return from; - return '/home'; - } - } +// // 4. Authenticated User +// if (isAuthenticated) { +// if (isLoginLoc || isEntryPointLoc || isLoadingLoc) { +// final from = state.uri.queryParameters['from']; +// if (from != null && from.isNotEmpty) return from; +// return '/home'; +// } +// } - // 5. Deep Link Handling - // If authenticated, we allow pass-through to feature routes +// // 5. Deep Link Handling +// // If authenticated, we allow pass-through to feature routes - // Handle redirect for root path with stream parameters - if (path == '/' && - state.uri.queryParameters.containsKey('channelName')) { - final channelName = state.uri.queryParameters['channelName']; - final url = state.uri.queryParameters['url']; +// // Handle redirect for root path with stream parameters +// if (path == '/' && +// state.uri.queryParameters.containsKey('channelName')) { +// final channelName = state.uri.queryParameters['channelName']; +// final url = state.uri.queryParameters['url']; - // Force redirect to the stream page - return '/pupil-identity-stream?code=$channelName&instance=$url'; - } +// // Force redirect to the stream page +// return '/pupil-identity-stream?code=$channelName&instance=$url'; +// } - return null; - }, - routes: [ - GoRoute(path: '/', builder: (context, state) => const EntryPoint()), - GoRoute(path: '/login', builder: (context, state) => const Login()), - GoRoute( - path: '/loading', - builder: (context, state) => const LoadingPage(), - ), - GoRoute( - path: '/no-connection', - builder: (context, state) => const NoConnectionPage(), - ), - // Deep Link Route for Pupil Identity Stream Receiver - GoRoute( - path: '/pupil-identity-stream', - builder: (context, state) { - final code = state.uri.queryParameters['code']; - final instanceUrl = state.uri.queryParameters['instance']; - final extra = state.extra as Map?; +// return null; +// }, +// routes: [ +// GoRoute(path: '/', builder: (context, state) => const EntryPoint()), +// GoRoute(path: '/login', builder: (context, state) => const Login()), +// GoRoute( +// path: '/loading', +// builder: (context, state) => const LoadingPage(), +// ), +// GoRoute( +// path: '/no-connection', +// builder: (context, state) => const NoConnectionPage(), +// ), +// // Deep Link Route for Pupil Identity Stream Receiver +// GoRoute( +// path: '/pupil-identity-stream', +// builder: (context, state) { +// final code = state.uri.queryParameters['code']; +// final instanceUrl = state.uri.queryParameters['instance']; +// final extra = state.extra as Map?; - if (extra != null && - extra['role'] == PupilIdentityStreamRole.sender) { - return PupilIdentityStreamPage( - role: PupilIdentityStreamRole.sender, - encryptedData: extra['encryptedData'], - selectedPupilIds: extra['selectedPupilIds'], - ); - } +// if (extra != null && +// extra['role'] == PupilIdentityStreamRole.sender) { +// return PupilIdentityStreamPage( +// role: PupilIdentityStreamRole.sender, +// encryptedData: extra['encryptedData'], +// selectedPupilIds: extra['selectedPupilIds'], +// ); +// } - return PupilIdentityStreamPage( - role: PupilIdentityStreamRole.receiver, - importedChannelName: code ?? extra?['importedChannelName'], - ); - }, - ), - GoRoute( - path: '/crop-avatar', - builder: (context, state) { - final image = state.extra as XFile; - return CropAvatarView(image: image); - }, - ), - GoRoute( - path: '/scanner', - builder: (context, state) { - final overlayText = state.extra as String? ?? 'Scanner'; - return ScannerPage(overlayText: overlayText); - }, - ), - GoRoute( - path: '/timetable', - builder: (context, state) => const TimetablePage(), - ), - GoRoute( - path: '/timetable/new', - builder: (context, state) => const NewTimetablePage(), - ), - GoRoute( - path: '/timetable/slots', - builder: (context, state) => const TimetableSlotListPage(), - ), - GoRoute( - path: '/timetable/groups', - builder: (context, state) => const LearningGroupListPage(), - ), - GoRoute( - path: '/timetable/subjects', - builder: (context, state) => const SubjectListPage(), - ), - GoRoute( - path: '/timetable/lesson', - builder: (context, state) { - final extra = state.extra as Map?; - final manager = di(); +// return PupilIdentityStreamPage( +// role: PupilIdentityStreamRole.receiver, +// importedChannelName: code ?? extra?['importedChannelName'], +// ); +// }, +// ), +// GoRoute( +// path: '/crop-avatar', +// builder: (context, state) { +// final image = state.extra as XFile; +// return CropAvatarView(image: image); +// }, +// ), +// GoRoute( +// path: '/scanner', +// builder: (context, state) { +// final overlayText = state.extra as String? ?? 'Scanner'; +// return ScannerPage(overlayText: overlayText); +// }, +// ), +// GoRoute( +// path: '/timetable', +// builder: (context, state) => const TimetablePage(), +// ), +// GoRoute( +// path: '/timetable/new', +// builder: (context, state) => const NewTimetablePage(), +// ), +// GoRoute( +// path: '/timetable/slots', +// builder: (context, state) => const TimetableSlotListPage(), +// ), +// GoRoute( +// path: '/timetable/groups', +// builder: (context, state) => const LearningGroupListPage(), +// ), +// GoRoute( +// path: '/timetable/subjects', +// builder: (context, state) => const SubjectListPage(), +// ), +// GoRoute( +// path: '/timetable/lesson', +// builder: (context, state) { +// final extra = state.extra as Map?; +// final manager = di(); - return NewScheduledLessonPage( - timetableManager: manager, - editingLessonId: extra?['editingLessonId'], - preselectedSlotId: extra?['preselectedSlotId'], - ); - }, - ), - GoRoute( - path: '/timetable/lesson-group', - builder: (context, state) => const NewLessonGroupPage(), - ), - GoRoute( - path: '/classrooms', - builder: (context, state) => const ClassroomListPage(), - ), - GoRoute( - path: '/charts', - builder: (context, state) => const ChartPageController(), - ), - GoRoute( - path: '/statistics', - builder: (context, state) => const Statistics(), - ), - GoRoute( - path: '/select-pupils', - builder: (context, state) { - final selectablePupils = state.extra as List? ?? []; - return SelectPupilsListPage(selectablePupils: selectablePupils); - }, - ), - GoRoute( - path: '/birthdays', - builder: (context, state) { - final selectedDate = state.extra as DateTime; - return BirthdaysView(selectedDate: selectedDate); - }, - ), - GoRoute( - path: '/crop-document', - builder: (context, state) { - final image = state.extra as XFile; - return CropDocumentView(image: image); - }, - ), - GoRoute( - path: '/admin/edit-school-data', - builder: (context, state) => const EditSchoolDataPage(), - ), - GoRoute( - path: '/admin/create-user', - builder: (context, state) => const CreateUserPage(), - ), - GoRoute( - path: '/admin/users', - builder: (context, state) => const UserListPage(), - ), - GoRoute( - path: '/admin/reset-password', - builder: (context, state) => const ResetUserPasswordPage(), - ), - GoRoute( - path: '/admin/matrix/set-environment', - builder: (context, state) => const SetMatrixEnvironment(), - ), - GoRoute( - path: '/admin/calendar', - builder: (context, state) => const SchooldaysCalendarPage(), - ), - GoRoute( - path: '/admin/new-semester', - builder: (context, state) => const NewSchoolSemesterPage(), - ), - // Learning Resources - GoRoute( - path: '/learning/competences', - builder: (context, state) => const CompetenceListPage(), - ), - GoRoute( - path: '/learning/categories', - builder: (context, state) => const CategoryList(), - ), - GoRoute( - path: '/learning/workbooks', - builder: (context, state) => const WorkbookList(), - ), - GoRoute( - path: '/learning/books', - builder: (context, state) => const BooksMainMenuPage(), - ), - GoRoute( - path: '/learning/books/tags', - builder: (context, state) => const BookTagManagement(), - ), - GoRoute( - path: '/learning/books/list', - builder: (context, state) => const BookListPage(), - ), - GoRoute( - path: '/learning/books/new', - builder: (context, state) { - final extra = state.extra as Map?; - return NewBook( - isEdit: extra?['isEdit'] ?? false, - isbn: extra?['isbn'] ?? 0, - ); - }, - ), - GoRoute( - path: '/learning/books/search', - builder: (context, state) => const BookSearchFormPage(), - ), - GoRoute( - path: '/learning/books/select-tags', - builder: (context, state) { - final extra = state.extra as List?; - return SelectBookTagsPage(initialSelectedTags: extra ?? []); - }, - ), - GoRoute( - path: '/learning/books/search-results', - builder: (context, state) { - final extra = state.extra as Map?; - return BookSearchResultsPage( - title: extra?['title'] as String?, - author: extra?['author'] as String?, - keywords: extra?['keywords'] as String?, - location: extra?['location'] as LibraryBookLocation?, - readingLevel: extra?['readingLevel'] as String?, - borrowStatus: extra?['borrowStatus'] as BorrowedStatus?, - selectedTags: extra?['selectedTags'] as List? ?? [], - ); - }, - ), - GoRoute( - path: '/pupil/new-support-category-status', - builder: (context, state) { - final extra = state.extra as Map?; - return NewSupportCategoryStatus( - appBarTitle: extra?['appBarTitle'] as String, - pupilId: extra?['pupilId'] as int, - goalCategoryId: extra?['goalCategoryId'] as int, - elementType: extra?['elementType'] as String, - ); - }, - ), - GoRoute( - path: '/pupil/new-learning-support-plan', - builder: (context, state) { - final pupil = state.extra as PupilProxy; - return NewLearningSupportPlan(pupil: pupil); - }, - ), - GoRoute( - path: '/pupil/learning-support-plan-pdf', - builder: (context, state) { - final file = state.extra as File; - return LearningSupportPlanPdfViewPage(pdfFile: file); - }, - ), - GoRoute( - path: '/pupil/schoolday-events', - builder: (context, state) => const SchooldayEventListPage(), - ), - GoRoute( - path: '/pupil/missed-schooldays', - builder: (context, state) => const MissedSchooldayesPupilListPage(), - ), - GoRoute( - path: '/pupil/attendance', - builder: (context, state) => const AttendanceListPage(), - ), - GoRoute( - path: '/pupil/credit', - builder: (context, state) => const CreditListPage(), - ), - GoRoute( - path: '/pupil/learning-lists', - builder: (context, state) => const LearningPupilListPage(), - ), - GoRoute( - path: '/pupil/support-lists', - builder: (context, state) => const LearningSupportListPage(), - ), - GoRoute( - path: '/pupil/special-info', - builder: (context, state) => const SpecialInfoListPage(), - ), - GoRoute( - path: '/pupil/religion', - builder: (context, state) => const ReligionListPage(), - ), - GoRoute( - path: '/pupil/family-language', - builder: (context, state) => const FamilyLanguageLessonsListPage(), - ), - GoRoute( - path: '/pupil/ogs', - builder: (context, state) => const OgsListPage(), - ), - GoRoute( - path: '/pupil/matrix-users', - builder: (context, state) => const MatrixUsersListPage(), - ), - GoRoute( - path: '/pupil/matrix-contacts', - builder: (context, state) => const PupilsMatrixContactsListPage(), - ), - GoRoute( - path: '/school/lists', - builder: (context, state) => const SchoolListsPage(), - ), - GoRoute( - path: '/school/authorizations', - builder: (context, state) => const AuthorizationsListPage(), - ), - GoRoute( - path: '/settings/change-password', - builder: (context, state) => const UserChangePasswordPage(), - ), - GoRoute( - path: '/pupil/:id', - builder: (context, state) { - final idStr = state.pathParameters['id']; - if (idStr == null) - return const ErrorPage(error: "Keine ID übergeben"); - final id = int.tryParse(idStr); - if (id == null) return const ErrorPage(error: "Ungültige ID"); +// return NewScheduledLessonPage( +// timetableManager: manager, +// editingLessonId: extra?['editingLessonId'], +// preselectedSlotId: extra?['preselectedSlotId'], +// ); +// }, +// ), +// GoRoute( +// path: '/timetable/lesson-group', +// builder: (context, state) => const NewLessonGroupPage(), +// ), +// GoRoute( +// path: '/classrooms', +// builder: (context, state) => const ClassroomListPage(), +// ), +// GoRoute( +// path: '/charts', +// builder: (context, state) => const ChartPageController(), +// ), +// GoRoute( +// path: '/statistics', +// builder: (context, state) => const Statistics(), +// ), +// GoRoute( +// path: '/select-pupils', +// builder: (context, state) { +// final selectablePupils = state.extra as List? ?? []; +// return SelectPupilsListPage(selectablePupils: selectablePupils); +// }, +// ), +// GoRoute( +// path: '/birthdays', +// builder: (context, state) { +// final selectedDate = state.extra as DateTime; +// return BirthdaysView(selectedDate: selectedDate); +// }, +// ), +// GoRoute( +// path: '/crop-document', +// builder: (context, state) { +// final image = state.extra as XFile; +// return CropDocumentView(image: image); +// }, +// ), +// GoRoute( +// path: '/admin/edit-school-data', +// builder: (context, state) => const EditSchoolDataPage(), +// ), +// GoRoute( +// path: '/admin/create-user', +// builder: (context, state) => const CreateUserPage(), +// ), +// GoRoute( +// path: '/admin/users', +// builder: (context, state) => const UserListPage(), +// ), +// GoRoute( +// path: '/admin/reset-password', +// builder: (context, state) => const ResetUserPasswordPage(), +// ), +// GoRoute( +// path: '/admin/matrix/set-environment', +// builder: (context, state) => const SetMatrixEnvironment(), +// ), +// GoRoute( +// path: '/admin/calendar', +// builder: (context, state) => const SchooldaysCalendarPage(), +// ), +// GoRoute( +// path: '/admin/new-semester', +// builder: (context, state) => const NewSchoolSemesterPage(), +// ), +// // Learning Resources +// GoRoute( +// path: '/learning/competences', +// builder: (context, state) => const CompetenceListPage(), +// ), +// GoRoute( +// path: '/learning/categories', +// builder: (context, state) => const CategoryList(), +// ), +// GoRoute( +// path: '/learning/workbooks', +// builder: (context, state) => const WorkbookList(), +// ), +// GoRoute( +// path: '/learning/books', +// builder: (context, state) => const BooksMainMenuPage(), +// ), +// GoRoute( +// path: '/learning/books/tags', +// builder: (context, state) => const BookTagManagement(), +// ), +// GoRoute( +// path: '/learning/books/list', +// builder: (context, state) => const BookListPage(), +// ), +// GoRoute( +// path: '/learning/books/new', +// builder: (context, state) { +// final extra = state.extra as Map?; +// return NewBook( +// isEdit: extra?['isEdit'] ?? false, +// isbn: extra?['isbn'] ?? 0, +// ); +// }, +// ), +// GoRoute( +// path: '/learning/books/search', +// builder: (context, state) => const BookSearchFormPage(), +// ), +// GoRoute( +// path: '/learning/books/select-tags', +// builder: (context, state) { +// final extra = state.extra as List?; +// return SelectBookTagsPage(initialSelectedTags: extra ?? []); +// }, +// ), +// GoRoute( +// path: '/learning/books/search-results', +// builder: (context, state) { +// final extra = state.extra as Map?; +// return BookSearchResultsPage( +// title: extra?['title'] as String?, +// author: extra?['author'] as String?, +// keywords: extra?['keywords'] as String?, +// location: extra?['location'] as LibraryBookLocation?, +// readingLevel: extra?['readingLevel'] as String?, +// borrowStatus: extra?['borrowStatus'] as BorrowedStatus?, +// selectedTags: extra?['selectedTags'] as List? ?? [], +// ); +// }, +// ), +// GoRoute( +// path: '/pupil/new-support-category-status', +// builder: (context, state) { +// final extra = state.extra as Map?; +// return NewSupportCategoryStatus( +// appBarTitle: extra?['appBarTitle'] as String, +// pupilId: extra?['pupilId'] as int, +// goalCategoryId: extra?['goalCategoryId'] as int, +// elementType: extra?['elementType'] as String, +// ); +// }, +// ), +// GoRoute( +// path: '/pupil/new-learning-support-plan', +// builder: (context, state) { +// final pupil = state.extra as PupilProxy; +// return NewLearningSupportPlan(pupil: pupil); +// }, +// ), +// GoRoute( +// path: '/pupil/learning-support-plan-pdf', +// builder: (context, state) { +// final file = state.extra as File; +// return LearningSupportPlanPdfViewPage(pdfFile: file); +// }, +// ), +// GoRoute( +// path: '/pupil/schoolday-events', +// builder: (context, state) => const SchooldayEventListPage(), +// ), +// GoRoute( +// path: '/pupil/missed-schooldays', +// builder: (context, state) => const MissedSchooldayesPupilListPage(), +// ), +// GoRoute( +// path: '/pupil/attendance', +// builder: (context, state) => const AttendanceListPage(), +// ), +// GoRoute( +// path: '/pupil/credit', +// builder: (context, state) => const CreditListPage(), +// ), +// GoRoute( +// path: '/pupil/learning-lists', +// builder: (context, state) => const LearningPupilListPage(), +// ), +// GoRoute( +// path: '/pupil/support-lists', +// builder: (context, state) => const LearningSupportListPage(), +// ), +// GoRoute( +// path: '/pupil/special-info', +// builder: (context, state) => const SpecialInfoListPage(), +// ), +// GoRoute( +// path: '/pupil/religion', +// builder: (context, state) => const ReligionListPage(), +// ), +// GoRoute( +// path: '/pupil/family-language', +// builder: (context, state) => const FamilyLanguageLessonsListPage(), +// ), +// GoRoute( +// path: '/pupil/ogs', +// builder: (context, state) => const OgsListPage(), +// ), +// GoRoute( +// path: '/pupil/matrix-users', +// builder: (context, state) => const MatrixUsersListPage(), +// ), +// GoRoute( +// path: '/pupil/matrix-contacts', +// builder: (context, state) => const PupilsMatrixContactsListPage(), +// ), +// GoRoute( +// path: '/school/lists', +// builder: (context, state) => const SchoolListsPage(), +// ), +// GoRoute( +// path: '/school/authorizations', +// builder: (context, state) => const AuthorizationsListPage(), +// ), +// GoRoute( +// path: '/settings/change-password', +// builder: (context, state) => const UserChangePasswordPage(), +// ), +// GoRoute( +// path: '/pupil/:id', +// builder: (context, state) { +// final idStr = state.pathParameters['id']; +// if (idStr == null) +// return const ErrorPage(error: "Keine ID übergeben"); +// final id = int.tryParse(idStr); +// if (id == null) return const ErrorPage(error: "Ungültige ID"); - final pupilManager = di(); - final pupil = pupilManager.getPupilByPupilId(id); +// final pupilManager = di(); +// final pupil = pupilManager.getPupilByPupilId(id); - if (pupil == null) { - return const ErrorPage(error: "Schüler nicht gefunden"); - } - return PupilProfilePage(pupil: pupil); - }, - ), - StatefulShellRoute( - branches: [ - StatefulShellBranch( - routes: [ - GoRoute( - path: '/home', // Default tab (Pupil Lists) - builder: (context, state) => const PupilListsMenuPage(), - ), - ], - ), - StatefulShellBranch( - routes: [ - GoRoute( - path: '/school', - builder: (context, state) => const SchoolListsMenuPage(), - ), - ], - ), - StatefulShellBranch( - routes: [ - GoRoute( - path: '/learning', - builder: (context, state) => const LearnResourcesMenuPage(), - ), - ], - ), - StatefulShellBranch( - routes: [ - GoRoute( - path: '/tools', - builder: (context, state) => const ToolsPage(), - ), - ], - ), - StatefulShellBranch( - routes: [ - GoRoute( - path: '/settings', - builder: (context, state) => const SettingsPage(), - ), - ], - ), - ], - navigatorContainerBuilder: (context, navigationShell, children) { - return MainMenuBottomNavigation( - navigationShell: navigationShell, - children: children, - ); - }, - builder: (context, state, navigationShell) { - return navigationShell; // Just return the shell, the container builder handles the UI logic - }, - ), - ], - ); - } -} +// if (pupil == null) { +// return const ErrorPage(error: "Schüler nicht gefunden"); +// } +// return PupilProfilePage(pupil: pupil); +// }, +// ), +// StatefulShellRoute( +// branches: [ +// StatefulShellBranch( +// routes: [ +// GoRoute( +// path: '/home', // Default tab (Pupil Lists) +// builder: (context, state) => const PupilListsMenuPage(), +// ), +// ], +// ), +// StatefulShellBranch( +// routes: [ +// GoRoute( +// path: '/school', +// builder: (context, state) => const SchoolListsMenuPage(), +// ), +// ], +// ), +// StatefulShellBranch( +// routes: [ +// GoRoute( +// path: '/learning', +// builder: (context, state) => const LearnResourcesMenuPage(), +// ), +// ], +// ), +// StatefulShellBranch( +// routes: [ +// GoRoute( +// path: '/tools', +// builder: (context, state) => const ToolsPage(), +// ), +// ], +// ), +// StatefulShellBranch( +// routes: [ +// GoRoute( +// path: '/settings', +// builder: (context, state) => const SettingsPage(), +// ), +// ], +// ), +// ], +// navigatorContainerBuilder: (context, navigationShell, children) { +// return MainMenuBottomNavigation( +// navigationShell: navigationShell, +// children: children, +// ); +// }, +// builder: (context, state, navigationShell) { +// return navigationShell; // Just return the shell, the container builder handles the UI logic +// }, +// ), +// ], +// ); +// } +// } diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart index c5bac09d..90bd4ec1 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/widgets/landing_bottom_nav_bar.dart @@ -242,7 +242,7 @@ class _MainMenuBottomNavigationState extends State { ), BottomNavigationBarItem( icon: const Icon(Icons.build_rounded), - label: locale.scanTools, + label: locale.tools, ), BottomNavigationBarItem( icon: const Icon(Icons.settings), diff --git a/school_data_hub_flutter/lib/l10n/app_de.arb b/school_data_hub_flutter/lib/l10n/app_de.arb index 495e11f1..7f47f9f5 100644 --- a/school_data_hub_flutter/lib/l10n/app_de.arb +++ b/school_data_hub_flutter/lib/l10n/app_de.arb @@ -18,7 +18,7 @@ "pupilLists": "Kinderlisten", "schoolLists": "Schullisten", "learningLists": "Lernlisten", - "scanTools": "Scan-Tools", + "tools": "Tools", "settings": "Einstellungen", "schooldayEvents": "Ereignisse", "missedSchooldays": "Fehlzeiten", diff --git a/school_data_hub_flutter/lib/l10n/app_en.arb b/school_data_hub_flutter/lib/l10n/app_en.arb index 23fb66e1..da2f9179 100644 --- a/school_data_hub_flutter/lib/l10n/app_en.arb +++ b/school_data_hub_flutter/lib/l10n/app_en.arb @@ -18,7 +18,7 @@ "pupilLists": "Listas de Alumnos", "schoolLists": "Listas de Escuelas", "learningLists": "Listas de Aprendizaje", - "scanTools": "Herramientas de Escaneo", + "tools": "Herramientas de Escaneo", "settings": "Configuración", "schooldayEvents": "Eventos del Día Escolar", "missedSchooldays": "Clases Perdidas", diff --git a/school_data_hub_flutter/lib/l10n/app_es.arb b/school_data_hub_flutter/lib/l10n/app_es.arb index a139a5fa..5a83c7ea 100644 --- a/school_data_hub_flutter/lib/l10n/app_es.arb +++ b/school_data_hub_flutter/lib/l10n/app_es.arb @@ -18,7 +18,7 @@ "pupilLists": "Listas de Alumnos", "schoolLists": "Listas de Escuelas", "learningLists": "Listas de Aprendizaje", - "scanTools": "Herramientas de Escaneo", + "tools": "Herramientas", "settings": "Configuración", "schooldayEvents": "Eventos del Día Escolar", "missedSchooldays": "Clases Perdidas", diff --git a/school_data_hub_flutter/lib/l10n/app_localizations.dart b/school_data_hub_flutter/lib/l10n/app_localizations.dart index 1360abf6..be9b7d36 100644 --- a/school_data_hub_flutter/lib/l10n/app_localizations.dart +++ b/school_data_hub_flutter/lib/l10n/app_localizations.dart @@ -214,11 +214,11 @@ abstract class AppLocalizations { /// **'Lernlisten'** String get learningLists; - /// No description provided for @scanTools. + /// No description provided for @tools. /// /// In de, this message translates to: - /// **'Scan-Tools'** - String get scanTools; + /// **'Tools'** + String get tools; /// No description provided for @settings. /// diff --git a/school_data_hub_flutter/lib/l10n/app_localizations_de.dart b/school_data_hub_flutter/lib/l10n/app_localizations_de.dart index 466b205f..35880d96 100644 --- a/school_data_hub_flutter/lib/l10n/app_localizations_de.dart +++ b/school_data_hub_flutter/lib/l10n/app_localizations_de.dart @@ -69,7 +69,7 @@ class AppLocalizationsDe extends AppLocalizations { String get learningLists => 'Lernlisten'; @override - String get scanTools => 'Scan-Tools'; + String get tools => 'Tools'; @override String get settings => 'Einstellungen'; diff --git a/school_data_hub_flutter/lib/l10n/app_localizations_en.dart b/school_data_hub_flutter/lib/l10n/app_localizations_en.dart index 97772ab1..de03c173 100644 --- a/school_data_hub_flutter/lib/l10n/app_localizations_en.dart +++ b/school_data_hub_flutter/lib/l10n/app_localizations_en.dart @@ -69,7 +69,7 @@ class AppLocalizationsEn extends AppLocalizations { String get learningLists => 'Listas de Aprendizaje'; @override - String get scanTools => 'Herramientas de Escaneo'; + String get tools => 'Herramientas de Escaneo'; @override String get settings => 'Configuración'; diff --git a/school_data_hub_flutter/lib/l10n/app_localizations_es.dart b/school_data_hub_flutter/lib/l10n/app_localizations_es.dart index f909085d..c7cd89e2 100644 --- a/school_data_hub_flutter/lib/l10n/app_localizations_es.dart +++ b/school_data_hub_flutter/lib/l10n/app_localizations_es.dart @@ -69,7 +69,7 @@ class AppLocalizationsEs extends AppLocalizations { String get learningLists => 'Listas de Aprendizaje'; @override - String get scanTools => 'Herramientas de Escaneo'; + String get tools => 'Herramientas'; @override String get settings => 'Configuración'; From 2af7e17c489f8ebc3a3f7c8497ea7ca38b78981e Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Tue, 2 Dec 2025 22:59:29 +0100 Subject: [PATCH 36/52] [APP] Add UI for logs --- .../lib/app_utils/logger/log_service.dart | 85 ++++++ .../lib/app_utils/logs_page.dart | 245 ++++++++++++++++++ .../logs_page/widgets/log_entry_card.dart | 107 ++++++++ school_data_hub_flutter/lib/main.dart | 6 +- 4 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 school_data_hub_flutter/lib/app_utils/logger/log_service.dart create mode 100644 school_data_hub_flutter/lib/app_utils/logs_page.dart create mode 100644 school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart diff --git a/school_data_hub_flutter/lib/app_utils/logger/log_service.dart b/school_data_hub_flutter/lib/app_utils/logger/log_service.dart new file mode 100644 index 00000000..010687d1 --- /dev/null +++ b/school_data_hub_flutter/lib/app_utils/logger/log_service.dart @@ -0,0 +1,85 @@ +// lib/services/log_service.dart +import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; + +// Represents a single log entry for display +class AppLog { + final Level level; + final String message; + final String loggerName; + final DateTime time; + + AppLog(this.level, this.message, this.loggerName) : time = DateTime.now(); + + @override + String toString() => + '[${time.toIso8601String().split('T').last.substring(0, 8)}] [${level.name}] [$loggerName] $message'; +} + +// Service to manage the list of logs and notify listeners +class LogService with ChangeNotifier { + final List _logs = []; + List get logs => _logs; + String _searchQuery = ''; + final Map _levelVisibility = {}; + + List get filteredLogs => _logs.where(_shouldIncludeLog).toList(); + + String get searchQuery => _searchQuery; + + bool get showInfo => _isLevelVisible(Level.INFO); + bool get showWarning => _isLevelVisible(Level.WARNING); + bool get showSevere => _isLevelVisible(Level.SEVERE); + bool get showShout => _isLevelVisible(Level.SHOUT); + + // Define a max number of logs to keep in memory + static const int _maxLogs = 100; + + void addLog(AppLog log) { + // Add the new log entry + _logs.insert( + 0, + log, + ); // Insert at the beginning for reverse-chronological order + + // Trim the list if it exceeds the maximum size + if (_logs.length > _maxLogs) { + _logs.removeRange(_maxLogs, _logs.length); + } + + // Notify listeners (your Flutter widget) that the list has changed + notifyListeners(); + } + + void updateSearchQuery(String query) { + final normalized = query.trim(); + if (_searchQuery == normalized) { + return; + } + _searchQuery = normalized; + notifyListeners(); + } + + void setLevelVisibility(Level level, bool isVisible) { + final current = _levelVisibility[level] ?? true; + if (current == isVisible) { + return; + } + _levelVisibility[level] = isVisible; + notifyListeners(); + } + + bool _shouldIncludeLog(AppLog log) { + if (!_isLevelVisible(log.level)) { + return false; + } + if (_searchQuery.isEmpty) { + return true; + } + final needle = _searchQuery.toLowerCase(); + final haystack = '${log.loggerName} ${log.message}'.toLowerCase(); + return haystack.contains(needle); + } + + bool _isLevelVisible(Level level) => _levelVisibility[level] ?? true; +} diff --git a/school_data_hub_flutter/lib/app_utils/logs_page.dart b/school_data_hub_flutter/lib/app_utils/logs_page.dart new file mode 100644 index 00000000..fbebf794 --- /dev/null +++ b/school_data_hub_flutter/lib/app_utils/logs_page.dart @@ -0,0 +1,245 @@ +// lib/log_viewer_page.dart +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:logging/logging.dart'; +import 'package:school_data_hub_flutter/app_utils/logger/log_service.dart'; +import 'package:school_data_hub_flutter/app_utils/logs_page/widgets/log_entry_card.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/bottom_nav_bar_no_filter_button.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_sliver_list.dart'; +import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_sliver_search_app_bar.dart'; +import 'package:watch_it/watch_it.dart'; + +class LogViewerPage extends WatchingWidget { + const LogViewerPage({super.key}); + + @override + Widget build(BuildContext context) { + final logService = watchIt(); + final logs = watchPropertyValue( + (LogService service) => service.filteredLogs, + ); + final searchQuery = watchPropertyValue( + (LogService service) => service.searchQuery, + ); + final showInfo = watchPropertyValue( + (LogService service) => service.showInfo, + ); + final showWarning = watchPropertyValue( + (LogService service) => service.showWarning, + ); + final showSevere = watchPropertyValue( + (LogService service) => service.showSevere, + ); + final showShout = watchPropertyValue( + (LogService service) => service.showShout, + ); + + final searchController = createOnce( + () => TextEditingController(text: logService.searchQuery), + ); + + if (searchController.text != searchQuery) { + searchController.value = TextEditingValue( + text: searchQuery, + selection: TextSelection.collapsed(offset: searchQuery.length), + ); + } + + return Scaffold( + appBar: const GenericAppBar( + iconData: Icons.bug_report_outlined, + title: 'In-App Logs', + ), + bottomNavigationBar: const BottomNavBarNoFilterButton(), + body: CustomScrollView( + slivers: [ + GenericSliverSearchAppBar( + height: 90, + title: _LogSearchField( + controller: searchController, + value: searchQuery, + onChanged: logService.updateSearchQuery, + onClear: () => logService.updateSearchQuery(''), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: _LogLevelSwitches( + showInfo: showInfo, + showWarning: showWarning, + showSevere: showSevere, + showShout: showShout, + onToggle: logService.setLevelVisibility, + ), + ), + ), + GenericSliverListWithEmptyListCheck( + items: logs, + itemBuilder: (context, log) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: LogEntryCard(log: log), + ), + ), + const SliverToBoxAdapter(child: SizedBox(height: 80)), + ], + ), + ); + } +} + +class _LogSearchField extends StatelessWidget { + const _LogSearchField({ + required this.controller, + required this.value, + required this.onChanged, + required this.onClear, + }); + + final TextEditingController controller; + final String value; + final ValueChanged onChanged; + final VoidCallback onClear; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: AppColors.canvasColor, + borderRadius: BorderRadius.circular(16), + boxShadow: const [ + BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2)), + ], + ), + child: TextField( + controller: controller, + onChanged: onChanged, + decoration: InputDecoration( + hintText: 'Protokolle durchsuchen', + prefixIcon: const Icon(Icons.search), + suffixIcon: value.isEmpty + ? null + : IconButton( + icon: const Icon(Icons.close), + onPressed: () { + controller.clear(); + onClear(); + }, + ), + border: InputBorder.none, + ), + ), + ); + } +} + +class _LogLevelSwitches extends StatelessWidget { + const _LogLevelSwitches({ + required this.showInfo, + required this.showWarning, + required this.showSevere, + required this.showShout, + required this.onToggle, + }); + + final bool showInfo; + final bool showWarning; + final bool showSevere; + final bool showShout; + final void Function(Level level, bool value) onToggle; + + @override + Widget build(BuildContext context) { + return Card( + color: AppColors.pupilProfileCardColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Log-Level anzeigen', style: AppStyles.subtitle), + const Gap(12), + Wrap( + spacing: 16, + runSpacing: 12, + children: [ + _LevelSwitch( + label: 'Info', + value: showInfo, + level: Level.INFO, + onToggle: onToggle, + ), + _LevelSwitch( + label: 'Warnung', + value: showWarning, + level: Level.WARNING, + onToggle: onToggle, + ), + _LevelSwitch( + label: 'Fehler', + value: showSevere, + level: Level.SEVERE, + onToggle: onToggle, + ), + _LevelSwitch( + label: 'Kritisch', + value: showShout, + level: Level.SHOUT, + onToggle: onToggle, + ), + ], + ), + ], + ), + ), + ); + } +} + +class _LevelSwitch extends StatelessWidget { + const _LevelSwitch({ + required this.label, + required this.value, + required this.level, + required this.onToggle, + }); + + final String label; + final bool value; + final Level level; + final void Function(Level level, bool value) onToggle; + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.cardInCardBorderColor), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + label, + style: AppStyles.textLabel.copyWith(fontWeight: FontWeight.bold), + ), + const Gap(8), + Switch.adaptive( + value: value, + activeTrackColor: AppColors.accentColor, + onChanged: (newValue) => onToggle(level, newValue), + ), + ], + ), + ), + ); + } +} diff --git a/school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart b/school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart new file mode 100644 index 00000000..5a279112 --- /dev/null +++ b/school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:school_data_hub_flutter/app_utils/logger/log_service.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/theme/styles.dart'; + +class LogEntryCard extends StatelessWidget { + const LogEntryCard({super.key, required this.log}); + + final AppLog log; + + @override + Widget build(BuildContext context) { + final levelColor = _levelColor(log.level); + final timestamp = log.time + .toLocal() + .toIso8601String() + .split('T') + .last + .substring(0, 8); + + return Card( + color: AppColors.cardInCardColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: levelColor, width: 2), + ), + elevation: 1, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + DecoratedBox( + decoration: BoxDecoration( + color: levelColor.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(6), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 4, + ), + child: Text( + log.level.name, + style: AppStyles.textLabel.copyWith( + color: levelColor, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + log.loggerName, + style: AppStyles.subtitle, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + timestamp, + style: AppStyles.textLabel.copyWith(color: Colors.black54), + ), + ], + ), + const SizedBox(height: 10), + Text( + log.message, + style: const TextStyle( + fontFamily: 'monospace', + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(height: 10), + ], + ), + ), + ); + } + + Color _levelColor(Level level) { + if (level == Level.SEVERE) return AppColors.dangerButtonColor; + if (level == Level.WARNING) return AppColors.warningButtonColor; + if (level == Level.INFO) return Colors.green.shade700; + if (level == Level.SHOUT) return Colors.red.shade900; + return Colors.blueGrey; + } + + IconData _levelIcon(Level level) { + if (level == Level.SEVERE || level == Level.SHOUT) { + return Icons.error_outline; + } + if (level == Level.WARNING) { + return Icons.warning_amber_rounded; + } + if (level == Level.INFO) { + return Icons.info_outline; + } + return Icons.bubble_chart_outlined; + } +} diff --git a/school_data_hub_flutter/lib/main.dart b/school_data_hub_flutter/lib/main.dart index 498d7752..62bc17a9 100644 --- a/school_data_hub_flutter/lib/main.dart +++ b/school_data_hub_flutter/lib/main.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:school_data_hub_flutter/app_utils/logger/log_service.dart'; import 'package:school_data_hub_flutter/app_utils/logger/logrecord_formatter.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; @@ -25,11 +26,14 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); // Set the global logging level Logger.root.level = Level.ALL; - + di.registerSingleton(LogService()); + final logService = di(); // Add your custom colored console listener Logger.root.onRecord.listen((record) { final colorFormatter = const ColorFormatter(); log(colorFormatter.format(record)); + final appLog = AppLog(record.level, record.message, record.loggerName); + logService.addLog(appLog); }); // using package window_manager to set a default windows window size if (Platform.isWindows) { From bf559904aa5b412668138ec070bfc54ab848de80 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 00:46:17 +0100 Subject: [PATCH 37/52] add logs button in settings page --- .../app_settings/settings_page/settings_page.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart b/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart index 9c9d89fc..7ec22bf5 100644 --- a/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart +++ b/school_data_hub_flutter/lib/features/app_settings/settings_page/settings_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart'; +import 'package:school_data_hub_flutter/app_utils/logs_page.dart'; import 'package:school_data_hub_flutter/app_utils/shorebird_code_push.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; @@ -64,6 +65,15 @@ class SettingsPage extends StatelessWidget { 'Patch level: ${di().currentPatch?.number.toString() ?? '0'}', ), ), + SettingsTile.navigation( + leading: const Icon(Icons.bug_report_rounded), + title: const Text('Logs'), + onPressed: (context) => Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => const LogViewerPage(), + ), + ), + ), SettingsTile.navigation( leading: const Icon(Icons.info_rounded), title: const Text('App Infos'), From 8c23093daf263e32eeacbe4485af85a250534d8c Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 01:38:33 +0100 Subject: [PATCH 38/52] add family language filter --- .../filters/pupil_selector_filters.dart | 15 +++++++ .../pupil/domain/filters/pupils_filter.dart | 1 + .../domain/filters/pupils_filter_impl.dart | 16 +++++++ .../features/pupil/domain/models/enums.dart | 16 +++++++ .../pupil/domain/models/pupil_proxy.dart | 7 +++ ...guage_lessons_list_page_bottom_navbar.dart | 43 +++++++++++++++++++ 6 files changed, 98 insertions(+) diff --git a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_selector_filters.dart b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_selector_filters.dart index 03884d47..f138e535 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_selector_filters.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupil_selector_filters.dart @@ -30,6 +30,21 @@ class ReligionCourseFilter extends SelectorFilter { } } +class FamilyLanguageFilter extends SelectorFilter { + FamilyLanguageFilter(FamilyLanguage familyLanguage) + : super( + name: familyLanguage.value, + selector: (proxy) => + FamilyLanguage.stringToValue[proxy.language] ?? + FamilyLanguage.other, + ); + + @override + bool matches(PupilProxy item) { + return selector(item).value == name; + } +} + class GroupFilter extends SelectorFilter { GroupFilter(String group) : super(name: group, selector: (proxy) => proxy.groupId); diff --git a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter.dart b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter.dart index 8cb5f6aa..a58e01a4 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter.dart @@ -97,6 +97,7 @@ abstract class PupilsFilter implements Listenable { List get schoolGradeFilters; List get genderFilters; List get religionCourseFilters; + List get familyLanguageFilters; PupilTextFilter get textFilter; /// must be called when this object is no longer needed diff --git a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter_impl.dart b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter_impl.dart index 9fd1daa8..b5fcd790 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter_impl.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/filters/pupils_filter_impl.dart @@ -88,6 +88,7 @@ class PupilsFilterImplementation with ChangeNotifier implements PupilsFilter { ..._groupFilters, ...genderFilters, ...religionCourseFilters, + ...familyLanguageFilters, _textFilter, ]; @@ -167,6 +168,9 @@ class PupilsFilterImplementation with ChangeNotifier implements PupilsFilter { bool isAnyReligionCourseFilterActive = religionCourseFilters.any( (filter) => filter.isActive, ); + bool isAnyFamilyLanguageFilterActive = familyLanguageFilters.any( + (filter) => filter.isActive, + ); bool isTextFilterActive = _textFilter.isActive; @@ -221,6 +225,15 @@ class PupilsFilterImplementation with ChangeNotifier implements PupilsFilter { if (filtersOn == false) filtersOn = true; continue; } + bool isMatchedByFamilyLanguageFilter = + !isAnyFamilyLanguageFilterActive || + familyLanguageFilters.any( + (filter) => filter.isActive && filter.matches(pupil), + ); + if (!isMatchedByFamilyLanguageFilter) { + if (filtersOn == false) filtersOn = true; + continue; + } // if the pupil is not matched by the text filter, skip it @@ -420,6 +433,9 @@ class PupilsFilterImplementation with ChangeNotifier implements PupilsFilter { @override List get religionCourseFilters => PupilProxy.religionCourseFilters; + @override + List get familyLanguageFilters => PupilProxy.familyLanguageFilters; + @override void populateGroupFilters(List groupIds) { final groupFilters = diff --git a/school_data_hub_flutter/lib/features/pupil/domain/models/enums.dart b/school_data_hub_flutter/lib/features/pupil/domain/models/enums.dart index d0d32a42..de6c8aca 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/models/enums.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/models/enums.dart @@ -62,6 +62,22 @@ enum ReligionCourse { const ReligionCourse(this.value); } +enum FamilyLanguage { + turkish('Türkisch'), + arabic('Arabisch'), + albanian('Albanisch'), + other('Andere'); + + static const stringToValue = { + 'Türkisch': FamilyLanguage.turkish, + 'Arabisch': FamilyLanguage.arabic, + 'Albanisch': FamilyLanguage.albanian, + 'Andere': FamilyLanguage.other, + }; + final String value; + const FamilyLanguage(this.value); +} + enum Gender { male('m'), female('w'); diff --git a/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart b/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart index 73bc9c51..2fc11d73 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart @@ -37,6 +37,13 @@ class PupilProxy with ChangeNotifier { ReligionCourseFilter(ReligionCourse.none), ]; + static List familyLanguageFilters = [ + FamilyLanguageFilter(FamilyLanguage.turkish), + FamilyLanguageFilter(FamilyLanguage.arabic), + FamilyLanguageFilter(FamilyLanguage.albanian), + FamilyLanguageFilter(FamilyLanguage.other), + ]; + late PupilData _pupilData; PupilIdentity _pupilIdentity; diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_list_page_bottom_navbar.dart b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_list_page_bottom_navbar.dart index 1d24202c..ba1db289 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_list_page_bottom_navbar.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_list_page_bottom_navbar.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/domain/filters/filters_state_manager.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; +import 'package:school_data_hub_flutter/common/theme/styles.dart'; import 'package:school_data_hub_flutter/common/widgets/bottom_nav_bar_layouts.dart'; import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_filter_bottom_sheet.dart'; +import 'package:school_data_hub_flutter/common/widgets/themed_filter_chip.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/common_pupil_filters.dart'; import 'package:watch_it/watch_it.dart'; @@ -42,6 +44,8 @@ class FamilyLanguageLessonsListPageBottomNavBar extends WatchingWidget { onTap: () => showGenericFilterBottomSheet(context: context, filterList: [ const CommonPupilFiltersWidget(), + const Gap(10), + const FamilyLanguageFiltersSection(), ]), onLongPress: () => _pupilsFilter.resetFilters(), child: Icon( @@ -59,3 +63,42 @@ class FamilyLanguageLessonsListPageBottomNavBar extends WatchingWidget { } } +class FamilyLanguageFiltersSection extends WatchingWidget { + const FamilyLanguageFiltersSection({super.key}); + + @override + Widget build(BuildContext context) { + final familyLanguageFilters = di().familyLanguageFilters; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + Text( + 'Familiensprache', + style: AppStyles.subtitle, + ), + ], + ), + const Gap(5), + Wrap( + spacing: 5, + crossAxisAlignment: WrapCrossAlignment.center, + alignment: WrapAlignment.center, + children: [ + for (final familyLanguageFilter in familyLanguageFilters) + ThemedFilterChip( + label: familyLanguageFilter.displayName, + selected: watch(familyLanguageFilter).isActive, + onSelected: (val) { + familyLanguageFilter.toggle(val); + }, + ), + ], + ), + ], + ); + } +} + From 883b9ec2050210f89e1aef7d6e8635c77950dcd0 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 01:39:47 +0100 Subject: [PATCH 39/52] statistics view add top navigation --- .../list_tiles/enrollment_list_tiles.dart | 196 +++++++++--------- .../list_tiles/group_list_tiles.dart | 196 ++++++++---------- .../list_tiles/group_tiles.dart | 28 +-- .../list_tiles/languages_list_tiles.dart | 102 ++++----- .../statistics_page/statistics_page.dart | 149 +++++++------ 5 files changed, 349 insertions(+), 322 deletions(-) diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart index ab8872a3..3cb6d95a 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/enrollment_list_tiles.dart @@ -5,27 +5,29 @@ import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/pupil_list_card.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; +import 'package:watch_it/watch_it.dart'; -class EnrollmentListTiles extends StatelessWidget { +class EnrollmentListTiles extends WatchingWidget { final StatisticsController controller; const EnrollmentListTiles({required this.controller, super.key}); @override Widget build(BuildContext context) { - final Set seenEnrollmentDates = {}; - + final seenEnrollmentDates = {}; final currentYearPupils = controller.pupilsEnrolledAfterDate( DateTime(2025, 08, 01), )..sort((a, b) => b.pupilSince.compareTo(a.pupilSince)); + final lastYearPupilsCount = controller + .pupilsEnrolledBetweenDates( + DateFormat('yyy-MM-dd').parse('2025-08-02'), + DateFormat('yyy-MM-dd').parse('2026-07-31'), + ) + .length; - return ListTileTheme( - contentPadding: const EdgeInsets.all(0), - dense: true, - horizontalTitleGap: 0.0, - minLeadingWidth: 0, - child: ExpansionTile( - tilePadding: const EdgeInsets.all(0), - title: Row( + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( children: [ const Text( 'Unterjährige Anmeldungen', @@ -45,105 +47,103 @@ class EnrollmentListTiles extends StatelessWidget { ), ], ), - children: [ - ExpansionTile( - title: Row( + const Gap(10), + Card( + child: Padding( + padding: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'im laufenden Schulahr:', - style: TextStyle(color: Colors.black, fontSize: 18), + Row( + children: [ + const Text( + 'im laufenden Schuljahr:', + style: TextStyle(color: Colors.black, fontSize: 18), + ), + const Gap(10), + Text( + currentYearPupils.length.toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + ], ), const Gap(10), - Text( - currentYearPupils.length.toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ], - ), - children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: currentYearPupils.length, - itemBuilder: (BuildContext context, int index) { - final pupil = currentYearPupils[index]; - final bool isDatePrinted = seenEnrollmentDates.contains( - DateTime( + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: currentYearPupils.length, + itemBuilder: (BuildContext context, int index) { + final pupil = currentYearPupils[index]; + final enrollmentDate = DateTime( pupil.pupilSince.year, pupil.pupilSince.month, pupil.pupilSince.day, - ), - ); - - if (!isDatePrinted) { - seenEnrollmentDates.add( - DateTime( - pupil.pupilSince.year, - pupil.pupilSince.month, - pupil.pupilSince.day, - ), ); - } + final isDatePrinted = seenEnrollmentDates.contains( + enrollmentDate, + ); + + if (!isDatePrinted) { + seenEnrollmentDates.add(enrollmentDate); + } - return Padding( - padding: const EdgeInsets.all(4.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (!isDatePrinted) - Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: Row( - children: [ - const Gap(5), - Text( - '${pupil.pupilSince.asWeekdayName(context)}, ${pupil.pupilSince.formatDateForUser()}', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, - fontSize: 18, + return Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!isDatePrinted) + Padding( + padding: const EdgeInsets.symmetric( + vertical: 5.0, + ), + child: Row( + children: [ + const Gap(5), + Text( + '${pupil.pupilSince.asWeekdayName(context)}, ${pupil.pupilSince.formatDateForUser()}', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: AppColors.backgroundColor, + fontSize: 18, + ), ), - ), - ], + ], + ), ), - ), - PupilListCard(passedPupil: pupil), - ], - ), - ); - }, - ), - ], - ), - Row( - children: [ - const Text( - 'im letzten Schulahr:', - style: TextStyle(color: Colors.black, fontSize: 18), - ), - const Gap(10), - Text( - controller - .pupilsEnrolledBetweenDates( - DateFormat('yyy-MM-dd').parse('2025-08-02'), - DateFormat('yyy-MM-dd').parse('2026-07-31'), - ) - .length - .toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, + PupilListCard(passedPupil: pupil), + ], + ), + ); + }, ), - ), - ], + ], + ), ), - ], - ), + ), + const Gap(20), + Row( + children: [ + const Text( + 'im letzten Schuljahr:', + style: TextStyle(color: Colors.black, fontSize: 18), + ), + const Gap(10), + Text( + lastYearPupilsCount.toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + ], + ), + ], ); } } diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_list_tiles.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_list_tiles.dart index 78bd565f..201bfc02 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_list_tiles.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_list_tiles.dart @@ -1,53 +1,47 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; +import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; +import 'package:school_data_hub_flutter/features/pupil/presentation/widgets/common_pupil_filters.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/list_tiles/group_card.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/list_tiles/group_tiles.dart'; +import 'package:watch_it/watch_it.dart'; -groupListTiles(context, StatisticsController controller) { - return ListTileTheme( - contentPadding: const EdgeInsets.all(0), - dense: true, - horizontalTitleGap: 0.0, - minLeadingWidth: 0, - child: ExpansionTile( - tilePadding: const EdgeInsets.all(0), - title: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ +class GroupListTiles extends WatchingWidget { + final StatisticsController controller; + + const GroupListTiles({required this.controller, super.key}); + + @override + Widget build(BuildContext context) { + final filteredPupils = watchValue( + (PupilsFilter filter) => filter.filteredPupils, + ); + final groupedPupils = _groupByClass(filteredPupils); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TotalSummaryRow( + total: filteredPupils.length, + ogs: controller.pupilsInOGS(filteredPupils).length, + ), + const Gap(15), + + if (filteredPupils.isEmpty) const Text( - 'SuS insgesamt:', - style: TextStyle(color: Colors.black, fontSize: 18), - ), - const Gap(10), - Text( - controller.pupils.length.toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), + 'Keine Schülerinnen und Schüler entsprechen den aktuellen Filtern.', + style: TextStyle(color: Colors.black54), + ) + else ...[ + statisticsGroupCard(controller, filteredPupils), + const FilterHeading(), + const Gap(5), + const CommonPupilFiltersWidget(), + const Gap(20), const Gap(20), const Text( - 'davon OGS:', - style: TextStyle(color: Colors.black, fontSize: 18), - ), - const Gap(10), - Text( - controller.pupilsInOGS(controller.pupils).length.toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), - ], - ), - children: [ - statisticsGroupCard(controller, controller.pupils), - ExpansionTile( - title: const Text( 'nach Klassen', style: TextStyle( color: Colors.black, @@ -55,70 +49,64 @@ groupListTiles(context, StatisticsController controller) { fontWeight: FontWeight.bold, ), ), - children: [ - if (controller.pupils.any((element) => element.group == 'A1')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('A1'), - ), - if (controller.pupils.any((element) => element.group == 'A2')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('A2'), - ), - if (controller.pupils.any((element) => element.group == 'A3')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('A3'), - ), - if (controller.pupils.any((element) => element.group == 'B1')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('B1'), - ), - if (controller.pupils.any((element) => element.group == 'B2')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('B2'), - ), - if (controller.pupils.any((element) => element.group == 'B3')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('B3'), - ), - if (controller.pupils.any((element) => element.group == 'B4')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('B4'), - ), - if (controller.pupils.any((element) => element.group == 'C1')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('C1'), - ), - if (controller.pupils.any((element) => element.group == 'C2')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('C2'), - ), - if (controller.pupils.any((element) => element.group == 'C3')) - groupTiles( - context, - controller, - controller.pupilsInaGivenGroup('C3'), - ), + const Gap(10), + for (final group in groupedPupils) ...[ + groupTiles(context, controller, group), + const Gap(20), ], + ], + ], + ); + } + + List> _groupByClass(List pupils) { + final Map> grouped = {}; + for (final pupil in pupils) { + grouped.putIfAbsent(pupil.group, () => []).add(pupil); + } + final keys = grouped.keys.toList()..sort(); + return [for (final key in keys) grouped[key]!]; + } +} + +class _TotalSummaryRow extends StatelessWidget { + final int total; + final int ogs; + + const _TotalSummaryRow({required this.total, required this.ogs}); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + const Text( + 'SuS insgesamt:', + style: TextStyle(color: Colors.black, fontSize: 18), + ), + const Gap(10), + Text( + total.toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + const Gap(20), + const Text( + 'davon OGS:', + style: TextStyle(color: Colors.black, fontSize: 18), + ), + const Gap(10), + Text( + ogs.toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, + ), ), ], - ), - ); + ); + } } diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_tiles.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_tiles.dart index 00c829dd..5536312f 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_tiles.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_tiles.dart @@ -4,16 +4,19 @@ import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/list_tiles/group_card.dart'; -groupTiles(context, StatisticsController controller, List group) { - String groupString = group[0].group; - return ListTileTheme( - contentPadding: const EdgeInsets.all(0), - dense: true, - horizontalTitleGap: 0.0, - minLeadingWidth: 0, - child: ExpansionTile( - tilePadding: const EdgeInsets.all(0), - title: Row( +groupTiles( + context, + StatisticsController controller, + List group, +) { + if (group.isEmpty) { + return const SizedBox.shrink(); + } + final groupString = group.first.group; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( children: [ Text( groupString, @@ -53,7 +56,8 @@ groupTiles(context, StatisticsController controller, List group) { ), ], ), - children: [statisticsGroupCard(controller, group)], - ), + const Gap(10), + statisticsGroupCard(controller, group), + ], ); } diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart index f8943df4..7e6672c2 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/languages_list_tiles.dart @@ -4,57 +4,59 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/pupil_list_dialog.dart'; import 'package:school_data_hub_flutter/features/statistics/statistics_page/controller/statistics.dart'; +import 'package:watch_it/watch_it.dart'; -languagesListTiles(context, StatisticsController controller) { - List> sortedLanguageOccurrences = - controller.languageOccurrences.entries.toList() - ..sort((a, b) => b.value.compareTo(a.value)); +class LanguagesListTiles extends WatchingWidget { + final StatisticsController controller; + const LanguagesListTiles({required this.controller, super.key}); - final List palette = [ - Colors.blue, - Colors.red, - Colors.green, - Colors.orange, - Colors.purple, - Colors.teal, - Colors.amber, - Colors.indigo, - Colors.brown, - Colors.pink, - Colors.cyan, - Colors.lime, - Colors.deepOrange, - Colors.lightBlue, - Colors.deepPurple, - ]; + @override + Widget build(BuildContext context) { + final sortedLanguageOccurrences = + controller.languageOccurrences.entries.toList() + ..sort((a, b) => b.value.compareTo(a.value)); - Color getColor(int index) => palette[index % palette.length]; + final palette = [ + Colors.blue, + Colors.red, + Colors.green, + Colors.orange, + Colors.purple, + Colors.teal, + Colors.amber, + Colors.indigo, + Colors.brown, + Colors.pink, + Colors.cyan, + Colors.lime, + Colors.deepOrange, + Colors.lightBlue, + Colors.deepPurple, + ]; - return ListTileTheme( - contentPadding: const EdgeInsets.all(0), - dense: true, - horizontalTitleGap: 0.0, - minLeadingWidth: 0, - child: ExpansionTile( - tilePadding: const EdgeInsets.all(0), - title: Row( - children: [ - const Text( - 'Sprachen', - style: TextStyle(color: Colors.black, fontSize: 18), - ), - const Gap(10), - Text( - sortedLanguageOccurrences.length.toString(), - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ], - ), + Color getColor(int index) => palette[index % palette.length]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row( + children: [ + const Text( + 'Sprachen', + style: TextStyle(color: Colors.black, fontSize: 18), + ), + const Gap(10), + Text( + sortedLanguageOccurrences.length.toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + ], + ), + const Gap(10), Card( child: Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -99,7 +101,7 @@ languagesListTiles(context, StatisticsController controller) { ), const Gap(10), Text( - "$language:", + '$language:', style: const TextStyle( color: Colors.black, fontSize: 18, @@ -107,7 +109,7 @@ languagesListTiles(context, StatisticsController controller) { ), const Gap(10), Text( - "$occurrences", + '$occurrences', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, @@ -157,6 +159,6 @@ languagesListTiles(context, StatisticsController controller) { ), ), ], - ), - ); + ); + } } diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart index 1ba578d9..eac73759 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/statistics_page.dart @@ -13,72 +13,105 @@ class StatisticsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.canvasColor, - appBar: AppBar( - automaticallyImplyLeading: false, - backgroundColor: AppColors.backgroundColor, - centerTitle: true, - title: const Row( - mainAxisAlignment: MainAxisAlignment.center, + return DefaultTabController( + length: 3, + child: Scaffold( + backgroundColor: AppColors.canvasColor, + appBar: AppBar( + automaticallyImplyLeading: false, + backgroundColor: AppColors.backgroundColor, + centerTitle: true, + title: const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.bar_chart_rounded, size: 25, color: Colors.white), + Gap(10), + Text('Statistik', style: AppStyles.appBarTextStyle), + ], + ), + bottom: const TabBar( + isScrollable: true, + labelColor: Colors.white, + unselectedLabelColor: Colors.white70, + indicatorColor: Colors.white, + tabs: [ + Tab(text: 'Schulzahlen'), + Tab(text: 'Sprachen'), + Tab(text: 'Unterjährige Anmeldungen'), + ], + ), + ), + body: TabBarView( children: [ - Icon(Icons.bar_chart_rounded, size: 25, color: Colors.white), - Gap(10), - Text('Statistik', style: AppStyles.appBarTextStyle), + _StatisticsTabContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(15), + GroupListTiles(controller: controller), + ], + ), + ), + _StatisticsTabContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(15), + LanguagesListTiles(controller: controller), + ], + ), + ), + _StatisticsTabContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(15), + EnrollmentListTiles(controller: controller), + ], + ), + ), ], ), - ), - body: Center( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 800), - child: Padding( - padding: const EdgeInsets.only(left: 10.0, right: 10, bottom: 20), - child: Column( - children: [ - const Gap(15), - const Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [Text('Schulzahlen', style: AppStyles.title)], - ), - const Gap(10), - Expanded( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - groupListTiles(context, controller), - languagesListTiles(context, controller), - EnrollmentListTiles(controller: controller), - ], - ), + bottomNavigationBar: BottomAppBar( + padding: const EdgeInsets.all(10), + shape: null, + color: AppColors.backgroundColor, + child: IconTheme( + data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 800), + child: Row( + children: [ + IconButton( + tooltip: 'zurück', + icon: const Icon(Icons.arrow_back, size: 30), + onPressed: () { + Navigator.pop(context); + }, ), - ), - ], + const Spacer(), + ], + ), ), ), ), ), - bottomNavigationBar: BottomAppBar( - padding: const EdgeInsets.all(10), - shape: null, - color: AppColors.backgroundColor, - child: IconTheme( - data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 800), - child: Row( - children: [ - IconButton( - tooltip: 'zurück', - icon: const Icon(Icons.arrow_back, size: 30), - onPressed: () { - Navigator.pop(context); - }, - ), - const Spacer(), - ], - ), - ), + ); + } +} + +class _StatisticsTabContainer extends StatelessWidget { + final Widget child; + const _StatisticsTabContainer({required this.child}); + + @override + Widget build(BuildContext context) { + return Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 800), + child: Padding( + padding: const EdgeInsets.only(left: 10.0, right: 10, bottom: 20), + child: SingleChildScrollView(child: child), ), ), ); From 1badbb4deb04f3c42db1d237b27e94dd00490733 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 01:40:29 +0100 Subject: [PATCH 40/52] ui changes --- .../books_main_menu_page.dart | 17 +- .../learning_pupil_list_page.dart | 20 +- .../learning_list_card.dart | 259 +++++++++--------- .../pupil_competence_list_search_bar.dart | 58 ++-- .../widgets/action_buttons.dart | 116 ++++---- .../pupil_profile_learning_content.dart | 9 +- 6 files changed, 252 insertions(+), 227 deletions(-) diff --git a/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart b/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart index ea6e3940..6f5cce38 100644 --- a/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart +++ b/school_data_hub_flutter/lib/features/books/presentation/books_main_menu_page/books_main_menu_page.dart @@ -14,7 +14,6 @@ import 'package:watch_it/watch_it.dart'; import '../../../../common/theme/app_colors.dart'; import '../../../../common/widgets/dialogs/short_textfield_dialog.dart'; -import '../book_list_page/book_list_page.dart'; import '../book_list_page/widgets/book_list_bottom_navbar.dart'; import '../new_book_page/new_book_controller.dart'; @@ -44,17 +43,17 @@ class BooksMainMenuPage extends WatchingWidget { ), ); }), - const SizedBox(height: 20), - _buildButton(context, "Ausgeliehene Bücher", () { - Navigator.of(context).push( - MaterialPageRoute(builder: (ctx) => const BookListPage()), - ); - }), - const SizedBox(height: 20), + // const SizedBox(height: 20), + // _buildButton(context, "Ausgeliehene Bücher", () { + // Navigator.of(context).push( + // MaterialPageRoute(builder: (ctx) => const BookListPage()), + // ); + // }), + const SizedBox(height: 5), _buildButton(context, "Buch erfassen", () async { await _showNewBookDialog(context); }), - const SizedBox(height: 20), + const SizedBox(height: 5), _buildButton(context, "Bücher suchen", () { Navigator.of(context).push( MaterialPageRoute( diff --git a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart index a8bfc82b..0bd47d37 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/learning_pupil_list_page.dart @@ -25,7 +25,9 @@ class LearningPupilListPage extends WatchingWidget { return Scaffold( backgroundColor: AppColors.canvasColor, appBar: const GenericAppBar( - iconData: Icons.lightbulb_rounded, title: 'Lernen'), + iconData: Icons.lightbulb_rounded, + title: 'Lernen', + ), body: RefreshIndicator( onRefresh: () async => di().updatePupilList(pupils), child: Center( @@ -35,20 +37,24 @@ class LearningPupilListPage extends WatchingWidget { slivers: [ const SliverGap(5), GenericSliverSearchAppBar( - height: 140, + height: 160, title: PupilCompetenceListSearchBar( - pupils: pupils, filtersOn: filtersOn), + pupils: pupils, + filtersOn: filtersOn, + ), ), GenericSliverListWithEmptyListCheck( - items: pupils, - itemBuilder: (_, pupil) => LearningListCard(pupil)), + items: pupils, + itemBuilder: (_, pupil) => LearningListCard(pupil), + ), ], ), ), ), ), - bottomNavigationBar: - PupilCompetenceListBottomNavBar(filtersOn: filtersOn), + bottomNavigationBar: PupilCompetenceListBottomNavBar( + filtersOn: filtersOn, + ), ); } } diff --git a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/learning_list_card/learning_list_card.dart b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/learning_list_card/learning_list_card.dart index dcd42851..e7303d64 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/learning_list_card/learning_list_card.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/learning_list_card/learning_list_card.dart @@ -71,146 +71,161 @@ class LearningListCard extends WatchingWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ const Gap(5), - Row( + Column( children: [ - Expanded( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: InkWell( - onTap: () { - di().setPupilProfileNavPage( - ProfileNavigationState.learning.value, - ); - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => - PupilProfilePage(pupil: pupil), - ), - ); - }, - child: Row( - children: [ - Text( - pupil.firstName, - overflow: TextOverflow.fade, - softWrap: false, - textAlign: TextAlign.left, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - const Gap(5), - Text( - pupil.lastName, - overflow: TextOverflow.fade, - softWrap: false, - textAlign: TextAlign.left, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.normal, - fontSize: 18, - ), + Row( + children: [ + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: InkWell( + onTap: () { + di() + .setPupilProfileNavPage( + ProfileNavigationState.learning.value, + ); + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => + PupilProfilePage(pupil: pupil), + ), + ); + }, + child: Row( + children: [ + Text( + pupil.firstName, + overflow: TextOverflow.fade, + softWrap: false, + textAlign: TextAlign.left, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + const Gap(5), + Text( + pupil.lastName, + overflow: TextOverflow.fade, + softWrap: false, + textAlign: TextAlign.left, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.normal, + fontSize: 18, + ), + ), + ], ), - ], + ), ), ), - ), + ], ), ], ), const Gap(5), - if (selectedContent == - SelectedContent.competenceStatuses) ...[ - CustomExpansionTileSwitch( - includeSwitch: true, - switchColor: AppColors.interactiveColor, - customExpansionTileController: expansionTileController, - expansionSwitchWidget: CompetenceChecksBadges( - pupil: pupil, - ), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text('Items dokumentiert: '), - const Gap(5), - Text( - '$totalCompetencesChecked/$totalCompetencesToReport', - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: AppColors.backgroundColor, + Column( + children: [ + if (selectedContent == + SelectedContent.competenceStatuses) ...[ + CustomExpansionTileSwitch( + includeSwitch: true, + switchColor: AppColors.interactiveColor, + customExpansionTileController: + expansionTileController, + expansionSwitchWidget: CompetenceChecksBadges( + pupil: pupil, ), ), - ], - ), - ], - if (selectedContent == SelectedContent.competenceGoals) ...[ - CustomExpansionTileSwitch( - customExpansionTileController: expansionTileController, - expansionSwitchWidget: const Text( - 'Lernziele sind noch nicht implementiert', - ), - ), - ], - if (selectedContent == SelectedContent.workbooks) ...[ - CustomExpansionTileSwitch( - customExpansionTileController: expansionTileController, - expansionSwitchWidget: WorkbooksInfoSwitch( - pupil: pupil, - ), - includeSwitch: true, - switchColor: AppColors.interactiveColor, - ), - ], - if (selectedContent == SelectedContent.books) ...[ - CustomExpansionTileSwitch( - customExpansionTileController: expansionTileController, - expansionSwitchWidget: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Gap(10), - Column( - children: [ - Text( - totalLendings.toString(), - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.green, - ), - ), - const Text( - 'gesamt', - style: TextStyle(fontSize: 10), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text('Items dokumentiert: '), + const Gap(5), + Text( + '$totalCompetencesChecked/$totalCompetencesToReport', + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: AppColors.backgroundColor, ), - ], + ), + ], + ), + ], + if (selectedContent == + SelectedContent.competenceGoals) ...[ + CustomExpansionTileSwitch( + customExpansionTileController: + expansionTileController, + expansionSwitchWidget: const Text( + 'Lernziele sind noch nicht implementiert', + ), + ), + ], + if (selectedContent == SelectedContent.workbooks) ...[ + CustomExpansionTileSwitch( + customExpansionTileController: + expansionTileController, + expansionSwitchWidget: WorkbooksInfoSwitch( + pupil: pupil, ), - const Gap(10), - Column( + includeSwitch: true, + switchColor: AppColors.interactiveColor, + ), + ], + if (selectedContent == SelectedContent.books) ...[ + CustomExpansionTileSwitch( + customExpansionTileController: + expansionTileController, + expansionSwitchWidget: Row( + mainAxisSize: MainAxisSize.min, children: [ - Text( - notReturnedLendings.toString(), - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.orange, - ), + const Gap(10), + + Column( + children: [ + Text( + totalLendings.toString(), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.green, + ), + ), + const Text( + 'gesamt', + style: TextStyle(fontSize: 10), + ), + ], ), - const Text( - 'aktiv', - style: TextStyle(fontSize: 10), + const Gap(10), + Column( + children: [ + Text( + notReturnedLendings.toString(), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.orange, + ), + ), + const Text( + 'aktiv', + style: TextStyle(fontSize: 10), + ), + ], ), + const Gap(15), ], ), - const Gap(15), - ], - ), - ), - ], + ), + ], + ], + ), ], ), ), diff --git a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_list_search_bar.dart b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_list_search_bar.dart index 42f1ffcc..902b1f62 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_list_search_bar.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_list_search_bar.dart @@ -13,8 +13,11 @@ import 'package:watch_it/watch_it.dart'; class PupilCompetenceListSearchBar extends StatelessWidget { final List pupils; final bool filtersOn; - const PupilCompetenceListSearchBar( - {required this.filtersOn, required this.pupils, super.key}); + const PupilCompetenceListSearchBar({ + required this.filtersOn, + required this.pupils, + super.key, + }); @override Widget build(BuildContext context) { @@ -24,14 +27,17 @@ class PupilCompetenceListSearchBar extends StatelessWidget { borderRadius: BorderRadius.circular(5.0), ), child: Column( + mainAxisAlignment: MainAxisAlignment.end, children: [ const Gap(5), - Flexible( - child: Padding( - padding: const EdgeInsets.only(left: 10.0, right: 10.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: SizedBox( + height: 30, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ const Icon( Icons.people_alt_rounded, @@ -49,15 +55,13 @@ class PupilCompetenceListSearchBar extends StatelessWidget { const Gap(15), const Text( 'Ebene 1: ', - style: TextStyle( - color: Colors.black, - fontSize: 13, - ), + style: TextStyle(color: Colors.black, fontSize: 13), ), const Gap(5), Text( - (LearningSupportHelper.developmentPlan1Pupils(pupils)) - .toString(), + (LearningSupportHelper.developmentPlan1Pupils( + pupils, + )).toString(), style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, @@ -67,15 +71,13 @@ class PupilCompetenceListSearchBar extends StatelessWidget { const Gap(15), const Text( '2: ', - style: TextStyle( - color: Colors.black, - fontSize: 13, - ), + style: TextStyle(color: Colors.black, fontSize: 13), ), const Gap(5), Text( - (LearningSupportHelper.developmentPlan2Pupils(pupils)) - .toString(), + (LearningSupportHelper.developmentPlan2Pupils( + pupils, + )).toString(), style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, @@ -85,15 +87,13 @@ class PupilCompetenceListSearchBar extends StatelessWidget { const Gap(15), const Text( '3: ', - style: TextStyle( - color: Colors.black, - fontSize: 13, - ), + style: TextStyle(color: Colors.black, fontSize: 13), ), const Gap(5), Text( - (LearningSupportHelper.developmentPlan3Pupils(pupils)) - .toString(), + (LearningSupportHelper.developmentPlan3Pupils( + pupils, + )).toString(), style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, @@ -110,10 +110,12 @@ class PupilCompetenceListSearchBar extends StatelessWidget { child: Row( children: [ Expanded( - child: PupilSearchTextField( - searchType: SearchType.pupil, - hintText: 'Schüler/in suchen', - refreshFunction: di().refreshs)), + child: PupilSearchTextField( + searchType: SearchType.pupil, + hintText: 'Schüler/in suchen', + refreshFunction: di().refreshs, + ), + ), InkWell( onTap: () => showLearningSupportFilterBottomSheet(context), onLongPress: () => di().resetFilters(), @@ -129,7 +131,7 @@ class PupilCompetenceListSearchBar extends StatelessWidget { ], ), ), - const PupilLearningContentNavBar() + const PupilLearningContentNavBar(), ], ), ); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/action_buttons.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/action_buttons.dart index 21f257eb..94b8df92 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/action_buttons.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/action_buttons.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; class StreamActionButtons extends StatelessWidget { final bool isConnected; @@ -18,70 +19,73 @@ class StreamActionButtons extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - if (!isConnected) - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: - null, // Disabled since connection starts automatically - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: const EdgeInsets.symmetric(vertical: 16), + return Column( + children: [ + if (!isConnected) + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: null, // Disabled since connection starts automatically + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - color: Colors.white, - strokeWidth: 2, - ), - ), - SizedBox(width: 8), - Text( - isProcessing - ? 'Stream startet...' - : 'Verbindung wird aufgebaut...', - style: TextStyle(color: Colors.white), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2, ), - ], - ), + ), + const SizedBox(width: 8), + Text( + isProcessing + ? 'Stream startet...' + : 'Verbindung wird aufgebaut...', + style: const TextStyle(color: Colors.white), + ), + ], ), ), - if (isConnected) - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: hasActiveTransfers ? null : onStopStream, - style: ElevatedButton.styleFrom( - backgroundColor: - hasActiveTransfers ? Colors.grey : Colors.red, - padding: const EdgeInsets.symmetric(vertical: 16), - ), - child: Text( - hasActiveTransfers - ? 'Transfer läuft - bitte warten' - : 'Stream beenden', - style: const TextStyle(color: Colors.white), + ), + if (isConnected) + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: hasActiveTransfers ? null : onStopStream, + style: ElevatedButton.styleFrom( + backgroundColor: hasActiveTransfers + ? Colors.grey + : AppColors.cancelButtonColor, + minimumSize: const Size.fromHeight(55), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), ), ), + child: Text( + hasActiveTransfers + ? 'Transfer läuft - bitte warten' + : 'STREAM BEENDEN', + style: const TextStyle(color: Colors.white, fontSize: 17.0), + ), ), - if (hasActiveTransfers) ...[ - const SizedBox(height: 8), - const Text( - 'Stream kann erst beendet werden, wenn alle Übertragungen abgeschlossen sind.', - style: TextStyle(fontSize: 12, color: Colors.grey), - textAlign: TextAlign.center, - ), - ], + ), + if (hasActiveTransfers) ...[ + const SizedBox(height: 8), + const Text( + 'Stream kann erst beendet werden, wenn alle Übertragungen abgeschlossen sind.', + style: TextStyle(fontSize: 12, color: Colors.grey), + textAlign: TextAlign.center, + ), ], - ), + ], ); } } diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart index dd548905..8277d7b3 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_content/pupil_profile_learning_content.dart @@ -4,7 +4,6 @@ import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; -import 'package:school_data_hub_flutter/features/learning/presentation/pupil_competence_list_page/widgets/pupil_competence_checks/competence_checks_badges.dart'; import 'package:school_data_hub_flutter/features/learning/presentation/widgets/pupil_learning_content_expansion_tile_nav_bar.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; @@ -129,10 +128,10 @@ class PupilLearningContent extends WatchingWidget { ), ], const Gap(10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [CompetenceChecksBadges(pupil: pupil)], - ), + // Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [CompetenceChecksBadges(pupil: pupil)], + // ), PupilLearningContentExpansionTileNavBar(pupil: pupil), ], ), From 59bc99aaeb6bfe1b7be14817da25d68964fe1ffc Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 01:41:05 +0100 Subject: [PATCH 41/52] prepare audio recording functionality --- .../app_utils/download_and_decrypt_file.dart | 76 ++++++++++ .../lib/common/widgets/document_audio.dart | 131 ++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 school_data_hub_flutter/lib/app_utils/download_and_decrypt_file.dart create mode 100644 school_data_hub_flutter/lib/common/widgets/document_audio.dart diff --git a/school_data_hub_flutter/lib/app_utils/download_and_decrypt_file.dart b/school_data_hub_flutter/lib/app_utils/download_and_decrypt_file.dart new file mode 100644 index 00000000..601379b0 --- /dev/null +++ b/school_data_hub_flutter/lib/app_utils/download_and_decrypt_file.dart @@ -0,0 +1,76 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:path/path.dart' as p; +import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/app_utils/custom_encrypter.dart'; +import 'package:school_data_hub_flutter/common/services/notification_service.dart'; +import 'package:watch_it/watch_it.dart'; + +Future downloadAndDecryptFile({ + required String documentId, + required bool decrypt, +}) async { + final cacheManager = di(); + final notificationService = di(); + + // Check cache + final fileInfo = await cacheManager.getFileFromCache(documentId); + + if (fileInfo != null && await fileInfo.file.exists()) { + if (!decrypt) { + return fileInfo.file; + } + + final fileBytes = await fileInfo.file.readAsBytes(); + final decryptedBytes = (kReleaseMode || kProfileMode) + ? await compute(customEncrypter.decryptTheseBytes, fileBytes) + : customEncrypter.decryptTheseBytes(fileBytes); + + final tempDir = await Directory.systemTemp.createTemp(); + final extension = p.extension(documentId); + final tempFile = File( + '${tempDir.path}/decrypted_${documentId.hashCode}$extension', + ); + await tempFile.writeAsBytes(decryptedBytes); + return tempFile; + } + + // Download + notificationService.apiRunning(true); + final ByteData? byteData = await di().files.getImage(documentId); + notificationService.apiRunning(false); + + if (byteData == null) { + notificationService.showSnackBar( + NotificationType.error, + 'Fehler beim Laden der Datei', + ); + return null; + } + + Uint8List fileBytes = byteData.buffer.asUint8List(); + // Cache it + await cacheManager.putFile(documentId, fileBytes); + + if (!decrypt) { + final tempDir = await Directory.systemTemp.createTemp(); + final extension = p.extension(documentId); + final tempFile = File('${tempDir.path}/${documentId.hashCode}$extension'); + await tempFile.writeAsBytes(fileBytes); + return tempFile; + } + + final decryptedBytes = (kReleaseMode || kProfileMode) + ? await compute(customEncrypter.decryptTheseBytes, fileBytes) + : customEncrypter.decryptTheseBytes(fileBytes); + + final tempDir = await Directory.systemTemp.createTemp(); + final extension = p.extension(documentId); + final tempFile = File( + '${tempDir.path}/decrypted_${documentId.hashCode}$extension', + ); + await tempFile.writeAsBytes(decryptedBytes); + return tempFile; +} diff --git a/school_data_hub_flutter/lib/common/widgets/document_audio.dart b/school_data_hub_flutter/lib/common/widgets/document_audio.dart new file mode 100644 index 00000000..06b3d09a --- /dev/null +++ b/school_data_hub_flutter/lib/common/widgets/document_audio.dart @@ -0,0 +1,131 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:just_audio/just_audio.dart'; +import 'package:school_data_hub_flutter/app_utils/download_and_decrypt_file.dart'; +import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; + +class DocumentAudio extends StatefulWidget { + const DocumentAudio({ + required this.documentId, + required this.decrypt, + super.key, + }); + + final String documentId; + final bool decrypt; + + @override + State createState() => _DocumentAudioState(); +} + +class _DocumentAudioState extends State { + final AudioPlayer _player = AudioPlayer(); + bool _isLoading = true; + bool _isPlaying = false; + String? _errorMessage; + + @override + void initState() { + super.initState(); + _loadAudio(); + _player.playerStateStream.listen((state) { + if (mounted) { + setState(() { + _isPlaying = + state.playing && + state.processingState != ProcessingState.completed; + }); + } + }); + } + + Future _loadAudio() async { + try { + final file = await downloadAndDecryptFile( + documentId: widget.documentId, + decrypt: widget.decrypt, + ); + if (mounted) { + if (file != null) { + // Use LockCachingAudioSource to avoid platform channel threading issues on Windows + // and for better performance with potentially large files. + // However, since we already have a decrypted file, we can just use AudioSource.file + // But given the threading errors, we might need to be careful. + // The errors "channel sent a message from native to Flutter on a non-platform thread" + // are usually harmless warnings in recent Flutter versions on Windows with just_audio, + // but "Broadcast playback event error" suggests an issue. + // Let's try wrap in try-catch for setFilePath and use AudioSource.file explicitly. + + try { + await _player.setFilePath(file.path); + } catch (e) { + if (kDebugMode) { + print("Error setting file path: $e"); + } + // Fallback or retry if needed, but usually setFilePath is robust. + throw e; + } + } else { + _errorMessage = 'Fehler beim Laden'; + } + } + } catch (e) { + if (mounted) { + _errorMessage = 'Fehler: $e'; + } + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + } + + @override + void dispose() { + _player.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_isLoading) { + return const SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator(strokeWidth: 2), + ); + } + + if (_errorMessage != null) { + return Text(_errorMessage!, style: const TextStyle(color: Colors.red)); + } + + return Container( + decoration: BoxDecoration( + color: AppColors.backgroundColor.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: AppColors.backgroundColor.withValues(alpha: 0.2), + ), + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow), + onPressed: () { + if (_isPlaying) { + _player.pause(); + } else { + _player.play(); + } + }, + ), + ], + ), + ); + } +} From 6043deac263305c15ffbc922ad4a5d3efa6c3fd4 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 01:42:01 +0100 Subject: [PATCH 42/52] fix state error when no admin matrix credentials stored --- .../pupils_matrix_contacts_list_page.dart | 553 ++++++++++-------- .../pupil_profile_infos_content.dart | 33 +- 2 files changed, 337 insertions(+), 249 deletions(-) diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart index b19227de..4cdc1baa 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/long_textfield_dialog.dart'; @@ -10,6 +11,7 @@ import 'package:school_data_hub_flutter/common/widgets/generic_components/generi import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_sliver_search_app_bar.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_helper.dart'; +import 'package:school_data_hub_flutter/features/matrix/domain/matrix_policy_manager.dart'; import 'package:school_data_hub_flutter/features/matrix/users/presentation/new_matrix_user_page/new_matrix_user_page.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/filters/pupils_filter.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/models/pupil_proxy.dart'; @@ -45,274 +47,335 @@ class PupilsMatrixContactsListPage extends WatchingWidget { ), GenericSliverListWithEmptyListCheck( items: pupils, - itemBuilder: (_, pupil) => Card( - color: + itemBuilder: (_, pupil) { + final missingContacts = pupil.tutorInfo?.parentsContact == null || - pupil.contact == null - ? Colors.orange - : Colors.white, - child: Column( - children: [ - Row( + pupil.contact == null; + final subtleBorder = AppColors.backgroundColor.withValues( + alpha: 102, + ); + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 1.0, + ), + child: Card( + color: missingContacts + ? const Color.fromARGB(255, 251, 232, 176) + : Colors.white, + shape: RoundedRectangleBorder( + side: BorderSide( + color: missingContacts ? Colors.orange : subtleBorder, + width: 3, + ), + borderRadius: BorderRadius.circular(12), + ), + child: Column( children: [ - const Gap(5), - AvatarWithBadges(pupil: pupil, size: 80), - const Gap(10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Gap(10), - Row( + Row( + children: [ + const Gap(5), + AvatarWithBadges(pupil: pupil, size: 80), + const Gap(10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - InkWell( - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => - PupilProfilePage(pupil: pupil), - ), - ); - }, - child: Text( - '${pupil.firstName} ${pupil.lastName}', - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - ), - if (pupil.family != null) - const Row( - children: [ - Gap(10), - Icon( - Icons.group, - size: 25, - color: AppColors.backgroundColor, - ), - ], - ), - ], - ), - Row( - children: [ - const Text('Kontakt: '), - InkWell( - onTap: () async { - if (pupil.contact == null) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => NewMatrixUserPage( - pupil: pupil, - matrixId: - MatrixPolicyHelper.generateMatrixId( - isParent: false, + const Gap(10), + Row( + children: [ + InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => + PupilProfilePage( + pupil: pupil, ), - displayName: - '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (${pupil.group})', ), - ), - ); - return; - } - final confirm = await confirmationDialog( - context: context, - title: 'Messenger öffnen', - message: - 'Nachricht an ${pupil.firstName} schicken?', - ); - if (confirm == true && - context.mounted) { - MatrixPolicyHelper.launchMatrixUrl( - context, - pupil.contact!, - ); - } - }, - onLongPress: () async { - final String? contact = - await longTextFieldDialog( - title: 'Kontakt', - labelText: 'Kontakt', - initialValue: pupil.contact, - parentContext: context, ); - if (contact == null) return; - - await PupilMutator() - .updateStringProperty( - pupilId: pupil.pupilId, - property: 'contact', - value: contact, - ); - }, - child: Text( - pupil.contact ?? 'nicht eingetragen', - style: TextStyle( - color: pupil.contact == null - ? Colors.black - : AppColors.backgroundColor, - fontWeight: pupil.contact == null - ? FontWeight.normal - : FontWeight.bold, - ), - ), - ), - const Gap(10), - IconButton( - icon: const Icon(Icons.copy), - onPressed: () { - Clipboard.setData( - ClipboardData(text: pupil.contact!), - ); - ScaffoldMessenger.of( - context, - ).showSnackBar( - const SnackBar( - content: Text( - 'In die Zwischenablage kopiert', + }, + child: Text( + '${pupil.firstName} ${pupil.lastName}', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, ), ), - ); - }, + ), + if (pupil.family != null) + const Row( + children: [ + Gap(10), + Icon( + Icons.group, + size: 25, + color: + AppColors.backgroundColor, + ), + ], + ), + ], ), - ], - ), + Row( + children: [ + const Text('Kontakt: '), + const Gap(5), + InkWell( + onTap: () async { + if (pupil.contact == null) { + if (!di + .isRegistered< + MatrixPolicyManager + >()) { + di() + .showInformationDialog( + 'Es sind keine Matrix-Admindaten hinterlegt.', + ); + return; + } + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => NewMatrixUserPage( + pupil: pupil, + matrixId: + MatrixPolicyHelper.generateMatrixId( + isParent: false, + ), + displayName: + '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (${pupil.group})', + ), + ), + ); + return; + } + final confirm = + await confirmationDialog( + context: context, + title: 'Messenger öffnen', + message: + 'Nachricht an ${pupil.firstName} schicken?', + ); + if (confirm == true && + context.mounted) { + MatrixPolicyHelper.launchMatrixUrl( + context, + pupil.contact!, + ); + } + }, + onLongPress: () async { + final String? contact = + await longTextFieldDialog( + title: 'Kontakt', + labelText: 'Kontakt', + initialValue: pupil.contact, + parentContext: context, + ); + if (contact == null) return; - Row( - children: [ - const Text('Elternkontakt: '), - InkWell( - onTap: () async { - if (pupil.tutorInfo?.parentsContact == - null) { - String? pupilSiblingsGroups; - if (pupil.family != null) { - pupilSiblingsGroups = - [ - ..._pupilManager - .getSiblings(pupil), - pupil, - ] - .map((e) => e.group) - .toList() - .join(); - } - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => NewMatrixUserPage( - pupil: pupil, - matrixId: - MatrixPolicyHelper.generateMatrixId( - isParent: true, - ), - displayName: - pupilSiblingsGroups != null - ? 'Fa. ${pupil.lastName} (E) $pupilSiblingsGroups' - : '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (E) ${pupil.group}', - isParent: true, - ), + await PupilMutator() + .updateStringProperty( + pupilId: pupil.pupilId, + property: 'contact', + value: contact, + ); + }, + child: Text( + pupil.contact ?? 'nicht vorhanden', + style: TextStyle( + color: pupil.contact == null + ? Colors.black + : AppColors.backgroundColor, + fontWeight: pupil.contact == null + ? FontWeight.bold + : FontWeight.bold, ), - ); - return; - } - final confirm = await confirmationDialog( - context: context, - title: 'Messenger öffnen', - message: - 'Nachricht an ${pupil.firstName}s Erziehungsberechtigte schicken?', - ); - if (confirm == true && - context.mounted) { - MatrixPolicyHelper.launchMatrixUrl( - context, - pupil.tutorInfo!.parentsContact!, - ); - } - }, - onLongPress: () async { - final String? tutorContact = - await longTextFieldDialog( - title: 'Elternkontakt', - labelText: 'Elternkontakt', - initialValue: pupil - .tutorInfo - ?.parentsContact, - parentContext: context, + ), + ), + const Gap(10), + IconButton( + icon: const Icon(Icons.copy), + onPressed: () { + if (pupil.contact == null) { + return; + } + Clipboard.setData( + ClipboardData( + text: pupil.contact!, + ), ); - if (tutorContact == null) return; - final TutorInfo? tutorInfo = - pupil.tutorInfo == null - ? TutorInfo( - parentsContact: - '@$tutorContact', - createdBy: - di() - .signedInUser! - .userName!, - ) - : pupil.tutorInfo!.copyWith( - parentsContact: - '@$tutorContact', - ); - await PupilMutator().updateTutorInfo( - pupilId: pupil.pupilId, - tutorInfo: tutorInfo, - ); - }, - child: Text( - pupil.tutorInfo?.parentsContact ?? - 'nicht eingetragen', - style: TextStyle( - color: - pupil.tutorInfo?.parentsContact == - null - ? Colors.black - : AppColors.backgroundColor, - fontWeight: - pupil.tutorInfo?.parentsContact == - null - ? FontWeight.normal - : FontWeight.bold, + di() + .showSnackBar( + NotificationType.success, + 'In die Zwischenablage kopiert', + ); + }, ), - ), + ], ), - const Gap(10), - IconButton( - icon: const Icon(Icons.copy), - onPressed: () { - Clipboard.setData( - ClipboardData( - text: - pupil + + Row( + children: [ + const Text('Elternkontakt: '), + const Gap(5), + InkWell( + onTap: () async { + if (pupil .tutorInfo - ?.parentsContact ?? - 'Kein Elternkontakt vorhanden!', - ), - ); - ScaffoldMessenger.of( - context, - ).showSnackBar( - const SnackBar( - content: Text( - 'In die Zwischenablage kopiert', + ?.parentsContact == + null) { + if (!di + .isRegistered< + MatrixPolicyManager + >()) { + di() + .showInformationDialog( + 'Es sind keine Matrix-Admindaten hinterlegt.', + ); + return; + } + String? pupilSiblingsGroups; + if (pupil.family != null) { + pupilSiblingsGroups = + [ + ..._pupilManager + .getSiblings( + pupil, + ), + pupil, + ] + .map((e) => e.group) + .toList() + .join(); + } + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => NewMatrixUserPage( + pupil: pupil, + matrixId: + MatrixPolicyHelper.generateMatrixId( + isParent: true, + ), + displayName: + pupilSiblingsGroups != + null + ? 'Fa. ${pupil.lastName} (E) $pupilSiblingsGroups' + : '${pupil.firstName} ${pupil.lastName.substring(0, 1).toUpperCase()}. (E) ${pupil.group}', + isParent: true, + ), + ), + ); + return; + } + final confirm = + await confirmationDialog( + context: context, + title: 'Messenger öffnen', + message: + 'Nachricht an ${pupil.firstName}s Erziehungsberechtigte schicken?', + ); + if (confirm == true && + context.mounted) { + MatrixPolicyHelper.launchMatrixUrl( + context, + pupil + .tutorInfo! + .parentsContact!, + ); + } + }, + onLongPress: () async { + final String? tutorContact = + await longTextFieldDialog( + title: 'Elternkontakt', + labelText: 'Elternkontakt', + initialValue: pupil + .tutorInfo + ?.parentsContact, + parentContext: context, + ); + if (tutorContact == null) return; + final TutorInfo? tutorInfo = + pupil.tutorInfo == null + ? TutorInfo( + parentsContact: + '@$tutorContact', + createdBy: + di() + .signedInUser! + .userName!, + ) + : pupil.tutorInfo!.copyWith( + parentsContact: + '@$tutorContact', + ); + await PupilMutator() + .updateTutorInfo( + pupilId: pupil.pupilId, + tutorInfo: tutorInfo, + ); + }, + child: Text( + pupil.tutorInfo?.parentsContact ?? + 'nicht vorhanden', + style: TextStyle( + color: + pupil + .tutorInfo + ?.parentsContact == + null + ? Colors.black + : AppColors.backgroundColor, + fontWeight: + pupil + .tutorInfo + ?.parentsContact == + null + ? FontWeight.bold + : FontWeight.bold, ), ), - ); - }, + ), + const Gap(10), + IconButton( + icon: const Icon(Icons.copy), + onPressed: () { + if (pupil + .tutorInfo + ?.parentsContact == + null) { + return; + } + Clipboard.setData( + ClipboardData( + text: + pupil + .tutorInfo + ?.parentsContact ?? + 'Kein Elternkontakt vorhanden!', + ), + ); + di() + .showSnackBar( + NotificationType.success, + 'In die Zwischenablage kopiert', + ); + }, + ), + ], ), + const Gap(10), ], ), - const Gap(10), - ], - ), + ), + ], ), ], ), - ], - ), - ), + ), + ); + }, ), ], ), diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart index 2c75ae6a..6a3eab67 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/infos_content/pupil_profile_infos_content.dart @@ -3,6 +3,7 @@ import 'package:gap/gap.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/app_utils/pdf_viewer_page.dart'; +import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/long_textfield_dialog.dart'; @@ -33,7 +34,7 @@ class PupilProfileInfosContent extends WatchingWidget { @override Widget build(BuildContext context) { final _pupilManager = di(); - final _matrixPolicyManager = di(); + final _hubSessionManager = di(); final pupilSiblings = _pupilManager.getSiblings(pupil); watch(pupil); @@ -181,7 +182,15 @@ class PupilProfileInfosContent extends WatchingWidget { }, actionButton: pupil.contact == null || pupil.contact!.isEmpty ? IconButton( - onPressed: () { + onPressed: () async { + if (_isMatrixAuthorized == false) return; + final confirm = await confirmationDialog( + context: context, + title: 'Matrix-Admindaten erstellen', + message: + 'Möchten Sie die Matrix-Admindaten wirklich erstellen?', + ); + if (confirm != true) return; Navigator.of(context).push( MaterialPageRoute( builder: (ctx) => NewMatrixUserPage( @@ -203,6 +212,8 @@ class PupilProfileInfosContent extends WatchingWidget { ) : IconButton( onPressed: () async { + if (_isMatrixAuthorized == false) return; + final confirmation = await confirmationDialog( context: context, title: 'Passwort zurücksetzen', @@ -215,7 +226,7 @@ class PupilProfileInfosContent extends WatchingWidget { context, ); if (logOutDevices == null) return; - final file = await _matrixPolicyManager.users + final file = await di().users .resetPasswordAndPrintCredentialsFile( user: MatrixUserHelper.usersFromUserIds([ pupil.contact!, @@ -310,6 +321,7 @@ class PupilProfileInfosContent extends WatchingWidget { ) : IconButton( onPressed: () async { + if (_isMatrixAuthorized == false) return; final confirmation = await confirmationDialog( context: context, title: 'Passwort zurücksetzen', @@ -322,7 +334,7 @@ class PupilProfileInfosContent extends WatchingWidget { context, ); if (logOutDevices == null) return; - final file = await _matrixPolicyManager.users + final file = await di().users .resetPasswordAndPrintCredentialsFile( user: MatrixUserHelper.usersFromUserIds([ pupil.tutorInfo!.parentsContact!, @@ -477,3 +489,16 @@ class PupilProfileInfosContent extends WatchingWidget { ); } } + +bool _isMatrixAuthorized() { + if (!di.isRegistered() || + !di().isAdmin) { + di().showInformationDialog( + !di().isAdmin + ? 'Keine Berechtigung. Admin-Rechte erforderlich.' + : 'Es sind keine Matrix-Admindaten hinterlegt.', + ); + return false; + } + return true; +} From 16aef8adb02cc00a16be70ba90437f142bee8f9f Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 01:42:38 +0100 Subject: [PATCH 43/52] pubspec, cursor rules, misc --- school_data_hub_flutter/.cursorrules | 1 + ...kotlin-compiler-5341982168051067233.salive | 0 .../Flutter/GeneratedPluginRegistrant.swift | 4 +++ school_data_hub_flutter/pubspec.lock | 32 +++++++++++++++++++ school_data_hub_flutter/pubspec.yaml | 1 + 5 files changed, 38 insertions(+) create mode 100644 school_data_hub_flutter/android/.kotlin/sessions/kotlin-compiler-5341982168051067233.salive diff --git a/school_data_hub_flutter/.cursorrules b/school_data_hub_flutter/.cursorrules index 84faab85..54857f9a 100644 --- a/school_data_hub_flutter/.cursorrules +++ b/school_data_hub_flutter/.cursorrules @@ -31,6 +31,7 @@ Please think out loud and provide ## Dart and flutter rules - Don't use print() statements!!! Implement or use instead final _log = Logger(); +- Always prefer WatchingWidget and createOnce functions for local state over StatefulWidget - Always prefer using watch functions over ListenableBuilder - always use package import even for our project files - Comply to the always_use_package_imports rule diff --git a/school_data_hub_flutter/android/.kotlin/sessions/kotlin-compiler-5341982168051067233.salive b/school_data_hub_flutter/android/.kotlin/sessions/kotlin-compiler-5341982168051067233.salive new file mode 100644 index 00000000..e69de29b diff --git a/school_data_hub_flutter/macos/Flutter/GeneratedPluginRegistrant.swift b/school_data_hub_flutter/macos/Flutter/GeneratedPluginRegistrant.swift index b5ff0cdc..4ddd4e7c 100644 --- a/school_data_hub_flutter/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/school_data_hub_flutter/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,11 +5,13 @@ import FlutterMacOS import Foundation +import audio_session import connectivity_plus import device_info_plus import file_picker import file_selector_macos import flutter_secure_storage_macos +import just_audio import mobile_scanner import package_info_plus import pasteboard @@ -22,11 +24,13 @@ import url_launcher_macos import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) diff --git a/school_data_hub_flutter/pubspec.lock b/school_data_hub_flutter/pubspec.lock index c76bd091..66f5e06e 100644 --- a/school_data_hub_flutter/pubspec.lock +++ b/school_data_hub_flutter/pubspec.lock @@ -57,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.13.0" + audio_session: + dependency: transitive + description: + name: audio_session + sha256: "8f96a7fecbb718cb093070f868b4cdcb8a9b1053dce342ff8ab2fde10eb9afb7" + url: "https://pub.dev" + source: hosted + version: "0.2.2" barcode: dependency: transitive description: @@ -877,6 +885,30 @@ packages: url: "https://pub.dev" source: hosted version: "6.11.0" + just_audio: + dependency: "direct main" + description: + name: just_audio + sha256: "9694e4734f515f2a052493d1d7e0d6de219ee0427c7c29492e246ff32a219908" + url: "https://pub.dev" + source: hosted + version: "0.10.5" + just_audio_platform_interface: + dependency: transitive + description: + name: just_audio_platform_interface + sha256: "2532c8d6702528824445921c5ff10548b518b13f808c2e34c2fd54793b999a6a" + url: "https://pub.dev" + source: hosted + version: "4.6.0" + just_audio_web: + dependency: transitive + description: + name: just_audio_web + sha256: "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663" + url: "https://pub.dev" + source: hosted + version: "0.4.16" leak_tracker: dependency: transitive description: diff --git a/school_data_hub_flutter/pubspec.yaml b/school_data_hub_flutter/pubspec.yaml index 0a7711e4..090e541d 100644 --- a/school_data_hub_flutter/pubspec.yaml +++ b/school_data_hub_flutter/pubspec.yaml @@ -79,6 +79,7 @@ dependencies: uml_for_serverpod: ^0.0.5 community_charts_flutter: ^1.0.4 go_router: ^17.0.0 + just_audio: ^0.10.5 dev_dependencies: From b6188266ef6991d2732588862c10912c7b19b4b9 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 20:34:40 +0100 Subject: [PATCH 44/52] [APP] Fix scanner --- .../lib/app_utils/scanner.dart | 45 ++++++------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/school_data_hub_flutter/lib/app_utils/scanner.dart b/school_data_hub_flutter/lib/app_utils/scanner.dart index 085a5f87..f4fa9722 100644 --- a/school_data_hub_flutter/lib/app_utils/scanner.dart +++ b/school_data_hub_flutter/lib/app_utils/scanner.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:watch_it/watch_it.dart'; @@ -20,33 +19,17 @@ Future qrScanner({ return null; } - return await Navigator.push( + final controller = MobileScannerController( + detectionSpeed: DetectionSpeed.normal, + formats: [BarcodeFormat.qrCode, BarcodeFormat.ean13], + facing: CameraFacing.back, + torchEnabled: false, + ); + final result = await Navigator.push( context, MaterialPageRoute( - builder: (context) => ScannerPage(overlayText: overlayText), - ), - ); - //return await context.push('/scanner', extra: overlayText); -} - -class ScannerPage extends WatchingWidget { - final String overlayText; - const ScannerPage({required this.overlayText, super.key}); - - @override - Widget build(BuildContext context) { - final controller = createOnce( - () => MobileScannerController( - detectionSpeed: DetectionSpeed.normal, - formats: [BarcodeFormat.qrCode, BarcodeFormat.ean13], - facing: CameraFacing.back, - torchEnabled: false, - ), - dispose: (controller) => controller.dispose(), - ); - - return Scaffold( - body: Stack( + fullscreenDialog: true, + builder: (context) => Stack( children: [ MobileScanner( controller: controller, @@ -55,9 +38,8 @@ class ScannerPage extends WatchingWidget { final Barcode barcode; barcode = capture.barcodes[0]; - if (context.mounted) { - context.pop(barcode.displayValue); - } + Navigator.pop(context, barcode.displayValue); + controller.dispose(); }, ), Positioned( @@ -83,6 +65,7 @@ class ScannerPage extends WatchingWidget { ), ], ), - ); - } + ), + ); + return result; } From dca8b111ff83308f1d9c3f3b63268df82c3e3942 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 20:37:05 +0100 Subject: [PATCH 45/52] [APP] Fix pupil identities import bug on mobile --- .../pupil_identity_helper_functions.dart | 29 ++++++++++++------- .../pupil/domain/pupil_identity_manager.dart | 5 ++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart index 55618311..eb96039c 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; @@ -30,7 +31,10 @@ class PupilIdentityHelper { final pupilsJson = await HubSecureStorage().getString(secureStorageKey); if (pupilsJson == null) return {}; - final Map decodedJson = jsonDecode(pupilsJson); + final Map decodedJson = await compute( + (json) => jsonDecode(json), + pupilsJson, + ); return Map.fromEntries( decodedJson.entries.map((entry) { @@ -114,7 +118,12 @@ class PupilIdentityHelper { default: throw Exception('Unknown school grade: ${pupilIdentityStringItems[5]}'); } - + // _log.info( + // 'Decoding pupil identity from string list: $pupilIdentityStringItems', + // ); + // if (pupilIdentityStringItems[0] == '1668') { + // debugger(); + // } final newPupilIdentity = PupilIdentity( id: int.parse(pupilIdentityStringItems[0]), firstName: pupilIdentityStringItems[1], @@ -134,15 +143,15 @@ class PupilIdentityHelper { : pupilIdentityStringItems[10], birthday: DateTime.tryParse( pupilIdentityStringItems[11], - )!.subtract(Duration(hours: 1)), + )!.subtract(const Duration(hours: 1)), migrationSupportEnds: pupilIdentityStringItems[12] == '' ? null : DateTime.tryParse( pupilIdentityStringItems[12], - )!.subtract(Duration(hours: 1)), + )!.subtract(const Duration(hours: 1)), pupilSince: DateTime.tryParse( pupilIdentityStringItems[13], - )!.subtract(Duration(hours: 1)), + )!.subtract(const Duration(hours: 1)), afterSchoolCare: pupilIdentityStringItems[14] != '' ? true : false, religion: pupilIdentityStringItems[15] == '' ? null @@ -151,17 +160,17 @@ class PupilIdentityHelper { ? null : DateTime.tryParse( pupilIdentityStringItems[16], - )!.subtract(Duration(hours: 1)), - familyLanguageLessonsSince: pupilIdentityStringItems[18] == '' + )!.subtract(const Duration(hours: 1)), + familyLanguageLessonsSince: pupilIdentityStringItems[17] == '' ? null : DateTime.tryParse( - pupilIdentityStringItems[18], - )!.subtract(Duration(hours: 1)), + pupilIdentityStringItems[17], + )!.subtract(const Duration(hours: 1)), leavingDate: pupilIdentityStringItems[18] == '' ? null : DateTime.tryParse( pupilIdentityStringItems[19], - )?.subtract(Duration(hours: 1)), + )?.subtract(const Duration(hours: 1)), ); return newPupilIdentity; } diff --git a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart index 63ffa886..eadb897f 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart @@ -377,6 +377,7 @@ class PupilIdentityManager { pupilIdentity.afterSchoolCare, pupilIdentity.religion ?? '', pupilIdentity.religionLessonsSince?.formatDateForJson() ?? '', + pupilIdentity.familyLanguageLessonsSince?.formatDateForJson() ?? '', pupilIdentity.leavingDate?.formatDateForJson() ?? '', ].join(',') + ',\n'; @@ -439,9 +440,7 @@ class PupilIdentityManager { .streamEncryptedPupilIds(channelName) .listen( (PupilIdentityDto event) async { - _log.info( - 'Received event: ${event.type} with value: ${event.value}', - ); + _log.info('Received event: [${event.type}] '); switch (role) { //- Stream behavior for sender role From d301f53db68db6bef3f4e055cd17aebb6a2c5079 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 22:35:22 +0100 Subject: [PATCH 46/52] [APP] Statistics, charts: added pupils with schoolyear held back, other changes --- .../widgets/attendance_stats_view.dart | 329 +++++++++--------- .../controller/statistics.dart | 10 + .../list_tiles/group_card.dart | 21 ++ 3 files changed, 198 insertions(+), 162 deletions(-) diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart index 3189e654..76f85b6b 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart @@ -5,9 +5,12 @@ import 'package:gap/gap.dart'; import 'package:intl/intl.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/features/_attendance/presentation/attendance_page/attendance_list_page.dart'; +import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page.dart'; +import 'package:watch_it/watch_it.dart'; -class AttendanceStatsView extends StatefulWidget { +class AttendanceStatsView extends WatchingWidget { final List sortedSchooldays; final Map attendanceChartData; @@ -19,58 +22,176 @@ class AttendanceStatsView extends StatefulWidget { }); @override - State createState() => _AttendanceStatsViewState(); -} + Widget build(BuildContext context) { + final _hiddenSeries = createOnce(() => ValueNotifier>(Set())); + void _onSelectionChanged(charts.SelectionModel model) { + final selectedDatum = model.selectedDatum; + + if (selectedDatum.isNotEmpty) { + final datum = selectedDatum.first; + final chartData = datum.datum as ChartData; + + // Construct data string with all values for this date + final dateData = attendanceChartData[chartData.date]; + if (dateData == null) return; + + final buffer = StringBuffer(); + buffer.writeln('Datum: ${chartData.dateString}'); + buffer.writeln(); + buffer.writeln('Entschuldigt: ${dateData.excused}'); + buffer.writeln('Unentschuldigt: ${dateData.unexcused}'); + buffer.writeln('Nach Hause geschickt: ${dateData.goneHome}'); -class _AttendanceStatsViewState extends State { - final Set _hiddenSeries = {}; + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Details'), + content: Text(buffer.toString()), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + di().setThisDate(chartData.date); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const AttendanceListPage(), + ), + ); + }, + child: const Text('Fehlzeiten anzeigen'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Schließen'), + ), + ], + ), + ); + } + } - void _toggleSeries(String seriesId) { - setState(() { - if (_hiddenSeries.contains(seriesId)) { - _hiddenSeries.remove(seriesId); + void _toggleSeries(String seriesId) { + final modifiedHiddenSeries = _hiddenSeries.value; + if (_hiddenSeries.value.contains(seriesId)) { + modifiedHiddenSeries.remove(seriesId); + _hiddenSeries.value = modifiedHiddenSeries; } else { - _hiddenSeries.add(seriesId); + modifiedHiddenSeries.add(seriesId); + _hiddenSeries.value = modifiedHiddenSeries; } - }); - } + } - void _onSelectionChanged(charts.SelectionModel model) { - final selectedDatum = model.selectedDatum; + Widget _buildLegendItem(String label, Color color, String seriesId) { + final isHidden = _hiddenSeries.value.contains(seriesId); + return InkWell( + onTap: () => _toggleSeries(seriesId), + child: Opacity( + opacity: isHidden ? 0.5 : 1.0, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4), + ), + child: isHidden + ? const Icon(Icons.close, size: 16, color: Colors.white) + : null, + ), + const Gap(8), + Text( + label, + style: TextStyle( + fontSize: 16, + decoration: isHidden + ? TextDecoration.lineThrough + : TextDecoration.none, + ), + ), + ], + ), + ), + ); + } - if (selectedDatum.isNotEmpty) { - final datum = selectedDatum.first; - final chartData = datum.datum as ChartData; + List> _createAttendanceSeries() { + final List> series = []; - // Construct data string with all values for this date - final dateData = widget.attendanceChartData[chartData.date]; - if (dateData == null) return; + if (!_hiddenSeries.value.contains('excused')) { + final excusedData = sortedSchooldays.map((schoolday) { + final data = attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.excused ?? 0, + seriesId: 'excused', + ); + }).toList(); - final buffer = StringBuffer(); - buffer.writeln('Datum: ${chartData.dateString}'); - buffer.writeln(); - buffer.writeln('Entschuldigt: ${dateData.excused}'); - buffer.writeln('Unentschuldigt: ${dateData.unexcused}'); - buffer.writeln('Nach Hause geschickt: ${dateData.goneHome}'); + series.add( + charts.Series( + id: 'Entschuldigt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.green), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: excusedData, + ), + ); + } - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Details'), - content: Text(buffer.toString()), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Schließen'), - ), - ], - ), - ); + if (!_hiddenSeries.value.contains('unexcused')) { + final unexcusedData = sortedSchooldays.map((schoolday) { + final data = attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.unexcused ?? 0, + seriesId: 'unexcused', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Unentschuldigt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: unexcusedData, + ), + ); + } + + if (!_hiddenSeries.value.contains('goneHome')) { + final goneHomeData = sortedSchooldays.map((schoolday) { + final data = attendanceChartData[schoolday.schoolday]; + final dateStr = _formatDateForChart(schoolday.schoolday); + return ChartData( + date: schoolday.schoolday, + dateString: dateStr, + count: data?.goneHome ?? 0, + seriesId: 'goneHome', + ); + }).toList(); + + series.add( + charts.Series( + id: 'Nach Hause geschickt', + colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), + domainFn: (ChartData data, _) => data.dateString, + measureFn: (ChartData data, _) => data.count, + data: goneHomeData, + ), + ); + } + + return series; } - } - @override - Widget build(BuildContext context) { final attendanceSeries = _createAttendanceSeries(); return SingleChildScrollView( child: Padding( @@ -80,10 +201,7 @@ class _AttendanceStatsViewState extends State { children: [ const Gap(15), const Center( - child: Text( - 'Fehlzeiten nach Schultag', - style: AppStyles.title, - ), + child: Text('Fehlzeiten nach Schultag', style: AppStyles.title), ), const Gap(10), SizedBox( @@ -113,7 +231,7 @@ class _AttendanceStatsViewState extends State { ), domainAxis: charts.OrdinalAxisSpec( tickProviderSpec: charts.StaticOrdinalTickProviderSpec( - widget.sortedSchooldays.map((schoolday) { + sortedSchooldays.map((schoolday) { final dateStr = _formatDateForChart( schoolday.schoolday, ); @@ -121,9 +239,7 @@ class _AttendanceStatsViewState extends State { if (firstOfMonthDates.contains(dateStr)) { try { - final date = DateFormat( - 'dd.MM', - ).parse(dateStr); + final date = DateFormat('dd.MM').parse(dateStr); return charts.TickSpec( dateStr, label: DateFormat('MMM').format(date), @@ -179,47 +295,11 @@ class _AttendanceStatsViewState extends State { ); } - Widget _buildLegendItem(String label, Color color, String seriesId) { - final isHidden = _hiddenSeries.contains(seriesId); - return InkWell( - onTap: () => _toggleSeries(seriesId), - child: Opacity( - opacity: isHidden ? 0.5 : 1.0, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(4), - ), - child: isHidden - ? const Icon(Icons.close, size: 16, color: Colors.white) - : null, - ), - const Gap(8), - Text( - label, - style: TextStyle( - fontSize: 16, - decoration: isHidden - ? TextDecoration.lineThrough - : TextDecoration.none, - ), - ), - ], - ), - ), - ); - } - Set _getFirstOfMonthDates() { final Set firstOfMonthDates = {}; String? currentMonth; - for (final schoolday in widget.sortedSchooldays) { + for (final schoolday in sortedSchooldays) { final date = schoolday.schoolday.toLocal(); final monthKey = '${date.year}-${date.month}'; if (monthKey != currentMonth) { @@ -234,79 +314,4 @@ class _AttendanceStatsViewState extends State { final localDate = date.isUtc ? date.toLocal() : date; return DateFormat('dd.MM').format(localDate); } - - List> _createAttendanceSeries() { - final List> series = []; - - if (!_hiddenSeries.contains('excused')) { - final excusedData = widget.sortedSchooldays.map((schoolday) { - final data = widget.attendanceChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.excused ?? 0, - seriesId: 'excused', - ); - }).toList(); - - series.add( - charts.Series( - id: 'Entschuldigt', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.green), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: excusedData, - ), - ); - } - - if (!_hiddenSeries.contains('unexcused')) { - final unexcusedData = widget.sortedSchooldays.map((schoolday) { - final data = widget.attendanceChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.unexcused ?? 0, - seriesId: 'unexcused', - ); - }).toList(); - - series.add( - charts.Series( - id: 'Unentschuldigt', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.red), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: unexcusedData, - ), - ); - } - - if (!_hiddenSeries.contains('goneHome')) { - final goneHomeData = widget.sortedSchooldays.map((schoolday) { - final data = widget.attendanceChartData[schoolday.schoolday]; - final dateStr = _formatDateForChart(schoolday.schoolday); - return ChartData( - date: schoolday.schoolday, - dateString: dateStr, - count: data?.goneHome ?? 0, - seriesId: 'goneHome', - ); - }).toList(); - - series.add( - charts.Series( - id: 'Nach Hause geschickt', - colorFn: (_, __) => charts.ColorUtil.fromDartColor(Colors.orange), - domainFn: (ChartData data, _) => data.dateString, - measureFn: (ChartData data, _) => data.count, - data: goneHomeData, - ), - ); - } - - return series; - } } diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart index 96fcdacb..9f322969 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/controller/statistics.dart @@ -71,6 +71,16 @@ class StatisticsController extends State { .toList(); } + List pupilsWithSchoolyearHeldBack(List givenPupils) { + List groupPupils = []; + for (PupilProxy pupil in givenPupils) { + if (pupil.schoolyearHeldBackAt != null) { + groupPupils.add(pupil); + } + } + return groupPupils; + } + List pupilsInaGivenGroup(String group) { List groupPupils = []; for (PupilProxy pupil in pupils) { diff --git a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_card.dart b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_card.dart index 0a08f52e..5b1db64f 100644 --- a/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_card.dart +++ b/school_data_hub_flutter/lib/features/statistics/statistics_page/list_tiles/group_card.dart @@ -204,6 +204,27 @@ Widget statisticsGroupCard( ], ), const Gap(5), + Row( + children: [ + const Text( + 'Verbleiber*innen:', + style: TextStyle(color: Colors.black, fontSize: 18), + ), + const Gap(5), + Text( + controller + .pupilsWithSchoolyearHeldBack(group) + .length + .toString(), + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + ], + ), + const Gap(5), Row( children: [ const Icon(Icons.support_rounded, color: Colors.red), From 3b32e685ffe2603b988b6c14ec363c8dc32833c8 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Wed, 3 Dec 2025 22:36:31 +0100 Subject: [PATCH 47/52] [APP] Clean logs, other changes --- ...kotlin-compiler-5341982168051067233.salive | 0 .../common/services/notification_service.dart | 12 +- .../lib/core/init/init_manager.dart | 69 ++---- .../lib/core/init/init_on_active_env.dart | 8 +- .../lib/core/init/init_on_user_auth.dart | 51 ++--- .../app_entry_point/loading_page.dart | 147 ++++++------ .../app_main_navigation/tools_page.dart | 17 +- .../post_or_patch_competence_page.dart | 214 ++++++++++-------- .../family_language_lessons_list_page.dart | 2 +- .../domain/managers/school_data_manager.dart | 15 +- .../managers/school_data_ui_manager.dart | 14 +- .../managers/timetable_data_manager.dart | 15 +- 12 files changed, 270 insertions(+), 294 deletions(-) delete mode 100644 school_data_hub_flutter/android/.kotlin/sessions/kotlin-compiler-5341982168051067233.salive diff --git a/school_data_hub_flutter/android/.kotlin/sessions/kotlin-compiler-5341982168051067233.salive b/school_data_hub_flutter/android/.kotlin/sessions/kotlin-compiler-5341982168051067233.salive deleted file mode 100644 index e69de29b..00000000 diff --git a/school_data_hub_flutter/lib/common/services/notification_service.dart b/school_data_hub_flutter/lib/common/services/notification_service.dart index 5fdc0da1..dec4b6b2 100644 --- a/school_data_hub_flutter/lib/common/services/notification_service.dart +++ b/school_data_hub_flutter/lib/common/services/notification_service.dart @@ -34,11 +34,9 @@ class NotificationService { return; } - _log.fine( - '''====================================================================================== SNACK BAR MESSAGE: + _log.fine('''SNACK BAR MESSAGE: ${type} $message - ''', - ); + '''); _snackBar.value = NotificationData(type, message); } @@ -46,11 +44,9 @@ class NotificationService { void showInformationDialog(String message) { _snackBar.value = NotificationData(NotificationType.dialog, message); - _log.fine( - '''====================================================================================== INFORMATION DIALOG: + _log.fine('''INFORMATION DIALOG: $message - ''', - ); + '''); } void apiRunning(bool value) { diff --git a/school_data_hub_flutter/lib/core/init/init_manager.dart b/school_data_hub_flutter/lib/core/init/init_manager.dart index ff74379b..dfc6efb8 100644 --- a/school_data_hub_flutter/lib/core/init/init_manager.dart +++ b/school_data_hub_flutter/lib/core/init/init_manager.dart @@ -62,16 +62,10 @@ class InitManager { await di.pushNewScopeAsync( scopeName: DiScope.onActiveEnvScope.name, dispose: () { - _log.info('Dropping scope[activeEnvScope]'); - _log.warning( - '======================================================== [SCOPE] [activeEnvScope]', - ); + _log.warning('[SCOPE] Dropping scope[activeEnvScope]'); }, init: (getIt) async { - _log.info('Pushing scope [activeEnvScope]'); - _log.fine( - '======================================================== [SCOPE] [activeEnvScope]', - ); + _log.info('[SCOPE] Pushing scope [activeEnvScope]'); await InitOnActiveEnv.registerManagers(); _log.info( @@ -94,16 +88,10 @@ class InitManager { await di.pushNewScopeAsync( scopeName: DiScope.onLoggedInUserScope.name, dispose: () { - _log.info('Dropping scope [loggedInUserScope]'); - _log.warning( - '======================================================== [SCOPE] [loggedInUserScope]', - ); + _log.warning('[SCOPE] Dropping scope [loggedInUserScope]'); }, init: (getIt) async { - _log.info('Pushing scope [loggedInUserScope]'); - _log.fine( - '======================================================== [SCOPE] [loggedInUserScope]', - ); + _log.info('[SCOPE] Pushing scope [loggedInUserScope]'); await InitOnUserAuth.registerManagers(); }, @@ -117,17 +105,10 @@ class InitManager { await di.dropScope( DiScope.onLoggedInUserScope.name, ); // This will dispose the 'logged_in_user_scope' - _log.info('[loggedInUserScope] dropped successfully'); - _log.warning( - '======================================================== [SCOPE] [loggedInUserScope]', - ); + _log.info('[SCOPE] [loggedInUserScope] dropped successfully'); } else { _log.severe( - '[loggedInUserScope] does not exist, skipping drop operation', - StackTrace.current, - ); - _log.severe( - '======================================================== [SCOPE] [loggedInUserScope]', + '[SCOPE] [loggedInUserScope] does not exist, skipping drop operation', ); } } @@ -137,29 +118,19 @@ class InitManager { if (di.hasScope(DiScope.onLoggedInUserScope.name)) { await di.dropScope(DiScope.onLoggedInUserScope.name); - _log.info('[loggedInUserScope] dropped successfully'); - _log.warning( - '======================================================== [SCOPE] [loggedInUserScope]', - ); + _log.info('[SCOPE] [loggedInUserScope] dropped successfully'); } if (di.hasScope(DiScope.onActiveEnvScope.name)) { await di.dropScope(DiScope.onActiveEnvScope.name); - _log.info('[activeEnvScope] dropped successfully'); - _log.warning( - '======================================================== [SCOPE] [activeEnvScope]', - ); + _log.info('[SCOPE] [activeEnvScope] dropped successfully'); } // Also drop matrix scope if it exists (it's environment-dependent) if (di.hasScope(DiScope.onMatrixEnvScope.name)) { - _log.info('Matrix scope exists, dropping it...'); - _log.warning('========================================================'); + _log.info('[SCOPE] Matrix scope exists, dropping it...'); await dropMatrixScope(); - _log.info('[matrixScope] dropped successfully'); - _log.warning( - '======================================================== [SCOPE] [matrixScope]', - ); + _log.info('[SCOPE] [matrixScope] dropped successfully'); } } @@ -180,16 +151,10 @@ class InitManager { await di.pushNewScopeAsync( scopeName: DiScope.onMatrixEnvScope.name, dispose: () { - _log.fine('Dropping scope [matrixScope]'); - _log.warning( - '======================================================== [SCOPE] [matrixScope]', - ); + _log.warning('[SCOPE] Dropping scope [matrixScope]'); }, init: (getIt) async { - _log.info('Pushing scope [matrixScope]'); - _log.fine( - '======================================================== [SCOPE] [matrixScope]', - ); + _log.info('[SCOPE] Pushing scope [matrixScope]'); // Register the manager inside the matrix scope getIt.registerLazySingletonAsync(() async { @@ -368,15 +333,9 @@ class InitManager { final hasScope = di.hasScope(DiScope.onMatrixEnvScope.name); if (hasScope) { - _log.info('Dropping scope [matrixScope]...'); - _log.warning( - '======================================================== [SCOPE] [matrixScope]', - ); + _log.warning('[SCOPE] Dropping scope [matrixScope]...'); await di.dropScope(DiScope.onMatrixEnvScope.name); - _log.info('[matrixScope] dropped successfully'); - _log.warning( - '======================================================== [SCOPE] [matrixScope]', - ); + _log.info('[SCOPE] [matrixScope] dropped successfully'); // Add a small delay to ensure the scope is fully dropped // await Future.delayed(const Duration(milliseconds: 50)); diff --git a/school_data_hub_flutter/lib/core/init/init_on_active_env.dart b/school_data_hub_flutter/lib/core/init/init_on_active_env.dart index 7008fa4b..91c02d4a 100644 --- a/school_data_hub_flutter/lib/core/init/init_on_active_env.dart +++ b/school_data_hub_flutter/lib/core/init/init_on_active_env.dart @@ -22,10 +22,7 @@ class InitOnActiveEnv { final envManager = di(); final activeEnv = envManager.activeEnv; final serverUrl = activeEnv!.serverUrl; - _log.info('ClientURL: [${activeEnv.serverUrl}]'); - _log.fine( - '======================================================== [CLIENT] [${activeEnv.serverName}] [${activeEnv.runMode.name}]', - ); + _log.info('[CLIENT] ClientURL: [${activeEnv.serverUrl}]'); return Client( serverUrl, @@ -44,8 +41,7 @@ class InitOnActiveEnv { // this will initialize the session manager and load the stored user info // it returns a bool await sessionManager.initialize(); - _log.fine('HubSessionManager initialized'); - _log.info('========================================================'); + _log.info('[SESSION] HubSessionManager initialized'); return sessionManager; }, dependsOn: [EnvManager, Client], diff --git a/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart b/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart index 7ce8c905..0ed05b8a 100644 --- a/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart +++ b/school_data_hub_flutter/lib/core/init/init_on_user_auth.dart @@ -56,8 +56,7 @@ class InitOnUserAuth { await pupilIdentityManager.init(); - _log.fine('PupilIdentityManager initialized'); - _log.info('========================================================'); + _log.info('[PUPIL IDENTITY MANAGER] PupilIdentityManager initialized'); return pupilIdentityManager; }); @@ -67,8 +66,7 @@ class InitOnUserAuth { await schoolCalendarManager.init(); - _log.fine('SchoolCalendarManager initialized'); - _log.info('========================================================'); + _log.info('[SCHOOL CALENDAR MANAGER] SchoolCalendarManager initialized'); return schoolCalendarManager; }); @@ -78,8 +76,9 @@ class InitOnUserAuth { await supportCategoryManager.init(); - _log.fine('SupportCategoryManager initialized'); - _log.info('========================================================'); + _log.info( + '[SUPPORT CATEGORY MANAGER] SupportCategoryManager initialized', + ); return supportCategoryManager; }); @@ -89,8 +88,7 @@ class InitOnUserAuth { await pupilManager.init(); - _log.fine('PupilManager initialized'); - _log.info('========================================================'); + _log.info('[PUPIL MANAGER] PupilManager initialized'); return pupilManager; }, dependsOn: [PupilIdentityManager, HubSessionManager]); @@ -103,24 +101,21 @@ class InitOnUserAuth { di.registerSingletonAsync(() async { final bookManager = BookManager(); await bookManager.init(); - _log.fine('BookManager initialized'); - _log.info('========================================================'); + _log.info('[BOOK MANAGER] BookManager initialized'); return bookManager; }, dependsOn: []); di.registerSingletonAsync(() async { final schoolDataManager = SchoolDataMainManager(); await schoolDataManager.init(); - _log.fine('SchoolDataMainManager initialized'); - _log.info('========================================================'); + _log.info('[SCHOOL DATA MAIN MANAGER] SchoolDataMainManager initialized'); return schoolDataManager; }, dependsOn: []); di.registerSingletonAsync(() async { final workbookManager = WorkbookManager(); await workbookManager.init(); - _log.fine('WorkbookManager initialized'); - _log.info('========================================================'); + _log.info('[WORKBOOK MANAGER] WorkbookManager initialized'); return workbookManager; }, dependsOn: [HubSessionManager, PupilManager]); @@ -129,8 +124,7 @@ class InitOnUserAuth { await competenceManager.init(); - _log.fine('CompetenceManager initialized'); - _log.info('========================================================'); + _log.info('[COMPETENCE MANAGER] CompetenceManager initialized'); return competenceManager; }); @@ -142,8 +136,7 @@ class InitOnUserAuth { di.registerSingletonAsync(() async { final authorizationManager = AuthorizationManager(); await authorizationManager.init(); - _log.fine('AuthorizationManager initialized'); - _log.info('========================================================'); + _log.info('[AUTHORIZATION MANAGER] AuthorizationManager initialized'); return authorizationManager; }, dependsOn: [HubSessionManager]); @@ -183,8 +176,9 @@ class InitOnUserAuth { di.registerSingletonWithDependencies(() { final schooldayEventFilterManager = SchooldayEventFilterManager(); - _log.fine('SchooldayEventFilterManager initialized'); - _log.info('========================================================'); + _log.info( + '[SCHOOLDAY EVENT FILTER MANAGER] SchooldayEventFilterManager initialized', + ); return schooldayEventFilterManager; }, dependsOn: [PupilManager, PupilFilterManager, SchooldayEventManager]); @@ -218,39 +212,36 @@ class InitOnUserAuth { di.registerSingletonAsync(() async { final schoolListManager = SchoolListManager(); await schoolListManager.init(); - _log.fine('SchoolListManager initialized'); - _log.info('========================================================'); + _log.info('[SCHOOL LIST MANAGER] SchoolListManager initialized'); return schoolListManager; }, dependsOn: [HubSessionManager, PupilManager]); di.registerSingletonWithDependencies(() { final schoolListFilterManager = SchoolListFilterManager(); schoolListFilterManager.init(); - _log.fine('SchoolListFilterManager initialized'); - _log.info('========================================================'); + _log.info( + '[SCHOOL LIST FILTER MANAGER] SchoolListFilterManager initialized', + ); return schoolListFilterManager; }, dependsOn: [PupilsFilter, SchoolListManager]); di.registerSingletonAsync(() async { final userManager = UserManager(); await userManager.init(); - _log.fine('UserManager initialized'); - _log.info('========================================================'); + _log.info('[USER MANAGER] UserManager initialized'); return userManager; }, dependsOn: [HubSessionManager]); di.registerSingletonAsync(() async { final timetableApiService = TimetableApiService(); - _log.fine('TimetableApiService initialized'); - _log.info('========================================================'); + _log.info('[TIMETABLE API SERVICE] TimetableApiService initialized'); return timetableApiService; }); di.registerSingletonAsync(() async { final timetableManager = TimetableManager(); await timetableManager.init(); - _log.fine('TimetableManager initialized'); - _log.info('========================================================'); + _log.info('[TIMETABLE MANAGER] TimetableManager initialized'); return timetableManager; }, dependsOn: [HubSessionManager, TimetableApiService]); diff --git a/school_data_hub_flutter/lib/features/app_entry_point/loading_page.dart b/school_data_hub_flutter/lib/features/app_entry_point/loading_page.dart index 96e65065..9e0f65f1 100644 --- a/school_data_hub_flutter/lib/features/app_entry_point/loading_page.dart +++ b/school_data_hub_flutter/lib/features/app_entry_point/loading_page.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:school_data_hub_flutter/l10n/app_localizations.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; +import 'package:school_data_hub_flutter/l10n/app_localizations.dart'; import 'package:watch_it/watch_it.dart'; final _envManager = di(); @@ -21,8 +21,9 @@ class LoadingPageState extends State { @override Widget build(BuildContext context) { final locale = AppLocalizations.of(context)!; - final NotificationData snackBarData = - watchValue((NotificationService x) => x.notification); + final NotificationData snackBarData = watchValue( + (NotificationService x) => x.notification, + ); String newValue = snackBarData.message; if (newValue != actualNotificationMessage) { @@ -31,71 +32,89 @@ class LoadingPageState extends State { } return Scaffold( - body: Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - decoration: const BoxDecoration( - color: AppColors.backgroundColor, - ), - child: Center( - child: SizedBox( - height: 600, - width: 600, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 300, - width: 300, - child: Image( - image: AssetImage('assets/foreground.png'), + body: SafeArea( + child: Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration(color: AppColors.backgroundColor), + child: LayoutBuilder( + builder: (context, constraints) { + final double maxContentWidth = constraints.maxWidth >= 600 + ? 600 + : constraints.maxWidth; + return Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 32, ), - ), - Text( - locale.schoolDataHub, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 30, - ), - ), - const Gap(15), - if (_envManager.activeEnv != null) - Text( - _envManager.activeEnv!.serverName, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: maxContentWidth), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 300, + width: 300, + child: Image( + image: AssetImage('assets/foreground.png'), + ), + ), + Text( + locale.schoolDataHub, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 30, + ), + ), + const Gap(15), + if (_envManager.activeEnv != null) + Text( + _envManager.activeEnv!.serverName, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + const Gap(40), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + lastNotificationMessage, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + const Gap(5), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + actualNotificationMessage, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + const Gap(30), + const CircularProgressIndicator(color: Colors.white), + ], ), ), - const Spacer(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text(lastNotificationMessage, - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.bold)), - ), - const Gap(5), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text(actualNotificationMessage, - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.bold)), ), - const Gap(30), - const CircularProgressIndicator( - color: Colors.white, - ) - ], - ), + ); + }, ), ), ), diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart index b8190dc2..6cc68bd1 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_flutter/app_utils/pick_file_return_content_as_string.dart'; import 'package:school_data_hub_flutter/app_utils/scanner.dart'; +import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; @@ -91,10 +92,9 @@ class ToolsPage extends WatchingWidget { } if (channelName == null || channelName.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Kein gültiger Verbindungscode.'), - ), + di().showSnackBar( + NotificationType.error, + 'Kein gültiger Verbindungscode.', ); return; } @@ -116,12 +116,9 @@ class ToolsPage extends WatchingWidget { labelText: 'Verbindungscode', ); if (channelName == null || channelName.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Kein gültiger Verbindungscode.', - ), - ), + di().showSnackBar( + NotificationType.error, + 'Kein gültiger Verbindungscode.', ); return; } diff --git a/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart b/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart index 0b8bd549..2b198053 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/post_or_patch_competence_page/post_or_patch_competence_page.dart @@ -12,8 +12,11 @@ class PostOrPatchCompetencePage extends StatefulWidget { final int? parentCompetence; final Competence? competence; - const PostOrPatchCompetencePage( - {super.key, this.competence, this.parentCompetence}); + const PostOrPatchCompetencePage({ + super.key, + this.competence, + this.parentCompetence, + }); @override PostOrPatchCompetencePageState createState() => @@ -31,33 +34,41 @@ class PostOrPatchCompetencePageState extends State { void postNewCompetence() async { if (competenceLevel.isEmpty) { - informationDialog(context, 'Kompetenzstufe auswählen', - 'Bitte mindestens eine Kompetenzstufe auswählen!'); + informationDialog( + context, + 'Kompetenzstufe auswählen', + 'Bitte mindestens eine Kompetenzstufe auswählen!', + ); return; } Navigator.pop(context); await _competenceManager.postNewCompetence( - parentCompetence: widget.parentCompetence, - competenceLevel: [competenceLevel], - competenceName: nameFieldController.text, - indicators: [indicatorsFieldController.text]); + parentCompetence: widget.parentCompetence, + competenceLevel: [competenceLevel], + competenceName: nameFieldController.text, + indicators: [indicatorsFieldController.text], + ); } void patchCompetence() async { if (competenceLevel.isEmpty) { - informationDialog(context, 'Kompetenzstufe auswählen', - 'Bitte mindestens eine Kompetenzstufe auswählen!'); + informationDialog( + context, + 'Kompetenzstufe auswählen', + 'Bitte mindestens eine Kompetenzstufe auswählen!', + ); return; } String competenceName = nameFieldController.text; String newCompetenceLevel = competenceLevel; String text3 = indicatorsFieldController.text; await _competenceManager.updateCompetenceProperty( - publicId: widget.competence!.publicId, - competenceName: competenceName, - competenceLevel: (value: [newCompetenceLevel]), - indicators: (value: [text3])); + publicId: widget.competence!.publicId, + competenceName: competenceName, + competenceLevel: (value: [newCompetenceLevel]), + indicators: (value: [text3]), + ); // ignore: use_build_context_synchronously Navigator.pop(context); } @@ -71,14 +82,16 @@ class PostOrPatchCompetencePageState extends State { super.initState(); setState(() { // TODO: implement lists for competence level and indicators - competenceLevel = - widget.competence != null ? widget.competence!.level!.join() : ''; - nameFieldController.text = - widget.competence != null ? widget.competence!.name : ''; + competenceLevel = widget.competence != null + ? widget.competence!.level!.join() + : ''; + nameFieldController.text = widget.competence != null + ? widget.competence!.name + : ''; indicatorsFieldController.text = widget.competence != null ? widget.competence!.indicators != null - ? widget.competence!.indicators!.join() - : '' + ? widget.competence!.indicators!.join() + : '' : ''; }); } @@ -92,10 +105,11 @@ class PostOrPatchCompetencePageState extends State { return Scaffold( appBar: GenericAppBar( - iconData: Icons.edit_document, - title: widget.competence != null - ? 'Kompetenz überarbeiten' - : 'Neue Kompetenz'), + iconData: Icons.edit_document, + title: widget.competence != null + ? 'Kompetenz überarbeiten' + : 'Neue Kompetenz', + ), body: Padding( padding: const EdgeInsets.all(16.0), child: Center( @@ -108,7 +122,9 @@ class PostOrPatchCompetencePageState extends State { Text( 'Übergeordnete Kompetenz: ${_competenceManager.findCompetenceById(widget.parentCompetence!).name}', style: const TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), + fontSize: 18, + fontWeight: FontWeight.bold, + ), ), const Text( 'Kompetenz', @@ -120,7 +136,8 @@ class PostOrPatchCompetencePageState extends State { maxLines: 2, controller: nameFieldController, decoration: AppStyles.textFieldDecoration( - labelText: 'Name der Kompetenz'), + labelText: 'Name der Kompetenz', + ), ), const Gap(10), const Text( @@ -132,73 +149,85 @@ class PostOrPatchCompetencePageState extends State { mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ ThemedFilterChip( - label: 'E1', - selected: gradeE1, - onSelected: (value) { - if (value) { - setState(() { - competenceLevel = '${competenceLevel}E1'; - gradeE1 = true; - }); - } else { - setState(() { - competenceLevel = - competenceLevel.replaceAll('E1', ''); - gradeE1 = false; - }); - } - }), + label: 'E1', + selected: gradeE1, + onSelected: (value) { + if (value) { + setState(() { + competenceLevel = '${competenceLevel}E1'; + gradeE1 = true; + }); + } else { + setState(() { + competenceLevel = competenceLevel.replaceAll( + 'E1', + '', + ); + gradeE1 = false; + }); + } + }, + ), ThemedFilterChip( - label: 'E2', - selected: gradeE2, - onSelected: (value) { - if (value) { - setState(() { - competenceLevel = '${competenceLevel}E2'; - gradeE2 = true; - }); - } else { - setState(() { - competenceLevel = - competenceLevel.replaceAll('E2', ''); - gradeE2 = false; - }); - } - }), + label: 'E2', + selected: gradeE2, + onSelected: (value) { + if (value) { + setState(() { + competenceLevel = '${competenceLevel}E2'; + gradeE2 = true; + }); + } else { + setState(() { + competenceLevel = competenceLevel.replaceAll( + 'E2', + '', + ); + gradeE2 = false; + }); + } + }, + ), ThemedFilterChip( - label: 'K3', - selected: gradeS3, - onSelected: (value) { - if (value) { - setState(() { - competenceLevel = '${competenceLevel}03'; - gradeS3 = true; - }); - } else { - setState(() { - competenceLevel = - competenceLevel.replaceAll('K3', ''); - gradeS3 = false; - }); - } - }), + label: 'K3', + selected: gradeS3, + onSelected: (value) { + if (value) { + setState(() { + competenceLevel = '${competenceLevel}K3'; + gradeS3 = true; + }); + } else { + setState(() { + competenceLevel = competenceLevel.replaceAll( + 'K3', + '', + ); + gradeS3 = false; + }); + } + }, + ), ThemedFilterChip( - label: 'K4', - selected: gradeS4, - onSelected: (value) { - if (value) { - setState(() { - competenceLevel = '${competenceLevel}04'; - gradeS4 = true; - }); - } else { - setState(() { - competenceLevel = - competenceLevel.replaceAll('K4', ''); - gradeS4 = false; - }); - } - }), + label: 'K4', + selected: gradeS4, + onSelected: (value) { + if (value) { + setState(() { + competenceLevel = '${competenceLevel}K4'; + gradeS4 = true; + }); + } else { + setState(() { + competenceLevel = competenceLevel.replaceAll( + 'K4', + '', + ); + gradeS4 = false; + }); + } + }, + ), ], ), const Gap(20), @@ -218,10 +247,7 @@ class PostOrPatchCompetencePageState extends State { ? postNewCompetence() : patchCompetence(); }, - child: const Text( - 'SENDEN', - style: AppStyles.buttonTextStyle, - ), + child: const Text('SENDEN', style: AppStyles.buttonTextStyle), ), const Gap(15), ElevatedButton( diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart index c14385bb..4d449008 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/family_language_lessons_list_page.dart @@ -94,7 +94,7 @@ class FamilyLanguageLessonsListPage extends WatchingWidget { ), ), ), - bottomNavigationBar: FamilyLanguageLessonsListPageBottomNavBar(), + bottomNavigationBar: const FamilyLanguageLessonsListPageBottomNavBar(), ), ); } diff --git a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart index b380e1f7..8b4d9fc8 100644 --- a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart +++ b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_manager.dart @@ -76,18 +76,11 @@ class SchoolDataManager extends ChangeNotifier { /// Debug method to print current state void debugPrintState() { - _log.info('=== SchoolDataManager Debug State ==='); _log.info( - 'School Data: ${_schoolData.value?.name ?? 'null'} (ID: ${_schoolData.value?.id ?? 'null'})', + '''School Data: ${_schoolData.value?.name ?? 'null'} (ID: ${_schoolData.value?.id ?? 'null'}) + Logo Image: ${_logoImage.value != null ? 'loaded' : 'not loaded'} + Official Seal Image: ${_officialSealImage.value != null ? 'loaded' : 'not loaded'} + ''', ); - _log.info( - 'Logo Image: ${_logoImage.value != null ? 'loaded' : 'not loaded'}', - ); - _log.info( - 'Official Seal Image: ${_officialSealImage.value != null ? 'loaded' : 'not loaded'}', - ); - _log.info('Is Loading: ${_isLoading.value}'); - _log.info('Is Saving: ${_isSaving.value}'); - _log.info('===================================='); } } diff --git a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart index 86c2fc76..f10c65b9 100644 --- a/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart +++ b/school_data_hub_flutter/lib/features/school/domain/managers/school_data_ui_manager.dart @@ -143,12 +143,12 @@ class SchoolDataUiManager extends ChangeNotifier { /// Debug method to print current state void debugPrintState() { - _log.info('=== SchoolDataUiManager Debug State ==='); - _log.info('Form Data: ${_formData.value?.name ?? 'null'}'); - _log.info('Is Form Valid: ${_isFormValid.value}'); - _log.info('Is Form Dirty: ${_isFormDirty.value}'); - _log.info('Selected Logo File: ${_selectedLogoFile.value ?? 'none'}'); - _log.info('Selected Seal File: ${_selectedSealFile.value ?? 'none'}'); - _log.info('===================================='); + _log.info(''' + Form Data: ${_formData.value?.name ?? 'null'} + Is Form Valid: ${_isFormValid.value} + Is Form Dirty: ${_isFormDirty.value} + Selected Logo File: ${_selectedLogoFile.value ?? 'none'} + Selected Seal File: ${_selectedSealFile.value ?? 'none'} + '''); } } diff --git a/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart b/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart index 7dc6e420..34d1fa2b 100644 --- a/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart +++ b/school_data_hub_flutter/lib/features/timetable/domain/managers/timetable_data_manager.dart @@ -63,16 +63,15 @@ class TimetableDataManager extends ChangeNotifier { /// Debug method to print current state void debugPrintState() { - _log.info('=== TimetableDataManager Debug State ==='); _log.info( - 'Timetable: ${_timetable.value?.name} (ID: ${_timetable.value?.id})', + '''Timetable: ${_timetable.value?.name} (ID: ${_timetable.value?.id}) + TimetableSlots: ${_timetableSlots.value.length} + ScheduledLessons: ${_scheduledLessons.value.length} + Subjects: ${_subjects.value.length} + Classrooms: ${_classrooms.value.length} + LessonGroups: ${_lessonGroups.value.length} + ''', ); - _log.info('TimetableSlots: ${_timetableSlots.value.length}'); - _log.info('ScheduledLessons: ${_scheduledLessons.value.length}'); - _log.info('Subjects: ${_subjects.value.length}'); - _log.info('Classrooms: ${_classrooms.value.length}'); - _log.info('LessonGroups: ${_lessonGroups.value.length}'); - _log.info('===================================='); } void clearData() { From ed11829cd440351224830f87c67e56ff279c67d2 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Thu, 4 Dec 2025 00:58:54 +0100 Subject: [PATCH 48/52] fix pupil identity issues and pupil identity stream transfer --- .../pupil_identity/pupil_identity_dto.dart | 28 +++++ .../lib/app_utils/barcode_stream_scanner.dart | 2 +- .../lib/core/env/env_manager.dart | 3 + .../app_main_navigation/tools_page.dart | 34 +++--- .../pupil/data/pupil_data_api_service.dart | 9 ++ .../pupil_identity_helper_functions.dart | 88 ++++++--------- .../pupil/domain/pupil_identity_manager.dart | 105 +++++++++++------- .../controllers/stream_controller.dart | 34 ++++-- .../pupil_identity_dto.spy.yaml | 2 + .../pupil_identity/pupil_identity_dto.dart | 30 +++++ 10 files changed, 210 insertions(+), 125 deletions(-) diff --git a/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart b/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart index 7c80033d..76706478 100644 --- a/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart +++ b/school_data_hub_client/lib/src/protocol/_features/pupil/models/pupil_identity/pupil_identity_dto.dart @@ -13,37 +13,54 @@ import 'package:serverpod_client/serverpod_client.dart' as _i1; abstract class PupilIdentityDto implements _i1.SerializableModel { PupilIdentityDto._({ + required this.sender, required this.type, + this.dataTimeStamp, required this.value, }); factory PupilIdentityDto({ + required String sender, required String type, + DateTime? dataTimeStamp, required String value, }) = _PupilIdentityDtoImpl; factory PupilIdentityDto.fromJson(Map jsonSerialization) { return PupilIdentityDto( + sender: jsonSerialization['sender'] as String, type: jsonSerialization['type'] as String, + dataTimeStamp: jsonSerialization['dataTimeStamp'] == null + ? null + : _i1.DateTimeJsonExtension.fromJson( + jsonSerialization['dataTimeStamp']), value: jsonSerialization['value'] as String, ); } + String sender; + String type; + DateTime? dataTimeStamp; + String value; /// Returns a shallow copy of this [PupilIdentityDto] /// with some or all fields replaced by the given arguments. @_i1.useResult PupilIdentityDto copyWith({ + String? sender, String? type, + DateTime? dataTimeStamp, String? value, }); @override Map toJson() { return { + 'sender': sender, 'type': type, + if (dataTimeStamp != null) 'dataTimeStamp': dataTimeStamp?.toJson(), 'value': value, }; } @@ -54,12 +71,18 @@ abstract class PupilIdentityDto implements _i1.SerializableModel { } } +class _Undefined {} + class _PupilIdentityDtoImpl extends PupilIdentityDto { _PupilIdentityDtoImpl({ + required String sender, required String type, + DateTime? dataTimeStamp, required String value, }) : super._( + sender: sender, type: type, + dataTimeStamp: dataTimeStamp, value: value, ); @@ -68,11 +91,16 @@ class _PupilIdentityDtoImpl extends PupilIdentityDto { @_i1.useResult @override PupilIdentityDto copyWith({ + String? sender, String? type, + Object? dataTimeStamp = _Undefined, String? value, }) { return PupilIdentityDto( + sender: sender ?? this.sender, type: type ?? this.type, + dataTimeStamp: + dataTimeStamp is DateTime? ? dataTimeStamp : this.dataTimeStamp, value: value ?? this.value, ); } diff --git a/school_data_hub_flutter/lib/app_utils/barcode_stream_scanner.dart b/school_data_hub_flutter/lib/app_utils/barcode_stream_scanner.dart index 7e168046..87a430e0 100644 --- a/school_data_hub_flutter/lib/app_utils/barcode_stream_scanner.dart +++ b/school_data_hub_flutter/lib/app_utils/barcode_stream_scanner.dart @@ -100,7 +100,7 @@ class _BarcodeStreamScannerState extends State { onPressed: () { unawaited( _pupilIdentityManager - .decryptAndAddOrUpdatePupilIdentities( + .updatePupilIdentitiesFromEncryptedSource( _scannedQrCodes, ), ); diff --git a/school_data_hub_flutter/lib/core/env/env_manager.dart b/school_data_hub_flutter/lib/core/env/env_manager.dart index f732cf34..7af17d04 100644 --- a/school_data_hub_flutter/lib/core/env/env_manager.dart +++ b/school_data_hub_flutter/lib/core/env/env_manager.dart @@ -269,6 +269,9 @@ class EnvManager with ChangeNotifier { ); _environments[_activeEnv!.serverName] = _activeEnv!; notifyListeners(); + _log.info('''Active environment updated: ${_activeEnv!.serverName} + lastIdentitiesUpdate: ${_activeEnv!.lastIdentitiesUpdate} + '''); // The active environment changed, we need to update // this information in storage final updatedEnvsForStorage = EnvsInStorage( diff --git a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart index 6cc68bd1..9508859c 100644 --- a/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart +++ b/school_data_hub_flutter/lib/features/app_main_navigation/tools_page.dart @@ -10,7 +10,6 @@ import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart'; -import 'package:school_data_hub_flutter/common/widgets/qr/qr_image_picker.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_manager.dart'; import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; @@ -28,7 +27,7 @@ class ToolsPage extends WatchingWidget { PupilIdentityManager get _pupilIdentityManager => di(); HubSessionManager get _hubSessionManager => di(); - void importFileWithWindows(String function) async { + void importUnencryptedPupilIdentitySourceFile(String function) async { final fileContent = await pickFileReturnContentAsString(); FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result == null || fileContent == null) { @@ -37,24 +36,16 @@ class ToolsPage extends WatchingWidget { } if (function == 'update_backend') { - _pupilIdentityManager.updateBackendPupilsFromSchoolPupilIdentitySource( + _pupilIdentityManager.updateBackendPupilsWithSchoolPupilIdentitySource( fileContent, ); } else if (function == 'pupil_identities') { - _pupilIdentityManager.addOrUpdateNewPupilIdentities( + _pupilIdentityManager.updatePupilIdentitiesFromUnencryptedSource( identitiesInStringLines: fileContent, ); } } - void importFromQrImage() async { - final rawTextResult = await scanPickedQrImage(); - if (rawTextResult == null) { - return; - } - _pupilIdentityManager.decryptAndAddOrUpdatePupilIdentities([rawTextResult]); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -133,7 +124,7 @@ class ToolsPage extends WatchingWidget { } : null, icon: Icons.qr_code_scanner_rounded, - title: 'Schülerdaten aus einem anderen Gerät importieren', + title: 'Schüler-Ids aus einem anderen Gerät importieren', subtitle: 'QR-Code scannen oder Verbindungscode eingeben', color: Colors.blue[700]!, ), @@ -158,7 +149,9 @@ class ToolsPage extends WatchingWidget { .getInternalIdsFromPupilIds(pupilIds); final String encryptedPupilIdentities = await di() - .generatePupilIdentitiesQrData(internalIds); + .generateEncryptedPupilIdentitiesTransferString( + internalIds, + ); if (!context.mounted) return; Navigator.of(context).push( MaterialPageRoute( @@ -171,7 +164,7 @@ class ToolsPage extends WatchingWidget { ); }, icon: Icons.mobile_screen_share, - title: 'Schüler-Ids mit einem anderen Gerät teilen', + title: 'Schüler-Ids teilen', subtitle: 'Daten auf ein anderes Gerät übertragen', color: Colors.indigo[700]!, ), @@ -251,8 +244,6 @@ class ToolsPage extends WatchingWidget { ), const Gap(16), - - const Gap(16), ], // Desktop-only section @@ -268,7 +259,9 @@ class ToolsPage extends WatchingWidget { 'Achtung! Nicht mehr vorhandene SchülerInnen auf dem Server werden gelöscht. Fortfahren?', ); if (confirm == true) { - importFileWithWindows('update_backend'); + importUnencryptedPupilIdentitySourceFile( + 'update_backend', + ); } }, icon: Icons.school, @@ -280,8 +273,9 @@ class ToolsPage extends WatchingWidget { const Gap(16), _buildToolButton( - onPressed: () => - importFileWithWindows('pupil_identities'), + onPressed: () => importUnencryptedPupilIdentitySourceFile( + 'pupil_identities', + ), icon: Icons.people, title: 'ID-Liste importieren', subtitle: 'Schüler-Identitäten aus Datei importieren', diff --git a/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart b/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart index 4a18e3c7..7a3108eb 100644 --- a/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart +++ b/school_data_hub_flutter/lib/features/pupil/data/pupil_data_api_service.dart @@ -275,4 +275,13 @@ class PupilDataApiService { ); return lastUpdate; } + + Future updateLastIdentitiesUpdate(DateTime date) async { + final updated = await ClientHelper.apiCall( + call: () => _client.pupilIdentity.updateLastPupilIdentitiesUpdate(date), + errorMessage: + 'Die letzte Aktualisierung konnte nicht aktualisiert werden', + ); + return updated; + } } diff --git a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart index eb96039c..ba644e2b 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_helper_functions.dart @@ -12,17 +12,6 @@ import 'package:watch_it/watch_it.dart'; final _log = Logger('PupilIdentityHelper'); class PupilIdentityHelper { - //- TIMEZONE CONVERSION HELPERS - - /// Converts a DateTime string from Berlin timezone (UTC+1) to UTC. - /// Returns null if the dateString is null or cannot be parsed. - static DateTime? _convertBerlinDateStringToUtc(String? dateString) { - if (dateString == null) return null; - final berlinDate = DateTime.tryParse(dateString); - if (berlinDate == null) return null; - return berlinDate.subtract(Duration(hours: 1)); - } - //- LOCAL STORAGE HELPERS static Future> readPupilIdentitiesFromStorage({ @@ -46,25 +35,25 @@ class PupilIdentityHelper { ); // Convert all DateTime fields from Berlin timezone to UTC - final dateTimeFields = [ - 'birthday', - 'migrationSupportEnds', - 'pupilSince', - 'religionLessonsSince', - 'familyLanguageLessonsSince', - 'leavingDate', - ]; - - for (final field in dateTimeFields) { - if (jsonData[field] != null) { - final convertedDate = _convertBerlinDateStringToUtc( - jsonData[field] as String?, - ); - if (convertedDate != null) { - jsonData[field] = convertedDate.toIso8601String(); - } - } - } + // final dateTimeFields = [ + // 'birthday', + // 'migrationSupportEnds', + // 'pupilSince', + // 'religionLessonsSince', + // 'familyLanguageLessonsSince', + // 'leavingDate', + // ]; + + // for (final field in dateTimeFields) { + // if (jsonData[field] != null) { + // final convertedDate = _convertBerlinDateStringToUtc( + // jsonData[field] as String?, + // ); + // if (convertedDate != null) { + // jsonData[field] = convertedDate.toIso8601String(); + // } + // } + // } final PupilIdentity pupilIdentity = PupilIdentity.fromJson(jsonData); @@ -118,12 +107,7 @@ class PupilIdentityHelper { default: throw Exception('Unknown school grade: ${pupilIdentityStringItems[5]}'); } - // _log.info( - // 'Decoding pupil identity from string list: $pupilIdentityStringItems', - // ); - // if (pupilIdentityStringItems[0] == '1668') { - // debugger(); - // } + final newPupilIdentity = PupilIdentity( id: int.parse(pupilIdentityStringItems[0]), firstName: pupilIdentityStringItems[1], @@ -136,41 +120,33 @@ class PupilIdentityHelper { specialNeeds: pupilIdentityStringItems[6] == '' ? null : '${pupilIdentityStringItems[6]}${pupilIdentityStringItems[7]}', + // gender: pupilIdentityStringItems[8], language: pupilIdentityStringItems[9], family: pupilIdentityStringItems[10] == '' ? null : pupilIdentityStringItems[10], - birthday: DateTime.tryParse( - pupilIdentityStringItems[11], - )!.subtract(const Duration(hours: 1)), + birthday: DateTime.parse(pupilIdentityStringItems[11]), migrationSupportEnds: pupilIdentityStringItems[12] == '' ? null - : DateTime.tryParse( - pupilIdentityStringItems[12], - )!.subtract(const Duration(hours: 1)), - pupilSince: DateTime.tryParse( - pupilIdentityStringItems[13], - )!.subtract(const Duration(hours: 1)), + : DateTime.tryParse(pupilIdentityStringItems[12]), + pupilSince: DateTime.tryParse(pupilIdentityStringItems[13])!, afterSchoolCare: pupilIdentityStringItems[14] != '' ? true : false, religion: pupilIdentityStringItems[15] == '' ? null : pupilIdentityStringItems[15], religionLessonsSince: pupilIdentityStringItems[16] == '' ? null - : DateTime.tryParse( - pupilIdentityStringItems[16], - )!.subtract(const Duration(hours: 1)), - familyLanguageLessonsSince: pupilIdentityStringItems[17] == '' + : DateTime.tryParse(pupilIdentityStringItems[16])!, + religionLessonsCancelledAt: pupilIdentityStringItems[17] == '' + ? null + : DateTime.tryParse(pupilIdentityStringItems[17])!, + familyLanguageLessonsSince: pupilIdentityStringItems[18] == '' ? null - : DateTime.tryParse( - pupilIdentityStringItems[17], - )!.subtract(const Duration(hours: 1)), - leavingDate: pupilIdentityStringItems[18] == '' + : DateTime.tryParse(pupilIdentityStringItems[18])!, + leavingDate: pupilIdentityStringItems[19] == '' ? null - : DateTime.tryParse( - pupilIdentityStringItems[19], - )?.subtract(const Duration(hours: 1)), + : DateTime.tryParse(pupilIdentityStringItems[19]), ); return newPupilIdentity; } diff --git a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart index eadb897f..1ecf2cfa 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart @@ -10,7 +10,6 @@ import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions import 'package:school_data_hub_flutter/app_utils/secure_storage.dart'; import 'package:school_data_hub_flutter/common/data/file_upload_service.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; -import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:school_data_hub_flutter/core/env/env_manager.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/app_main_navigation/domain/main_menu_bottom_nav_manager.dart'; @@ -22,8 +21,6 @@ import 'package:school_data_hub_flutter/features/pupil/domain/pupil_identity_hel import 'package:school_data_hub_flutter/features/pupil/domain/pupil_manager.dart'; import 'package:watch_it/watch_it.dart'; -final _envManager = di(); - enum PupilIdentityStreamRole { sender, receiver } class PupilIdentityManager { @@ -33,7 +30,7 @@ class PupilIdentityManager { final _log = Logger('PupilIdentityManager'); - final secureStorageKey = _envManager.storageKeyForPupilIdentities; + final secureStorageKey = di().storageKeyForPupilIdentities; Map _pupilIdentities = {}; @@ -85,13 +82,13 @@ class PupilIdentityManager { await getPupilIdentitiesForEnv(); _log.info( - 'PupilIdentityManager re-initialized for environment: ${_envManager.activeEnv?.serverName}', + 'PupilIdentityManager re-initialized for environment: ${di().activeEnv?.serverName}', ); } /// Checks if the manager is ready for the current environment bool get isReadyForCurrentEnvironment { - return _envManager.activeEnv != null && _pupilIdentities.isNotEmpty; + return di().activeEnv != null && _pupilIdentities.isNotEmpty; } void dispose() { @@ -114,7 +111,7 @@ class PupilIdentityManager { } Future getPupilIdentitiesForEnv() async { - final activeEnv = _envManager.activeEnv!; + final activeEnv = di().activeEnv!; final Map pupilIdentities = await PupilIdentityHelper.readPupilIdentitiesFromStorage( @@ -154,7 +151,7 @@ class PupilIdentityManager { return; } - Future decryptAndAddOrUpdatePupilIdentities( + Future updatePupilIdentitiesFromEncryptedSource( List encryptedCodes, ) async { // We need the decrypted information as a string with line breaks. @@ -166,10 +163,12 @@ class PupilIdentityManager { decryptedString += '${customEncrypter.decryptString(code)}\n'; } - addOrUpdateNewPupilIdentities(identitiesInStringLines: decryptedString); + updatePupilIdentitiesFromUnencryptedSource( + identitiesInStringLines: decryptedString, + ); } - Future addOrUpdateNewPupilIdentities({ + Future updatePupilIdentitiesFromUnencryptedSource({ required String identitiesInStringLines, }) async { late final String? decryptedIdentitiesAsString; @@ -215,7 +214,7 @@ class PupilIdentityManager { _groups.value = availableGroups; di().populateGroupFilters(availableGroups.toList()); } - await _envManager.updateActiveEnv( + await di().updateActiveEnv( lastIdentitiesUpdate: DateTime.now().toUtc(), ); di().fetchAllPupils(); @@ -237,7 +236,7 @@ class PupilIdentityManager { ); } - Future updateBackendPupilsFromSchoolPupilIdentitySource( + Future updateBackendPupilsWithSchoolPupilIdentitySource( String textFileContent, ) async { // The pupils in the string are separated by a line break - let's split them out @@ -307,17 +306,15 @@ class PupilIdentityManager { } await writePupilIdentitiesToStorage(); - await _envManager.updateActiveEnv( - lastIdentitiesUpdate: DateTime.now().toUtc(), + // This is the new reference data for all the clients to adopt. + // We need to update the active environment and the backend with the new last identities update. + final newLastIdentitiesUpdate = DateTime.now().toUtc(); + await di().updateActiveEnv( + lastIdentitiesUpdate: newLastIdentitiesUpdate, ); - - await ClientHelper.apiCall( - call: () => di().pupilIdentity.updateLastPupilIdentitiesUpdate( - DateTime.now().toUtc(), - ), - errorMessage: 'Die letzte Aktualisierung konnte nicht gespeichert werden', + await PupilDataApiService().updateLastIdentitiesUpdate( + newLastIdentitiesUpdate, ); - await di().fetchAllPupils(); _notificationService.showSnackBar( @@ -345,12 +342,12 @@ class PupilIdentityManager { ); } - Future generatePupilIdentitiesQrData(List internalIds) async { - String qrString = ''; + Future generateEncryptedPupilIdentitiesTransferString( + List internalIds, + ) async { + String transferString = ''; for (int internalId in internalIds) { - PupilIdentity pupilIdentity = _pupilIdentities.values - .where((element) => element.id == internalId) - .single; + PupilIdentity pupilIdentity = _pupilIdentities[internalId]!; final migrationSupportEnds = pupilIdentity.migrationSupportEnds != null ? pupilIdentity.migrationSupportEnds!.formatDateForJson() : ''; @@ -374,16 +371,17 @@ class PupilIdentityManager { pupilIdentity.birthday.formatDateForJson(), migrationSupportEnds, pupilIdentity.pupilSince.formatDateForJson(), - pupilIdentity.afterSchoolCare, + pupilIdentity.afterSchoolCare ? 'OFFGANZ' : '', pupilIdentity.religion ?? '', pupilIdentity.religionLessonsSince?.formatDateForJson() ?? '', + pupilIdentity.religionLessonsCancelledAt?.formatDateForJson() ?? '', pupilIdentity.familyLanguageLessonsSince?.formatDateForJson() ?? '', pupilIdentity.leavingDate?.formatDateForJson() ?? '', ].join(',') + ',\n'; - qrString = qrString + pupilIdentityString; + transferString = transferString + pupilIdentityString; } - final encryptedString = customEncrypter.encryptString(qrString); + final encryptedString = customEncrypter.encryptString(transferString); return encryptedString; } @@ -440,7 +438,7 @@ class PupilIdentityManager { .streamEncryptedPupilIds(channelName) .listen( (PupilIdentityDto event) async { - _log.info('Received event: [${event.type}] '); + _log.info('Received event: [${event.type}] from ${event.sender}'); switch (role) { //- Stream behavior for sender role @@ -453,22 +451,22 @@ class PupilIdentityManager { onReceiverJoined(event.value); } onStatusUpdate( - 'Empfänger ${event.value} ist der Übertragung beigetreten.', + 'Empfänger ${event.sender} ist der Übertragung beigetreten.', ); break; case 'request': // Receiver requests data if (onRequestReceived != null) { - onRequestReceived(event.value); + onRequestReceived(event.sender); } onStatusUpdate( - 'Empfänger ${event.value} hat Daten angefordert. Warten auf Bestätigung...', + 'Empfänger ${event.sender} hat Daten angefordert. Warten auf Bestätigung...', ); break; case 'confirmed': // Check if confirmation is for a specific user - final targetUser = event.value.isNotEmpty - ? event.value + final targetUser = event.sender.isNotEmpty + ? event.sender : null; if (targetUser == null) { // Legacy: no targeting, proceed for any receiver @@ -479,6 +477,10 @@ class PupilIdentityManager { await _client.pupilIdentity.sendPupilIdentityMessage( channelName, PupilIdentityDto( + sender: + di().user!.userInfo!.userName!, + dataTimeStamp: + di().activeEnv?.lastIdentitiesUpdate, type: 'data', value: encryptedPupilIds ?? '', ), @@ -495,7 +497,11 @@ class PupilIdentityManager { await _client.pupilIdentity.sendPupilIdentityMessage( channelName, PupilIdentityDto( + sender: + di().user!.userInfo!.userName!, type: 'data', + dataTimeStamp: + di().activeEnv?.lastIdentitiesUpdate, value: '$targetUser:${encryptedPupilIds ?? ''}', ), ); @@ -587,19 +593,42 @@ class PupilIdentityManager { 'Verschlüsselte Schülerdaten empfangen. Verarbeite...', ); _log.info( - 'Received encrypted pupil identities: ${actualData.length} characters', + 'Received encrypted pupil identities dated on ${event.dataTimeStamp}.', ); try { final beforeCount = _pupilIdentities.length; - await decryptAndAddOrUpdatePupilIdentities([actualData]); + if (event.dataTimeStamp!.isBefore( + di().activeEnv!.lastIdentitiesUpdate!, + )) { + _notificationService.showInformationDialog( + 'Die empfangenen Schülerdaten sind veraltet. Die neueste Version ist vom ${di().activeEnv!.lastIdentitiesUpdate!.formatDateForUser()}.', + ); + return; + } + _log.info('''Received newer pupil identities: + Timestamp: ${event.dataTimeStamp} + Last identities update: ${di().activeEnv!.lastIdentitiesUpdate} + '''); + await updatePupilIdentitiesFromEncryptedSource([ + actualData, + ]); final afterCount = _pupilIdentities.length; final newCount = afterCount - beforeCount; + // Set the last identities update to the received data time stamp + di().updateActiveEnv( + lastIdentitiesUpdate: event.dataTimeStamp, + ); // Send confirmation await _client.pupilIdentity.sendPupilIdentityMessage( channelName, - PupilIdentityDto(type: 'ok', value: ''), + PupilIdentityDto( + sender: + di().user!.userInfo!.userName!, + type: 'ok', + value: '', + ), ); onCompleted(); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart index 125546cf..0d8f2294 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart @@ -11,7 +11,6 @@ import 'package:school_data_hub_flutter/features/pupil/presentation/pupil_identi import 'package:watch_it/watch_it.dart'; final _log = Logger('StreamController'); -final _notificationService = di(); class PupilIdentityStreamController { final PupilIdentityStreamRole role; @@ -20,6 +19,10 @@ class PupilIdentityStreamController { final String? importedChannelName; late String channelName; + final String thisUserName = di().user!.userInfo!.userName!; + + final _notificationService = di(); + // Controller creates and owns the state late final PupilIdentityStreamState state; StreamSubscription? _subscription; @@ -121,7 +124,11 @@ class PupilIdentityStreamController { // Send confirmation to specific receiver await di().pupilIdentity.sendPupilIdentityMessage( channelName, - PupilIdentityDto(type: 'confirmed', value: userName), + PupilIdentityDto( + sender: thisUserName, + type: 'confirmed', + value: userName, + ), ); } @@ -172,7 +179,11 @@ class PupilIdentityStreamController { ); await di().pupilIdentity.sendPupilIdentityMessage( channelName, - PupilIdentityDto(type: 'rejected', value: rejectionValue), + PupilIdentityDto( + sender: thisUserName, + type: 'rejected', + value: rejectionValue, + ), ); _log.info('Rejection message sent successfully to $userName'); return; // Success, exit retry loop @@ -212,7 +223,7 @@ class PupilIdentityStreamController { 'Generating encrypted data for ${selectedPupilIds!.length} pupils', ); dataToSend = await di() - .generatePupilIdentitiesQrData(selectedPupilIds!); + .generateEncryptedPupilIdentitiesTransferString(selectedPupilIds!); } _log.info('Creating stream subscription...'); @@ -268,8 +279,9 @@ class PupilIdentityStreamController { .sendPupilIdentityMessage( channelName, PupilIdentityDto( + sender: thisUserName, type: 'joined', - value: di().user!.userInfo!.userName!, + value: thisUserName, ), ) .then((_) { @@ -293,8 +305,9 @@ class PupilIdentityStreamController { return di().pupilIdentity.sendPupilIdentityMessage( channelName, PupilIdentityDto( + sender: thisUserName, type: 'request', - value: di().user!.userInfo!.userName!, + value: thisUserName, ), ); }) @@ -505,9 +518,9 @@ class PupilIdentityStreamController { .sendPupilIdentityMessage( channelName, PupilIdentityDto( + sender: thisUserName, type: 'close', - value: - di().user?.userInfo?.userName ?? 'Unknown', + value: thisUserName, ), ) .ignore(); @@ -532,6 +545,7 @@ class PupilIdentityStreamController { await di().pupilIdentity.sendPupilIdentityMessage( channelName, PupilIdentityDto( + sender: thisUserName, type: 'shutdown', value: 'Sender hat den Stream beendet', ), @@ -552,9 +566,9 @@ class PupilIdentityStreamController { await di().pupilIdentity.sendPupilIdentityMessage( channelName, PupilIdentityDto( + sender: thisUserName, type: 'close', - value: - di().user?.userInfo?.userName ?? 'Unknown', + value: thisUserName, ), ); _log.info('Sent close message to sender before leaving'); diff --git a/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity_dto.spy.yaml b/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity_dto.spy.yaml index 26bf3c8e..782e3151 100644 --- a/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity_dto.spy.yaml +++ b/school_data_hub_server/lib/src/_features/pupil/models/pupil_identity/pupil_identity_dto.spy.yaml @@ -1,4 +1,6 @@ class: PupilIdentityDto fields: + sender: String type: String + dataTimeStamp: DateTime? value: String diff --git a/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart b/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart index 1912ad9a..a39057fd 100644 --- a/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart +++ b/school_data_hub_server/lib/src/generated/_features/pupil/models/pupil_identity/pupil_identity_dto.dart @@ -14,37 +14,54 @@ import 'package:serverpod/serverpod.dart' as _i1; abstract class PupilIdentityDto implements _i1.SerializableModel, _i1.ProtocolSerialization { PupilIdentityDto._({ + required this.sender, required this.type, + this.dataTimeStamp, required this.value, }); factory PupilIdentityDto({ + required String sender, required String type, + DateTime? dataTimeStamp, required String value, }) = _PupilIdentityDtoImpl; factory PupilIdentityDto.fromJson(Map jsonSerialization) { return PupilIdentityDto( + sender: jsonSerialization['sender'] as String, type: jsonSerialization['type'] as String, + dataTimeStamp: jsonSerialization['dataTimeStamp'] == null + ? null + : _i1.DateTimeJsonExtension.fromJson( + jsonSerialization['dataTimeStamp']), value: jsonSerialization['value'] as String, ); } + String sender; + String type; + DateTime? dataTimeStamp; + String value; /// Returns a shallow copy of this [PupilIdentityDto] /// with some or all fields replaced by the given arguments. @_i1.useResult PupilIdentityDto copyWith({ + String? sender, String? type, + DateTime? dataTimeStamp, String? value, }); @override Map toJson() { return { + 'sender': sender, 'type': type, + if (dataTimeStamp != null) 'dataTimeStamp': dataTimeStamp?.toJson(), 'value': value, }; } @@ -52,7 +69,9 @@ abstract class PupilIdentityDto @override Map toJsonForProtocol() { return { + 'sender': sender, 'type': type, + if (dataTimeStamp != null) 'dataTimeStamp': dataTimeStamp?.toJson(), 'value': value, }; } @@ -63,12 +82,18 @@ abstract class PupilIdentityDto } } +class _Undefined {} + class _PupilIdentityDtoImpl extends PupilIdentityDto { _PupilIdentityDtoImpl({ + required String sender, required String type, + DateTime? dataTimeStamp, required String value, }) : super._( + sender: sender, type: type, + dataTimeStamp: dataTimeStamp, value: value, ); @@ -77,11 +102,16 @@ class _PupilIdentityDtoImpl extends PupilIdentityDto { @_i1.useResult @override PupilIdentityDto copyWith({ + String? sender, String? type, + Object? dataTimeStamp = _Undefined, String? value, }) { return PupilIdentityDto( + sender: sender ?? this.sender, type: type ?? this.type, + dataTimeStamp: + dataTimeStamp is DateTime? ? dataTimeStamp : this.dataTimeStamp, value: value ?? this.value, ); } From 6eca1ec540b3f6bc7e66fb3f502a4c6eaa49cbc1 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Thu, 4 Dec 2025 02:19:07 +0100 Subject: [PATCH 49/52] fix pupil identity stream --- .../pupil/domain/pupil_identity_manager.dart | 50 +++++++++++++------ .../controllers/stream_controller.dart | 6 +++ .../widgets/dialogs/rejection_dialog.dart | 4 +- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart index 1ecf2cfa..3aee26c6 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart @@ -439,7 +439,7 @@ class PupilIdentityManager { .listen( (PupilIdentityDto event) async { _log.info('Received event: [${event.type}] from ${event.sender}'); - + final eventSender = event.sender; switch (role) { //- Stream behavior for sender role case PupilIdentityStreamRole.sender: @@ -448,25 +448,25 @@ class PupilIdentityManager { case 'joined': // Receiver joined the stream if (onReceiverJoined != null) { - onReceiverJoined(event.value); + onReceiverJoined(eventSender); } onStatusUpdate( - 'Empfänger ${event.sender} ist der Übertragung beigetreten.', + 'Empfänger $eventSender ist der Übertragung beigetreten.', ); break; case 'request': // Receiver requests data if (onRequestReceived != null) { - onRequestReceived(event.sender); + onRequestReceived(eventSender); } onStatusUpdate( - 'Empfänger ${event.sender} hat Daten angefordert. Warten auf Bestätigung...', + 'Empfänger $eventSender hat Daten angefordert. Warten auf Bestätigung...', ); break; case 'confirmed': // Check if confirmation is for a specific user - final targetUser = event.sender.isNotEmpty - ? event.sender + final targetUser = event.value.isNotEmpty + ? event.value : null; if (targetUser == null) { // Legacy: no targeting, proceed for any receiver @@ -598,18 +598,40 @@ class PupilIdentityManager { try { final beforeCount = _pupilIdentities.length; - if (event.dataTimeStamp!.isBefore( + final dataUpdateIsUpToDate = event.dataTimeStamp!.isAfter( di().activeEnv!.lastIdentitiesUpdate!, - )) { - _notificationService.showInformationDialog( - 'Die empfangenen Schülerdaten sind veraltet. Die neueste Version ist vom ${di().activeEnv!.lastIdentitiesUpdate!.formatDateForUser()}.', - ); - return; - } + ); + _log.info('''Received newer pupil identities: Timestamp: ${event.dataTimeStamp} Last identities update: ${di().activeEnv!.lastIdentitiesUpdate} '''); + if (!dataUpdateIsUpToDate) { + // Send confirmation + await _client.pupilIdentity.sendPupilIdentityMessage( + channelName, + PupilIdentityDto( + sender: di() + .user! + .userInfo! + .userName!, + type: 'ok', + value: '', + ), + ); + + onCompleted(); + onStatusUpdate('Schülerdaten sind veraltet!'); + _encryptedPupilIdsSubscription!.cancel(); + if (onRequestRejected != null) { + onRequestRejected(false); + } + _notificationService.showInformationDialog( + 'Schülerdaten sind veraltet und wurden nicht verarbeitet.', + ); + + return; + } await updatePupilIdentitiesFromEncryptedSource([ actualData, ]); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart index 0d8f2294..4fada51e 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/controllers/stream_controller.dart @@ -62,6 +62,12 @@ class PupilIdentityStreamController { state.streamState.isTransmitting.value = false; _notificationService.setHeavyLoadingValue(false); + if (role == PupilIdentityStreamRole.receiver) { + state.streamState.receiverJoined.value = false; + state.streamState.requestSent.value = false; + state.streamState.isConnected.value = false; + } + // For sender, record the successful transfer if (role == PupilIdentityStreamRole.sender && state.streamState.receiverUserName.value.isNotEmpty) { diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/dialogs/rejection_dialog.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/dialogs/rejection_dialog.dart index 34718be7..5b5c58e9 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/dialogs/rejection_dialog.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_identity_stream_page/widgets/dialogs/rejection_dialog.dart @@ -25,8 +25,8 @@ class RejectionDialog extends StatelessWidget { title: const Text('Anfrage abgelehnt'), content: Text( wasAutoRejected - ? 'Der Sender hat Ihre Anfrage bereits zuvor abgelehnt. Sie können diesem Stream nicht mehr beitreten.' - : 'Der Sender hat Ihre Anfrage für Schülerdaten abgelehnt.', + ? 'Der Sender hat Ihre Anfrage bereits zuvor abgelehnt. Sie können diesem Stream nicht beitreten.' + : 'Der Sender hat Ihre Anfrage abgelehnt.', ), actions: [ ElevatedButton( From 525b8d5a095fea941572657c43a729076b1e0c48 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Thu, 4 Dec 2025 09:08:13 +0100 Subject: [PATCH 50/52] [APP] Matrix contacts: fix card color --- .../pupils_matrix_contacts_list_page.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart b/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart index 4cdc1baa..56f4fb70 100644 --- a/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart +++ b/school_data_hub_flutter/lib/features/matrix/users/presentation/pupil_matrix_contacts_list_page/pupils_matrix_contacts_list_page.dart @@ -51,9 +51,7 @@ class PupilsMatrixContactsListPage extends WatchingWidget { final missingContacts = pupil.tutorInfo?.parentsContact == null || pupil.contact == null; - final subtleBorder = AppColors.backgroundColor.withValues( - alpha: 102, - ); + final subtleBorder = AppColors.canvasColor; return Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, @@ -195,6 +193,7 @@ class PupilsMatrixContactsListPage extends WatchingWidget { ), const Gap(10), IconButton( + iconSize: 18, icon: const Icon(Icons.copy), onPressed: () { if (pupil.contact == null) { @@ -339,6 +338,7 @@ class PupilsMatrixContactsListPage extends WatchingWidget { ), const Gap(10), IconButton( + iconSize: 18, icon: const Icon(Icons.copy), onPressed: () { if (pupil From ea374132514c256adc1bc1b33ac7e09d5463d42c Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Thu, 4 Dec 2025 09:09:47 +0100 Subject: [PATCH 51/52] [APP][SERVER]: Fix date issues --- .../extensions/datetime_extensions.dart | 2 +- .../data/attendance_api_service.dart | 34 +++++++++++-------- .../pupil/presentation/birthdays_page.dart | 2 +- .../widgets/attendance_stats_view.dart | 24 ++++++++++++- .../endpoints/schoolday_admin_endpoint.dart | 12 ++++--- .../lib/src/helpers/date_extension.dart | 29 ++++++++++++++++ 6 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 school_data_hub_server/lib/src/helpers/date_extension.dart diff --git a/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart b/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart index 186cf13e..4fd80ecf 100644 --- a/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart +++ b/school_data_hub_flutter/lib/app_utils/extensions/datetime_extensions.dart @@ -23,7 +23,7 @@ extension DateHubExtension on DateTime { } String formatDateForUser() { - final date = this.toLocal().add(const Duration(hours: 2)); + final date = this.toLocal(); final DateFormat dateFormat = DateFormat("dd.MM.yyyy"); return dateFormat.format(date).toString(); } diff --git a/school_data_hub_flutter/lib/features/_attendance/data/attendance_api_service.dart b/school_data_hub_flutter/lib/features/_attendance/data/attendance_api_service.dart index b00dfbee..dd91e93f 100644 --- a/school_data_hub_flutter/lib/features/_attendance/data/attendance_api_service.dart +++ b/school_data_hub_flutter/lib/features/_attendance/data/attendance_api_service.dart @@ -14,7 +14,7 @@ class AttendanceApiService { final _notificationService = di(); -//- fetch all missed schooldays -// + //- fetch all missed schooldays -// Future?> fetchAllMissedSchooldayes() async { final missedSchooldays = await ClientHelper.apiCall( @@ -28,7 +28,8 @@ class AttendanceApiService { //- fetch missed schooldays for a date -// Future?> fetchMissedSchooldayesOnASchoolday( - DateTime schoolday) async { + DateTime schoolday, + ) async { final missedSchooldays = await ClientHelper.apiCall( call: () => _client.missedSchoolday.fetchMissedSchooldaysOnASchoolday(schoolday), @@ -54,14 +55,17 @@ class AttendanceApiService { final schoolday = _schoolCalendarManager.getSchooldayByDate(date); final MissedSchoolday missedSchoolday = MissedSchoolday( - missedType: missedType, - unexcused: unexcused ?? false, - contacted: contactedType ?? ContactedType.notSet, - returned: returned ?? false, - writtenExcuse: writtenExcuse ?? false, - createdBy: _session.signedInUser!.userName!, - schooldayId: schoolday!.id!, - pupilId: pupilId); + missedType: missedType, + unexcused: unexcused ?? false, + contacted: contactedType ?? ContactedType.notSet, + returned: returned ?? false, + minutesLate: minutesLate ?? null, + returnedAt: returnedAt ?? null, + writtenExcuse: writtenExcuse ?? false, + createdBy: _session.signedInUser!.userName!, + schooldayId: schoolday!.id!, + pupilId: pupilId, + ); final missedSchooldayResponse = await ClientHelper.apiCall( call: () => _client.missedSchoolday.postMissedSchoolday(missedSchoolday), @@ -73,8 +77,9 @@ class AttendanceApiService { //- post a list of missed schooldays -// - Future?> postMissedSchooldayList( - {required List missedSchooldays}) async { + Future?> postMissedSchooldayList({ + required List missedSchooldays, + }) async { final missedSchooldaysList = await ClientHelper.apiCall( call: () => _client.missedSchoolday.postMissedSchooldays(missedSchooldays), @@ -90,8 +95,9 @@ class AttendanceApiService { required MissedSchoolday missedSchooldayToUpdate, }) async { final missedSchoolday = await ClientHelper.apiCall( - call: () => _client.missedSchoolday - .updateMissedSchoolday(missedSchooldayToUpdate), + call: () => _client.missedSchoolday.updateMissedSchoolday( + missedSchooldayToUpdate, + ), errorMessage: 'Fehler beim Aktualisieren der Fehlzeit', ); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/birthdays_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/birthdays_page.dart index 5eb35cda..7cc48552 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/birthdays_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/birthdays_page.dart @@ -84,7 +84,7 @@ class BirthdaysView extends StatelessWidget { children: [ const Gap(5), Text( - '${DateTime(DateTime.now().year, listedPupil.birthday.month, listedPupil.birthday.day).asWeekdayName(context)}, ${listedPupil.birthday.formatDateForUser()}', + '${DateTime(DateTime.now().year, listedPupil.birthday.month, listedPupil.birthday.day).asWeekdayName(context)}, ${DateTime(DateTime.now().year, listedPupil.birthday.month, listedPupil.birthday.day).formatDateForUser()}', style: const TextStyle( fontWeight: FontWeight.bold, color: diff --git a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart index 76f85b6b..5e1d213f 100644 --- a/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart +++ b/school_data_hub_flutter/lib/features/statistics/chart_page/widgets/attendance_stats_view.dart @@ -5,6 +5,8 @@ import 'package:gap/gap.dart'; import 'package:intl/intl.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; import 'package:school_data_hub_flutter/common/theme/styles.dart'; +import 'package:school_data_hub_flutter/features/_attendance/domain/filters/attendance_pupil_filter.dart'; +import 'package:school_data_hub_flutter/features/_attendance/domain/models/enums.dart'; import 'package:school_data_hub_flutter/features/_attendance/presentation/attendance_page/attendance_list_page.dart'; import 'package:school_data_hub_flutter/features/school_calendar/domain/school_calendar_manager.dart'; import 'package:school_data_hub_flutter/features/statistics/chart_page/chart_page.dart'; @@ -45,13 +47,33 @@ class AttendanceStatsView extends WatchingWidget { showDialog( context: context, builder: (context) => AlertDialog( - title: const Text('Details'), + title: const Text('Details', style: AppStyles.title), content: Text(buffer.toString()), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); di().setThisDate(chartData.date); + di().setAttendancePupilFilter( + attendancePupilFilterRecords: [ + ( + attendancePupilFilter: AttendancePupilFilter.missed, + value: true, + ), + ( + attendancePupilFilter: AttendancePupilFilter.notPresent, + value: true, + ), + ( + attendancePupilFilter: AttendancePupilFilter.unexcused, + value: false, + ), + ( + attendancePupilFilter: AttendancePupilFilter.goneHome, + value: true, + ), + ], + ); Navigator.of(context).push( MaterialPageRoute( builder: (context) => const AttendanceListPage(), diff --git a/school_data_hub_server/lib/src/_features/schoolday/endpoints/schoolday_admin_endpoint.dart b/school_data_hub_server/lib/src/_features/schoolday/endpoints/schoolday_admin_endpoint.dart index 5b90730b..30a1c855 100644 --- a/school_data_hub_server/lib/src/_features/schoolday/endpoints/schoolday_admin_endpoint.dart +++ b/school_data_hub_server/lib/src/_features/schoolday/endpoints/schoolday_admin_endpoint.dart @@ -1,4 +1,5 @@ import 'package:collection/collection.dart'; +import 'package:school_data_hub_server/src/helpers/date_extension.dart'; import 'package:school_data_hub_server/src/generated/protocol.dart'; import 'package:serverpod/serverpod.dart'; @@ -61,9 +62,10 @@ class SchooldayAdminEndpoint extends Endpoint { } Future createSchoolday(Session session, DateTime date) async { + final newDate = date.justDate(); final schoolSemester = await SchoolSemester.db.findFirstRow( session, - where: (t) => (t.startDate <= date) & (t.endDate >= date), + where: (t) => (t.startDate <= newDate) & (t.endDate >= newDate), ); if (schoolSemester == null) { @@ -71,7 +73,7 @@ class SchooldayAdminEndpoint extends Endpoint { } final schoolday = Schoolday( schoolSemesterId: schoolSemester.id!, - schoolday: date, + schoolday: newDate, ); await session.db.insertRow(schoolday); @@ -88,8 +90,8 @@ class SchooldayAdminEndpoint extends Endpoint { for (final date in dates) { final schoolSemester = allSemesters.firstWhereOrNull((t) => - (t.startDate.isBefore(date) || t.startDate.isAtSameMomentAs(date)) && - (t.endDate.isAfter(date) || t.endDate.isAtSameMomentAs(date))); + (t.startDate.isBeforeDay(date) || t.startDate.isSameDay(date)) && + (t.endDate.isAfterDay(date) || t.endDate.isSameDay(date))); if (schoolSemester == null) { throw Exception('No school semester found for date: $date'); @@ -97,7 +99,7 @@ class SchooldayAdminEndpoint extends Endpoint { final schoolday = Schoolday( schoolSemesterId: schoolSemester.id!, - schoolday: date, + schoolday: date.justDate(), ); schooldays.add(schoolday); } diff --git a/school_data_hub_server/lib/src/helpers/date_extension.dart b/school_data_hub_server/lib/src/helpers/date_extension.dart new file mode 100644 index 00000000..c068dff8 --- /dev/null +++ b/school_data_hub_server/lib/src/helpers/date_extension.dart @@ -0,0 +1,29 @@ +extension DateTimeComparison on DateTime { + bool isSameDay(DateTime other) { + return year == other.year && month == other.month && day == other.day; + } + + bool isBeforeDay(DateTime other) { + return year < other.year || + (year == other.year && month < other.month) || + (year == other.year && month == other.month && day < other.day); + } + + bool isAfterDay(DateTime other) { + return year > other.year || + (year == other.year && month > other.month) || + (year == other.year && month == other.month && day > other.day); + } + + DateTime justDate() { + return DateTime(year, month, day); + } + + String formatDateForUser() { + return '$day.$month.$year'; + } + + String formatDateTimeForUser() { + return '$day.$month.$year $hour:$minute'; + } +} From 2b553bb4b253bcb331b425b232570a6d1bfafdb6 Mon Sep 17 00:00:00 2001 From: dabblingwithcode Date: Thu, 4 Dec 2025 12:52:04 +0100 Subject: [PATCH 52/52] transtion as schoolday event topic, add comment to schoolday events, and other stuff --- .../models/schoolday_event.dart | 11 + .../logs_page/widgets/log_entry_card.dart | 2 +- .../domain/attendance_helper_functions.dart | 17 + .../data/schoolday_event_api_service.dart | 2 + .../domain/models/schoolday_event_enums.dart | 3 + .../domain/schoolday_event_manager.dart | 2 + .../new_schoolday_event_page.dart | 16 +- .../schoolday_event_reason_dialog.dart | 16 + .../widgets/pupil_schoolday_event_card.dart | 24 +- .../widgets/schoolday_event_reason_chips.dart | 20 +- .../books/utils/book_ids_pdf_generator.dart | 22 +- ...arning_content_expansion_tile_nav_bar.dart | 2 +- .../pupil/domain/models/pupil_proxy.dart | 2 + .../pupil/domain/pupil_identity_manager.dart | 9 +- .../widgets/family_language_lessons_card.dart | 2 +- ...profile_learning_support_content_list.dart | 48 +- .../religion_page/religion_list_page.dart | 37 +- .../religion_page/widgets/religion_card.dart | 14 +- .../pupil/presentation/widgets/avatar.dart | 136 +- .../endpoints/schoolday_event_endpoint.dart | 6 +- .../models/schoolday_event.spy.yaml | 1 + .../models/schoolday_event.dart | 19 + .../lib/src/generated/protocol.dart | 6 + .../20251204113239243/definition.json | 6532 +++++++++++++++++ .../20251204113239243/definition.sql | 1728 +++++ .../20251204113239243/definition_project.json | 4839 ++++++++++++ .../20251204113239243/migration.json | 28 + .../20251204113239243/migration.sql | 33 + .../migrations/migration_registry.txt | 1 + 29 files changed, 13434 insertions(+), 144 deletions(-) create mode 100644 school_data_hub_server/migrations/20251204113239243/definition.json create mode 100644 school_data_hub_server/migrations/20251204113239243/definition.sql create mode 100644 school_data_hub_server/migrations/20251204113239243/definition_project.json create mode 100644 school_data_hub_server/migrations/20251204113239243/migration.json create mode 100644 school_data_hub_server/migrations/20251204113239243/migration.sql diff --git a/school_data_hub_client/lib/src/protocol/_features/schoolday_events/models/schoolday_event.dart b/school_data_hub_client/lib/src/protocol/_features/schoolday_events/models/schoolday_event.dart index 9535fd68..1a8a968d 100644 --- a/school_data_hub_client/lib/src/protocol/_features/schoolday_events/models/schoolday_event.dart +++ b/school_data_hub_client/lib/src/protocol/_features/schoolday_events/models/schoolday_event.dart @@ -34,6 +34,7 @@ abstract class SchooldayEvent implements _i1.SerializableModel { this.schoolday, required this.pupilId, this.pupil, + this.comment, }); factory SchooldayEvent({ @@ -53,6 +54,7 @@ abstract class SchooldayEvent implements _i1.SerializableModel { _i4.Schoolday? schoolday, required int pupilId, _i5.PupilData? pupil, + String? comment, }) = _SchooldayEventImpl; factory SchooldayEvent.fromJson(Map jsonSerialization) { @@ -89,6 +91,7 @@ abstract class SchooldayEvent implements _i1.SerializableModel { ? null : _i5.PupilData.fromJson( (jsonSerialization['pupil'] as Map)), + comment: jsonSerialization['comment'] as String?, ); } @@ -127,6 +130,8 @@ abstract class SchooldayEvent implements _i1.SerializableModel { _i5.PupilData? pupil; + String? comment; + /// Returns a shallow copy of this [SchooldayEvent] /// with some or all fields replaced by the given arguments. @_i1.useResult @@ -147,6 +152,7 @@ abstract class SchooldayEvent implements _i1.SerializableModel { _i4.Schoolday? schoolday, int? pupilId, _i5.PupilData? pupil, + String? comment, }); @override Map toJson() { @@ -169,6 +175,7 @@ abstract class SchooldayEvent implements _i1.SerializableModel { if (schoolday != null) 'schoolday': schoolday?.toJson(), 'pupilId': pupilId, if (pupil != null) 'pupil': pupil?.toJson(), + if (comment != null) 'comment': comment, }; } @@ -198,6 +205,7 @@ class _SchooldayEventImpl extends SchooldayEvent { _i4.Schoolday? schoolday, required int pupilId, _i5.PupilData? pupil, + String? comment, }) : super._( id: id, eventId: eventId, @@ -215,6 +223,7 @@ class _SchooldayEventImpl extends SchooldayEvent { schoolday: schoolday, pupilId: pupilId, pupil: pupil, + comment: comment, ); /// Returns a shallow copy of this [SchooldayEvent] @@ -238,6 +247,7 @@ class _SchooldayEventImpl extends SchooldayEvent { Object? schoolday = _Undefined, int? pupilId, Object? pupil = _Undefined, + Object? comment = _Undefined, }) { return SchooldayEvent( id: id is int? ? id : this.id, @@ -262,6 +272,7 @@ class _SchooldayEventImpl extends SchooldayEvent { schoolday is _i4.Schoolday? ? schoolday : this.schoolday?.copyWith(), pupilId: pupilId ?? this.pupilId, pupil: pupil is _i5.PupilData? ? pupil : this.pupil?.copyWith(), + comment: comment is String? ? comment : this.comment, ); } } diff --git a/school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart b/school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart index 5a279112..c20bfa4f 100644 --- a/school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart +++ b/school_data_hub_flutter/lib/app_utils/logs_page/widgets/log_entry_card.dart @@ -63,7 +63,7 @@ class LogEntryCard extends StatelessWidget { ), Text( timestamp, - style: AppStyles.textLabel.copyWith(color: Colors.black54), + style: AppStyles.subtitle.copyWith(color: Colors.black54), ), ], ), diff --git a/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart b/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart index cb2b0fdb..a90581e1 100644 --- a/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart +++ b/school_data_hub_flutter/lib/features/_attendance/domain/attendance_helper_functions.dart @@ -282,6 +282,23 @@ class AttendanceHelper { return missedSchooldayUnexcusedCount; } + static int lateExcusedSum(PupilProxy pupil) { + int missedSchooldayUnexcusedCount = 0; + final missedSchooldays = _attendanceManager + .getPupilMissedSchooldaysProxy(pupil.pupilId) + .missedSchooldays; + if (missedSchooldays.isNotEmpty) { + missedSchooldayUnexcusedCount = missedSchooldays + .where( + (element) => + element.missedType == MissedType.late && + element.unexcused == false, + ) + .length; + } + return missedSchooldayUnexcusedCount; + } + static int contactedSum(PupilProxy pupil) { final missedSchooldays = _attendanceManager .getPupilMissedSchooldaysProxy(pupil.pupilId) diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart b/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart index 697b0093..e25c3564 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/data/schoolday_event_api_service.dart @@ -81,6 +81,7 @@ class SchooldayEventApiService { NullableStringRecord? processedBy, NullableDateTimeRecord? processedAt, int? schooldayId, + NullableStringRecord? comment, }) async { bool changedProcessedStatus = false; // if the schooldayEvent is patched as processed, @@ -113,6 +114,7 @@ class SchooldayEventApiService { processedAt: processedAt != null ? processedAt.value : schooldayEvent.processedAt, + comment: comment != null ? comment.value : schooldayEvent.comment, ); final pupil = di().getPupilByPupilId(schooldayEvent.pupilId)!; try { diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/domain/models/schoolday_event_enums.dart b/school_data_hub_flutter/lib/features/_schoolday_events/domain/models/schoolday_event_enums.dart index 79025eaf..65605e6b 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/domain/models/schoolday_event_enums.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/domain/models/schoolday_event_enums.dart @@ -10,6 +10,7 @@ enum SchooldayEventReason { other('ss'), learningDevelopmentInfo('le'), learningSupportInfo('fi'), + transitionAdvice('üb'), admonitionInfo('ki'); final String value; @@ -35,6 +36,7 @@ enum SchooldayEventFilter { learningDevelopmentInfo, learningSupportInfo, admonitionInfo, + transitionAdvice, processed, } @@ -57,5 +59,6 @@ Map initialSchooldayEventFilterValues = { SchooldayEventFilter.learningDevelopmentInfo: false, SchooldayEventFilter.learningSupportInfo: false, SchooldayEventFilter.admonitionInfo: false, + SchooldayEventFilter.transitionAdvice: false, SchooldayEventFilter.processed: false, }; diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart b/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart index c3bd3419..5ed75ecd 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/domain/schoolday_event_manager.dart @@ -164,6 +164,7 @@ class SchooldayEventManager with ChangeNotifier { NullableStringRecord? processedBy, NullableDateTimeRecord? processedAt, int? schooldayId, + NullableStringRecord? comment, }) async { String? cacheKey; if (processed == false && eventToUpdate.processedDocumentId != null) { @@ -179,6 +180,7 @@ class SchooldayEventManager with ChangeNotifier { processedAt: processedAt, schooldayId: schooldayId, type: schoolEventType, + comment: comment, ); _updateSchooldayEventCollections(schooldayEvent); diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart index 3e073087..d050a625 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/new_schoolday_event_page/new_schoolday_event_page.dart @@ -55,6 +55,7 @@ class NewSchooldayEventPage extends WatchingWidget { required bool learningDevelopmentInfo, required bool learningSupportInfo, required bool admonitionInfo, + required bool transitionAdvice, required bool other, }) async { Set schooldayEventReason = {}; @@ -114,7 +115,9 @@ class NewSchooldayEventPage extends WatchingWidget { if (other == true) { schooldayEventReason.add(SchooldayEventReason.other.value); } - + if (transitionAdvice == true) { + schooldayEventReason.add(SchooldayEventReason.transitionAdvice.value); + } for (final reason in schooldayEventReason) { schooldayEventReasons = '$schooldayEventReasons$reason*'; } @@ -157,6 +160,7 @@ class NewSchooldayEventPage extends WatchingWidget { () => ValueNotifier(false), ); final learningSupportInfo = createOnce(() => ValueNotifier(false)); + final transitionAdvice = createOnce(() => ValueNotifier(false)); final admonitionInfo = createOnce(() => ValueNotifier(false)); final schooldayEventType = watch(schooldayEventTypeDropdown).value; return Scaffold( @@ -309,6 +313,14 @@ class NewSchooldayEventPage extends WatchingWidget { emojis: '🛟🧠', text: 'Förderung', ), + SchooldayEventReasonFilterChip( + isReason: watch(transitionAdvice).value, + onSelected: (value) { + transitionAdvice.value = value; + }, + emojis: '🧠🗺️', + text: 'Übergang', + ), const Gap(5), SchooldayEventReasonFilterChip( isReason: watch(admonitionInfo).value, @@ -459,6 +471,7 @@ class NewSchooldayEventPage extends WatchingWidget { learningDevelopmentInfo.value == false && learningSupportInfo.value == false && admonitionInfo.value == false && + transitionAdvice.value == false && other.value == false) { informationDialog( context, @@ -484,6 +497,7 @@ class NewSchooldayEventPage extends WatchingWidget { learningDevelopmentInfo: learningDevelopmentInfo.value, learningSupportInfo: learningSupportInfo.value, admonitionInfo: admonitionInfo.value, + transitionAdvice: transitionAdvice.value, other: other.value, ), ); diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart index e1951223..3d6a22e4 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/dialogues/schoolday_event_reason_dialog.dart @@ -89,6 +89,13 @@ class SchooldayEventReasonDialog extends WatchingWidget { ), ), ); + final transitionAdvice = createOnce( + () => ValueNotifier( + schooldayEvent.eventReason.contains( + SchooldayEventReason.transitionAdvice.value, + ), + ), + ); final admonitionInfo = createOnce( () => ValueNotifier( schooldayEvent.eventReason.contains( @@ -128,6 +135,15 @@ class SchooldayEventReasonDialog extends WatchingWidget { text: 'Förderung', ), const Gap(5), + SchooldayEventReasonFilterChip( + isReason: watch(transitionAdvice).value, + onSelected: (value) { + transitionAdvice.value = value; + }, + emojis: '🧠🗺️', + text: 'Übergang', + ), + const Gap(5), SchooldayEventReasonFilterChip( isReason: watch(admonitionInfo).value, onSelected: (value) { diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart index 6b222fed..87795189 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/pupil_schoolday_event_card.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/app_utils/extensions/datetime_extensions.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; @@ -10,7 +11,6 @@ import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dial import 'package:school_data_hub_flutter/common/widgets/dialogs/schoolday_date_picker.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/short_textfield_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/_schoolday_events/domain/schoolday_event_manager.dart'; @@ -481,6 +481,28 @@ class PupilSchooldayEventCard extends StatelessWidget { ), ], ), + const Gap(5), + InkWell( + onTap: () async { + final String? comment = await shortTextfieldDialog( + context: context, + title: 'Kommentar', + labelText: 'Kommentar', + hintText: 'Kommentar', + obscureText: false, + ); + if (comment != null) { + await _schooldayEventManager.updateSchooldayEvent( + eventToUpdate: schooldayEvent, + comment: (value: comment), + ); + } + }, + child: Text( + schooldayEvent.comment ?? 'Kommentar hinzufügen', + style: const TextStyle(fontSize: 16), + ), + ), ], ), ), diff --git a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_reason_chips.dart b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_reason_chips.dart index 09c19c07..88254e96 100644 --- a/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_reason_chips.dart +++ b/school_data_hub_flutter/lib/features/_schoolday_events/presentation/schoolday_event_list_page/widgets/schoolday_event_reason_chips.dart @@ -9,15 +9,14 @@ class SchooldayEventReasonChip extends StatelessWidget { @override Widget build(BuildContext context) { return Chip( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), - labelPadding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(30))), - backgroundColor: AppColors.filterChipUnselectedColor, - label: Text( - reason, - style: const TextStyle(fontSize: emojiSize), - )); + padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), + labelPadding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(30)), + ), + backgroundColor: AppColors.filterChipUnselectedColor, + label: Text(reason, style: const TextStyle(fontSize: emojiSize)), + ); } } @@ -57,6 +56,9 @@ List schooldayEventReasonChips(String reason) { if (reason.contains(SchooldayEventReason.learningSupportInfo.value)) { chips.add(const SchooldayEventReasonChip(reason: '🛟🧠')); } + if (reason.contains(SchooldayEventReason.transitionAdvice.value)) { + chips.add(const SchooldayEventReasonChip(reason: '🧠🗺️')); + } if (reason.contains(SchooldayEventReason.admonitionInfo.value)) { chips.add(const SchooldayEventReasonChip(reason: '⚠️ℹ️')); } diff --git a/school_data_hub_flutter/lib/features/books/utils/book_ids_pdf_generator.dart b/school_data_hub_flutter/lib/features/books/utils/book_ids_pdf_generator.dart index c7882f88..2a7db04f 100644 --- a/school_data_hub_flutter/lib/features/books/utils/book_ids_pdf_generator.dart +++ b/school_data_hub_flutter/lib/features/books/utils/book_ids_pdf_generator.dart @@ -10,7 +10,7 @@ final _notificationService = di(); List generateBookIds({required int startAtIdNr, required int count}) { List bookIds = []; for (int index = startAtIdNr; index <= count + startAtIdNr; index++) { - String bookId = 'C3 ${index.toString().padLeft(5, '0')}'; + String bookId = 'LI ${index.toString().padLeft(5, '0')}'; bookIds.add(bookId); } return bookIds; @@ -19,6 +19,10 @@ List generateBookIds({required int startAtIdNr, required int count}) { pw.Widget buildBarcodePage(List bookIds, int startIndex) { final schoolData = di().schoolData.value; final List addressLines = schoolData?.address.split(', ') ?? []; + final String? addressLine1 = addressLines.isNotEmpty + ? addressLines.first + : null; + final String? addressLine2 = addressLines.length > 1 ? addressLines[1] : null; return pw.Column( children: List.generate(9, (rowIndex) { return pw.Row( @@ -52,16 +56,10 @@ pw.Widget buildBarcodePage(List bookIds, int startIndex) { fontWeight: pw.FontWeight.bold, ), ), - - pw.Text( - addressLines[0], - style: const pw.TextStyle(fontSize: 6), - ), - - pw.Text( - addressLines[1], - style: const pw.TextStyle(fontSize: 6), - ), + if (addressLine1 != null) + pw.Text(addressLine1, style: const pw.TextStyle(fontSize: 6)), + if (addressLine2 != null) + pw.Text(addressLine2, style: const pw.TextStyle(fontSize: 6)), ], ); } else { @@ -76,7 +74,7 @@ pw.Widget buildBarcodePage(List bookIds, int startIndex) { void generateBookIdsPdf() async { _notificationService.setHeavyLoadingValue(true); final pdf = pw.Document(); - List bookIds = generateBookIds(startAtIdNr: 1, count: 200); + List bookIds = generateBookIds(startAtIdNr: 1002, count: 300); int itemsPerPage = 9 * 6; int totalPages = (bookIds.length / itemsPerPage).ceil(); diff --git a/school_data_hub_flutter/lib/features/learning/presentation/widgets/pupil_learning_content_expansion_tile_nav_bar.dart b/school_data_hub_flutter/lib/features/learning/presentation/widgets/pupil_learning_content_expansion_tile_nav_bar.dart index 2deee560..dca5cd81 100644 --- a/school_data_hub_flutter/lib/features/learning/presentation/widgets/pupil_learning_content_expansion_tile_nav_bar.dart +++ b/school_data_hub_flutter/lib/features/learning/presentation/widgets/pupil_learning_content_expansion_tile_nav_bar.dart @@ -29,7 +29,7 @@ class SelectedLearningContentNotifier extends ChangeNotifier { return _instance; } - SelectedContent _selectedContent = SelectedContent.competenceStatuses; + SelectedContent _selectedContent = SelectedContent.books; SelectedContent get selectedContent => _selectedContent; diff --git a/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart b/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart index 2fc11d73..25a99fbd 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/models/pupil_proxy.dart @@ -103,6 +103,8 @@ class PupilProxy with ChangeNotifier { DateTime? get familyLanguageLessonsSince => _pupilIdentity.familyLanguageLessonsSince; DateTime? get religionLessonsSince => _pupilIdentity.religionLessonsSince; + DateTime? get religionLessonsCancelledAt => + _pupilIdentity.religionLessonsCancelledAt; String? get religion => _pupilIdentity.religion; //- PUPIL DATA GETTERS diff --git a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart index 3aee26c6..48fba7f6 100644 --- a/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart +++ b/school_data_hub_flutter/lib/features/pupil/domain/pupil_identity_manager.dart @@ -701,10 +701,11 @@ class PupilIdentityManager { 'Der Server konnte nicht gefunden werden. Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.', ); } else { - _notificationService.showSnackBar( - NotificationType.error, - 'Ein unbekannter Fehler ist aufgetreten: $errorString', - ); + _log.severe('Error in pupil identity stream: $errorString'); + // _notificationService.showSnackBar( + // NotificationType.error, + // 'Ein unbekannter Fehler ist aufgetreten: $errorString', + // ); } // retry the subscription after a delay await Future.delayed(const Duration(seconds: 1)); diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_card.dart index a2cd0128..21e2287b 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_card.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/family_language_lessons_page/widgets/family_language_lessons_card.dart @@ -124,7 +124,7 @@ class FamilyLanguageLessonsCard extends WatchingWidget { const Gap(5), Row( children: [ - Text('Angemeldet am:'), + const Text('Angemeldet am:'), const Gap(10), Text( pupil.familyLanguageLessonsSince != null diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart index 8dffa27b..946c2a63 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/pupil_profile_page/widgets/pupil_profile_page_content/learning_support_content/pupil_profile_learning_support_content_list.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:school_data_hub_client/school_data_hub_client.dart'; +import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/common/data/file_upload_service.dart'; import 'package:school_data_hub_flutter/common/services/notification_service.dart'; import 'package:school_data_hub_flutter/common/theme/app_colors.dart'; @@ -12,7 +13,6 @@ import 'package:school_data_hub_flutter/common/widgets/custom_expansion_tile/cus import 'package:school_data_hub_flutter/common/widgets/custom_expansion_tile/custom_expansion_tile_switch.dart'; import 'package:school_data_hub_flutter/common/widgets/dialogs/confirmation_dialog.dart'; import 'package:school_data_hub_flutter/common/widgets/document_image.dart'; -import 'package:school_data_hub_flutter/app_utils/create_and_crop_image_file.dart'; import 'package:school_data_hub_flutter/core/client/client_helper.dart'; import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart'; import 'package:school_data_hub_flutter/features/learning_support/domain/support_category_manager.dart'; @@ -384,31 +384,33 @@ class PupilProfileLearningSupportContentList extends WatchingWidget { ...otherPlans.map((plan) => _buildPlanCard(context, plan)), const Gap(10), // New Learning Support Plan Button - Padding( - padding: const EdgeInsets.all(10.0), - child: ElevatedButton( - style: AppStyles.actionButtonStyle, - onPressed: () { - if (pupil.supportLevelHistory == null || - pupil.supportLevelHistory!.isEmpty) { - di().showSnackBar( - NotificationType.error, - 'Förderebene nicht festgelegt', + // TODO: show if special educator or group tutor + if (di().isAdmin) + Padding( + padding: const EdgeInsets.all(10.0), + child: ElevatedButton( + style: AppStyles.actionButtonStyle, + onPressed: () { + if (pupil.supportLevelHistory == null || + pupil.supportLevelHistory!.isEmpty) { + di().showSnackBar( + NotificationType.error, + 'Förderebene nicht festgelegt', + ); + return; + } + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => NewLearningSupportPlan(pupil: pupil), + ), ); - return; - } - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) => NewLearningSupportPlan(pupil: pupil), - ), - ); - }, - child: const Text( - "NEUER FÖRDERPLAN", - style: AppStyles.buttonTextStyle, + }, + child: const Text( + "NEUER FÖRDERPLAN", + style: AppStyles.buttonTextStyle, + ), ), ), - ), ], ), ], diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart b/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart index 0cdeb53a..3806cbb0 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/religion_list_page.dart @@ -22,12 +22,21 @@ List religionFilter(List pupils) { filtersOn = true; continue; } + if (pupil.religionLessonsCancelledAt != null && + pupil.religionLessonsCancelledAt!.isAfter( + pupil.religionLessonsSince!, + )) { + filtersOn = true; + continue; + } filteredPupils.add(pupil); } if (filtersOn) { _filterStateManager.setFilterState( - filterState: FilterState.pupil, value: true); + filterState: FilterState.pupil, + value: true, + ); } return filteredPupils; } @@ -44,8 +53,9 @@ class ReligionListPage extends WatchingWidget { Widget build(BuildContext context) { final _filterStateManager = di(); final _pupilManager = di(); - List filteredPupils = - watchValue((PupilsFilter x) => x.filteredPupils); + List filteredPupils = watchValue( + (PupilsFilter x) => x.filteredPupils, + ); List pupils = religionFilter(filteredPupils); onDispose(() { _filterStateManager.resetFilters(); @@ -60,16 +70,9 @@ class ReligionListPage extends WatchingWidget { title: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.church, - size: 25, - color: Colors.white, - ), + Icon(Icons.church, size: 25, color: Colors.white), Gap(10), - Text( - 'Religion', - style: AppStyles.appBarTextStyle, - ), + Text('Religion', style: AppStyles.appBarTextStyle), ], ), automaticallyImplyLeading: false, @@ -84,13 +87,12 @@ class ReligionListPage extends WatchingWidget { const SliverGap(5), GenericSliverSearchAppBar( height: 110, - title: ReligionListSearchBar( - pupils: pupils, - ), + title: ReligionListSearchBar(pupils: pupils), ), GenericSliverListWithEmptyListCheck( - items: pupils, - itemBuilder: (_, pupil) => ReligionCard(pupil)), + items: pupils, + itemBuilder: (_, pupil) => ReligionCard(pupil), + ), ], ), ), @@ -101,4 +103,3 @@ class ReligionListPage extends WatchingWidget { ); } } - diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/widgets/religion_card.dart b/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/widgets/religion_card.dart index a866764e..63f790a9 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/widgets/religion_card.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/religion_page/widgets/religion_card.dart @@ -128,7 +128,7 @@ class ReligionCard extends WatchingWidget { const Gap(5), Row( children: [ - Text('Angemeldet seit:'), + const Text('Angemeldet seit:'), const Gap(10), Text( pupil.religionLessonsSince != null @@ -137,6 +137,18 @@ class ReligionCard extends WatchingWidget { ), ], ), + if (pupil.religionLessonsCancelledAt != null) ...[ + const Gap(5), + Row( + children: [ + const Text('Abgemeldet am:'), + const Gap(10), + Text( + pupil.religionLessonsCancelledAt!.formatDateForUser(), + ), + ], + ), + ], ], ), ), diff --git a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/avatar.dart b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/avatar.dart index 9fd4193c..ed0d9bf0 100644 --- a/school_data_hub_flutter/lib/features/pupil/presentation/widgets/avatar.dart +++ b/school_data_hub_flutter/lib/features/pupil/presentation/widgets/avatar.dart @@ -36,56 +36,54 @@ class AvatarImage extends WatchingWidget { width: size, height: size, child: Center( - child: - avatar != null - ? WidgetZoom( - heroAnimationTag: avatar.documentId, - zoomWidget: FutureBuilder( - future: cachedImageOrDownloadImage( - documentId: avatar.documentId, - decrypt: true, - ), - builder: (context, snapshot) { - Widget child; - if (snapshot.connectionState == ConnectionState.waiting) { - // Display a loading indicator while the future is not complete - child = const CircularProgressIndicator( - strokeWidth: 8, - color: AppColors.backgroundColor, - ); - } else if (snapshot.hasError) { - // Display an error message if the future encounters an error - child = Text('Error: ${snapshot.error}'); - } else { - child = snapshot.data!; - } - return AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: ClipRRect( - borderRadius: BorderRadius.circular(size / 2), - child: child, - ), - ); - }, + child: avatar != null + ? WidgetZoom( + heroAnimationTag: avatar.documentId, + zoomWidget: FutureBuilder( + future: cachedImageOrDownloadImage( + documentId: avatar.documentId, + decrypt: true, ), - ) - : ClipRRect( - borderRadius: BorderRadius.circular(size / 2), - child: - avatarAuth - ? Image.asset( - 'assets/dummy-profile-pic-auth.png', - width: 100, - height: 100, - fit: BoxFit.cover, - ) - : Image.asset( - 'assets/dummy-profile-pic-unauth.png', - width: 100, - height: 100, - fit: BoxFit.cover, - ), + builder: (context, snapshot) { + Widget child; + if (snapshot.connectionState == ConnectionState.waiting) { + // Display a loading indicator while the future is not complete + child = const CircularProgressIndicator( + strokeWidth: 8, + color: AppColors.backgroundColor, + ); + } else if (snapshot.hasError) { + // Display an error message if the future encounters an error + child = Text('Error: ${snapshot.error}'); + } else { + child = snapshot.data!; + } + return AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: ClipRRect( + borderRadius: BorderRadius.circular(size / 2), + child: child, + ), + ); + }, ), + ) + : ClipRRect( + borderRadius: BorderRadius.circular(size / 2), + child: avatarAuth + ? Image.asset( + 'assets/dummy-profile-pic-auth.png', + width: 100, + height: 100, + fit: BoxFit.cover, + ) + : Image.asset( + 'assets/dummy-profile-pic-unauth.png', + width: 100, + height: 100, + fit: BoxFit.cover, + ), + ), ), ); } @@ -125,10 +123,9 @@ class AvatarWithBadges extends WatchingWidget { position: position, items: [ PopupMenuItem( - child: - pupil.avatar == null - ? const Text('Foto hochladen') - : const Text('Foto ersetzen'), + child: pupil.avatar == null + ? const Text('Foto hochladen') + : const Text('Foto ersetzen'), onTap: () => setAvatar(context: context, pupil: pupil), ), if (pupil.avatar != null) @@ -163,10 +160,9 @@ class AvatarWithBadges extends WatchingWidget { width: 30.0, height: 30.0, decoration: BoxDecoration( - color: - AttendanceHelper.pupilIsMissedToday(pupil) - ? AppColors.warningButtonColor - : AppColors.groupColor, + color: AttendanceHelper.pupilIsMissedToday(pupil) + ? AppColors.warningButtonColor + : AppColors.groupColor, shape: BoxShape.circle, ), child: Center( @@ -185,23 +181,25 @@ class AvatarWithBadges extends WatchingWidget { bottom: 0, right: 0, child: Container( - width: 30.0, - height: 30.0, + width: pupil.schoolyearHeldBackAt != null ? 33.0 : 30.0, + height: pupil.schoolyearHeldBackAt != null ? 33.0 : 30.0, decoration: BoxDecoration( - color: - SchoolDayEventHelper.pupilIsAdmonishedToday(pupil) - ? Colors.red - : AppColors.schoolyearColor, + border: pupil.schoolyearHeldBackAt != null + ? Border.all( + color: const Color.fromARGB(255, 250, 197, 98), + width: 3, + ) + : null, + color: SchoolDayEventHelper.pupilIsAdmonishedToday(pupil) + ? Colors.red + : AppColors.schoolyearColor, shape: BoxShape.circle, ), child: Center( child: Text( pupil.schoolGrade.name, - style: TextStyle( - color: - pupil.schoolyearHeldBackAt != null - ? const Color.fromARGB(255, 250, 197, 98) - : Colors.white, + style: const TextStyle( + color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold, ), @@ -242,8 +240,8 @@ class AvatarWithBadges extends WatchingWidget { decoration: BoxDecoration( color: PupilHelper.hasLanguageSupport(pupil.migrationSupportEnds) - ? Colors.green - : Colors.grey, + ? Colors.green + : Colors.grey, shape: BoxShape.circle, ), child: const Center( diff --git a/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart b/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart index 35c55fb8..171c48ce 100644 --- a/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart +++ b/school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart @@ -94,7 +94,7 @@ class SchooldayEventEndpoint extends Endpoint { } await session.db.updateRow(schooldayEvent); - final updatedSchooldayEvent = + final updatedSchooldayEventInDatabase = await SchooldayEvent.db.findById(session, schooldayEvent.id!, include: SchooldayEvent.include( schoolday: Schoolday.include(), @@ -107,12 +107,12 @@ class SchooldayEventEndpoint extends Endpoint { session: session, pupilNameAndGroup: pupilNameAndGroup, tutor: tutor, - eventWithSchoolday: updatedSchooldayEvent!, + eventWithSchoolday: updatedSchooldayEventInDatabase!, dateTimeAsString: dateTimeAsString, changedProcessedStatus: changedProcessedStatus, )); } - return updatedSchooldayEvent!; + return updatedSchooldayEventInDatabase!; } Future deleteSchooldayEvent( diff --git a/school_data_hub_server/lib/src/_features/schoolday_events/models/schoolday_event.spy.yaml b/school_data_hub_server/lib/src/_features/schoolday_events/models/schoolday_event.spy.yaml index 6b17198a..128be965 100644 --- a/school_data_hub_server/lib/src/_features/schoolday_events/models/schoolday_event.spy.yaml +++ b/school_data_hub_server/lib/src/_features/schoolday_events/models/schoolday_event.spy.yaml @@ -12,3 +12,4 @@ fields: processedDocument: HubDocument?, relation(optional) schoolday: Schoolday?, relation(name=schoolday_event_schoolday) pupil: PupilData?, relation(name=schoolday_event_pupil) + comment: String? diff --git a/school_data_hub_server/lib/src/generated/_features/schoolday_events/models/schoolday_event.dart b/school_data_hub_server/lib/src/generated/_features/schoolday_events/models/schoolday_event.dart index 6081f839..798de148 100644 --- a/school_data_hub_server/lib/src/generated/_features/schoolday_events/models/schoolday_event.dart +++ b/school_data_hub_server/lib/src/generated/_features/schoolday_events/models/schoolday_event.dart @@ -37,6 +37,7 @@ abstract class SchooldayEvent this.schoolday, required this.pupilId, this.pupil, + this.comment, }); factory SchooldayEvent({ @@ -56,6 +57,7 @@ abstract class SchooldayEvent _i4.Schoolday? schoolday, required int pupilId, _i5.PupilData? pupil, + String? comment, }) = _SchooldayEventImpl; factory SchooldayEvent.fromJson(Map jsonSerialization) { @@ -92,6 +94,7 @@ abstract class SchooldayEvent ? null : _i5.PupilData.fromJson( (jsonSerialization['pupil'] as Map)), + comment: jsonSerialization['comment'] as String?, ); } @@ -132,6 +135,8 @@ abstract class SchooldayEvent _i5.PupilData? pupil; + String? comment; + @override _i1.Table get table => t; @@ -155,6 +160,7 @@ abstract class SchooldayEvent _i4.Schoolday? schoolday, int? pupilId, _i5.PupilData? pupil, + String? comment, }); @override Map toJson() { @@ -177,6 +183,7 @@ abstract class SchooldayEvent if (schoolday != null) 'schoolday': schoolday?.toJson(), 'pupilId': pupilId, if (pupil != null) 'pupil': pupil?.toJson(), + if (comment != null) 'comment': comment, }; } @@ -201,6 +208,7 @@ abstract class SchooldayEvent if (schoolday != null) 'schoolday': schoolday?.toJsonForProtocol(), 'pupilId': pupilId, if (pupil != null) 'pupil': pupil?.toJsonForProtocol(), + if (comment != null) 'comment': comment, }; } @@ -264,6 +272,7 @@ class _SchooldayEventImpl extends SchooldayEvent { _i4.Schoolday? schoolday, required int pupilId, _i5.PupilData? pupil, + String? comment, }) : super._( id: id, eventId: eventId, @@ -281,6 +290,7 @@ class _SchooldayEventImpl extends SchooldayEvent { schoolday: schoolday, pupilId: pupilId, pupil: pupil, + comment: comment, ); /// Returns a shallow copy of this [SchooldayEvent] @@ -304,6 +314,7 @@ class _SchooldayEventImpl extends SchooldayEvent { Object? schoolday = _Undefined, int? pupilId, Object? pupil = _Undefined, + Object? comment = _Undefined, }) { return SchooldayEvent( id: id is int? ? id : this.id, @@ -328,6 +339,7 @@ class _SchooldayEventImpl extends SchooldayEvent { schoolday is _i4.Schoolday? ? schoolday : this.schoolday?.copyWith(), pupilId: pupilId ?? this.pupilId, pupil: pupil is _i5.PupilData? ? pupil : this.pupil?.copyWith(), + comment: comment is String? ? comment : this.comment, ); } } @@ -380,6 +392,10 @@ class SchooldayEventTable extends _i1.Table { 'pupilId', this, ); + comment = _i1.ColumnString( + 'comment', + this, + ); } late final _i1.ColumnString eventId; @@ -412,6 +428,8 @@ class SchooldayEventTable extends _i1.Table { _i5.PupilDataTable? _pupil; + late final _i1.ColumnString comment; + _i3.HubDocumentTable get document { if (_document != null) return _document!; _document = _i1.createRelationTable( @@ -478,6 +496,7 @@ class SchooldayEventTable extends _i1.Table { processedDocumentId, schooldayId, pupilId, + comment, ]; @override diff --git a/school_data_hub_server/lib/src/generated/protocol.dart b/school_data_hub_server/lib/src/generated/protocol.dart index 881e9c5f..f5b7f840 100644 --- a/school_data_hub_server/lib/src/generated/protocol.dart +++ b/school_data_hub_server/lib/src/generated/protocol.dart @@ -3780,6 +3780,12 @@ class Protocol extends _i1.SerializationManagerServer { isNullable: false, dartType: 'int', ), + _i2.ColumnDefinition( + name: 'comment', + columnType: _i2.ColumnType.text, + isNullable: true, + dartType: 'String?', + ), ], foreignKeys: [ _i2.ForeignKeyDefinition( diff --git a/school_data_hub_server/migrations/20251204113239243/definition.json b/school_data_hub_server/migrations/20251204113239243/definition.json new file mode 100644 index 00000000..678c629b --- /dev/null +++ b/school_data_hub_server/migrations/20251204113239243/definition.json @@ -0,0 +1,6532 @@ +{ + "moduleName": "school_data_hub", + "tables": [ + { + "name": "authorization", + "dartName": "Authorization", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('authorization_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "authorization_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "book", + "dartName": "Book", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('book_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "title", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "author", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "readingLevel", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "imagePath", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "book_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "book_id_unique_idx", + "elements": [ + { + "type": 0, + "definition": "isbn" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "book_tag", + "dartName": "BookTag", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('book_tag_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "book_tag_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "book_tagging", + "dartName": "BookTagging", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('book_tagging_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "bookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "bookTagId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "book_tagging_fk_0", + "columns": [ + "bookId" + ], + "referenceTable": "book", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "book_tagging_fk_1", + "columns": [ + "bookTagId" + ], + "referenceTable": "book_tag", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "book_tagging_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "book_tagging_index_idx", + "elements": [ + { + "type": 0, + "definition": "bookId" + }, + { + "type": 0, + "definition": "bookTagId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "competence", + "dartName": "Competence", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "parentCompetence", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "level", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "indicators", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "order", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "competence_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_check", + "dartName": "CompetenceCheck", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_check_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "checkId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "valueFactor", + "columnType": 3, + "isNullable": false, + "dartType": "double" + }, + { + "name": "groupCheckId", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "groupCheckName", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_check_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_check_fk_1", + "columns": [ + "competenceId" + ], + "referenceTable": "competence", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_check_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_goal", + "dartName": "CompetenceGoal", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_goal_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "strategies", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "score", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "achievedAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_goal_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_goal_fk_1", + "columns": [ + "competenceId" + ], + "referenceTable": "competence", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_goal_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_report", + "dartName": "CompetenceReport", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_report_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "reportId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "achievement", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "achievedAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_report_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_report_fk_1", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_report_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_report_check", + "dartName": "CompetenceReportCheck", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_report_check_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "achievement", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceReportId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_report_check_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_report_check_fk_1", + "columns": [ + "competenceId" + ], + "referenceTable": "competence_report_item", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_report_check_fk_2", + "columns": [ + "competenceReportId" + ], + "referenceTable": "competence_report", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_report_check_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_report_item", + "dartName": "CompetenceReportItem", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_report_item_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "parentItem", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "level", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "order", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "competence_report_item_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "compulsory_room", + "dartName": "CompulsoryRoom", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('compulsory_room_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "roomId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "roomType", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:MatrixRoomType" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "compulsory_room_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "credit_transaction", + "dartName": "CreditTransaction", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('credit_transaction_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "sender", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "receiver", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "amount", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "dateTime", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "description", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "_pupilDataCredittransactionsPupilDataId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "credit_transaction_fk_0", + "columns": [ + "_pupilDataCredittransactionsPupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "credit_transaction_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "reciever_idx", + "elements": [ + { + "type": 0, + "definition": "receiver" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "sender_idx", + "elements": [ + { + "type": 0, + "definition": "sender" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "hub_document", + "dartName": "HubDocument", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('hub_document_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "documentId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "documentPath", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "_pupilBookLendingPupilbooklendingfilesPupilBookLendingId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_competenceCheckDocumentsCompetenceCheckId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_competenceGoalDocumentsCompetenceGoalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportCategoryStatusDocumentsSupportCategoryStatusId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportGoalCheckDocumentsSupportGoalCheckId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_preSchoolMedicalPreschoolmedicalfilesPreSchoolMedicalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_preSchoolTestPreschooltestdocumentsPreSchoolTestId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "hub_document_fk_0", + "columns": [ + "_pupilBookLendingPupilbooklendingfilesPupilBookLendingId" + ], + "referenceTable": "pupil_book_lending", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_1", + "columns": [ + "_competenceCheckDocumentsCompetenceCheckId" + ], + "referenceTable": "competence_check", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_2", + "columns": [ + "_competenceGoalDocumentsCompetenceGoalId" + ], + "referenceTable": "competence_goal", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_3", + "columns": [ + "_supportCategoryStatusDocumentsSupportCategoryStatusId" + ], + "referenceTable": "support_category_status", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_4", + "columns": [ + "_supportGoalCheckDocumentsSupportGoalCheckId" + ], + "referenceTable": "support_goal_check", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_5", + "columns": [ + "_preSchoolMedicalPreschoolmedicalfilesPreSchoolMedicalId" + ], + "referenceTable": "pre_school_medical", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_6", + "columns": [ + "_preSchoolTestPreschooltestdocumentsPreSchoolTestId" + ], + "referenceTable": "pre_school_test", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "hub_document_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "kindergarden", + "dartName": "Kindergarden", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('kindergarden_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "phone", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "address", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "email", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "contactPerson", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "kindergarden_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "last_pupil_identities_update", + "dartName": "LastPupilIdentiesUpdate", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('last_pupil_identities_update_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "date", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "last_pupil_identities_update_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "learning_support_plan", + "dartName": "LearningSupportPlan", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('learning_support_plan_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "planId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "number", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "socialPedagogue", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "proffesionalsInvolved", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "strengthsDescription", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "problemsDescription", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "learningSupportLevelId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "learning_support_plan_fk_0", + "columns": [ + "learningSupportLevelId" + ], + "referenceTable": "support_level", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "learning_support_plan_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "learning_support_plan_fk_2", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "learning_support_plan_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson", + "dartName": "Lesson", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "subjectId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_fk_0", + "columns": [ + "subjectId" + ], + "referenceTable": "subject", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson_attendance", + "dartName": "LessonAttendance", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_attendance_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "lessonId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "modifiedAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_attendance_fk_0", + "columns": [ + "lessonId" + ], + "referenceTable": "lesson", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "lesson_attendance_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_attendance_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson_group", + "dartName": "LessonGroup", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_group_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "color", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "timetableId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "modifiedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_group_fk_0", + "columns": [ + "timetableId" + ], + "referenceTable": "timetable", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_group_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson_group_pupil", + "dartName": "ScheduledLessonGroupMembership", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_group_pupil_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "lessonGroupId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilDataId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_group_pupil_fk_0", + "columns": [ + "lessonGroupId" + ], + "referenceTable": "lesson_group", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "lesson_group_pupil_fk_1", + "columns": [ + "pupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_group_pupil_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "lesson_group_membership_index_idx", + "elements": [ + { + "type": 0, + "definition": "lessonGroupId" + }, + { + "type": 0, + "definition": "pupilDataId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "lesson_teacher", + "dartName": "LessonTeacher", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_teacher_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "scheduledLessonId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_teacher_fk_0", + "columns": [ + "userId" + ], + "referenceTable": "user", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "lesson_teacher_fk_1", + "columns": [ + "scheduledLessonId" + ], + "referenceTable": "lesson", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_teacher_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "lesson_teacher_unique_idx", + "elements": [ + { + "type": 0, + "definition": "userId" + }, + { + "type": 0, + "definition": "scheduledLessonId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "library_book", + "dartName": "LibraryBook", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('library_book_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "libraryId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "bookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "locationId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "available", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + } + ], + "foreignKeys": [ + { + "constraintName": "library_book_fk_0", + "columns": [ + "bookId" + ], + "referenceTable": "book", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "library_book_fk_1", + "columns": [ + "locationId" + ], + "referenceTable": "library_book_location", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "library_book_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "library_id_unique_idx", + "elements": [ + { + "type": 0, + "definition": "libraryId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "library_book_location", + "dartName": "LibraryBookLocation", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('library_book_location_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "location", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "library_book_location_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "location_unique_idx", + "elements": [ + { + "type": 0, + "definition": "location" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "missed_class", + "dartName": "MissedSchoolday", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('missed_class_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "missedType", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:MissedType" + }, + { + "name": "unexcused", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "contacted", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:ContactedType" + }, + { + "name": "returned", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "returnedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "writtenExcuse", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "minutesLate", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "schooldayId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "missed_class_fk_0", + "columns": [ + "schooldayId" + ], + "referenceTable": "schoolday", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "missed_class_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "missed_class_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "schoolday_pupil_data_idx", + "elements": [ + { + "type": 0, + "definition": "schooldayId" + }, + { + "type": 0, + "definition": "pupilId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "pre_school_medical", + "dartName": "PreSchoolMedical", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pre_school_medical_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "preschoolMedicalStatus", + "columnType": 0, + "isNullable": true, + "dartType": "protocol:PreSchoolMedicalStatus?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "updatedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "updatedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "pre_school_medical_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pre_school_test", + "dartName": "PreSchoolTest", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pre_school_test_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "careNeedsIntensity", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "pre_school_test_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_authorization", + "dartName": "PupilAuthorization", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_authorization_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "status", + "columnType": 1, + "isNullable": true, + "dartType": "bool?" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "fileId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "authorizationId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_authorization_fk_0", + "columns": [ + "fileId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "pupil_authorization_fk_1", + "columns": [ + "authorizationId" + ], + "referenceTable": "authorization", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_authorization_fk_2", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "pupil_authorization_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_book_lending", + "dartName": "PupilBookLending", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_book_lending_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "lendingId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "status", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "lentAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "lentBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "returnedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "receivedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "libraryBookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_book_lending_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_book_lending_fk_1", + "columns": [ + "libraryBookId" + ], + "referenceTable": "library_book", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "pupil_book_lending_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_data", + "dartName": "PupilData", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_data_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "status", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:PupilStatus" + }, + { + "name": "internalId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "password", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "preSchoolMedicalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "kindergardenId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "kindergardenData", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:KindergardenInfo?" + }, + { + "name": "preSchoolTestId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "avatarId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "avatarAuthId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "publicMediaAuth", + "columnType": 8, + "isNullable": false, + "dartType": "protocol:PublicMediaAuth" + }, + { + "name": "publicMediaAuthDocumentId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "contact", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "communicationPupil", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:CommunicationSkills?" + }, + { + "name": "specialInformation", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "tutorInfo", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:TutorInfo?" + }, + { + "name": "afterSchoolCare", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:AfterSchoolCare?" + }, + { + "name": "credit", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "creditEarned", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "schoolyearHeldBackAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "swimmer", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "_kindergardenPupilsKindergardenId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_data_fk_0", + "columns": [ + "preSchoolMedicalId" + ], + "referenceTable": "pre_school_medical", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_1", + "columns": [ + "kindergardenId" + ], + "referenceTable": "kindergarden", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_2", + "columns": [ + "preSchoolTestId" + ], + "referenceTable": "pre_school_test", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_3", + "columns": [ + "avatarId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_4", + "columns": [ + "avatarAuthId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_5", + "columns": [ + "publicMediaAuthDocumentId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_6", + "columns": [ + "_kindergardenPupilsKindergardenId" + ], + "referenceTable": "kindergarden", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "pupil_data_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "pupil_data_status_idx", + "elements": [ + { + "type": 0, + "definition": "status" + }, + { + "type": 0, + "definition": "internalId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "pupil_data_internal_id_idx", + "elements": [ + { + "type": 0, + "definition": "internalId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "pupil_list_entry", + "dartName": "PupilListEntry", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_list_entry_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "status", + "columnType": 1, + "isNullable": true, + "dartType": "bool?" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "entryBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "schoolListId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_list_entry_fk_0", + "columns": [ + "schoolListId" + ], + "referenceTable": "school_list", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "pupil_list_entry_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "pupil_list_entry_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_workbook", + "dartName": "PupilWorkbook", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_workbook_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "finishedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "workbookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_workbook_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "pupil_workbook_fk_1", + "columns": [ + "workbookId" + ], + "referenceTable": "workbook", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "pupil_workbook_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "room", + "dartName": "Classroom", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('room_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "roomCode", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "roomName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "room_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "scheduled_lesson", + "dartName": "ScheduledLesson", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('scheduled_lesson_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "active", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "subjectId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "scheduledAtId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "timetableSlotOrder", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "timetableId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "mainTeacherId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "lessonId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "roomId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "lessonGroupId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "modifiedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "recordtest", + "columnType": 8, + "isNullable": true, + "dartType": "( {int testint, String testString})?" + }, + { + "name": "_roomScheduledlessonsRoomId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "scheduled_lesson_fk_0", + "columns": [ + "subjectId" + ], + "referenceTable": "subject", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_1", + "columns": [ + "scheduledAtId" + ], + "referenceTable": "timetable_slot", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_2", + "columns": [ + "timetableId" + ], + "referenceTable": "timetable", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_3", + "columns": [ + "roomId" + ], + "referenceTable": "room", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_4", + "columns": [ + "lessonGroupId" + ], + "referenceTable": "lesson_group", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_5", + "columns": [ + "_roomScheduledlessonsRoomId" + ], + "referenceTable": "room", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "scheduled_lesson_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "scheduled_lesson_teacher", + "dartName": "ScheduledLessonTeacher", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('scheduled_lesson_teacher_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "scheduledLessonId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "scheduled_lesson_teacher_fk_0", + "columns": [ + "userId" + ], + "referenceTable": "user", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_teacher_fk_1", + "columns": [ + "scheduledLessonId" + ], + "referenceTable": "scheduled_lesson", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "scheduled_lesson_teacher_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "scheduled_lesson_teacher_unique_idx", + "elements": [ + { + "type": 0, + "definition": "userId" + }, + { + "type": 0, + "definition": "scheduledLessonId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "school_data", + "dartName": "SchoolData", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('school_data_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "officialName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "address", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "schoolNumber", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "telephoneNumber", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "email", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "website", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "logoId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "officialSealId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "school_data_fk_0", + "columns": [ + "logoId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "school_data_fk_1", + "columns": [ + "officialSealId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "school_data_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "school_list", + "dartName": "SchoolList", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('school_list_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "listId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "archived", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "public", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "authorizedUsers", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "school_list_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "school_semester", + "dartName": "SchoolSemester", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('school_semester_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "schoolYear", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "isFirst", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "startDate", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "endDate", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "classConferenceDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "supportConferenceDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "reportConferenceDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "reportSignedDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "school_semester_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "schoolday", + "dartName": "Schoolday", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('schoolday_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "schoolday", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "schoolday_fk_0", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "schoolday_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "schoolday_event", + "dartName": "SchooldayEvent", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('schoolday_event_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "eventId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "eventType", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:SchooldayEventType" + }, + { + "name": "eventReason", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "processed", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "processedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "processedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "documentId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "processedDocumentId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "schooldayId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + } + ], + "foreignKeys": [ + { + "constraintName": "schoolday_event_fk_0", + "columns": [ + "documentId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "schoolday_event_fk_1", + "columns": [ + "processedDocumentId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "schoolday_event_fk_2", + "columns": [ + "schooldayId" + ], + "referenceTable": "schoolday", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "schoolday_event_fk_3", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "schoolday_event_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "subject", + "dartName": "Subject", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('subject_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "color", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "subject_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_category", + "dartName": "SupportCategory", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_category_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "categoryId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "parentCategory", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "support_category_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_category_goal", + "dartName": "SupportGoal", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_category_goal_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "goalId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "achievedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "strategies", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "supportCategoryId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "_learningSupportPlanSupportgoalsLearningSupportPlanId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportCategoryCategorygoalsSupportCategoryId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_pupilDataSupportgoalsPupilDataId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "support_category_goal_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_goal_fk_1", + "columns": [ + "supportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "support_category_goal_fk_2", + "columns": [ + "_learningSupportPlanSupportgoalsLearningSupportPlanId" + ], + "referenceTable": "learning_support_plan", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_goal_fk_3", + "columns": [ + "_supportCategoryCategorygoalsSupportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_goal_fk_4", + "columns": [ + "_pupilDataSupportgoalsPupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "support_category_goal_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_category_status", + "dartName": "SupportCategoryStatus", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_category_status_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "supportCategoryId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "learningSupportPlanId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "_learningSupportPlanSupportcategorystatusesLearningSupporfb7bId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportCategoryCategorystatuesSupportCategoryId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_pupilDataSupportcategorystatusesPupilDataId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "support_category_status_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_1", + "columns": [ + "supportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_2", + "columns": [ + "learningSupportPlanId" + ], + "referenceTable": "learning_support_plan", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_3", + "columns": [ + "_learningSupportPlanSupportcategorystatusesLearningSupporfb7bId" + ], + "referenceTable": "learning_support_plan", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_4", + "columns": [ + "_supportCategoryCategorystatuesSupportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_5", + "columns": [ + "_pupilDataSupportcategorystatusesPupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "support_category_status_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_goal_check", + "dartName": "SupportGoalCheck", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_goal_check_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "checkId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "supportGoalId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "_supportCategoryGoalGoalchecksSupportCategoryGoalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "support_goal_check_fk_0", + "columns": [ + "supportGoalId" + ], + "referenceTable": "support_category_goal", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "support_goal_check_fk_1", + "columns": [ + "_supportCategoryGoalGoalchecksSupportCategoryGoalId" + ], + "referenceTable": "support_category_goal", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "support_goal_check_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_level", + "dartName": "SupportLevel", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_level_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "level", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "support_level_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "support_level_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "timetable", + "dartName": "Timetable", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('timetable_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "active", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "startsAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "endsAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modified", + "columnType": 8, + "isNullable": true, + "dartType": "List<( {String modifiedBy, DateTime modifiedAt})>?" + } + ], + "foreignKeys": [ + { + "constraintName": "timetable_fk_0", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "timetable_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "timetable_school_semester_idx", + "elements": [ + { + "type": 0, + "definition": "schoolSemesterId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "timetable_slot", + "dartName": "TimetableSlot", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('timetable_slot_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "day", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:Weekday" + }, + { + "name": "startTime", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "endTime", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "timetableId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "timetable_slot_fk_0", + "columns": [ + "timetableId" + ], + "referenceTable": "timetable", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "timetable_slot_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "user", + "dartName": "User", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('user_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userInfoId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "role", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:Role" + }, + { + "name": "matrixUserId", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "timeUnits", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "reliefTimeUnits", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilsAuth", + "columnType": 8, + "isNullable": true, + "dartType": "Set?" + }, + { + "name": "schooldayEventsProcessingTeam", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "credit", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "userFlags", + "columnType": 8, + "isNullable": false, + "dartType": "protocol:UserFlags" + } + ], + "foreignKeys": [ + { + "constraintName": "user_fk_0", + "columns": [ + "userInfoId" + ], + "referenceTable": "serverpod_user_info", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "user_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "user_info_id_unique_idx", + "elements": [ + { + "type": 0, + "definition": "userInfoId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "user_device", + "dartName": "UserDevice", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('user_device_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userInfoId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "deviceId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "deviceName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "lastLogin", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "isActive", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "authId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "user_device_fk_0", + "columns": [ + "userInfoId" + ], + "referenceTable": "serverpod_user_info", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "user_device_fk_1", + "columns": [ + "authId" + ], + "referenceTable": "serverpod_auth_key", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "user_device_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "auth_key_user_device_idx", + "elements": [ + { + "type": 0, + "definition": "authId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "workbook", + "dartName": "Workbook", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('workbook_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "subject", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "level", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "amount", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "imageUrl", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "workbook_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "serverpod_cloud_storage", + "dartName": "CloudStorageEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_cloud_storage_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "storageId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "path", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "addedTime", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "expiration", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "byteData", + "columnType": 5, + "isNullable": false, + "dartType": "dart:typed_data:ByteData" + }, + { + "name": "verified", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_cloud_storage_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_cloud_storage_path_idx", + "elements": [ + { + "type": 0, + "definition": "storageId" + }, + { + "type": 0, + "definition": "path" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + }, + { + "indexName": "serverpod_cloud_storage_expiration", + "elements": [ + { + "type": 0, + "definition": "expiration" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_cloud_storage_direct_upload", + "dartName": "CloudStorageDirectUploadEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_cloud_storage_direct_upload_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "storageId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "path", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "expiration", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "authKey", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_cloud_storage_direct_upload_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_cloud_storage_direct_upload_storage_path", + "elements": [ + { + "type": 0, + "definition": "storageId" + }, + { + "type": 0, + "definition": "path" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_future_call", + "dartName": "FutureCallEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_future_call_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "time", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "serializedObject", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "serverId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "identifier", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_future_call_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_future_call_time_idx", + "elements": [ + { + "type": 0, + "definition": "time" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "serverpod_future_call_serverId_idx", + "elements": [ + { + "type": 0, + "definition": "serverId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "serverpod_future_call_identifier_idx", + "elements": [ + { + "type": 0, + "definition": "identifier" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_health_connection_info", + "dartName": "ServerHealthConnectionInfo", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_health_connection_info_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "serverId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "timestamp", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "active", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "closing", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "idle", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "granularity", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_health_connection_info_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_health_connection_info_timestamp_idx", + "elements": [ + { + "type": 0, + "definition": "timestamp" + }, + { + "type": 0, + "definition": "serverId" + }, + { + "type": 0, + "definition": "granularity" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_health_metric", + "dartName": "ServerHealthMetric", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_health_metric_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "serverId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "timestamp", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "isHealthy", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "value", + "columnType": 3, + "isNullable": false, + "dartType": "double" + }, + { + "name": "granularity", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_health_metric_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_health_metric_timestamp_idx", + "elements": [ + { + "type": 0, + "definition": "timestamp" + }, + { + "type": 0, + "definition": "serverId" + }, + { + "type": 0, + "definition": "name" + }, + { + "type": 0, + "definition": "granularity" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_log", + "dartName": "LogEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_log_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "sessionLogId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "messageId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "reference", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "serverId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "time", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "logLevel", + "columnType": 6, + "isNullable": false, + "dartType": "protocol:LogLevel" + }, + { + "name": "message", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "error", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "stackTrace", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "order", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "serverpod_log_fk_0", + "columns": [ + "sessionLogId" + ], + "referenceTable": "serverpod_session_log", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "serverpod_log_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_log_sessionLogId_idx", + "elements": [ + { + "type": 0, + "definition": "sessionLogId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_message_log", + "dartName": "MessageLogEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_message_log_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "sessionLogId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "serverId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "messageId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "endpoint", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "messageName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "duration", + "columnType": 3, + "isNullable": false, + "dartType": "double" + }, + { + "name": "error", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "stackTrace", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "slow", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "order", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "serverpod_message_log_fk_0", + "columns": [ + "sessionLogId" + ], + "referenceTable": "serverpod_session_log", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "serverpod_message_log_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "serverpod_method", + "dartName": "MethodInfo", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_method_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "endpoint", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "method", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_method_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_method_endpoint_method_idx", + "elements": [ + { + "type": 0, + "definition": "endpoint" + }, + { + "type": 0, + "definition": "method" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_migrations", + "dartName": "DatabaseMigrationVersion", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_migrations_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "module", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "version", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "timestamp", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_migrations_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_migrations_ids", + "elements": [ + { + "type": 0, + "definition": "module" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_query_log", + "dartName": "QueryLogEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_query_log_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "serverId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "sessionLogId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "messageId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "query", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "duration", + "columnType": 3, + "isNullable": false, + "dartType": "double" + }, + { + "name": "numRows", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "error", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "stackTrace", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "slow", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "order", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "serverpod_query_log_fk_0", + "columns": [ + "sessionLogId" + ], + "referenceTable": "serverpod_session_log", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "serverpod_query_log_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_query_log_sessionLogId_idx", + "elements": [ + { + "type": 0, + "definition": "sessionLogId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_readwrite_test", + "dartName": "ReadWriteTestEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_readwrite_test_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "number", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_readwrite_test_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "serverpod_runtime_settings", + "dartName": "RuntimeSettings", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_runtime_settings_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "logSettings", + "columnType": 8, + "isNullable": false, + "dartType": "protocol:LogSettings" + }, + { + "name": "logSettingsOverrides", + "columnType": 8, + "isNullable": false, + "dartType": "List" + }, + { + "name": "logServiceCalls", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "logMalformedCalls", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_runtime_settings_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "serverpod_session_log", + "dartName": "SessionLogEntry", + "module": "serverpod", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_session_log_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "serverId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "time", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "module", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "endpoint", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "method", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "duration", + "columnType": 3, + "isNullable": true, + "dartType": "double?" + }, + { + "name": "numQueries", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "slow", + "columnType": 1, + "isNullable": true, + "dartType": "bool?" + }, + { + "name": "error", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "stackTrace", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "authenticatedUserId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "isOpen", + "columnType": 1, + "isNullable": true, + "dartType": "bool?" + }, + { + "name": "touched", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_session_log_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_session_log_serverid_idx", + "elements": [ + { + "type": 0, + "definition": "serverId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "serverpod_session_log_touched_idx", + "elements": [ + { + "type": 0, + "definition": "touched" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "serverpod_session_log_isopen_idx", + "elements": [ + { + "type": 0, + "definition": "isOpen" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_auth_key", + "dartName": "AuthKey", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_auth_key_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "hash", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "scopeNames", + "columnType": 8, + "isNullable": false, + "dartType": "List" + }, + { + "name": "method", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_auth_key_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_auth_key_userId_idx", + "elements": [ + { + "type": 0, + "definition": "userId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_email_auth", + "dartName": "EmailAuth", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_email_auth_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "email", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "hash", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_email_auth_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_email_auth_email", + "elements": [ + { + "type": 0, + "definition": "email" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_email_create_request", + "dartName": "EmailCreateAccountRequest", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_email_create_request_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "email", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "hash", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "verificationCode", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_email_create_request_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_email_auth_create_account_request_idx", + "elements": [ + { + "type": 0, + "definition": "email" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_email_failed_sign_in", + "dartName": "EmailFailedSignIn", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_email_failed_sign_in_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "email", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "time", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "ipAddress", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_email_failed_sign_in_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_email_failed_sign_in_email_idx", + "elements": [ + { + "type": 0, + "definition": "email" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "serverpod_email_failed_sign_in_time_idx", + "elements": [ + { + "type": 0, + "definition": "time" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_email_reset", + "dartName": "EmailReset", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_email_reset_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "verificationCode", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "expiration", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_email_reset_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_email_reset_verification_idx", + "elements": [ + { + "type": 0, + "definition": "verificationCode" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_google_refresh_token", + "dartName": "GoogleRefreshToken", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_google_refresh_token_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "refreshToken", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_google_refresh_token_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_google_refresh_token_userId_idx", + "elements": [ + { + "type": 0, + "definition": "userId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_user_image", + "dartName": "UserImage", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_user_image_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "version", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "url", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_user_image_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_user_image_user_id", + "elements": [ + { + "type": 0, + "definition": "userId" + }, + { + "type": 0, + "definition": "version" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "serverpod_user_info", + "dartName": "UserInfo", + "module": "serverpod_auth", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('serverpod_user_info_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userIdentifier", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "userName", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "fullName", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "email", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "created", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "imageUrl", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "scopeNames", + "columnType": 8, + "isNullable": false, + "dartType": "List" + }, + { + "name": "blocked", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "serverpod_user_info_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "serverpod_user_info_user_identifier", + "elements": [ + { + "type": 0, + "definition": "userIdentifier" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + }, + { + "indexName": "serverpod_user_info_email", + "elements": [ + { + "type": 0, + "definition": "email" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + } + ], + "installedModules": [ + { + "module": "school_data_hub", + "version": "20251204113239243" + }, + { + "module": "serverpod", + "version": "20240516151843329" + }, + { + "module": "serverpod_auth", + "version": "20240520102713718" + } + ], + "migrationApiVersion": 1 +} \ No newline at end of file diff --git a/school_data_hub_server/migrations/20251204113239243/definition.sql b/school_data_hub_server/migrations/20251204113239243/definition.sql new file mode 100644 index 00000000..830e37d4 --- /dev/null +++ b/school_data_hub_server/migrations/20251204113239243/definition.sql @@ -0,0 +1,1728 @@ +BEGIN; + +-- +-- Class Authorization as table authorization +-- +CREATE TABLE "authorization" ( + "id" bigserial PRIMARY KEY, + "name" text NOT NULL, + "description" text NOT NULL, + "createdBy" text NOT NULL +); + +-- +-- Class Book as table book +-- +CREATE TABLE "book" ( + "id" bigserial PRIMARY KEY, + "isbn" bigint NOT NULL, + "title" text NOT NULL, + "author" text NOT NULL, + "description" text NOT NULL, + "readingLevel" text, + "imagePath" text NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "book_id_unique_idx" ON "book" USING btree ("isbn"); + +-- +-- Class BookTag as table book_tag +-- +CREATE TABLE "book_tag" ( + "id" bigserial PRIMARY KEY, + "name" text NOT NULL +); + +-- +-- Class BookTagging as table book_tagging +-- +CREATE TABLE "book_tagging" ( + "id" bigserial PRIMARY KEY, + "bookId" bigint NOT NULL, + "bookTagId" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "book_tagging_index_idx" ON "book_tagging" USING btree ("bookId", "bookTagId"); + +-- +-- Class Competence as table competence +-- +CREATE TABLE "competence" ( + "id" bigserial PRIMARY KEY, + "publicId" bigint NOT NULL, + "parentCompetence" bigint, + "name" text NOT NULL, + "level" json, + "indicators" json, + "order" bigint +); + +-- +-- Class CompetenceCheck as table competence_check +-- +CREATE TABLE "competence_check" ( + "id" bigserial PRIMARY KEY, + "checkId" text NOT NULL, + "score" bigint NOT NULL, + "comment" text, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "valueFactor" double precision NOT NULL, + "groupCheckId" text, + "groupCheckName" text, + "pupilId" bigint NOT NULL, + "competenceId" bigint NOT NULL +); + +-- +-- Class CompetenceGoal as table competence_goal +-- +CREATE TABLE "competence_goal" ( + "id" bigserial PRIMARY KEY, + "publicId" text NOT NULL, + "description" text NOT NULL, + "strategies" json, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "modifiedBy" text NOT NULL, + "score" bigint, + "achievedAt" timestamp without time zone NOT NULL, + "pupilId" bigint NOT NULL, + "competenceId" bigint NOT NULL +); + +-- +-- Class CompetenceReport as table competence_report +-- +CREATE TABLE "competence_report" ( + "id" bigserial PRIMARY KEY, + "reportId" text NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "modifiedBy" text NOT NULL, + "achievement" text NOT NULL, + "achievedAt" timestamp without time zone NOT NULL, + "pupilId" bigint NOT NULL, + "schoolSemesterId" bigint NOT NULL +); + +-- +-- Class CompetenceReportCheck as table competence_report_check +-- +CREATE TABLE "competence_report_check" ( + "id" bigserial PRIMARY KEY, + "publicId" text NOT NULL, + "achievement" bigint NOT NULL, + "comment" text NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "pupilId" bigint NOT NULL, + "competenceId" bigint NOT NULL, + "competenceReportId" bigint NOT NULL +); + +-- +-- Class CompetenceReportItem as table competence_report_item +-- +CREATE TABLE "competence_report_item" ( + "id" bigserial PRIMARY KEY, + "publicId" bigint NOT NULL, + "parentItem" bigint, + "name" text NOT NULL, + "level" json, + "order" bigint +); + +-- +-- Class CompulsoryRoom as table compulsory_room +-- +CREATE TABLE "compulsory_room" ( + "id" bigserial PRIMARY KEY, + "roomId" text NOT NULL, + "roomType" text NOT NULL +); + +-- +-- Class CreditTransaction as table credit_transaction +-- +CREATE TABLE "credit_transaction" ( + "id" bigserial PRIMARY KEY, + "sender" text NOT NULL, + "receiver" bigint NOT NULL, + "amount" bigint NOT NULL, + "dateTime" timestamp without time zone NOT NULL, + "description" text, + "_pupilDataCredittransactionsPupilDataId" bigint +); + +-- Indexes +CREATE INDEX "reciever_idx" ON "credit_transaction" USING btree ("receiver"); +CREATE INDEX "sender_idx" ON "credit_transaction" USING btree ("sender"); + +-- +-- Class HubDocument as table hub_document +-- +CREATE TABLE "hub_document" ( + "id" bigserial PRIMARY KEY, + "documentId" text NOT NULL, + "documentPath" text, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "_pupilBookLendingPupilbooklendingfilesPupilBookLendingId" bigint, + "_competenceCheckDocumentsCompetenceCheckId" bigint, + "_competenceGoalDocumentsCompetenceGoalId" bigint, + "_supportCategoryStatusDocumentsSupportCategoryStatusId" bigint, + "_supportGoalCheckDocumentsSupportGoalCheckId" bigint, + "_preSchoolMedicalPreschoolmedicalfilesPreSchoolMedicalId" bigint, + "_preSchoolTestPreschooltestdocumentsPreSchoolTestId" bigint +); + +-- +-- Class Kindergarden as table kindergarden +-- +CREATE TABLE "kindergarden" ( + "id" bigserial PRIMARY KEY, + "name" text NOT NULL, + "phone" text NOT NULL, + "address" text NOT NULL, + "email" text NOT NULL, + "contactPerson" text NOT NULL +); + +-- +-- Class LastPupilIdentiesUpdate as table last_pupil_identities_update +-- +CREATE TABLE "last_pupil_identities_update" ( + "id" bigserial PRIMARY KEY, + "date" timestamp without time zone +); + +-- +-- Class LearningSupportPlan as table learning_support_plan +-- +CREATE TABLE "learning_support_plan" ( + "id" bigserial PRIMARY KEY, + "planId" text NOT NULL, + "number" bigint, + "createdBy" text NOT NULL, + "socialPedagogue" text, + "proffesionalsInvolved" text, + "strengthsDescription" text, + "problemsDescription" text, + "learningSupportLevelId" bigint NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "comment" text, + "pupilId" bigint NOT NULL, + "schoolSemesterId" bigint NOT NULL +); + +-- +-- Class Lesson as table lesson +-- +CREATE TABLE "lesson" ( + "id" bigserial PRIMARY KEY, + "publicId" text NOT NULL, + "subjectId" bigint NOT NULL +); + +-- +-- Class LessonAttendance as table lesson_attendance +-- +CREATE TABLE "lesson_attendance" ( + "id" bigserial PRIMARY KEY, + "lessonId" bigint NOT NULL, + "pupilId" bigint NOT NULL, + "comment" text, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "modifiedBy" text NOT NULL, + "modifiedAt" timestamp without time zone NOT NULL +); + +-- +-- Class LessonGroup as table lesson_group +-- +CREATE TABLE "lesson_group" ( + "id" bigserial PRIMARY KEY, + "publicId" text NOT NULL, + "name" text NOT NULL, + "color" text, + "timetableId" bigint NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "modifiedBy" text, + "modifiedAt" timestamp without time zone +); + +-- +-- Class ScheduledLessonGroupMembership as table lesson_group_pupil +-- +CREATE TABLE "lesson_group_pupil" ( + "id" bigserial PRIMARY KEY, + "lessonGroupId" bigint NOT NULL, + "pupilDataId" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "lesson_group_membership_index_idx" ON "lesson_group_pupil" USING btree ("lessonGroupId", "pupilDataId"); + +-- +-- Class LessonTeacher as table lesson_teacher +-- +CREATE TABLE "lesson_teacher" ( + "id" bigserial PRIMARY KEY, + "userId" bigint NOT NULL, + "scheduledLessonId" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "lesson_teacher_unique_idx" ON "lesson_teacher" USING btree ("userId", "scheduledLessonId"); + +-- +-- Class LibraryBook as table library_book +-- +CREATE TABLE "library_book" ( + "id" bigserial PRIMARY KEY, + "libraryId" text NOT NULL, + "bookId" bigint NOT NULL, + "locationId" bigint NOT NULL, + "available" boolean NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "library_id_unique_idx" ON "library_book" USING btree ("libraryId"); + +-- +-- Class LibraryBookLocation as table library_book_location +-- +CREATE TABLE "library_book_location" ( + "id" bigserial PRIMARY KEY, + "location" text NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "location_unique_idx" ON "library_book_location" USING btree ("location"); + +-- +-- Class MissedSchoolday as table missed_class +-- +CREATE TABLE "missed_class" ( + "id" bigserial PRIMARY KEY, + "missedType" text NOT NULL, + "unexcused" boolean NOT NULL, + "contacted" text NOT NULL, + "returned" boolean NOT NULL, + "returnedAt" timestamp without time zone, + "writtenExcuse" boolean NOT NULL, + "minutesLate" bigint, + "createdBy" text NOT NULL, + "modifiedBy" text, + "comment" text, + "schooldayId" bigint NOT NULL, + "pupilId" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "schoolday_pupil_data_idx" ON "missed_class" USING btree ("schooldayId", "pupilId"); + +-- +-- Class PreSchoolMedical as table pre_school_medical +-- +CREATE TABLE "pre_school_medical" ( + "id" bigserial PRIMARY KEY, + "preschoolMedicalStatus" text, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "updatedBy" text, + "updatedAt" timestamp without time zone +); + +-- +-- Class PreSchoolTest as table pre_school_test +-- +CREATE TABLE "pre_school_test" ( + "id" bigserial PRIMARY KEY, + "careNeedsIntensity" bigint +); + +-- +-- Class PupilAuthorization as table pupil_authorization +-- +CREATE TABLE "pupil_authorization" ( + "id" bigserial PRIMARY KEY, + "status" boolean, + "comment" text, + "createdBy" text, + "fileId" bigint, + "authorizationId" bigint NOT NULL, + "pupilId" bigint NOT NULL +); + +-- +-- Class PupilBookLending as table pupil_book_lending +-- +CREATE TABLE "pupil_book_lending" ( + "id" bigserial PRIMARY KEY, + "lendingId" text NOT NULL, + "status" text, + "score" bigint NOT NULL, + "lentAt" timestamp without time zone NOT NULL, + "lentBy" text NOT NULL, + "returnedAt" timestamp without time zone, + "receivedBy" text, + "pupilId" bigint NOT NULL, + "isbn" bigint NOT NULL, + "libraryBookId" bigint NOT NULL +); + +-- +-- Class PupilData as table pupil_data +-- +CREATE TABLE "pupil_data" ( + "id" bigserial PRIMARY KEY, + "status" text NOT NULL, + "internalId" bigint NOT NULL, + "password" text, + "preSchoolMedicalId" bigint, + "kindergardenId" bigint, + "kindergardenData" json, + "preSchoolTestId" bigint, + "avatarId" bigint, + "avatarAuthId" bigint, + "publicMediaAuth" json NOT NULL, + "publicMediaAuthDocumentId" bigint, + "contact" text, + "communicationPupil" json, + "specialInformation" text, + "tutorInfo" json, + "afterSchoolCare" json, + "credit" bigint NOT NULL, + "creditEarned" bigint NOT NULL, + "schoolyearHeldBackAt" timestamp without time zone, + "swimmer" text, + "_kindergardenPupilsKindergardenId" bigint +); + +-- Indexes +CREATE INDEX "pupil_data_status_idx" ON "pupil_data" USING btree ("status", "internalId"); +CREATE UNIQUE INDEX "pupil_data_internal_id_idx" ON "pupil_data" USING btree ("internalId"); + +-- +-- Class PupilListEntry as table pupil_list_entry +-- +CREATE TABLE "pupil_list_entry" ( + "id" bigserial PRIMARY KEY, + "status" boolean, + "comment" text, + "entryBy" text, + "schoolListId" bigint NOT NULL, + "pupilId" bigint NOT NULL +); + +-- +-- Class PupilWorkbook as table pupil_workbook +-- +CREATE TABLE "pupil_workbook" ( + "id" bigserial PRIMARY KEY, + "isbn" bigint NOT NULL, + "comment" text, + "score" bigint NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "finishedAt" timestamp without time zone, + "pupilId" bigint NOT NULL, + "workbookId" bigint NOT NULL +); + +-- +-- Class Classroom as table room +-- +CREATE TABLE "room" ( + "id" bigserial PRIMARY KEY, + "roomCode" text NOT NULL, + "roomName" text NOT NULL +); + +-- +-- Class ScheduledLesson as table scheduled_lesson +-- +CREATE TABLE "scheduled_lesson" ( + "id" bigserial PRIMARY KEY, + "active" boolean NOT NULL, + "subjectId" bigint NOT NULL, + "scheduledAtId" bigint NOT NULL, + "timetableSlotOrder" bigint NOT NULL, + "timetableId" bigint NOT NULL, + "mainTeacherId" bigint NOT NULL, + "lessonId" text NOT NULL, + "roomId" bigint NOT NULL, + "lessonGroupId" bigint NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "modifiedBy" text, + "modifiedAt" timestamp without time zone, + "recordtest" json, + "_roomScheduledlessonsRoomId" bigint +); + +-- +-- Class ScheduledLessonTeacher as table scheduled_lesson_teacher +-- +CREATE TABLE "scheduled_lesson_teacher" ( + "id" bigserial PRIMARY KEY, + "userId" bigint NOT NULL, + "scheduledLessonId" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "scheduled_lesson_teacher_unique_idx" ON "scheduled_lesson_teacher" USING btree ("userId", "scheduledLessonId"); + +-- +-- Class SchoolData as table school_data +-- +CREATE TABLE "school_data" ( + "id" bigserial PRIMARY KEY, + "name" text NOT NULL, + "officialName" text NOT NULL, + "address" text NOT NULL, + "schoolNumber" text NOT NULL, + "telephoneNumber" text NOT NULL, + "email" text NOT NULL, + "website" text NOT NULL, + "logoId" bigint, + "officialSealId" bigint +); + +-- +-- Class SchoolList as table school_list +-- +CREATE TABLE "school_list" ( + "id" bigserial PRIMARY KEY, + "listId" text NOT NULL, + "archived" boolean NOT NULL, + "name" text NOT NULL, + "description" text NOT NULL, + "createdBy" text NOT NULL, + "public" boolean NOT NULL, + "authorizedUsers" text +); + +-- +-- Class SchoolSemester as table school_semester +-- +CREATE TABLE "school_semester" ( + "id" bigserial PRIMARY KEY, + "schoolYear" text NOT NULL, + "isFirst" boolean NOT NULL, + "startDate" timestamp without time zone NOT NULL, + "endDate" timestamp without time zone NOT NULL, + "classConferenceDate" timestamp without time zone, + "supportConferenceDate" timestamp without time zone, + "reportConferenceDate" timestamp without time zone, + "reportSignedDate" timestamp without time zone +); + +-- +-- Class Schoolday as table schoolday +-- +CREATE TABLE "schoolday" ( + "id" bigserial PRIMARY KEY, + "schoolday" timestamp without time zone NOT NULL, + "schoolSemesterId" bigint NOT NULL +); + +-- +-- Class SchooldayEvent as table schoolday_event +-- +CREATE TABLE "schoolday_event" ( + "id" bigserial PRIMARY KEY, + "eventId" text NOT NULL, + "eventType" text NOT NULL, + "eventReason" text NOT NULL, + "createdBy" text NOT NULL, + "processed" boolean NOT NULL, + "processedBy" text, + "processedAt" timestamp without time zone, + "documentId" bigint, + "processedDocumentId" bigint, + "schooldayId" bigint NOT NULL, + "pupilId" bigint NOT NULL, + "comment" text +); + +-- +-- Class Subject as table subject +-- +CREATE TABLE "subject" ( + "id" bigserial PRIMARY KEY, + "publicId" text NOT NULL, + "name" text NOT NULL, + "description" text, + "color" text, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "modifiedBy" text NOT NULL +); + +-- +-- Class SupportCategory as table support_category +-- +CREATE TABLE "support_category" ( + "id" bigserial PRIMARY KEY, + "name" text NOT NULL, + "categoryId" bigint NOT NULL, + "parentCategory" bigint +); + +-- +-- Class SupportGoal as table support_category_goal +-- +CREATE TABLE "support_category_goal" ( + "id" bigserial PRIMARY KEY, + "goalId" text NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "score" bigint NOT NULL, + "achievedAt" timestamp without time zone, + "description" text NOT NULL, + "strategies" text NOT NULL, + "pupilId" bigint NOT NULL, + "supportCategoryId" bigint NOT NULL, + "_learningSupportPlanSupportgoalsLearningSupportPlanId" bigint, + "_supportCategoryCategorygoalsSupportCategoryId" bigint, + "_pupilDataSupportgoalsPupilDataId" bigint +); + +-- +-- Class SupportCategoryStatus as table support_category_status +-- +CREATE TABLE "support_category_status" ( + "id" bigserial PRIMARY KEY, + "score" bigint NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "comment" text NOT NULL, + "pupilId" bigint NOT NULL, + "supportCategoryId" bigint NOT NULL, + "learningSupportPlanId" bigint NOT NULL, + "_learningSupportPlanSupportcategorystatusesLearningSupporfb7bId" bigint, + "_supportCategoryCategorystatuesSupportCategoryId" bigint, + "_pupilDataSupportcategorystatusesPupilDataId" bigint +); + +-- +-- Class SupportGoalCheck as table support_goal_check +-- +CREATE TABLE "support_goal_check" ( + "id" bigserial PRIMARY KEY, + "checkId" text NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "score" bigint NOT NULL, + "comment" text NOT NULL, + "supportGoalId" bigint NOT NULL, + "_supportCategoryGoalGoalchecksSupportCategoryGoalId" bigint +); + +-- +-- Class SupportLevel as table support_level +-- +CREATE TABLE "support_level" ( + "id" bigserial PRIMARY KEY, + "level" bigint NOT NULL, + "comment" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "createdBy" text NOT NULL, + "pupilId" bigint NOT NULL +); + +-- +-- Class Timetable as table timetable +-- +CREATE TABLE "timetable" ( + "id" bigserial PRIMARY KEY, + "active" boolean NOT NULL, + "startsAt" timestamp without time zone NOT NULL, + "endsAt" timestamp without time zone, + "name" text NOT NULL, + "schoolSemesterId" bigint NOT NULL, + "createdBy" text NOT NULL, + "createdAt" timestamp without time zone NOT NULL, + "modified" json +); + +-- Indexes +CREATE INDEX "timetable_school_semester_idx" ON "timetable" USING btree ("schoolSemesterId"); + +-- +-- Class TimetableSlot as table timetable_slot +-- +CREATE TABLE "timetable_slot" ( + "id" bigserial PRIMARY KEY, + "day" text NOT NULL, + "startTime" text NOT NULL, + "endTime" text NOT NULL, + "timetableId" bigint NOT NULL +); + +-- +-- Class User as table user +-- +CREATE TABLE "user" ( + "id" bigserial PRIMARY KEY, + "userInfoId" bigint NOT NULL, + "role" text NOT NULL, + "matrixUserId" text, + "timeUnits" bigint NOT NULL, + "reliefTimeUnits" bigint NOT NULL, + "pupilsAuth" json, + "schooldayEventsProcessingTeam" text, + "credit" bigint NOT NULL, + "userFlags" json NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "user_info_id_unique_idx" ON "user" USING btree ("userInfoId"); + +-- +-- Class UserDevice as table user_device +-- +CREATE TABLE "user_device" ( + "id" bigserial PRIMARY KEY, + "userInfoId" bigint NOT NULL, + "deviceId" text NOT NULL, + "deviceName" text NOT NULL, + "lastLogin" timestamp without time zone NOT NULL, + "isActive" boolean NOT NULL, + "authId" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "auth_key_user_device_idx" ON "user_device" USING btree ("authId"); + +-- +-- Class Workbook as table workbook +-- +CREATE TABLE "workbook" ( + "id" bigserial PRIMARY KEY, + "isbn" bigint NOT NULL, + "name" text NOT NULL, + "subject" text, + "level" text, + "amount" bigint, + "imageUrl" text NOT NULL +); + +-- +-- Class CloudStorageEntry as table serverpod_cloud_storage +-- +CREATE TABLE "serverpod_cloud_storage" ( + "id" bigserial PRIMARY KEY, + "storageId" text NOT NULL, + "path" text NOT NULL, + "addedTime" timestamp without time zone NOT NULL, + "expiration" timestamp without time zone, + "byteData" bytea NOT NULL, + "verified" boolean NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_cloud_storage_path_idx" ON "serverpod_cloud_storage" USING btree ("storageId", "path"); +CREATE INDEX "serverpod_cloud_storage_expiration" ON "serverpod_cloud_storage" USING btree ("expiration"); + +-- +-- Class CloudStorageDirectUploadEntry as table serverpod_cloud_storage_direct_upload +-- +CREATE TABLE "serverpod_cloud_storage_direct_upload" ( + "id" bigserial PRIMARY KEY, + "storageId" text NOT NULL, + "path" text NOT NULL, + "expiration" timestamp without time zone NOT NULL, + "authKey" text NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_cloud_storage_direct_upload_storage_path" ON "serverpod_cloud_storage_direct_upload" USING btree ("storageId", "path"); + +-- +-- Class FutureCallEntry as table serverpod_future_call +-- +CREATE TABLE "serverpod_future_call" ( + "id" bigserial PRIMARY KEY, + "name" text NOT NULL, + "time" timestamp without time zone NOT NULL, + "serializedObject" text, + "serverId" text NOT NULL, + "identifier" text +); + +-- Indexes +CREATE INDEX "serverpod_future_call_time_idx" ON "serverpod_future_call" USING btree ("time"); +CREATE INDEX "serverpod_future_call_serverId_idx" ON "serverpod_future_call" USING btree ("serverId"); +CREATE INDEX "serverpod_future_call_identifier_idx" ON "serverpod_future_call" USING btree ("identifier"); + +-- +-- Class ServerHealthConnectionInfo as table serverpod_health_connection_info +-- +CREATE TABLE "serverpod_health_connection_info" ( + "id" bigserial PRIMARY KEY, + "serverId" text NOT NULL, + "timestamp" timestamp without time zone NOT NULL, + "active" bigint NOT NULL, + "closing" bigint NOT NULL, + "idle" bigint NOT NULL, + "granularity" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_health_connection_info_timestamp_idx" ON "serverpod_health_connection_info" USING btree ("timestamp", "serverId", "granularity"); + +-- +-- Class ServerHealthMetric as table serverpod_health_metric +-- +CREATE TABLE "serverpod_health_metric" ( + "id" bigserial PRIMARY KEY, + "name" text NOT NULL, + "serverId" text NOT NULL, + "timestamp" timestamp without time zone NOT NULL, + "isHealthy" boolean NOT NULL, + "value" double precision NOT NULL, + "granularity" bigint NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_health_metric_timestamp_idx" ON "serverpod_health_metric" USING btree ("timestamp", "serverId", "name", "granularity"); + +-- +-- Class LogEntry as table serverpod_log +-- +CREATE TABLE "serverpod_log" ( + "id" bigserial PRIMARY KEY, + "sessionLogId" bigint NOT NULL, + "messageId" bigint, + "reference" text, + "serverId" text NOT NULL, + "time" timestamp without time zone NOT NULL, + "logLevel" bigint NOT NULL, + "message" text NOT NULL, + "error" text, + "stackTrace" text, + "order" bigint NOT NULL +); + +-- Indexes +CREATE INDEX "serverpod_log_sessionLogId_idx" ON "serverpod_log" USING btree ("sessionLogId"); + +-- +-- Class MessageLogEntry as table serverpod_message_log +-- +CREATE TABLE "serverpod_message_log" ( + "id" bigserial PRIMARY KEY, + "sessionLogId" bigint NOT NULL, + "serverId" text NOT NULL, + "messageId" bigint NOT NULL, + "endpoint" text NOT NULL, + "messageName" text NOT NULL, + "duration" double precision NOT NULL, + "error" text, + "stackTrace" text, + "slow" boolean NOT NULL, + "order" bigint NOT NULL +); + +-- +-- Class MethodInfo as table serverpod_method +-- +CREATE TABLE "serverpod_method" ( + "id" bigserial PRIMARY KEY, + "endpoint" text NOT NULL, + "method" text NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_method_endpoint_method_idx" ON "serverpod_method" USING btree ("endpoint", "method"); + +-- +-- Class DatabaseMigrationVersion as table serverpod_migrations +-- +CREATE TABLE "serverpod_migrations" ( + "id" bigserial PRIMARY KEY, + "module" text NOT NULL, + "version" text NOT NULL, + "timestamp" timestamp without time zone +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_migrations_ids" ON "serverpod_migrations" USING btree ("module"); + +-- +-- Class QueryLogEntry as table serverpod_query_log +-- +CREATE TABLE "serverpod_query_log" ( + "id" bigserial PRIMARY KEY, + "serverId" text NOT NULL, + "sessionLogId" bigint NOT NULL, + "messageId" bigint, + "query" text NOT NULL, + "duration" double precision NOT NULL, + "numRows" bigint, + "error" text, + "stackTrace" text, + "slow" boolean NOT NULL, + "order" bigint NOT NULL +); + +-- Indexes +CREATE INDEX "serverpod_query_log_sessionLogId_idx" ON "serverpod_query_log" USING btree ("sessionLogId"); + +-- +-- Class ReadWriteTestEntry as table serverpod_readwrite_test +-- +CREATE TABLE "serverpod_readwrite_test" ( + "id" bigserial PRIMARY KEY, + "number" bigint NOT NULL +); + +-- +-- Class RuntimeSettings as table serverpod_runtime_settings +-- +CREATE TABLE "serverpod_runtime_settings" ( + "id" bigserial PRIMARY KEY, + "logSettings" json NOT NULL, + "logSettingsOverrides" json NOT NULL, + "logServiceCalls" boolean NOT NULL, + "logMalformedCalls" boolean NOT NULL +); + +-- +-- Class SessionLogEntry as table serverpod_session_log +-- +CREATE TABLE "serverpod_session_log" ( + "id" bigserial PRIMARY KEY, + "serverId" text NOT NULL, + "time" timestamp without time zone NOT NULL, + "module" text, + "endpoint" text, + "method" text, + "duration" double precision, + "numQueries" bigint, + "slow" boolean, + "error" text, + "stackTrace" text, + "authenticatedUserId" bigint, + "isOpen" boolean, + "touched" timestamp without time zone NOT NULL +); + +-- Indexes +CREATE INDEX "serverpod_session_log_serverid_idx" ON "serverpod_session_log" USING btree ("serverId"); +CREATE INDEX "serverpod_session_log_touched_idx" ON "serverpod_session_log" USING btree ("touched"); +CREATE INDEX "serverpod_session_log_isopen_idx" ON "serverpod_session_log" USING btree ("isOpen"); + +-- +-- Class AuthKey as table serverpod_auth_key +-- +CREATE TABLE "serverpod_auth_key" ( + "id" bigserial PRIMARY KEY, + "userId" bigint NOT NULL, + "hash" text NOT NULL, + "scopeNames" json NOT NULL, + "method" text NOT NULL +); + +-- Indexes +CREATE INDEX "serverpod_auth_key_userId_idx" ON "serverpod_auth_key" USING btree ("userId"); + +-- +-- Class EmailAuth as table serverpod_email_auth +-- +CREATE TABLE "serverpod_email_auth" ( + "id" bigserial PRIMARY KEY, + "userId" bigint NOT NULL, + "email" text NOT NULL, + "hash" text NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_email_auth_email" ON "serverpod_email_auth" USING btree ("email"); + +-- +-- Class EmailCreateAccountRequest as table serverpod_email_create_request +-- +CREATE TABLE "serverpod_email_create_request" ( + "id" bigserial PRIMARY KEY, + "userName" text NOT NULL, + "email" text NOT NULL, + "hash" text NOT NULL, + "verificationCode" text NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_email_auth_create_account_request_idx" ON "serverpod_email_create_request" USING btree ("email"); + +-- +-- Class EmailFailedSignIn as table serverpod_email_failed_sign_in +-- +CREATE TABLE "serverpod_email_failed_sign_in" ( + "id" bigserial PRIMARY KEY, + "email" text NOT NULL, + "time" timestamp without time zone NOT NULL, + "ipAddress" text NOT NULL +); + +-- Indexes +CREATE INDEX "serverpod_email_failed_sign_in_email_idx" ON "serverpod_email_failed_sign_in" USING btree ("email"); +CREATE INDEX "serverpod_email_failed_sign_in_time_idx" ON "serverpod_email_failed_sign_in" USING btree ("time"); + +-- +-- Class EmailReset as table serverpod_email_reset +-- +CREATE TABLE "serverpod_email_reset" ( + "id" bigserial PRIMARY KEY, + "userId" bigint NOT NULL, + "verificationCode" text NOT NULL, + "expiration" timestamp without time zone NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_email_reset_verification_idx" ON "serverpod_email_reset" USING btree ("verificationCode"); + +-- +-- Class GoogleRefreshToken as table serverpod_google_refresh_token +-- +CREATE TABLE "serverpod_google_refresh_token" ( + "id" bigserial PRIMARY KEY, + "userId" bigint NOT NULL, + "refreshToken" text NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_google_refresh_token_userId_idx" ON "serverpod_google_refresh_token" USING btree ("userId"); + +-- +-- Class UserImage as table serverpod_user_image +-- +CREATE TABLE "serverpod_user_image" ( + "id" bigserial PRIMARY KEY, + "userId" bigint NOT NULL, + "version" bigint NOT NULL, + "url" text NOT NULL +); + +-- Indexes +CREATE INDEX "serverpod_user_image_user_id" ON "serverpod_user_image" USING btree ("userId", "version"); + +-- +-- Class UserInfo as table serverpod_user_info +-- +CREATE TABLE "serverpod_user_info" ( + "id" bigserial PRIMARY KEY, + "userIdentifier" text NOT NULL, + "userName" text, + "fullName" text, + "email" text, + "created" timestamp without time zone NOT NULL, + "imageUrl" text, + "scopeNames" json NOT NULL, + "blocked" boolean NOT NULL +); + +-- Indexes +CREATE UNIQUE INDEX "serverpod_user_info_user_identifier" ON "serverpod_user_info" USING btree ("userIdentifier"); +CREATE INDEX "serverpod_user_info_email" ON "serverpod_user_info" USING btree ("email"); + +-- +-- Foreign relations for "book_tagging" table +-- +ALTER TABLE ONLY "book_tagging" + ADD CONSTRAINT "book_tagging_fk_0" + FOREIGN KEY("bookId") + REFERENCES "book"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "book_tagging" + ADD CONSTRAINT "book_tagging_fk_1" + FOREIGN KEY("bookTagId") + REFERENCES "book_tag"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "competence_check" table +-- +ALTER TABLE ONLY "competence_check" + ADD CONSTRAINT "competence_check_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "competence_check" + ADD CONSTRAINT "competence_check_fk_1" + FOREIGN KEY("competenceId") + REFERENCES "competence"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "competence_goal" table +-- +ALTER TABLE ONLY "competence_goal" + ADD CONSTRAINT "competence_goal_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "competence_goal" + ADD CONSTRAINT "competence_goal_fk_1" + FOREIGN KEY("competenceId") + REFERENCES "competence"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "competence_report" table +-- +ALTER TABLE ONLY "competence_report" + ADD CONSTRAINT "competence_report_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "competence_report" + ADD CONSTRAINT "competence_report_fk_1" + FOREIGN KEY("schoolSemesterId") + REFERENCES "school_semester"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "competence_report_check" table +-- +ALTER TABLE ONLY "competence_report_check" + ADD CONSTRAINT "competence_report_check_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "competence_report_check" + ADD CONSTRAINT "competence_report_check_fk_1" + FOREIGN KEY("competenceId") + REFERENCES "competence_report_item"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "competence_report_check" + ADD CONSTRAINT "competence_report_check_fk_2" + FOREIGN KEY("competenceReportId") + REFERENCES "competence_report"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "credit_transaction" table +-- +ALTER TABLE ONLY "credit_transaction" + ADD CONSTRAINT "credit_transaction_fk_0" + FOREIGN KEY("_pupilDataCredittransactionsPupilDataId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "hub_document" table +-- +ALTER TABLE ONLY "hub_document" + ADD CONSTRAINT "hub_document_fk_0" + FOREIGN KEY("_pupilBookLendingPupilbooklendingfilesPupilBookLendingId") + REFERENCES "pupil_book_lending"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "hub_document" + ADD CONSTRAINT "hub_document_fk_1" + FOREIGN KEY("_competenceCheckDocumentsCompetenceCheckId") + REFERENCES "competence_check"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "hub_document" + ADD CONSTRAINT "hub_document_fk_2" + FOREIGN KEY("_competenceGoalDocumentsCompetenceGoalId") + REFERENCES "competence_goal"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "hub_document" + ADD CONSTRAINT "hub_document_fk_3" + FOREIGN KEY("_supportCategoryStatusDocumentsSupportCategoryStatusId") + REFERENCES "support_category_status"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "hub_document" + ADD CONSTRAINT "hub_document_fk_4" + FOREIGN KEY("_supportGoalCheckDocumentsSupportGoalCheckId") + REFERENCES "support_goal_check"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "hub_document" + ADD CONSTRAINT "hub_document_fk_5" + FOREIGN KEY("_preSchoolMedicalPreschoolmedicalfilesPreSchoolMedicalId") + REFERENCES "pre_school_medical"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "hub_document" + ADD CONSTRAINT "hub_document_fk_6" + FOREIGN KEY("_preSchoolTestPreschooltestdocumentsPreSchoolTestId") + REFERENCES "pre_school_test"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "learning_support_plan" table +-- +ALTER TABLE ONLY "learning_support_plan" + ADD CONSTRAINT "learning_support_plan_fk_0" + FOREIGN KEY("learningSupportLevelId") + REFERENCES "support_level"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "learning_support_plan" + ADD CONSTRAINT "learning_support_plan_fk_1" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "learning_support_plan" + ADD CONSTRAINT "learning_support_plan_fk_2" + FOREIGN KEY("schoolSemesterId") + REFERENCES "school_semester"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "lesson" table +-- +ALTER TABLE ONLY "lesson" + ADD CONSTRAINT "lesson_fk_0" + FOREIGN KEY("subjectId") + REFERENCES "subject"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "lesson_attendance" table +-- +ALTER TABLE ONLY "lesson_attendance" + ADD CONSTRAINT "lesson_attendance_fk_0" + FOREIGN KEY("lessonId") + REFERENCES "lesson"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "lesson_attendance" + ADD CONSTRAINT "lesson_attendance_fk_1" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "lesson_group" table +-- +ALTER TABLE ONLY "lesson_group" + ADD CONSTRAINT "lesson_group_fk_0" + FOREIGN KEY("timetableId") + REFERENCES "timetable"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "lesson_group_pupil" table +-- +ALTER TABLE ONLY "lesson_group_pupil" + ADD CONSTRAINT "lesson_group_pupil_fk_0" + FOREIGN KEY("lessonGroupId") + REFERENCES "lesson_group"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "lesson_group_pupil" + ADD CONSTRAINT "lesson_group_pupil_fk_1" + FOREIGN KEY("pupilDataId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "lesson_teacher" table +-- +ALTER TABLE ONLY "lesson_teacher" + ADD CONSTRAINT "lesson_teacher_fk_0" + FOREIGN KEY("userId") + REFERENCES "user"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "lesson_teacher" + ADD CONSTRAINT "lesson_teacher_fk_1" + FOREIGN KEY("scheduledLessonId") + REFERENCES "lesson"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "library_book" table +-- +ALTER TABLE ONLY "library_book" + ADD CONSTRAINT "library_book_fk_0" + FOREIGN KEY("bookId") + REFERENCES "book"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "library_book" + ADD CONSTRAINT "library_book_fk_1" + FOREIGN KEY("locationId") + REFERENCES "library_book_location"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "missed_class" table +-- +ALTER TABLE ONLY "missed_class" + ADD CONSTRAINT "missed_class_fk_0" + FOREIGN KEY("schooldayId") + REFERENCES "schoolday"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; +ALTER TABLE ONLY "missed_class" + ADD CONSTRAINT "missed_class_fk_1" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "pupil_authorization" table +-- +ALTER TABLE ONLY "pupil_authorization" + ADD CONSTRAINT "pupil_authorization_fk_0" + FOREIGN KEY("fileId") + REFERENCES "hub_document"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_authorization" + ADD CONSTRAINT "pupil_authorization_fk_1" + FOREIGN KEY("authorizationId") + REFERENCES "authorization"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_authorization" + ADD CONSTRAINT "pupil_authorization_fk_2" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "pupil_book_lending" table +-- +ALTER TABLE ONLY "pupil_book_lending" + ADD CONSTRAINT "pupil_book_lending_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_book_lending" + ADD CONSTRAINT "pupil_book_lending_fk_1" + FOREIGN KEY("libraryBookId") + REFERENCES "library_book"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "pupil_data" table +-- +ALTER TABLE ONLY "pupil_data" + ADD CONSTRAINT "pupil_data_fk_0" + FOREIGN KEY("preSchoolMedicalId") + REFERENCES "pre_school_medical"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_data" + ADD CONSTRAINT "pupil_data_fk_1" + FOREIGN KEY("kindergardenId") + REFERENCES "kindergarden"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_data" + ADD CONSTRAINT "pupil_data_fk_2" + FOREIGN KEY("preSchoolTestId") + REFERENCES "pre_school_test"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_data" + ADD CONSTRAINT "pupil_data_fk_3" + FOREIGN KEY("avatarId") + REFERENCES "hub_document"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_data" + ADD CONSTRAINT "pupil_data_fk_4" + FOREIGN KEY("avatarAuthId") + REFERENCES "hub_document"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_data" + ADD CONSTRAINT "pupil_data_fk_5" + FOREIGN KEY("publicMediaAuthDocumentId") + REFERENCES "hub_document"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_data" + ADD CONSTRAINT "pupil_data_fk_6" + FOREIGN KEY("_kindergardenPupilsKindergardenId") + REFERENCES "kindergarden"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "pupil_list_entry" table +-- +ALTER TABLE ONLY "pupil_list_entry" + ADD CONSTRAINT "pupil_list_entry_fk_0" + FOREIGN KEY("schoolListId") + REFERENCES "school_list"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_list_entry" + ADD CONSTRAINT "pupil_list_entry_fk_1" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "pupil_workbook" table +-- +ALTER TABLE ONLY "pupil_workbook" + ADD CONSTRAINT "pupil_workbook_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; +ALTER TABLE ONLY "pupil_workbook" + ADD CONSTRAINT "pupil_workbook_fk_1" + FOREIGN KEY("workbookId") + REFERENCES "workbook"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "scheduled_lesson" table +-- +ALTER TABLE ONLY "scheduled_lesson" + ADD CONSTRAINT "scheduled_lesson_fk_0" + FOREIGN KEY("subjectId") + REFERENCES "subject"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "scheduled_lesson" + ADD CONSTRAINT "scheduled_lesson_fk_1" + FOREIGN KEY("scheduledAtId") + REFERENCES "timetable_slot"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "scheduled_lesson" + ADD CONSTRAINT "scheduled_lesson_fk_2" + FOREIGN KEY("timetableId") + REFERENCES "timetable"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "scheduled_lesson" + ADD CONSTRAINT "scheduled_lesson_fk_3" + FOREIGN KEY("roomId") + REFERENCES "room"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "scheduled_lesson" + ADD CONSTRAINT "scheduled_lesson_fk_4" + FOREIGN KEY("lessonGroupId") + REFERENCES "lesson_group"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "scheduled_lesson" + ADD CONSTRAINT "scheduled_lesson_fk_5" + FOREIGN KEY("_roomScheduledlessonsRoomId") + REFERENCES "room"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "scheduled_lesson_teacher" table +-- +ALTER TABLE ONLY "scheduled_lesson_teacher" + ADD CONSTRAINT "scheduled_lesson_teacher_fk_0" + FOREIGN KEY("userId") + REFERENCES "user"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "scheduled_lesson_teacher" + ADD CONSTRAINT "scheduled_lesson_teacher_fk_1" + FOREIGN KEY("scheduledLessonId") + REFERENCES "scheduled_lesson"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "school_data" table +-- +ALTER TABLE ONLY "school_data" + ADD CONSTRAINT "school_data_fk_0" + FOREIGN KEY("logoId") + REFERENCES "hub_document"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "school_data" + ADD CONSTRAINT "school_data_fk_1" + FOREIGN KEY("officialSealId") + REFERENCES "hub_document"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "schoolday" table +-- +ALTER TABLE ONLY "schoolday" + ADD CONSTRAINT "schoolday_fk_0" + FOREIGN KEY("schoolSemesterId") + REFERENCES "school_semester"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "schoolday_event" table +-- +ALTER TABLE ONLY "schoolday_event" + ADD CONSTRAINT "schoolday_event_fk_0" + FOREIGN KEY("documentId") + REFERENCES "hub_document"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "schoolday_event" + ADD CONSTRAINT "schoolday_event_fk_1" + FOREIGN KEY("processedDocumentId") + REFERENCES "hub_document"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "schoolday_event" + ADD CONSTRAINT "schoolday_event_fk_2" + FOREIGN KEY("schooldayId") + REFERENCES "schoolday"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "schoolday_event" + ADD CONSTRAINT "schoolday_event_fk_3" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "support_category_goal" table +-- +ALTER TABLE ONLY "support_category_goal" + ADD CONSTRAINT "support_category_goal_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_goal" + ADD CONSTRAINT "support_category_goal_fk_1" + FOREIGN KEY("supportCategoryId") + REFERENCES "support_category"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_goal" + ADD CONSTRAINT "support_category_goal_fk_2" + FOREIGN KEY("_learningSupportPlanSupportgoalsLearningSupportPlanId") + REFERENCES "learning_support_plan"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_goal" + ADD CONSTRAINT "support_category_goal_fk_3" + FOREIGN KEY("_supportCategoryCategorygoalsSupportCategoryId") + REFERENCES "support_category"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_goal" + ADD CONSTRAINT "support_category_goal_fk_4" + FOREIGN KEY("_pupilDataSupportgoalsPupilDataId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "support_category_status" table +-- +ALTER TABLE ONLY "support_category_status" + ADD CONSTRAINT "support_category_status_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_status" + ADD CONSTRAINT "support_category_status_fk_1" + FOREIGN KEY("supportCategoryId") + REFERENCES "support_category"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_status" + ADD CONSTRAINT "support_category_status_fk_2" + FOREIGN KEY("learningSupportPlanId") + REFERENCES "learning_support_plan"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_status" + ADD CONSTRAINT "support_category_status_fk_3" + FOREIGN KEY("_learningSupportPlanSupportcategorystatusesLearningSupporfb7bId") + REFERENCES "learning_support_plan"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_status" + ADD CONSTRAINT "support_category_status_fk_4" + FOREIGN KEY("_supportCategoryCategorystatuesSupportCategoryId") + REFERENCES "support_category"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_category_status" + ADD CONSTRAINT "support_category_status_fk_5" + FOREIGN KEY("_pupilDataSupportcategorystatusesPupilDataId") + REFERENCES "pupil_data"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "support_goal_check" table +-- +ALTER TABLE ONLY "support_goal_check" + ADD CONSTRAINT "support_goal_check_fk_0" + FOREIGN KEY("supportGoalId") + REFERENCES "support_category_goal"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; +ALTER TABLE ONLY "support_goal_check" + ADD CONSTRAINT "support_goal_check_fk_1" + FOREIGN KEY("_supportCategoryGoalGoalchecksSupportCategoryGoalId") + REFERENCES "support_category_goal"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "support_level" table +-- +ALTER TABLE ONLY "support_level" + ADD CONSTRAINT "support_level_fk_0" + FOREIGN KEY("pupilId") + REFERENCES "pupil_data"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "timetable" table +-- +ALTER TABLE ONLY "timetable" + ADD CONSTRAINT "timetable_fk_0" + FOREIGN KEY("schoolSemesterId") + REFERENCES "school_semester"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "timetable_slot" table +-- +ALTER TABLE ONLY "timetable_slot" + ADD CONSTRAINT "timetable_slot_fk_0" + FOREIGN KEY("timetableId") + REFERENCES "timetable"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "user" table +-- +ALTER TABLE ONLY "user" + ADD CONSTRAINT "user_fk_0" + FOREIGN KEY("userInfoId") + REFERENCES "serverpod_user_info"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "user_device" table +-- +ALTER TABLE ONLY "user_device" + ADD CONSTRAINT "user_device_fk_0" + FOREIGN KEY("userInfoId") + REFERENCES "serverpod_user_info"("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION; +ALTER TABLE ONLY "user_device" + ADD CONSTRAINT "user_device_fk_1" + FOREIGN KEY("authId") + REFERENCES "serverpod_auth_key"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "serverpod_log" table +-- +ALTER TABLE ONLY "serverpod_log" + ADD CONSTRAINT "serverpod_log_fk_0" + FOREIGN KEY("sessionLogId") + REFERENCES "serverpod_session_log"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "serverpod_message_log" table +-- +ALTER TABLE ONLY "serverpod_message_log" + ADD CONSTRAINT "serverpod_message_log_fk_0" + FOREIGN KEY("sessionLogId") + REFERENCES "serverpod_session_log"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + +-- +-- Foreign relations for "serverpod_query_log" table +-- +ALTER TABLE ONLY "serverpod_query_log" + ADD CONSTRAINT "serverpod_query_log_fk_0" + FOREIGN KEY("sessionLogId") + REFERENCES "serverpod_session_log"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION; + + +-- +-- MIGRATION VERSION FOR school_data_hub +-- +INSERT INTO "serverpod_migrations" ("module", "version", "timestamp") + VALUES ('school_data_hub', '20251204113239243', now()) + ON CONFLICT ("module") + DO UPDATE SET "version" = '20251204113239243', "timestamp" = now(); + +-- +-- MIGRATION VERSION FOR serverpod +-- +INSERT INTO "serverpod_migrations" ("module", "version", "timestamp") + VALUES ('serverpod', '20240516151843329', now()) + ON CONFLICT ("module") + DO UPDATE SET "version" = '20240516151843329', "timestamp" = now(); + +-- +-- MIGRATION VERSION FOR serverpod_auth +-- +INSERT INTO "serverpod_migrations" ("module", "version", "timestamp") + VALUES ('serverpod_auth', '20240520102713718', now()) + ON CONFLICT ("module") + DO UPDATE SET "version" = '20240520102713718', "timestamp" = now(); + + +COMMIT; diff --git a/school_data_hub_server/migrations/20251204113239243/definition_project.json b/school_data_hub_server/migrations/20251204113239243/definition_project.json new file mode 100644 index 00000000..75c38322 --- /dev/null +++ b/school_data_hub_server/migrations/20251204113239243/definition_project.json @@ -0,0 +1,4839 @@ +{ + "moduleName": "school_data_hub", + "tables": [ + { + "name": "authorization", + "dartName": "Authorization", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('authorization_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "authorization_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "book", + "dartName": "Book", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('book_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "title", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "author", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "readingLevel", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "imagePath", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "book_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "book_id_unique_idx", + "elements": [ + { + "type": 0, + "definition": "isbn" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "book_tag", + "dartName": "BookTag", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('book_tag_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "book_tag_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "book_tagging", + "dartName": "BookTagging", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('book_tagging_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "bookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "bookTagId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "book_tagging_fk_0", + "columns": [ + "bookId" + ], + "referenceTable": "book", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "book_tagging_fk_1", + "columns": [ + "bookTagId" + ], + "referenceTable": "book_tag", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "book_tagging_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "book_tagging_index_idx", + "elements": [ + { + "type": 0, + "definition": "bookId" + }, + { + "type": 0, + "definition": "bookTagId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "competence", + "dartName": "Competence", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "parentCompetence", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "level", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "indicators", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "order", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "competence_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_check", + "dartName": "CompetenceCheck", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_check_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "checkId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "valueFactor", + "columnType": 3, + "isNullable": false, + "dartType": "double" + }, + { + "name": "groupCheckId", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "groupCheckName", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_check_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_check_fk_1", + "columns": [ + "competenceId" + ], + "referenceTable": "competence", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_check_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_goal", + "dartName": "CompetenceGoal", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_goal_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "strategies", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "score", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "achievedAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_goal_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_goal_fk_1", + "columns": [ + "competenceId" + ], + "referenceTable": "competence", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_goal_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_report", + "dartName": "CompetenceReport", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_report_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "reportId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "achievement", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "achievedAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_report_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_report_fk_1", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_report_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_report_check", + "dartName": "CompetenceReportCheck", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_report_check_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "achievement", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "competenceReportId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "competence_report_check_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_report_check_fk_1", + "columns": [ + "competenceId" + ], + "referenceTable": "competence_report_item", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "competence_report_check_fk_2", + "columns": [ + "competenceReportId" + ], + "referenceTable": "competence_report", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "competence_report_check_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "competence_report_item", + "dartName": "CompetenceReportItem", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('competence_report_item_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "parentItem", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "level", + "columnType": 8, + "isNullable": true, + "dartType": "List?" + }, + { + "name": "order", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "competence_report_item_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "compulsory_room", + "dartName": "CompulsoryRoom", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('compulsory_room_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "roomId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "roomType", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:MatrixRoomType" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "compulsory_room_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "credit_transaction", + "dartName": "CreditTransaction", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('credit_transaction_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "sender", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "receiver", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "amount", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "dateTime", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "description", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "_pupilDataCredittransactionsPupilDataId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "credit_transaction_fk_0", + "columns": [ + "_pupilDataCredittransactionsPupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "credit_transaction_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "reciever_idx", + "elements": [ + { + "type": 0, + "definition": "receiver" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "sender_idx", + "elements": [ + { + "type": 0, + "definition": "sender" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "hub_document", + "dartName": "HubDocument", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('hub_document_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "documentId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "documentPath", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "_pupilBookLendingPupilbooklendingfilesPupilBookLendingId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_competenceCheckDocumentsCompetenceCheckId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_competenceGoalDocumentsCompetenceGoalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportCategoryStatusDocumentsSupportCategoryStatusId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportGoalCheckDocumentsSupportGoalCheckId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_preSchoolMedicalPreschoolmedicalfilesPreSchoolMedicalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_preSchoolTestPreschooltestdocumentsPreSchoolTestId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "hub_document_fk_0", + "columns": [ + "_pupilBookLendingPupilbooklendingfilesPupilBookLendingId" + ], + "referenceTable": "pupil_book_lending", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_1", + "columns": [ + "_competenceCheckDocumentsCompetenceCheckId" + ], + "referenceTable": "competence_check", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_2", + "columns": [ + "_competenceGoalDocumentsCompetenceGoalId" + ], + "referenceTable": "competence_goal", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_3", + "columns": [ + "_supportCategoryStatusDocumentsSupportCategoryStatusId" + ], + "referenceTable": "support_category_status", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_4", + "columns": [ + "_supportGoalCheckDocumentsSupportGoalCheckId" + ], + "referenceTable": "support_goal_check", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_5", + "columns": [ + "_preSchoolMedicalPreschoolmedicalfilesPreSchoolMedicalId" + ], + "referenceTable": "pre_school_medical", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "hub_document_fk_6", + "columns": [ + "_preSchoolTestPreschooltestdocumentsPreSchoolTestId" + ], + "referenceTable": "pre_school_test", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "hub_document_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "kindergarden", + "dartName": "Kindergarden", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('kindergarden_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "phone", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "address", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "email", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "contactPerson", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "kindergarden_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "last_pupil_identities_update", + "dartName": "LastPupilIdentiesUpdate", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('last_pupil_identities_update_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "date", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "last_pupil_identities_update_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "learning_support_plan", + "dartName": "LearningSupportPlan", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('learning_support_plan_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "planId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "number", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "socialPedagogue", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "proffesionalsInvolved", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "strengthsDescription", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "problemsDescription", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "learningSupportLevelId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "learning_support_plan_fk_0", + "columns": [ + "learningSupportLevelId" + ], + "referenceTable": "support_level", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "learning_support_plan_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "learning_support_plan_fk_2", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "learning_support_plan_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson", + "dartName": "Lesson", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "subjectId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_fk_0", + "columns": [ + "subjectId" + ], + "referenceTable": "subject", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson_attendance", + "dartName": "LessonAttendance", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_attendance_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "lessonId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "modifiedAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_attendance_fk_0", + "columns": [ + "lessonId" + ], + "referenceTable": "lesson", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "lesson_attendance_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_attendance_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson_group", + "dartName": "LessonGroup", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_group_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "color", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "timetableId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "modifiedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_group_fk_0", + "columns": [ + "timetableId" + ], + "referenceTable": "timetable", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_group_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "lesson_group_pupil", + "dartName": "ScheduledLessonGroupMembership", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_group_pupil_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "lessonGroupId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilDataId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_group_pupil_fk_0", + "columns": [ + "lessonGroupId" + ], + "referenceTable": "lesson_group", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "lesson_group_pupil_fk_1", + "columns": [ + "pupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_group_pupil_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "lesson_group_membership_index_idx", + "elements": [ + { + "type": 0, + "definition": "lessonGroupId" + }, + { + "type": 0, + "definition": "pupilDataId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "lesson_teacher", + "dartName": "LessonTeacher", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('lesson_teacher_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "scheduledLessonId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "lesson_teacher_fk_0", + "columns": [ + "userId" + ], + "referenceTable": "user", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "lesson_teacher_fk_1", + "columns": [ + "scheduledLessonId" + ], + "referenceTable": "lesson", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "lesson_teacher_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "lesson_teacher_unique_idx", + "elements": [ + { + "type": 0, + "definition": "userId" + }, + { + "type": 0, + "definition": "scheduledLessonId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "library_book", + "dartName": "LibraryBook", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('library_book_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "libraryId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "bookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "locationId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "available", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + } + ], + "foreignKeys": [ + { + "constraintName": "library_book_fk_0", + "columns": [ + "bookId" + ], + "referenceTable": "book", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "library_book_fk_1", + "columns": [ + "locationId" + ], + "referenceTable": "library_book_location", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "library_book_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "library_id_unique_idx", + "elements": [ + { + "type": 0, + "definition": "libraryId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "library_book_location", + "dartName": "LibraryBookLocation", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('library_book_location_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "location", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "library_book_location_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "location_unique_idx", + "elements": [ + { + "type": 0, + "definition": "location" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "missed_class", + "dartName": "MissedSchoolday", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('missed_class_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "missedType", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:MissedType" + }, + { + "name": "unexcused", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "contacted", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:ContactedType" + }, + { + "name": "returned", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "returnedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "writtenExcuse", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "minutesLate", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "schooldayId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "missed_class_fk_0", + "columns": [ + "schooldayId" + ], + "referenceTable": "schoolday", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "missed_class_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "missed_class_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "schoolday_pupil_data_idx", + "elements": [ + { + "type": 0, + "definition": "schooldayId" + }, + { + "type": 0, + "definition": "pupilId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "pre_school_medical", + "dartName": "PreSchoolMedical", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pre_school_medical_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "preschoolMedicalStatus", + "columnType": 0, + "isNullable": true, + "dartType": "protocol:PreSchoolMedicalStatus?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "updatedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "updatedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "pre_school_medical_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pre_school_test", + "dartName": "PreSchoolTest", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pre_school_test_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "careNeedsIntensity", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "pre_school_test_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_authorization", + "dartName": "PupilAuthorization", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_authorization_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "status", + "columnType": 1, + "isNullable": true, + "dartType": "bool?" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "fileId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "authorizationId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_authorization_fk_0", + "columns": [ + "fileId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "pupil_authorization_fk_1", + "columns": [ + "authorizationId" + ], + "referenceTable": "authorization", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_authorization_fk_2", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "pupil_authorization_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_book_lending", + "dartName": "PupilBookLending", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_book_lending_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "lendingId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "status", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "lentAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "lentBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "returnedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "receivedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "libraryBookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_book_lending_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_book_lending_fk_1", + "columns": [ + "libraryBookId" + ], + "referenceTable": "library_book", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "pupil_book_lending_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_data", + "dartName": "PupilData", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_data_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "status", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:PupilStatus" + }, + { + "name": "internalId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "password", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "preSchoolMedicalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "kindergardenId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "kindergardenData", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:KindergardenInfo?" + }, + { + "name": "preSchoolTestId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "avatarId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "avatarAuthId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "publicMediaAuth", + "columnType": 8, + "isNullable": false, + "dartType": "protocol:PublicMediaAuth" + }, + { + "name": "publicMediaAuthDocumentId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "contact", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "communicationPupil", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:CommunicationSkills?" + }, + { + "name": "specialInformation", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "tutorInfo", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:TutorInfo?" + }, + { + "name": "afterSchoolCare", + "columnType": 8, + "isNullable": true, + "dartType": "protocol:AfterSchoolCare?" + }, + { + "name": "credit", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "creditEarned", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "schoolyearHeldBackAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "swimmer", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "_kindergardenPupilsKindergardenId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_data_fk_0", + "columns": [ + "preSchoolMedicalId" + ], + "referenceTable": "pre_school_medical", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_1", + "columns": [ + "kindergardenId" + ], + "referenceTable": "kindergarden", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_2", + "columns": [ + "preSchoolTestId" + ], + "referenceTable": "pre_school_test", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_3", + "columns": [ + "avatarId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_4", + "columns": [ + "avatarAuthId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_5", + "columns": [ + "publicMediaAuthDocumentId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "pupil_data_fk_6", + "columns": [ + "_kindergardenPupilsKindergardenId" + ], + "referenceTable": "kindergarden", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "pupil_data_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "pupil_data_status_idx", + "elements": [ + { + "type": 0, + "definition": "status" + }, + { + "type": 0, + "definition": "internalId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + }, + { + "indexName": "pupil_data_internal_id_idx", + "elements": [ + { + "type": 0, + "definition": "internalId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "pupil_list_entry", + "dartName": "PupilListEntry", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_list_entry_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "status", + "columnType": 1, + "isNullable": true, + "dartType": "bool?" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "entryBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "schoolListId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_list_entry_fk_0", + "columns": [ + "schoolListId" + ], + "referenceTable": "school_list", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "pupil_list_entry_fk_1", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "pupil_list_entry_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "pupil_workbook", + "dartName": "PupilWorkbook", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('pupil_workbook_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "finishedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "workbookId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "pupil_workbook_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "pupil_workbook_fk_1", + "columns": [ + "workbookId" + ], + "referenceTable": "workbook", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "pupil_workbook_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "room", + "dartName": "Classroom", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('room_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "roomCode", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "roomName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "room_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "scheduled_lesson", + "dartName": "ScheduledLesson", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('scheduled_lesson_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "active", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "subjectId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "scheduledAtId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "timetableSlotOrder", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "timetableId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "mainTeacherId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "lessonId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "roomId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "lessonGroupId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "modifiedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "recordtest", + "columnType": 8, + "isNullable": true, + "dartType": "( {int testint, String testString})?" + }, + { + "name": "_roomScheduledlessonsRoomId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "scheduled_lesson_fk_0", + "columns": [ + "subjectId" + ], + "referenceTable": "subject", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_1", + "columns": [ + "scheduledAtId" + ], + "referenceTable": "timetable_slot", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_2", + "columns": [ + "timetableId" + ], + "referenceTable": "timetable", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_3", + "columns": [ + "roomId" + ], + "referenceTable": "room", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_4", + "columns": [ + "lessonGroupId" + ], + "referenceTable": "lesson_group", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_fk_5", + "columns": [ + "_roomScheduledlessonsRoomId" + ], + "referenceTable": "room", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "scheduled_lesson_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "scheduled_lesson_teacher", + "dartName": "ScheduledLessonTeacher", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('scheduled_lesson_teacher_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "scheduledLessonId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "scheduled_lesson_teacher_fk_0", + "columns": [ + "userId" + ], + "referenceTable": "user", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "scheduled_lesson_teacher_fk_1", + "columns": [ + "scheduledLessonId" + ], + "referenceTable": "scheduled_lesson", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "scheduled_lesson_teacher_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "scheduled_lesson_teacher_unique_idx", + "elements": [ + { + "type": 0, + "definition": "userId" + }, + { + "type": 0, + "definition": "scheduledLessonId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "school_data", + "dartName": "SchoolData", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('school_data_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "officialName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "address", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "schoolNumber", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "telephoneNumber", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "email", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "website", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "logoId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "officialSealId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "school_data_fk_0", + "columns": [ + "logoId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "school_data_fk_1", + "columns": [ + "officialSealId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "school_data_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "school_list", + "dartName": "SchoolList", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('school_list_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "listId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "archived", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "public", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "authorizedUsers", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "school_list_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "school_semester", + "dartName": "SchoolSemester", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('school_semester_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "schoolYear", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "isFirst", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "startDate", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "endDate", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "classConferenceDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "supportConferenceDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "reportConferenceDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "reportSignedDate", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "school_semester_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "schoolday", + "dartName": "Schoolday", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('schoolday_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "schoolday", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "schoolday_fk_0", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "schoolday_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "schoolday_event", + "dartName": "SchooldayEvent", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('schoolday_event_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "eventId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "eventType", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:SchooldayEventType" + }, + { + "name": "eventReason", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "processed", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "processedBy", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "processedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "documentId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "processedDocumentId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "schooldayId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + } + ], + "foreignKeys": [ + { + "constraintName": "schoolday_event_fk_0", + "columns": [ + "documentId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "schoolday_event_fk_1", + "columns": [ + "processedDocumentId" + ], + "referenceTable": "hub_document", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "schoolday_event_fk_2", + "columns": [ + "schooldayId" + ], + "referenceTable": "schoolday", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "schoolday_event_fk_3", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "schoolday_event_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "subject", + "dartName": "Subject", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('subject_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "publicId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "description", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "color", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modifiedBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "subject_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_category", + "dartName": "SupportCategory", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_category_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "categoryId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "parentCategory", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "support_category_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_category_goal", + "dartName": "SupportGoal", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_category_goal_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "goalId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "achievedAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "description", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "strategies", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "supportCategoryId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "_learningSupportPlanSupportgoalsLearningSupportPlanId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportCategoryCategorygoalsSupportCategoryId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_pupilDataSupportgoalsPupilDataId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "support_category_goal_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_goal_fk_1", + "columns": [ + "supportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "support_category_goal_fk_2", + "columns": [ + "_learningSupportPlanSupportgoalsLearningSupportPlanId" + ], + "referenceTable": "learning_support_plan", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_goal_fk_3", + "columns": [ + "_supportCategoryCategorygoalsSupportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_goal_fk_4", + "columns": [ + "_pupilDataSupportgoalsPupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "support_category_goal_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_category_status", + "dartName": "SupportCategoryStatus", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_category_status_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "supportCategoryId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "learningSupportPlanId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "_learningSupportPlanSupportcategorystatusesLearningSupporfb7bId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_supportCategoryCategorystatuesSupportCategoryId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "_pupilDataSupportcategorystatusesPupilDataId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "support_category_status_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_1", + "columns": [ + "supportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_2", + "columns": [ + "learningSupportPlanId" + ], + "referenceTable": "learning_support_plan", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_3", + "columns": [ + "_learningSupportPlanSupportcategorystatusesLearningSupporfb7bId" + ], + "referenceTable": "learning_support_plan", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_4", + "columns": [ + "_supportCategoryCategorystatuesSupportCategoryId" + ], + "referenceTable": "support_category", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "support_category_status_fk_5", + "columns": [ + "_pupilDataSupportcategorystatusesPupilDataId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "support_category_status_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_goal_check", + "dartName": "SupportGoalCheck", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_goal_check_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "checkId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "score", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "supportGoalId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "_supportCategoryGoalGoalchecksSupportCategoryGoalId", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + } + ], + "foreignKeys": [ + { + "constraintName": "support_goal_check_fk_0", + "columns": [ + "supportGoalId" + ], + "referenceTable": "support_category_goal", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + }, + { + "constraintName": "support_goal_check_fk_1", + "columns": [ + "_supportCategoryGoalGoalchecksSupportCategoryGoalId" + ], + "referenceTable": "support_category_goal", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "support_goal_check_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "support_level", + "dartName": "SupportLevel", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('support_level_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "level", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "comment", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "pupilId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "support_level_fk_0", + "columns": [ + "pupilId" + ], + "referenceTable": "pupil_data", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "support_level_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "timetable", + "dartName": "Timetable", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('timetable_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "active", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "startsAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "endsAt", + "columnType": 4, + "isNullable": true, + "dartType": "DateTime?" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "schoolSemesterId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "createdBy", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "createdAt", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "modified", + "columnType": 8, + "isNullable": true, + "dartType": "List<( {String modifiedBy, DateTime modifiedAt})>?" + } + ], + "foreignKeys": [ + { + "constraintName": "timetable_fk_0", + "columns": [ + "schoolSemesterId" + ], + "referenceTable": "school_semester", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "timetable_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "timetable_school_semester_idx", + "elements": [ + { + "type": 0, + "definition": "schoolSemesterId" + } + ], + "type": "btree", + "isUnique": false, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "timetable_slot", + "dartName": "TimetableSlot", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('timetable_slot_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "day", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:Weekday" + }, + { + "name": "startTime", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "endTime", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "timetableId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "timetable_slot_fk_0", + "columns": [ + "timetableId" + ], + "referenceTable": "timetable", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + } + ], + "indexes": [ + { + "indexName": "timetable_slot_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + }, + { + "name": "user", + "dartName": "User", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('user_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userInfoId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "role", + "columnType": 0, + "isNullable": false, + "dartType": "protocol:Role" + }, + { + "name": "matrixUserId", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "timeUnits", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "reliefTimeUnits", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "pupilsAuth", + "columnType": 8, + "isNullable": true, + "dartType": "Set?" + }, + { + "name": "schooldayEventsProcessingTeam", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "credit", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "userFlags", + "columnType": 8, + "isNullable": false, + "dartType": "protocol:UserFlags" + } + ], + "foreignKeys": [ + { + "constraintName": "user_fk_0", + "columns": [ + "userInfoId" + ], + "referenceTable": "serverpod_user_info", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "user_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "user_info_id_unique_idx", + "elements": [ + { + "type": 0, + "definition": "userInfoId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "user_device", + "dartName": "UserDevice", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('user_device_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "userInfoId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "deviceId", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "deviceName", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "lastLogin", + "columnType": 4, + "isNullable": false, + "dartType": "DateTime" + }, + { + "name": "isActive", + "columnType": 1, + "isNullable": false, + "dartType": "bool" + }, + { + "name": "authId", + "columnType": 6, + "isNullable": false, + "dartType": "int" + } + ], + "foreignKeys": [ + { + "constraintName": "user_device_fk_0", + "columns": [ + "userInfoId" + ], + "referenceTable": "serverpod_user_info", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 3 + }, + { + "constraintName": "user_device_fk_1", + "columns": [ + "authId" + ], + "referenceTable": "serverpod_auth_key", + "referenceTableSchema": "public", + "referenceColumns": [ + "id" + ], + "onUpdate": 3, + "onDelete": 4 + } + ], + "indexes": [ + { + "indexName": "user_device_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + }, + { + "indexName": "auth_key_user_device_idx", + "elements": [ + { + "type": 0, + "definition": "authId" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": false + } + ], + "managed": true + }, + { + "name": "workbook", + "dartName": "Workbook", + "module": "school_data_hub", + "schema": "public", + "columns": [ + { + "name": "id", + "columnType": 6, + "isNullable": false, + "columnDefault": "nextval('workbook_id_seq'::regclass)", + "dartType": "int?" + }, + { + "name": "isbn", + "columnType": 6, + "isNullable": false, + "dartType": "int" + }, + { + "name": "name", + "columnType": 0, + "isNullable": false, + "dartType": "String" + }, + { + "name": "subject", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "level", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + }, + { + "name": "amount", + "columnType": 6, + "isNullable": true, + "dartType": "int?" + }, + { + "name": "imageUrl", + "columnType": 0, + "isNullable": false, + "dartType": "String" + } + ], + "foreignKeys": [], + "indexes": [ + { + "indexName": "workbook_pkey", + "elements": [ + { + "type": 0, + "definition": "id" + } + ], + "type": "btree", + "isUnique": true, + "isPrimary": true + } + ], + "managed": true + } + ], + "installedModules": [ + { + "module": "serverpod", + "version": "20240516151843329" + }, + { + "module": "serverpod_auth", + "version": "20240520102713718" + } + ], + "migrationApiVersion": 1 +} \ No newline at end of file diff --git a/school_data_hub_server/migrations/20251204113239243/migration.json b/school_data_hub_server/migrations/20251204113239243/migration.json new file mode 100644 index 00000000..15942f60 --- /dev/null +++ b/school_data_hub_server/migrations/20251204113239243/migration.json @@ -0,0 +1,28 @@ +{ + "actions": [ + { + "type": "alterTable", + "alterTable": { + "name": "schoolday_event", + "schema": "public", + "addColumns": [ + { + "name": "comment", + "columnType": 0, + "isNullable": true, + "dartType": "String?" + } + ], + "deleteColumns": [], + "modifyColumns": [], + "addIndexes": [], + "deleteIndexes": [], + "addForeignKeys": [], + "deleteForeignKeys": [], + "warnings": [] + } + } + ], + "warnings": [], + "migrationApiVersion": 1 +} \ No newline at end of file diff --git a/school_data_hub_server/migrations/20251204113239243/migration.sql b/school_data_hub_server/migrations/20251204113239243/migration.sql new file mode 100644 index 00000000..92c825c9 --- /dev/null +++ b/school_data_hub_server/migrations/20251204113239243/migration.sql @@ -0,0 +1,33 @@ +BEGIN; + +-- +-- ACTION ALTER TABLE +-- +ALTER TABLE "schoolday_event" ADD COLUMN "comment" text; + +-- +-- MIGRATION VERSION FOR school_data_hub +-- +INSERT INTO "serverpod_migrations" ("module", "version", "timestamp") + VALUES ('school_data_hub', '20251204113239243', now()) + ON CONFLICT ("module") + DO UPDATE SET "version" = '20251204113239243', "timestamp" = now(); + +-- +-- MIGRATION VERSION FOR serverpod +-- +INSERT INTO "serverpod_migrations" ("module", "version", "timestamp") + VALUES ('serverpod', '20240516151843329', now()) + ON CONFLICT ("module") + DO UPDATE SET "version" = '20240516151843329', "timestamp" = now(); + +-- +-- MIGRATION VERSION FOR serverpod_auth +-- +INSERT INTO "serverpod_migrations" ("module", "version", "timestamp") + VALUES ('serverpod_auth', '20240520102713718', now()) + ON CONFLICT ("module") + DO UPDATE SET "version" = '20240520102713718', "timestamp" = now(); + + +COMMIT; diff --git a/school_data_hub_server/migrations/migration_registry.txt b/school_data_hub_server/migrations/migration_registry.txt index 5940708c..ec2f17be 100644 --- a/school_data_hub_server/migrations/migration_registry.txt +++ b/school_data_hub_server/migrations/migration_registry.txt @@ -13,3 +13,4 @@ 20250917215220208 20251121184735230 20251123092602494 +20251204113239243