added guardian login with views for their assigned childs
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import '../../../api/marianumconnect/queries/get_capabilities/guardian_child.dart';
|
||||
import '../../../session/session.dart';
|
||||
import '../../../state/app/modules/children/child_selection_cubit.dart';
|
||||
|
||||
/// Who an absence report is filed for, and what the form lets the user edit.
|
||||
class AbsenceFormPolicy {
|
||||
/// The child the report is for; null when users report for themselves.
|
||||
final GuardianChild? child;
|
||||
|
||||
const AbsenceFormPolicy._(this.child);
|
||||
|
||||
/// Guardians report for a linked child whose identity the server knows,
|
||||
/// so name and class are fixed.
|
||||
bool get identityEditable => child == null;
|
||||
|
||||
/// Null when the session cannot file a report (guardian without children).
|
||||
static AbsenceFormPolicy? resolve({
|
||||
required Session? session,
|
||||
required List<GuardianChild> children,
|
||||
required String? selectedChildId,
|
||||
}) => switch (session) {
|
||||
GuardianSession() => switch (effectiveChild(children, selectedChildId)) {
|
||||
null => null,
|
||||
final child => AbsenceFormPolicy._(child),
|
||||
},
|
||||
_ => const AbsenceFormPolicy._(null),
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../api/errors/error_mapper.dart';
|
||||
import '../../../api/marianumconnect/queries/absence/absence_classes.dart';
|
||||
@@ -6,24 +7,55 @@ import '../../../api/marianumconnect/queries/absence/absence_prefill.dart';
|
||||
import '../../../api/marianumconnect/queries/absence/absence_prefill_response.dart';
|
||||
import '../../../api/marianumconnect/queries/absence/absence_submit.dart';
|
||||
import '../../../extensions/date_time.dart';
|
||||
import '../../../session/session_manager.dart';
|
||||
import '../../../state/app/modules/capabilities/bloc/capabilities_cubit.dart';
|
||||
import '../../../state/app/modules/children/child_selection_cubit.dart';
|
||||
import '../../../widget/app_progress_indicator.dart';
|
||||
import '../../../widget/async_action_button.dart';
|
||||
import '../../../widget/child_switcher.dart';
|
||||
import '../../../widget/demo_restricted.dart';
|
||||
import '../../../widget/focus_behaviour.dart';
|
||||
import '../../../widget/placeholder_view.dart';
|
||||
import 'absence_form_policy.dart';
|
||||
|
||||
/// Mobile mirror of the public absence-report form: submit-only (no history —
|
||||
/// that lives on the web). Identity/class/phone are prefilled from the backend
|
||||
/// but stay editable; the class list matches the submit validation source.
|
||||
/// Guardians report for the selected child, whose identity is fixed.
|
||||
/// User-facing texts mirror the public web form (`AbsencePublicForm.svelte`).
|
||||
class AbsenceReportView extends StatefulWidget {
|
||||
class AbsenceReportView extends StatelessWidget {
|
||||
const AbsenceReportView({super.key});
|
||||
|
||||
@override
|
||||
State<AbsenceReportView> createState() => _AbsenceReportViewState();
|
||||
Widget build(BuildContext context) {
|
||||
final policy = AbsenceFormPolicy.resolve(
|
||||
session: SessionManager().current,
|
||||
children: context.watch<CapabilitiesCubit>().state.children,
|
||||
selectedChildId: context.watch<ChildSelectionCubit>().state,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Abwesenheitsmeldung'),
|
||||
actions: const [ChildSwitcher()],
|
||||
),
|
||||
body: policy == null
|
||||
? const NoChildrenPlaceholder()
|
||||
// Re-created per child so no input leaks into another child's report.
|
||||
: _AbsenceForm(key: ValueKey(policy.child?.id), policy: policy),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AbsenceReportViewState extends State<AbsenceReportView> {
|
||||
class _AbsenceForm extends StatefulWidget {
|
||||
final AbsenceFormPolicy policy;
|
||||
|
||||
const _AbsenceForm({required this.policy, super.key});
|
||||
|
||||
@override
|
||||
State<_AbsenceForm> createState() => _AbsenceFormState();
|
||||
}
|
||||
|
||||
class _AbsenceFormState extends State<_AbsenceForm> {
|
||||
static const String _required = 'Dieses Feld ist erforderlich.';
|
||||
|
||||
final TextEditingController _firstName = TextEditingController();
|
||||
@@ -67,7 +99,17 @@ class _AbsenceReportViewState extends State<AbsenceReportView> {
|
||||
if (_submitted) setState(() {});
|
||||
}
|
||||
|
||||
String? get _childId => widget.policy.child?.id;
|
||||
|
||||
Future<void> _load() async {
|
||||
if (!widget.policy.identityEditable) {
|
||||
// The child's identity is the whole point of the form, so a failed
|
||||
// prefill is an error here, not a degraded start.
|
||||
final prefill = await AbsencePrefill().run(childId: _childId);
|
||||
_classes = [prefill.className];
|
||||
_applyPrefill(prefill, _classes);
|
||||
return;
|
||||
}
|
||||
// Both GETs are independent — fire them together. Prefill is best-effort
|
||||
// (mapped to null on failure), so a classes error still propagates while a
|
||||
// prefill failure never surfaces as an unhandled async error.
|
||||
@@ -148,6 +190,7 @@ class _AbsenceReportViewState extends State<AbsenceReportView> {
|
||||
absentUntil: _absentUntil,
|
||||
phone: _phone.text.trim(),
|
||||
note: _note.text.trim(),
|
||||
childId: _childId,
|
||||
);
|
||||
if (!mounted) return;
|
||||
// Replace the whole form with a terminal success screen. There is
|
||||
@@ -157,10 +200,8 @@ class _AbsenceReportViewState extends State<AbsenceReportView> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(title: const Text('Abwesenheitsmeldung')),
|
||||
body: _done ? const _SubmittedView() : _buildBody(context),
|
||||
);
|
||||
Widget build(BuildContext context) =>
|
||||
_done ? const _SubmittedView() : _buildBody(context);
|
||||
|
||||
Widget _buildBody(BuildContext context) => FutureBuilder<void>(
|
||||
future: _init,
|
||||
@@ -205,6 +246,7 @@ class _AbsenceReportViewState extends State<AbsenceReportView> {
|
||||
const SizedBox(height: 20),
|
||||
TextField(
|
||||
controller: _firstName,
|
||||
readOnly: !widget.policy.identityEditable,
|
||||
textCapitalization: TextCapitalization.words,
|
||||
decoration: _decoration(
|
||||
'Vorname',
|
||||
@@ -215,6 +257,7 @@ class _AbsenceReportViewState extends State<AbsenceReportView> {
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _lastName,
|
||||
readOnly: !widget.policy.identityEditable,
|
||||
textCapitalization: TextCapitalization.words,
|
||||
decoration: _decoration(
|
||||
'Nachname',
|
||||
@@ -234,7 +277,9 @@ class _AbsenceReportViewState extends State<AbsenceReportView> {
|
||||
items: _classes
|
||||
.map((c) => DropdownMenuItem(value: c, child: Text(c)))
|
||||
.toList(),
|
||||
onChanged: (value) => setState(() => _selectedClass = value),
|
||||
onChanged: widget.policy.identityEditable
|
||||
? (value) => setState(() => _selectedClass = value)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_DateField(
|
||||
|
||||
Reference in New Issue
Block a user