From c4f5be22054f1501e4bb9adefe7079c8e59bff95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Elias=20M=C3=BCller?= <elias@elias-mueller.com>
Date: Sat, 23 Mar 2024 15:42:31 +0100
Subject: [PATCH] WIP high education graduation calculator

---
 lib/api/requestCache.dart                     | 12 +--
 .../highEduGraduationCalculator.dart          | 75 +++++++++++++++++++
 .../models/subject.dart                       | 48 ++++++++++++
 .../selectLkView.dart                         | 47 ++++++++++++
 lib/view/pages/overhang.dart                  |  2 +
 5 files changed, 179 insertions(+), 5 deletions(-)
 create mode 100644 lib/view/pages/more/highEduGraduationCalculator/highEduGraduationCalculator.dart
 create mode 100644 lib/view/pages/more/highEduGraduationCalculator/models/subject.dart
 create mode 100644 lib/view/pages/more/highEduGraduationCalculator/selectLkView.dart

diff --git a/lib/api/requestCache.dart b/lib/api/requestCache.dart
index b08c89a..99d1309 100644
--- a/lib/api/requestCache.dart
+++ b/lib/api/requestCache.dart
@@ -1,9 +1,9 @@
 import 'dart:convert';
+import 'dart:developer';
 
 import 'package:localstore/localstore.dart';
 
 import 'apiResponse.dart';
-import 'webuntis/webuntisError.dart';
 
 abstract class RequestCache<T extends ApiResponse?> {
   static const int cacheNothing = 0;
@@ -30,16 +30,18 @@ abstract class RequestCache<T extends ApiResponse?> {
       if(renew == null || !renew!) return;
     }
 
+    T? newValue;
     try {
-      T newValue = await onLoad();
-      onUpdate(newValue);
+      newValue = await onLoad();
+      onUpdate(newValue as T);
 
       Localstore.instance.collection(file).doc(document).set({
         "json": jsonEncode(newValue),
         "lastupdate": DateTime.now().millisecondsSinceEpoch
       });
-    } on WebuntisError catch(e) {
-      onError(e);
+    } catch(e) {
+      log("Error while fetching/ parsing. Raw server response: ${newValue?.rawResponse.body ?? "no response"}");
+      onError(Exception(e.toString()));
     }
   }
 
