diff --git a/app/lib/app.dart b/app/lib/app.dart index aff8cc7..33deeab 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -8,6 +8,7 @@ import 'features/onboarding/presentation/bloc/user_initialization_bloc.dart'; import 'features/quiz_exercise/presentation/bloc/quiz_exercise_cubit.dart'; import 'features/quiz_registration/presentation/bloc/quiz_registration_cubit.dart'; import 'features/quiz_result/presentation/bloc/quiz_result_cubit.dart'; +import 'features/quiz_start/presentation/bloc/quiz_start_cubit.dart'; import 'services/di.dart'; import 'services/router_service.dart'; @@ -30,6 +31,7 @@ class App extends StatelessWidget { ), BlocProvider(create: (context) => QuizExerciseCubit()), BlocProvider(create: (context) => QuizResultCubit()), + BlocProvider(create: (context) => QuizStartCubit()), BlocProvider(create: (context) => QuizRegistrationCubit()), BlocProvider(create: (context) => get()), ], diff --git a/app/lib/features/quiz_exercise/presentation/bloc/quiz_exercise_cubit.dart b/app/lib/features/quiz_exercise/presentation/bloc/quiz_exercise_cubit.dart index c9d9c42..dbf07c8 100644 --- a/app/lib/features/quiz_exercise/presentation/bloc/quiz_exercise_cubit.dart +++ b/app/lib/features/quiz_exercise/presentation/bloc/quiz_exercise_cubit.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../../models/quiz_participation.dart'; import '../../../../models/weekly_quiz.dart'; import '../../../../services/quiz_service.dart'; import '../model/quiz_exercise.dart'; @@ -18,11 +19,11 @@ class QuizExerciseCubit extends Cubit { QuizExerciseCubit() : super(QuizExerciseInitialState()); late WeeklyQuiz quiz; + late WeeklyQuizParticipation participation; late QuizExercise currentProblem; int currentProblemIndex = 0; late List problemIdList; late QuizExerciseAttempt attempt; - late String challengeGroup; String? quizParticipantId; String selectedAnswer = ''; @@ -33,43 +34,35 @@ class QuizExerciseCubit extends Cubit { late QuizService quizService; late QuizExerciseRepository quizExerciseRepository; - FutureOr initialize( - {String? quizId, - String? quizParticipantId, - String? challengeGroup}) async { + FutureOr initialize({String? quizParticipantId}) async { try { quizService = QuizService(); quizExerciseRepository = QuizExerciseRepository(); emit(QuizExerciseLoading()); - if (quizId == null) { - throw Exception('Quiz Id null'); - } if (quizParticipantId == null) { throw Exception('Quiz Participant Id null'); } - if (challengeGroup == null) { - throw Exception('Challenge Group null'); - } this.quizParticipantId = quizParticipantId; - this.challengeGroup = challengeGroup; - quiz = await quizService.getWeeklyQuizById(quizId); + final participation = await quizService.getWeeklyQuizParticipant( + quizParticipantId: quizParticipantId, + ); + + quiz = await quizService.getWeeklyQuizById(participation.quiz_id); - final _problemIdList = quiz.problems[challengeGroup]; - if (_problemIdList == null) { - throw Exception('Task set for `$challengeGroup` is not found'); + final list = quiz.problems[participation.challenge_group]; + if (list == null) { + throw Exception( + 'Task set for `${participation.challenge_group}` is not found'); } - problemIdList = _problemIdList; + problemIdList = list; if (problemIdList.isEmpty) { - throw Exception('Task set for `$challengeGroup` is empty'); + throw Exception( + 'Task set for `${participation.challenge_group}` is empty'); } // TODO(someone): fix the check logic later - // final weeklyQuizParticipant = await quizService.getWeeklyQuizParticipant( - // quizParticipantId: quizParticipantId, - // ); - // // if (weeklyQuizParticipant.attempts.isEmpty) { attempt = QuizExerciseAttempt( startAt: DateTime.now(), @@ -88,7 +81,7 @@ class QuizExerciseCubit extends Cubit { currentProblem = await quizExerciseRepository.getQuizExercise(problemIdList.first); - final duration = quiz.duration_minute[challengeGroup]; + final duration = quiz.duration_minute[participation.challenge_group]; if (duration == null) { throw Exception('Duration for selected Challenge Group not found'); } diff --git a/app/lib/features/quiz_exercise/presentation/pages/quiz_exercise_page.dart b/app/lib/features/quiz_exercise/presentation/pages/quiz_exercise_page.dart index 483b7a0..bdffcfa 100644 --- a/app/lib/features/quiz_exercise/presentation/pages/quiz_exercise_page.dart +++ b/app/lib/features/quiz_exercise/presentation/pages/quiz_exercise_page.dart @@ -3,11 +3,8 @@ part of '_pages.dart'; class QuizExercisePage extends StatefulWidget { - final String? quizId; - final String? challengeGroup; final String? quizParticipantId; - QuizExercisePage( - {super.key, this.quizId, this.challengeGroup, this.quizParticipantId}); + QuizExercisePage({super.key, this.quizParticipantId}); @override State createState() => _QuizExercisePageState(); @@ -18,10 +15,7 @@ class _QuizExercisePageState extends State { void initState() { final cubit = context.read(); // if (cubit.quizParticipantId != widget.quizParticipantId) { - cubit.initialize( - quizId: widget.quizId, - quizParticipantId: widget.quizParticipantId, - challengeGroup: widget.challengeGroup); + cubit.initialize(quizParticipantId: widget.quizParticipantId); // } super.initState(); } diff --git a/app/lib/features/quiz_registration/presentation/pages/quiz_registration_page.dart b/app/lib/features/quiz_registration/presentation/pages/quiz_registration_page.dart index a1b8bc8..8d5c255 100644 --- a/app/lib/features/quiz_registration/presentation/pages/quiz_registration_page.dart +++ b/app/lib/features/quiz_registration/presentation/pages/quiz_registration_page.dart @@ -48,40 +48,14 @@ class _QuizRegistrationPageState extends State { String score, String level, BuildContext context) { return InkWell( onTap: () async { - Future future; - if (weeklyQuizParticipant.attempts.length >= - weeklyQuizParticipant.quiz_max_attempts) { - future = context.push( - Uri( - path: '/quiz_result', - queryParameters: { - 'quiz_participant_id': weeklyQuizParticipant.id, - }, - ).toString(), - ); - } else if (weeklyQuizParticipant.attempts.isNotEmpty) { - future = context.push( - Uri( - path: '/quiz_result', - queryParameters: { - 'quiz_participant_id': weeklyQuizParticipant.id, - }, - ).toString(), - ); - } else { - future = context.push( - Uri( - path: '/quiz_exercise', - queryParameters: { - 'quiz_id': weeklyQuizParticipant.quiz_id, - 'challenge_group': weeklyQuizParticipant.challenge_group, - 'quiz_participant_id': weeklyQuizParticipant.id, - }, - ).toString(), - ); - } - - await future; + await context.push( + Uri( + path: '/quiz_start', + queryParameters: { + 'quiz_participant_id': weeklyQuizParticipant.id, + }, + ).toString(), + ); await context .read() .fetchParticipantWeeklyQuiz(); diff --git a/app/lib/features/quiz_start/presentation/bloc/quiz_start_cubit.dart b/app/lib/features/quiz_start/presentation/bloc/quiz_start_cubit.dart new file mode 100644 index 0000000..c0dbdb3 --- /dev/null +++ b/app/lib/features/quiz_start/presentation/bloc/quiz_start_cubit.dart @@ -0,0 +1,42 @@ +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +import '../../../../models/quiz_participation.dart'; +import '../../../../models/weekly_quiz.dart'; +import '../../../../services/quiz_service.dart'; + +part 'quiz_start_state.dart'; + +class QuizStartCubit extends Cubit { + QuizStartCubit() : super(QuizStartInitial()); + + bool agreement = false; + late WeeklyQuiz quiz; + late WeeklyQuizParticipation participation; + + Future initialize(String? quizParticipantId) async { + try { + final quizService = QuizService(); + + if (quizParticipantId == null) { + throw Exception('QuizParticipantId is null'); + } + + participation = await quizService.getWeeklyQuizParticipant( + quizParticipantId: quizParticipantId); + + quiz = await quizService.getWeeklyQuizById(participation.quiz_id); + + emit(QuizStartSuccess( + participation: participation, quiz: quiz, agreement: agreement)); + } catch (e) { + emit(QuizStartFailed(e.toString())); + } + } + + void setAgreement(bool value) { + agreement = value; + emit(QuizStartSuccess( + participation: participation, quiz: quiz, agreement: agreement)); + } +} diff --git a/app/lib/features/quiz_start/presentation/bloc/quiz_start_state.dart b/app/lib/features/quiz_start/presentation/bloc/quiz_start_state.dart new file mode 100644 index 0000000..dbb4e9b --- /dev/null +++ b/app/lib/features/quiz_start/presentation/bloc/quiz_start_state.dart @@ -0,0 +1,28 @@ +part of 'quiz_start_cubit.dart'; + +@immutable +abstract class QuizStartState {} + +class QuizStartInitial extends QuizStartState {} + +class QuizStartSuccess extends QuizStartState { + final WeeklyQuizParticipation participation; + final WeeklyQuiz quiz; + final bool agreement; + + QuizStartSuccess( + {required this.participation, + required this.quiz, + required this.agreement}); + @override + List get props => [participation, quiz, agreement]; +} + +class QuizStartFailed extends QuizStartState { + final String error; + + QuizStartFailed(this.error); + + @override + List get props => [error]; +} diff --git a/app/lib/features/quiz_start/presentation/pages/_pages.dart b/app/lib/features/quiz_start/presentation/pages/_pages.dart new file mode 100644 index 0000000..0ca6461 --- /dev/null +++ b/app/lib/features/quiz_start/presentation/pages/_pages.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../../core/bases/enum/button_type.dart'; +import '../../../../core/bases/widgets/atoms/button.dart'; +import '../../../../core/bases/widgets/layout/bebras_scaffold.dart'; +import '../../../../core/constants/assets.dart'; +import '../bloc/quiz_start_cubit.dart'; + +part 'quiz_start_page.dart'; diff --git a/app/lib/features/quiz_start/presentation/pages/quiz_start_page.dart b/app/lib/features/quiz_start/presentation/pages/quiz_start_page.dart new file mode 100644 index 0000000..804799e --- /dev/null +++ b/app/lib/features/quiz_start/presentation/pages/quiz_start_page.dart @@ -0,0 +1,146 @@ +part of '_pages.dart'; + +class QuizStartPage extends StatefulWidget { + final String? quizParticipantId; + + QuizStartPage({super.key, this.quizParticipantId}); + + @override + State createState() => _QuizStartPageState(); +} + +class _QuizStartPageState extends State { + @override + void initState() { + context.read().initialize(widget.quizParticipantId); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BebrasScaffold( + body: SingleChildScrollView( + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(32), + child: Column( + children: [ + Image.asset( + Assets.bebrasPandaiText, + ), + const SizedBox( + height: 20, + ), + BlocBuilder( + builder: (context, state) { + if (state is QuizStartSuccess) { + return buildSuccessState(state); + } + if (state is QuizStartFailed) { + return Text(state.error); + } + return Text('OK'); + }, + ), + ], + ), + ), + ], + ), + ), + ); + } + + Widget buildSuccessState(QuizStartSuccess state) { + return Column( + children: [ + Row( + children: [ + Expanded( + flex: 1, + child: Container( + padding: const EdgeInsets.all(16), + decoration: + BoxDecoration(border: Border.all(color: Colors.black)), + child: Column( + children: [ + Text(state.quiz.title), + Text( + 'Jumlah soal: ${state.quiz.problems[state.participation.challenge_group]?.length}'), + Text( + 'Alokasi waktu: ${state.quiz.duration_minute[state.participation.challenge_group]} menit'), + Text( + 'Sisa coba lagi ${state.participation.attempts.length} / ${state.participation.quiz_max_attempts}'), + const SizedBox( + height: 10, + ), + Text('Peraturan'), + Text('Pra Tantangan'), + Text('1. 12312312312'), + Text('2. qwerqwrqrwf'), + ], + ), + ), + ), + ], + ), + const SizedBox( + height: 10, + ), + Row( + children: [ + Checkbox( + value: state.agreement, + onChanged: (value) { + if (value != null) { + context.read().setAgreement(value); + } + }), + Flexible( + child: Text( + 'Saya telah membaca peraturan & akan mengerjakan dengan jujur')) + ], + ), + const SizedBox( + height: 10, + ), + Button( + buttonType: ButtonType.tertiary, + isDisabled: !state.agreement || + state.participation.attempts.length >= + state.participation.quiz_max_attempts, + onTap: () async { + await context.push( + Uri( + path: '/quiz_exercise', + queryParameters: { + 'quiz_participant_id': state.participation.id, + }, + ).toString(), + ); + }, + text: 'Mulai', + ), + const SizedBox( + height: 10, + ), + Button( + buttonType: ButtonType.primary, + isDisabled: state.participation.attempts.isEmpty, + onTap: () async { + await context.push( + Uri( + path: '/quiz_result', + queryParameters: { + 'quiz_participant_id': state.participation.id, + }, + ).toString(), + ); + }, + text: 'Lihat Nilai', + ), + ], + ); + } +} diff --git a/app/lib/services/router_service.dart b/app/lib/services/router_service.dart index 1b248a9..923e4a6 100644 --- a/app/lib/services/router_service.dart +++ b/app/lib/services/router_service.dart @@ -10,6 +10,7 @@ import '../features/onboarding/presentation/pages/_pages.dart'; import '../features/quiz_exercise/presentation/pages/_pages.dart'; import '../features/quiz_registration/presentation/pages/_pages.dart'; import '../features/quiz_result/presentation/pages/_pages.dart'; +import '../features/quiz_start/presentation/pages/_pages.dart'; GoRouter router = GoRouter( routes: [ @@ -42,8 +43,6 @@ GoRouter router = GoRouter( GoRoute( path: '/quiz_exercise', builder: (context, state) => QuizExercisePage( - quizId: state.queryParameters['quiz_id'], - challengeGroup: state.queryParameters['challenge_group'], quizParticipantId: state.queryParameters['quiz_participant_id'], ), ), @@ -57,6 +56,12 @@ GoRouter router = GoRouter( quizParticipantId: state.queryParameters['quiz_participant_id'], ), ), + GoRoute( + path: '/quiz_start', + builder: (context, state) => QuizStartPage( + quizParticipantId: state.queryParameters['quiz_participant_id'], + ), + ), GoRoute( path: '/material', builder: (context, state) => const MaterialMenu(),