From ee0613d720b8fd234998e8e65533bc83754abf8c Mon Sep 17 00:00:00 2001 From: Kima Date: Sun, 28 Apr 2024 15:47:45 +0200 Subject: [PATCH] finished new grades page (subjects now actually) --- refilc/lib/ui/widgets/lesson/lesson_tile.dart | 201 ++++++----- .../lib/pages/grades/grade_subject_view.dart | 340 ++++++++++++------ .../lib/pages/grades/grades_page.i18n.dart | 15 + 3 files changed, 351 insertions(+), 205 deletions(-) diff --git a/refilc/lib/ui/widgets/lesson/lesson_tile.dart b/refilc/lib/ui/widgets/lesson/lesson_tile.dart index 284de3d..7c3df2a 100644 --- a/refilc/lib/ui/widgets/lesson/lesson_tile.dart +++ b/refilc/lib/ui/widgets/lesson/lesson_tile.dart @@ -17,11 +17,16 @@ import 'package:provider/provider.dart'; import 'lesson_tile.i18n.dart'; class LessonTile extends StatelessWidget { - const LessonTile(this.lesson, {super.key, this.onTap, this.swapDesc = false}); + const LessonTile(this.lesson, + {super.key, + this.onTap, + this.swapDesc = false, + this.subjectPageView = false}); final Lesson lesson; final bool swapDesc; final void Function()? onTap; + final bool subjectPageView; @override Widget build(BuildContext context) { @@ -147,73 +152,90 @@ class LessonTile extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: 4.0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), - title: Text( - !lesson.isEmpty - ? lesson.subject.renamedTo ?? - lesson.subject.name.capital() - : "empty".i18n, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16.5, - color: fill - ? accent - : AppColors.of(context) - .text - .withOpacity(!lesson.isEmpty ? 1.0 : 0.5), - fontStyle: lesson.subject.isRenamed && - settingsProvider.renamedSubjectsItalics - ? FontStyle.italic - : null), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Row( - // children: [ - // Container( - // padding: const EdgeInsets.symmetric( - // horizontal: 6.0, vertical: 3.5), - // decoration: BoxDecoration( - // color: Theme.of(context) - // .colorScheme - // .secondary - // .withOpacity(.15), - // borderRadius: BorderRadius.circular(10.0), - // ), - // child: Text( - // lesson.room, - // style: TextStyle( - // height: 1.1, - // fontSize: 12.5, - // fontWeight: FontWeight.w600, - // color: Theme.of(context) - // .colorScheme - // .secondary - // .withOpacity(.9), - // ), - // ), - // ) - // ], - // ), - // if (cleanDesc != '') - // const SizedBox( - // height: 10.0, - // ), - if (cleanDesc != '') - Text( - cleanDesc, - maxLines: 1, + title: !subjectPageView + ? Text( + !lesson.isEmpty + ? lesson.subject.renamedTo ?? + lesson.subject.name.capital() + : "empty".i18n, + maxLines: 2, overflow: TextOverflow.ellipsis, - textAlign: TextAlign.start, style: TextStyle( - fontSize: 14.0, - color: fill ? accent.withOpacity(0.5) : null, + fontWeight: FontWeight.w600, + fontSize: 16.5, + color: fill + ? accent + : AppColors.of(context) + .text + .withOpacity(!lesson.isEmpty ? 1.0 : 0.5), + fontStyle: lesson.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), + ) + : Transform.translate( + offset: const Offset(0, -2.0), + child: Text( + "${DateFormat("H:mm").format(lesson.start)}-${DateFormat("H:mm").format(lesson.end)}", + textAlign: TextAlign.start, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.0, + color: fill + ? accent.withOpacity(.9) + : AppColors.of(context).text.withOpacity(.9), + ), ), ), - ], - ), + subtitle: !subjectPageView + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Row( + // children: [ + // Container( + // padding: const EdgeInsets.symmetric( + // horizontal: 6.0, vertical: 3.5), + // decoration: BoxDecoration( + // color: Theme.of(context) + // .colorScheme + // .secondary + // .withOpacity(.15), + // borderRadius: BorderRadius.circular(10.0), + // ), + // child: Text( + // lesson.room, + // style: TextStyle( + // height: 1.1, + // fontSize: 12.5, + // fontWeight: FontWeight.w600, + // color: Theme.of(context) + // .colorScheme + // .secondary + // .withOpacity(.9), + // ), + // ), + // ) + // ], + // ), + // if (cleanDesc != '') + // const SizedBox( + // height: 10.0, + // ), + if (cleanDesc != '') + Text( + cleanDesc, + maxLines: 1, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.start, + style: TextStyle( + fontSize: 14.0, + color: fill ? accent.withOpacity(0.5) : null, + ), + ), + ], + ) + : null, // subtitle: description != "" // ? Text( @@ -237,8 +259,8 @@ class LessonTile extends StatelessWidget { color: fill ? accent : AppColors.of(context).text, width: 1.0, icon: SizedBox( - width: 25, - height: 25, + width: subjectPageView ? 22 : 25, + height: subjectPageView ? 22 : 25, child: Center( child: Padding( padding: const EdgeInsets.only(left: 3.0), @@ -246,7 +268,7 @@ class LessonTile extends StatelessWidget { lesson.lessonIndex + lessonIndexTrailing, textAlign: TextAlign.center, style: TextStyle( - fontSize: 17.5, + fontSize: subjectPageView ? 15.5 : 17.5, fontWeight: FontWeight.w700, color: fill ? accent : null, ), @@ -349,28 +371,31 @@ class LessonTile extends StatelessWidget { ), ), ), - const SizedBox( - width: 10, - ), - Stack( - alignment: Alignment.center, - children: [ - // xix alignment hack :p - const Opacity(opacity: 0, child: Text("EE:EE")), - Text( - "${DateFormat("H:mm").format(lesson.start)}\n${DateFormat("H:mm").format(lesson.end)}", - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.w500, - color: fill - ? accent.withOpacity(.9) - : AppColors.of(context) - .text - .withOpacity(.9), + if (!subjectPageView) + const SizedBox( + width: 10, + ), + if (!subjectPageView) + Stack( + alignment: Alignment.center, + children: [ + // xix alignment hack :p + const Opacity( + opacity: 0, child: Text("EE:EE")), + Text( + "${DateFormat("H:mm").format(lesson.start)}\n${DateFormat("H:mm").format(lesson.end)}", + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.w500, + color: fill + ? accent.withOpacity(.9) + : AppColors.of(context) + .text + .withOpacity(.9), + ), ), - ), - ], - ), + ], + ), ], ) : null, diff --git a/refilc_mobile_ui/lib/pages/grades/grade_subject_view.dart b/refilc_mobile_ui/lib/pages/grades/grade_subject_view.dart index ec2cd0c..bfa50cb 100644 --- a/refilc_mobile_ui/lib/pages/grades/grade_subject_view.dart +++ b/refilc_mobile_ui/lib/pages/grades/grade_subject_view.dart @@ -4,7 +4,12 @@ import 'package:animations/animations.dart'; import 'package:refilc/api/providers/database_provider.dart'; import 'package:refilc/api/providers/user_provider.dart'; import 'package:refilc/models/settings.dart'; +import 'package:refilc/ui/widgets/lesson/lesson_tile.dart'; import 'package:refilc/utils/format.dart'; +import 'package:refilc_kreta_api/controllers/timetable_controller.dart'; +import 'package:refilc_kreta_api/models/exam.dart'; +import 'package:refilc_kreta_api/models/lesson.dart'; +import 'package:refilc_kreta_api/providers/exam_provider.dart'; // import 'package:refilc_kreta_api/client/api.dart'; // import 'package:refilc_kreta_api/client/client.dart'; import 'package:refilc_kreta_api/providers/grade_provider.dart'; @@ -14,10 +19,13 @@ import 'package:refilc_kreta_api/models/grade.dart'; import 'package:refilc_kreta_api/models/subject.dart'; import 'package:refilc_mobile_ui/common/average_display.dart'; import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; +import 'package:refilc_mobile_ui/common/filter_bar.dart'; import 'package:refilc_mobile_ui/common/panel/panel.dart'; +import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart'; import 'package:refilc_mobile_ui/common/trend_display.dart'; import 'package:refilc_mobile_ui/common/widgets/cretification/certification_tile.dart'; import 'package:refilc/ui/widgets/grade/grade_tile.dart'; +import 'package:refilc_mobile_ui/common/widgets/exam/exam_viewable.dart'; import 'package:refilc_mobile_ui/common/widgets/grade/grade_viewable.dart'; import 'package:refilc_mobile_ui/common/hero_scrollview.dart'; import 'package:refilc_mobile_ui/pages/grades/calculator/grade_calculator.dart'; @@ -53,12 +61,14 @@ class GradeSubjectView extends StatefulWidget { State createState() => _GradeSubjectViewState(); } -class _GradeSubjectViewState extends State { +class _GradeSubjectViewState extends State + with TickerProviderStateMixin { final GlobalKey _scaffoldKey = GlobalKey(); // Controllers PersistentBottomSheetController? _sheetController; final ScrollController _scrollController = ScrollController(); + late TabController _tabController; List gradeTiles = []; @@ -68,6 +78,9 @@ class _GradeSubjectViewState extends State { late SettingsProvider settingsProvider; late DatabaseProvider dbProvider; late UserProvider user; + late ExamProvider examProvider; + + late TimetableController _timetableController; late double average; late Widget gradeGraph; @@ -79,6 +92,8 @@ class _GradeSubjectViewState extends State { List getSubjectGrades(GradeSubject subject) => !gradeCalcMode ? gradeProvider.grades.where((e) => e.subject == subject).toList() : calculatorProvider.grades.where((e) => e.subject == subject).toList(); + List getSubjectExams(GradeSubject subject) => + examProvider.exams.where((e) => e.subject == subject).toList(); bool showGraph(List subjectGrades) { if (gradeCalcMode) return true; @@ -93,7 +108,11 @@ class _GradeSubjectViewState extends State { return subjectGrades.where((e) => e.type == GradeType.midYear).length > 1; } - void buildTiles(List subjectGrades) { + void buildTiles( + List subjectGrades, { + List? nextWeekLessons, + List? subjectExams, + }) { List tiles = []; tiles.add(Panel( @@ -128,57 +147,99 @@ class _GradeSubjectViewState extends State { ), )); - if (showGraph(subjectGrades)) { + tiles.addAll( + [ + const SizedBox( + height: 5.0, + ), + FilterBar( + padding: EdgeInsets.zero, + items: [ + Tab(text: "grades".i18n), + Tab(text: "timetable".i18n), + Tab(text: "exams".i18n), + ], + controller: _tabController, + disableFading: true, + ), + ], + ); + + if (showGraph(subjectGrades) && _tabController.index == 0) { tiles.add(gradeGraph); } else { - tiles.add(Container(height: 24.0)); + tiles.add(Container(height: 20.0)); } - tiles.add(Padding( - padding: const EdgeInsets.only(bottom: 24.0), - child: Panel( - child: GradesCount(grades: getSubjectGrades(widget.subject).toList()), - ), - )); + if (_tabController.index == 0) { + tiles.add(Padding( + padding: const EdgeInsets.only(bottom: 24.0), + child: Panel( + child: GradesCount(grades: getSubjectGrades(widget.subject).toList()), + ), + )); + } // ignore: no_leading_underscores_for_local_identifiers - List _gradeTiles = []; + List _tiles = []; if (!gradeCalcMode) { - subjectGrades.sort((a, b) => -a.date.compareTo(b.date)); + if (_tabController.index == 0) { + subjectGrades.sort((a, b) => -a.date.compareTo(b.date)); - _gradeTiles.add(const SizedBox( - height: 4.0, - )); + _tiles.add(const SizedBox( + height: 4.0, + )); - for (var grade in subjectGrades) { - if (grade.type == GradeType.midYear) { - _gradeTiles.add(GradeViewable(grade)); - } else { - _gradeTiles.add(CertificationTile( - grade, - padding: EdgeInsets.only( - bottom: 8.0, - top: (subjectGrades.first.id == grade.id) ? 0.0 : 8.0), - newStyle: true, + for (var grade in subjectGrades) { + if (grade.type == GradeType.midYear) { + _tiles.add(GradeViewable(grade)); + } else { + _tiles.add(CertificationTile( + grade, + padding: EdgeInsets.only( + bottom: 8.0, + top: (subjectGrades.first.id == grade.id) ? 0.0 : 8.0), + newStyle: true, + )); + } + } + + _tiles.add(const SizedBox( + height: 4.0, + )); + } + if (_tabController.index == 1) { + nextWeekLessons!.sort((a, b) => -a.date.compareTo(b.date)); + + for (var lesson in nextWeekLessons) { + _tiles.add(LessonTile( + lesson, + subjectPageView: true, )); } } + if (_tabController.index == 2) { + subjectExams!.sort((a, b) => -a.writeDate.compareTo(b.writeDate)); - _gradeTiles.add(const SizedBox( - height: 4.0, - )); + for (var exam in subjectExams) { + _tiles.add(ExamViewable( + exam, + showSubject: false, + )); + } + } } else if (subjectGrades.isNotEmpty) { - _gradeTiles.add(const SizedBox( + _tiles.add(const SizedBox( height: 8.0, )); subjectGrades.sort((a, b) => -a.date.compareTo(b.date)); for (var grade in subjectGrades) { - _gradeTiles.add(GradeTile(grade)); + _tiles.add(GradeTile(grade)); } - _gradeTiles.add(const SizedBox( + _tiles.add(const SizedBox( height: 8.0, )); } @@ -197,15 +258,29 @@ class _GradeSubjectViewState extends State { child: child, ); }, - child: _gradeTiles.isNotEmpty - ? Panel( - key: ValueKey(gradeCalcMode), - title: Text( - gradeCalcMode ? "Ghost Grades".i18n : "Grades".i18n, - ), - child: Column( - children: _gradeTiles, - )) + child: _tiles.isNotEmpty + ? (_tabController.index == 0 + ? Panel( + key: ValueKey(gradeCalcMode), + title: Text( + gradeCalcMode ? "Ghost Grades".i18n : "Grades".i18n, + ), + child: Column( + children: _tiles, + )) + : _tabController.index == 1 + ? SplittedPanel( + padding: EdgeInsets.zero, + cardPadding: const EdgeInsets.only( + left: 6.0, right: 12.0, top: 6.0, bottom: 6.0), + title: Text("upcoming_lessons".i18n), + children: _tiles, + ) + : SplittedPanel( + padding: EdgeInsets.zero, + title: Text("exams".i18n), + children: _tiles, + )) : const SizedBox(), ), ); @@ -220,6 +295,15 @@ class _GradeSubjectViewState extends State { super.initState(); user = Provider.of(context, listen: false); dbProvider = Provider.of(context, listen: false); + + _tabController = TabController(length: 3, vsync: this); + + _timetableController = TimetableController(); + _timetableController.jump(_timetableController.currentWeek, + context: context, initial: true, skip: true); + if (DateTime.now().day > 5) { + _timetableController.next(context); + } } void fetchGoalPlans() async { @@ -234,6 +318,13 @@ class _GradeSubjectViewState extends State { gradeProvider = Provider.of(context); calculatorProvider = Provider.of(context); settingsProvider = Provider.of(context); + examProvider = Provider.of(context); + + List nextWeekLessons = (_timetableController.days ?? []) + .expand((e) => e) + .where((e) => e.subject.id == widget.subject.id) + .toList(); + List subjectExams = getSubjectExams(widget.subject); List subjectGrades = getSubjectGrades(widget.subject).toList(); average = AverageHelper.averageEvals(subjectGrades); @@ -266,7 +357,11 @@ class _GradeSubjectViewState extends State { ); if (!gradeCalcMode) { - buildTiles(subjectGrades); + buildTiles( + subjectGrades, + nextWeekLessons: nextWeekLessons, + subjectExams: subjectExams, + ); } else { List ghostGrades = calculatorProvider.ghosts .where((e) => e.subject == widget.subject) @@ -324,94 +419,105 @@ class _GradeSubjectViewState extends State { onRefresh: () async {}, color: Theme.of(context).colorScheme.secondary, child: HeroScrollView( - showTitleUnscroll: false, - onClose: () { - if (_sheetController != null && gradeCalcMode) { - _sheetController!.close(); - } else { - Navigator.of(context).pop(); - } - }, - navBarItems: [ - const SizedBox(width: 6.0), - if (widget.groupAverage != 0) - Center( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - borderRadius: BorderRadius.circular(50.0), - ), - child: AverageDisplay( - average: widget.groupAverage, border: true), + showTitleUnscroll: false, + onClose: () { + if (_sheetController != null && gradeCalcMode) { + _sheetController!.close(); + } else { + Navigator.of(context).pop(); + } + }, + navBarItems: [ + const SizedBox(width: 6.0), + if (widget.groupAverage != 0) + Center( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: BorderRadius.circular(50.0), ), + child: AverageDisplay( + average: widget.groupAverage, border: true), ), - const SizedBox(width: 6.0), - if (average != 0) - Center( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - borderRadius: BorderRadius.circular(50.0), - ), - child: AverageDisplay(average: average), + ), + const SizedBox(width: 6.0), + if (average != 0) + Center( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: BorderRadius.circular(50.0), ), + child: AverageDisplay(average: average), ), - const SizedBox(width: 6.0), - if (plan != '') - Center( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - borderRadius: BorderRadius.circular(50.0), - ), - child: GestureDetector( - onTap: () { - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => - GoalStateScreen(subject: widget.subject))); - }, - child: Container( - width: 54.0, - padding: const EdgeInsets.symmetric(vertical: 8.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(45.0), - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(.15), - ), - child: Icon( - FeatherIcons.flag, - size: 17.0, - weight: 2.5, - color: Theme.of(context).colorScheme.primary, - ), + ), + const SizedBox(width: 6.0), + if (plan != '') + Center( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: BorderRadius.circular(50.0), + ), + child: GestureDetector( + onTap: () { + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => + GoalStateScreen(subject: widget.subject))); + }, + child: Container( + width: 54.0, + padding: const EdgeInsets.symmetric(vertical: 8.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(45.0), + color: Theme.of(context) + .colorScheme + .primary + .withOpacity(.15), + ), + child: Icon( + FeatherIcons.flag, + size: 17.0, + weight: 2.5, + color: Theme.of(context).colorScheme.primary, ), ), ), ), - const SizedBox(width: 12.0), - ], - icon: SubjectIcon.resolveVariant( - subject: widget.subject, context: context), - scrollController: _scrollController, - title: widget.subject.renamedTo ?? widget.subject.name.capital(), - italic: settingsProvider.renamedSubjectsItalics && - widget.subject.isRenamed, - child: SubjectGradesContainer( - child: CupertinoScrollbar( - child: ListView.builder( - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric(horizontal: 24.0), - shrinkWrap: true, - itemBuilder: (context, index) => gradeTiles[index], - itemCount: gradeTiles.length, - ), ), - )), + const SizedBox(width: 12.0), + ], + icon: SubjectIcon.resolveVariant( + subject: widget.subject, context: context), + scrollController: _scrollController, + title: widget.subject.renamedTo ?? widget.subject.name.capital(), + italic: settingsProvider.renamedSubjectsItalics && + widget.subject.isRenamed, + // child: TabBarView( + // physics: const BouncingScrollPhysics(), + // controller: _tabController, + // children: List.generate( + // 3, (index) => filterViewBuilder(context, index))), + // ), + child: SubjectGradesContainer( + child: CupertinoScrollbar( + child: ListView.builder( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 24.0), + shrinkWrap: true, + itemBuilder: (context, index) => gradeTiles[index], + itemCount: gradeTiles.length, + ), + ), + ), + ), )); } + Widget filterViewBuilder(context, int activeData) { + return Container(); + } + void gradeCalc(BuildContext context) { // Scroll to the top of the page _scrollController.animateTo(100, diff --git a/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart b/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart index 9a69431..1985e34 100644 --- a/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart +++ b/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart @@ -22,6 +22,11 @@ extension Localization on String { "data": "Data", "you_have_hw": "You have %s homework(s) to do", "grades_cnt": "Grade count: %s", + // v5 + "upcoming_lessons": "Upcoming Lessons", + "exams": "Exams", + "timetable": "Timetable", + "grades": "Grades", }, "hu_hu": { "Grades": "Tantárgyak", @@ -41,6 +46,11 @@ extension Localization on String { "data": "Adatok", "you_have_hw": "%s házi feladat vár rád", "grades_cnt": "Jegyek száma: %s", + // v5 + "upcoming_lessons": "Közelgő órák", + "exams": "Számonkérések", + "timetable": "Órarend", + "grades": "Jegyek", }, "de_de": { "Grades": "Fächer", @@ -60,6 +70,11 @@ extension Localization on String { "data": "Daten", "you_have_hw": "Du hast %s Hausaufgaben", "grades_cnt": "Anzahl der Noten: %s", + // v5 + "upcoming_lessons": "Bevorstehende Lektionen", + "exams": "Prüfungen", + "timetable": "Stundenplan", + "grades": "Noten", }, };