diff --git a/lib/view/pages/more/highEduGraduationCalculator/highEduGraduationCalculator.dart b/lib/view/pages/more/highEduGraduationCalculator/highEduGraduationCalculator.dart
new file mode 100644
index 0000000..1fe8faa
--- /dev/null
+++ b/lib/view/pages/more/highEduGraduationCalculator/highEduGraduationCalculator.dart
@@ -0,0 +1,75 @@
+import 'package:flutter/material.dart';
+import 'package:marianum_mobile/view/pages/more/highEduGraduationCalculator/models/subject.dart';
+import 'package:marianum_mobile/view/pages/more/highEduGraduationCalculator/selectLkView.dart';
+
+class HighEduGraduationCalculator extends StatefulWidget {
+  const HighEduGraduationCalculator({super.key});
+
+  @override
+  State<HighEduGraduationCalculator> createState() => _HighEduGraduationCalculatorState();
+}
+
+class _HighEduGraduationCalculatorState extends State<HighEduGraduationCalculator> {
+  SubjectCollection allSubjects = SubjectCollection([
+    Subject(displayName: "Mathe", lkApplicable: true),
+    Subject(displayName: "Physik", lkApplicable: true),
+    Subject(displayName: "Chemie", lkApplicable: true),
+    Subject(displayName: "Biologie", lkApplicable: true),
+    Subject(displayName: "Englisch", lkApplicable: true),
+    Subject(displayName: "Latein", lkApplicable: true),
+  ]);
+
+  SubjectCollection? lkSubjects;
+
+  int currentStep = 0;
+  List<Step> steps() => [
+    Step(title: const Text("Willkommen"), content: Text("test")),
+    Step(title: const Text("Leistungskurse wählen"), content: lkSubjectsStep()),
+    const Step(title: Text("Grundkurse wählen"), content: Text("Test")),
+    const Step(title: Text("Prüfungsfächer wählen"), content: Text("Test")),
+    const Step(title: Text("Ergebnis auswerten"), content: Text("Test")),
+  ];
+
+  Widget lkSubjectsStep() {
+    return Column(
+      children: [
+        Text(lkSubjects?.subjects.where((e) => e.isLk()).map((e) => e.displayName).join(", ")  ?? "Keine Auswahl"),
+        TextButton(
+          onPressed: () {
+            showDialog(context: context, builder: (context) => SelectLkView(all: allSubjects, update: () => setState(() => { })));
+          },
+          child: const Text("Ändern"),
+        ),
+      ],
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text("Abitur Notenrechner"),
+      ),
+      body: Stepper(
+        type: StepperType.vertical,
+        steps: steps(),
+        currentStep: currentStep,
+        onStepContinue: () {
+          setState(() {
+            currentStep++;
+          });
+        },
+        controlsBuilder: (context, details) {
+          return Row(
+            children: [
+              TextButton(
+              onPressed: () => details.onStepContinue,
+                child: const Text("Weiter"),
+              )
+            ],
+          );
+        },
+      )
+    );
+  }
+}
diff --git a/lib/view/pages/more/highEduGraduationCalculator/models/subject.dart b/lib/view/pages/more/highEduGraduationCalculator/models/subject.dart
new file mode 100644
index 0000000..881c6ee
--- /dev/null
+++ b/lib/view/pages/more/highEduGraduationCalculator/models/subject.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/material.dart';
+import 'package:marianum_mobile/widget/centeredLeading.dart';
+
+enum SubjectState {
+  none,
+  isLk,
+  isGk,
+}
+
+class HighEduGraduationConstants {
+  static const int maxLks = 2;
+  static const int maxGks = 9;
+}
+
+class SubjectCollection {
+  List<Subject> subjects;
+
+  SubjectCollection(this.subjects);
+}
+
+class Subject {
+  String displayName;
+  bool lkApplicable;
+
+  SubjectState _subjectState = SubjectState.none;
+
+  bool isLk() => _subjectState == SubjectState.isLk;
+  bool isGk() => _subjectState == SubjectState.isGk;
+  bool canLk(List<Subject> other) => lkApplicable && other.where((s) => s.isLk()).length < HighEduGraduationConstants.maxLks;
+  bool canGk(List<Subject> other) => !isLk() && other.where((s) => s.isGk()).length < HighEduGraduationConstants.maxGks;
+
+  set unsafeSubjectSet(SubjectState newState) => _subjectState = newState;
+  set unsafeLkToggle(bool isLk) {
+    _subjectState = isLk ? SubjectState.isLk : SubjectState.none;
+  }
+
+  ListTile get asTile => ListTile(
+    leading: const CenteredLeading(Icon(Icons.subject_outlined)),
+    title: Text(displayName),
+    trailing: Checkbox(
+      value: isGk(),
+      onChanged: (e) => {},
+    ),
+  );
+
+
+  Subject({required this.displayName, required this.lkApplicable});
+}
\ No newline at end of file
diff --git a/lib/view/pages/more/highEduGraduationCalculator/selectLkView.dart b/lib/view/pages/more/highEduGraduationCalculator/selectLkView.dart
new file mode 100644
index 0000000..65fbfd7
--- /dev/null
+++ b/lib/view/pages/more/highEduGraduationCalculator/selectLkView.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:marianum_mobile/view/pages/more/highEduGraduationCalculator/models/subject.dart';
+import 'package:marianum_mobile/widget/centeredLeading.dart';
+
+class SelectLkView extends StatefulWidget {
+  final SubjectCollection all;
+  final void Function() update;
+  const SelectLkView({required this.all, required this.update, super.key});
+
+  @override
+  State<SelectLkView> createState() => _SelectLkViewState();
+}
+
+class _SelectLkViewState extends State<SelectLkView> {
+  @override
+  Widget build(BuildContext context) {
+    return AlertDialog(
+      title: const Text("Leistungskurse"),
+      content: Column(
+        mainAxisSize: MainAxisSize.min,
+        children: widget.all.subjects.map((e) {
+          return ListTile(
+            leading: const CenteredLeading(Icon(Icons.subject_outlined)),
+            title: Text(e.displayName),
+            trailing: Checkbox(
+              value: e.isLk(),
+              onChanged: e.isLk() || e.canLk(widget.all.subjects) ? (value) {
+                setState(() {
+                  e.unsafeLkToggle = value!;
+                });
+              } : null,
+            ),
+          );
+        }).toList(),
+      ),
+      actions: [
+        TextButton(
+          onPressed: () {
+            widget.update();
+            Navigator.of(context).pop();
+          },
+          child: const Text("Fertig"),
+        )
+      ],
+    );
+  }
+}
diff --git a/lib/view/pages/overhang.dart b/lib/view/pages/overhang.dart
index 0da5293..1bb5ab9 100644
--- a/lib/view/pages/overhang.dart
+++ b/lib/view/pages/overhang.dart
@@ -4,6 +4,7 @@ import 'dart:io';
 import 'package:flutter/material.dart';
 import 'package:in_app_review/in_app_review.dart';
 import 'package:marianum_mobile/extensions/renderNotNull.dart';
+import 'package:marianum_mobile/view/pages/more/highEduGraduationCalculator/highEduGraduationCalculator.dart';
 import 'package:persistent_bottom_nav_bar_v2/persistent-tab-view.dart';
 
 import '../../widget/ListItem.dart';
@@ -35,6 +36,7 @@ class Overhang extends StatelessWidget {
           const ListItemNavigator(icon: Icons.newspaper, text: "Marianum Message", target: Message()),
           const ListItemNavigator(icon: Icons.room, text: "Raumplan", target: Roomplan()),
           const ListItemNavigator(icon: Icons.calculate, text: "Notendurschnittsrechner", target: GradeAverage()),
+          const ListItemNavigator(icon: Icons.backup_table_outlined, text: "Abirechner", target: HighEduGraduationCalculator()),
           const ListItemNavigator(icon: Icons.calendar_month, text: "Schulferien", target: Holidays()),
           const Divider(),
           ListTile(