diff --git a/lib/presentation/core/routes/routes.dart b/lib/presentation/core/routes/routes.dart index bf7c5e97..8437bf1a 100644 --- a/lib/presentation/core/routes/routes.dart +++ b/lib/presentation/core/routes/routes.dart @@ -22,8 +22,9 @@ import 'package:rtu_mirea_app/presentation/pages/profile/profile_scores_page.dar import 'package:rtu_mirea_app/presentation/pages/profile/profile_page.dart'; import 'package:rtu_mirea_app/presentation/pages/profile/profile_settings_page.dart'; import 'package:rtu_mirea_app/presentation/widgets/images_view_gallery.dart'; +import 'package:rtu_mirea_app/rating_system_calculator/view/about_rating_system_page.dart'; import 'package:rtu_mirea_app/schedule/view/schedule_details_page.dart'; -// import 'package:rtu_mirea_app/rating_system_calculator/view/rating_system_calculator_page.dart'; +import 'package:rtu_mirea_app/rating_system_calculator/view/rating_system_calculator_page.dart'; import 'package:rtu_mirea_app/schedule/view/schedule_page.dart'; import 'package:rtu_mirea_app/search/view/search_page.dart'; import 'package:rtu_mirea_app/services/view/view.dart'; @@ -86,23 +87,23 @@ GoRouter createRouter() => GoRouter( ), GoRoute( path: 'details', - builder: (context, state) { + redirect: (context, state) { try { - final extra = - state.extra as (Map, DateTime); - return ScheduleDetailsPage( - lesson: LessonSchedulePart.fromJson(extra.$1), - selectedDate: extra.$2, - ); + state.extra as (LessonSchedulePart, DateTime); + return null; } catch (e) { - final extra = - state.extra as (LessonSchedulePart, DateTime); - return ScheduleDetailsPage( - lesson: extra.$1, - selectedDate: extra.$2, - ); + return '/schedule'; } }, + builder: (context, state) { + final extra = + state.extra as (LessonSchedulePart, DateTime); + + return ScheduleDetailsPage( + lesson: extra.$1, + selectedDate: extra.$2, + ); + }, ), ], ), @@ -111,13 +112,19 @@ GoRouter createRouter() => GoRouter( GoRoute( path: '/services', builder: (context, state) => const ServicesPage(), - // routes: [ - // GoRoute( - // path: 'rating-system-calculator', - // builder: (context, state) => - // const RatingSystemCalculatorPage(), - // ), - // ], + routes: [ + GoRoute( + path: 'rating-system-calculator', + builder: (context, state) => + const RatingSystemCalculatorPage(), + routes: [ + GoRoute( + path: 'about', + builder: (context, state) => + const AboutRatingSystemPage(), + ), + ]), + ], ), ]), StatefulShellBranch(navigatorKey: _profileNavigatorKey, routes: [ diff --git a/lib/presentation/widgets/page_with_theme_consumer.dart b/lib/presentation/widgets/page_with_theme_consumer.dart deleted file mode 100644 index 75fe92ad..00000000 --- a/lib/presentation/widgets/page_with_theme_consumer.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:rtu_mirea_app/presentation/app_notifier.dart'; - -abstract class PageWithThemeConsumer extends StatelessWidget { - const PageWithThemeConsumer({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, appNotifier, child) { - return buildPage(context); - }, - ); - } - - Widget buildPage(BuildContext context); -} diff --git a/lib/rating_system_calculator/rating_system_calculator.dart b/lib/rating_system_calculator/rating_system_calculator.dart new file mode 100644 index 00000000..3b46d13d --- /dev/null +++ b/lib/rating_system_calculator/rating_system_calculator.dart @@ -0,0 +1,2 @@ +export 'view/view.dart'; +export 'widgets/widgets.dart'; diff --git a/lib/rating_system_calculator/view/about_rating_system_page.dart b/lib/rating_system_calculator/view/about_rating_system_page.dart new file mode 100644 index 00000000..4d1ed0f1 --- /dev/null +++ b/lib/rating_system_calculator/view/about_rating_system_page.dart @@ -0,0 +1,198 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:rtu_mirea_app/presentation/theme.dart'; +import 'package:rtu_mirea_app/presentation/typography.dart'; +import 'package:rtu_mirea_app/presentation/widgets/buttons/primary_button.dart'; +import 'package:rtu_mirea_app/rating_system_calculator/rating_system_calculator.dart'; +import 'package:rtu_mirea_app/rating_system_calculator/widgets/scores_table.dart'; +import 'package:unicons/unicons.dart'; + +class AboutRatingSystemPage extends StatelessWidget { + const AboutRatingSystemPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppTheme.colors.background01, + appBar: AppBar( + backgroundColor: AppTheme.colors.background01, + title: const Text( + "О БРС", + ), + ), + body: const SafeArea( + child: Padding( + padding: EdgeInsets.all(24), + child: RatingSystemCalculatorView(), + ), + ), + ); + } +} + +class RatingSystemCalculatorView extends StatefulWidget { + const RatingSystemCalculatorView({super.key}); + + @override + State createState() => + _RatingSystemCalculatorViewState(); +} + +class _RatingSystemCalculatorViewState + extends State { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return CustomScrollView( + slivers: [ + SliverList( + delegate: SliverChildListDelegate( + [ + Text("Баллы", style: AppTextStyle.h4), + const SizedBox(height: 16), + Text( + "Баллы — это единица измерения успеваемости студента. Они начисляются за выполнение академических требований в рамках учебного плана.", + style: AppTextStyle.body, + ), + const ShortDescriptionCard( + icon: UniconsSolid.check_circle, + text: 'Максимальное количество баллов - 90', + ), + const ShortDescriptionCard( + icon: UniconsSolid.exclamation_circle, + text: + 'Полученные баллы можно будет увидеть в СДО по каждой дисциплине', + ), + const SizedBox(height: 24), + Text("Основные баллы", style: AppTextStyle.h6), + Text( + "максимум 30", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.primary, + ), + ), + const SizedBox(height: 16), + RichText( + text: TextSpan( + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.active, + ), + children: [ + const TextSpan(text: 'Выставляются за выполненеие '), + TextSpan( + text: + 'обязательных заданий на аудиторных практических занятиях', + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.primary, + ), + ), + const TextSpan( + text: + ' (контрольные работы, лабораторные и т.д.)\n\nВыставляет преподаватель, ведущий практические занятия.\n\nВ начале семестра преподаватель должен объявить за что и сколько основных баллов можно будет получить в течение семестра.'), + ], + ), + ), + const SizedBox(height: 24), + Text("Баллы за работу на занятиях", style: AppTextStyle.h6), + Text( + "максимум 30", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.primary, + ), + ), + const SizedBox(height: 16), + RichText( + text: TextSpan( + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.active, + ), + children: [ + const TextSpan(text: 'Выставляются за '), + TextSpan( + text: 'посещаемость и активность на занятиях', + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.primary, + ), + ), + const TextSpan( + text: + '.\n\nВыставляет лектор или преподаватель, ведущий практические занятия.\n\nВ начале семестра лектор и семинарист должны объявить за что и сколько баллов можно будет получить в течение семестра.'), + ], + ), + ), + const SizedBox(height: 24), + Text("Дополнительные баллы", style: AppTextStyle.h6), + Text( + "максимум 30", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.primary, + ), + ), + const SizedBox(height: 16), + RichText( + text: TextSpan( + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.active, + ), + children: [ + const TextSpan(text: 'Выставляются за выполнение '), + TextSpan( + text: 'тестовых заданий в СДО', + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.primary, + ), + ), + const TextSpan( + text: + '.\n\nВыставляет преподаватель, ведущий практические занятия.\n\nВ начале семестра преподаватель должен объявить за что и сколько дополнительных баллов можно будет получить в течение семестра.'), + ], + ), + ), + const SizedBox(height: 24), + Text("Что дают баллы", style: AppTextStyle.h6), + const SizedBox(height: 16), + RichText( + text: TextSpan( + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.active, + ), + children: [ + const TextSpan(text: '1. Если общая '), + TextSpan( + text: 'сумма баллов 60 и более', + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.primary, + ), + ), + const TextSpan( + text: + ', можно автоматически получить зачет или оценку «удовлетворительно» на экзамене.\n\n', + ), + const TextSpan( + text: + '2. На экзамене можно получить оценки выше, чем «удовлетворительно», однако из баллов, полученных в течение семестра, учитываются только основные баллы (макс. 30 баллов), а ещё 60 баллов можно получить за ответы на экзамене.\n\n', + ), + ], + ), + ), + const SizedBox(height: 24), + Text("Таблица баллов", style: AppTextStyle.h6), + const SizedBox(height: 16), + const ScoresTable(), + ], + ), + ), + ], + ); + } +} diff --git a/lib/rating_system_calculator/view/rating_system_calculator_page.dart b/lib/rating_system_calculator/view/rating_system_calculator_page.dart new file mode 100644 index 00000000..5f36fffa --- /dev/null +++ b/lib/rating_system_calculator/view/rating_system_calculator_page.dart @@ -0,0 +1,268 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:rtu_mirea_app/presentation/theme.dart'; +import 'package:rtu_mirea_app/presentation/typography.dart'; +import 'package:rtu_mirea_app/presentation/widgets/buttons/primary_button.dart'; +import 'package:rtu_mirea_app/presentation/widgets/buttons/text_outlined_button.dart'; +import 'package:rtu_mirea_app/schedule/bloc/schedule_bloc.dart'; +import 'package:rtu_mirea_app/schedule/models/models.dart'; + +class RatingSystemCalculatorPage extends StatelessWidget { + const RatingSystemCalculatorPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppTheme.colors.background01, + appBar: AppBar( + backgroundColor: AppTheme.colors.background01, + elevation: 0, + title: const Text( + "Бально-рейтинговая система", + ), + ), + body: const SafeArea( + child: Padding( + padding: EdgeInsets.all(16), + child: RatingSystemCalculatorView(), + ), + ), + ); + } +} + +class RatingSystemCalculatorView extends StatefulWidget { + const RatingSystemCalculatorView({super.key}); + + @override + State createState() => + _RatingSystemCalculatorViewState(); +} + +class _RatingSystemCalculatorViewState + extends State { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final scheduleBloc = context.watch(); + + return CustomScrollView( + slivers: [ + SliverList( + delegate: SliverChildListDelegate( + [ + Container( + decoration: ShapeDecoration( + gradient: const LinearGradient( + stops: [0.1, 0.3, 0.7, 0.9], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFF85FF99), + Color(0xFF87BFFB), + Color(0xFFBD9DFE), + Color(0xFFFFB8DF), + ], + transform: GradientRotation(0.5 * 3.14), + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Бально-рейтинговая система", + style: AppTextStyle.titleM.copyWith( + color: AppTheme.colors.activeLightMode, + fontWeight: FontWeight.w700, + fontSize: 21, + ), + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: Text( + "📖 Перейдите, чтобы узнать, как это устроено", + style: AppTextStyle.bodyL.copyWith( + color: AppTheme.colors.activeLightMode, + fontSize: 16, + ), + maxLines: 2, + ), + ), + ], + ), + const SizedBox(height: 16), + PrimaryButton( + text: "Подробнее", + onClick: () { + context.go('/services/rating-system-calculator/about'); + }, + ), + ], + ), + ).animate().slideX( + duration: 400.ms, + begin: -1.5, + end: 0, + ), + const SizedBox(height: 32), + SubjectsListView( + selectedSchedule: scheduleBloc.state.selectedSchedule, + ), + ], + ), + ), + ], + ); + } +} + +class SubjectsListView extends StatelessWidget { + const SubjectsListView({Key? key, this.selectedSchedule}) : super(key: key); + + final SelectedSchedule? selectedSchedule; + + @override + Widget build(BuildContext context) { + if (selectedSchedule is SelectedGroupSchedule) { + return Column( + children: [ + Card( + color: AppTheme.colors.background02, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFF9ADB7F), + Color(0xFF6EA95C), + ], + ), + ), + child: const Icon( + Icons.school, + color: Colors.white, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Text( + "Математический анализ", + style: AppTextStyle.buttonL.copyWith(), + ), + ), + Padding( + padding: const EdgeInsets.only(right: 18.0), + child: Text( + "32", + style: AppTextStyle.buttonL.copyWith( + color: AppTheme.colors.colorful05, + ), + ), + ), + const Icon(Icons.arrow_forward_ios), + ], + ), + ), + ), + Card( + color: AppTheme.colors.background02, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: AppTheme.colors.colorful02, + ), + child: Icon( + Icons.school, + color: AppTheme.colors.activeLightMode, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Text( + "Линейная алгебра", + style: AppTextStyle.buttonL.copyWith(), + ), + ), + Padding( + padding: const EdgeInsets.only(right: 18.0), + child: Text( + "8", + style: AppTextStyle.buttonL.copyWith( + color: AppTheme.colors.colorful02, + ), + ), + ), + const Icon(Icons.arrow_forward_ios), + ], + ), + ), + ) + ], + ); + } else { + return Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "У вас не выбрано расписание Группы!", + style: AppTextStyle.titleM, + ), + const SizedBox(height: 16), + Text( + "Выберите расписание, на основе которого мы отобразим для вас предметы", + style: AppTextStyle.body, + ), + const SizedBox(height: 24), + TextOutlinedButton( + content: "Выбрать расписание", + onPressed: () { + context.go('/schedule'); + }, + ), + ], + ), + ); + } + } +} diff --git a/lib/rating_system_calculator/view/view.dart b/lib/rating_system_calculator/view/view.dart new file mode 100644 index 00000000..01f75399 --- /dev/null +++ b/lib/rating_system_calculator/view/view.dart @@ -0,0 +1 @@ +export 'rating_system_calculator_page.dart'; diff --git a/lib/rating_system_calculator/widgets/scores_table.dart b/lib/rating_system_calculator/widgets/scores_table.dart new file mode 100644 index 00000000..8adb9442 --- /dev/null +++ b/lib/rating_system_calculator/widgets/scores_table.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:rtu_mirea_app/presentation/theme.dart'; +import 'package:rtu_mirea_app/presentation/typography.dart'; + +class ScoresTable extends StatelessWidget { + const ScoresTable({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Table( + border: TableBorder.all( + color: AppTheme.colors.primary.withOpacity(0.2), + ), + children: [ + TableRow( + children: [ + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Баллы", + style: AppTextStyle.body.copyWith( + fontWeight: FontWeight.w600, + ), + ), + ), + ), + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Оценка", + style: AppTextStyle.body.copyWith( + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + TableRow( + children: [ + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "85+", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.colorful05, + ), + ), + ), + ), + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "5", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.colorful05, + ), + ), + ), + ), + ], + ), + TableRow( + children: [ + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "70-84", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.colorful01, + ), + ), + ), + ), + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "4", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.colorful01, + ), + ), + ), + ), + ], + ), + TableRow(children: [ + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "60-69", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.colorful07, + ), + ), + ), + ), + TableCell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "3 (зачет)", + style: AppTextStyle.body.copyWith( + color: AppTheme.colors.colorful07, + ), + ), + ), + ), + ]), + ], + ); + } +} diff --git a/lib/rating_system_calculator/widgets/short_description_card.dart b/lib/rating_system_calculator/widgets/short_description_card.dart new file mode 100644 index 00000000..ed637ac5 --- /dev/null +++ b/lib/rating_system_calculator/widgets/short_description_card.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:rtu_mirea_app/presentation/theme.dart'; +import 'package:rtu_mirea_app/presentation/typography.dart'; +import 'package:unicons/unicons.dart'; + +class ShortDescriptionCard extends StatelessWidget { + const ShortDescriptionCard({ + Key? key, + required this.icon, + required this.text, + }) : super(key: key); + + final IconData icon; + final String text; + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.only(top: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24), + color: AppTheme.colors.primary.withOpacity(0.1), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Icon( + icon, + color: AppTheme.colors.primary, + ), + const SizedBox(width: 16), + Expanded( + child: Text( + text, + style: AppTextStyle.body, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/rating_system_calculator/widgets/widgets.dart b/lib/rating_system_calculator/widgets/widgets.dart new file mode 100644 index 00000000..0d562f30 --- /dev/null +++ b/lib/rating_system_calculator/widgets/widgets.dart @@ -0,0 +1 @@ +export 'short_description_card.dart'; diff --git a/lib/services/view/services_page.dart b/lib/services/view/services_page.dart index 7f6772cf..001070b2 100644 --- a/lib/services/view/services_page.dart +++ b/lib/services/view/services_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:rtu_mirea_app/presentation/theme.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -69,17 +70,17 @@ class _ServicesViewState extends State { launchMode: LaunchMode.inAppBrowserView, description: 'Найди нужный кабинет', ), - // ServiceCard( - // title: 'Калькулятор БРС', - // onTap: () { - // context.go('/services/rating-system-calculator'); - // }, - // icon: ServiceIcon( - // color: AppTheme.colors.colorful02, - // iconColor: AppTheme.colors.background01, - // icon: Icons.calculate, - // ), - // ), + ServiceCard( + title: 'Калькулятор БРС', + onTap: () { + context.go('/services/rating-system-calculator'); + }, + icon: ServiceIcon( + color: AppTheme.colors.colorful02, + iconColor: AppTheme.colors.background01, + icon: Icons.calculate, + ), + ), ServiceCard( title: 'Бюро находок', url: 'https://finds.mirea.ru/',