feat: done final result on the multiplayer quiz
This commit is contained in:
parent
871ec13c31
commit
abe21031ec
|
@ -1,7 +1,9 @@
|
||||||
import 'package:get/get_navigation/src/routes/get_route.dart';
|
import 'package:get/get_navigation/src/routes/get_route.dart';
|
||||||
import 'package:quiz_app/app/middleware/auth_middleware.dart';
|
import 'package:quiz_app/app/middleware/auth_middleware.dart';
|
||||||
import 'package:quiz_app/feature/admin_result_page/bindings/admin_result_binding.dart';
|
import 'package:quiz_app/feature/admin_result_page/bindings/admin_result_binding.dart';
|
||||||
|
import 'package:quiz_app/feature/admin_result_page/bindings/detail_participant_result_binding.dart';
|
||||||
import 'package:quiz_app/feature/admin_result_page/view/admin_result_page.dart';
|
import 'package:quiz_app/feature/admin_result_page/view/admin_result_page.dart';
|
||||||
|
import 'package:quiz_app/feature/admin_result_page/view/detail_participant_result_page.dart';
|
||||||
import 'package:quiz_app/feature/history/binding/detail_history_binding.dart';
|
import 'package:quiz_app/feature/history/binding/detail_history_binding.dart';
|
||||||
import 'package:quiz_app/feature/history/binding/history_binding.dart';
|
import 'package:quiz_app/feature/history/binding/history_binding.dart';
|
||||||
import 'package:quiz_app/feature/history/view/detail_history_view.dart';
|
import 'package:quiz_app/feature/history/view/detail_history_view.dart';
|
||||||
|
@ -148,6 +150,11 @@ class AppPages {
|
||||||
name: AppRoutes.monitorResultMPLPage,
|
name: AppRoutes.monitorResultMPLPage,
|
||||||
page: () => AdminResultPage(),
|
page: () => AdminResultPage(),
|
||||||
binding: AdminResultBinding(),
|
binding: AdminResultBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: AppRoutes.quizMPLResultPage,
|
||||||
|
page: () => ParticipantDetailPage(),
|
||||||
|
binding: DetailParticipantResultBinding(),
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,6 @@ abstract class AppRoutes {
|
||||||
static const monitorResultMPLPage = "/room/quiz/monitor/result";
|
static const monitorResultMPLPage = "/room/quiz/monitor/result";
|
||||||
|
|
||||||
static const updateProfilePage = "/profile/update";
|
static const updateProfilePage = "/profile/update";
|
||||||
|
|
||||||
|
static const quizMPLResultPage = "/room/quiz/result";
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ class APIEndpoint {
|
||||||
static const String quiz = "/quiz";
|
static const String quiz = "/quiz";
|
||||||
static const String quizGenerate = "/quiz/ai";
|
static const String quizGenerate = "/quiz/ai";
|
||||||
static const String quizAnswer = "/quiz/answer";
|
static const String quizAnswer = "/quiz/answer";
|
||||||
|
static const String quizAnswerSession = "/quiz/answer/session";
|
||||||
|
|
||||||
static const String userQuiz = "/quiz/user";
|
static const String userQuiz = "/quiz/user";
|
||||||
static const String quizRecomendation = "/quiz/recomendation";
|
static const String quizRecomendation = "/quiz/recomendation";
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
class QuestionAnswer {
|
||||||
|
final int index;
|
||||||
|
final String question;
|
||||||
|
final dynamic targetAnswer;
|
||||||
|
final int duration;
|
||||||
|
final String type;
|
||||||
|
final List<String>? options;
|
||||||
|
final String answer;
|
||||||
|
final bool isCorrect;
|
||||||
|
final double timeSpent;
|
||||||
|
|
||||||
|
QuestionAnswer({
|
||||||
|
required this.index,
|
||||||
|
required this.question,
|
||||||
|
required this.targetAnswer,
|
||||||
|
required this.duration,
|
||||||
|
required this.type,
|
||||||
|
required this.options,
|
||||||
|
required this.answer,
|
||||||
|
required this.isCorrect,
|
||||||
|
required this.timeSpent,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory QuestionAnswer.fromJson(Map<String, dynamic> json) {
|
||||||
|
return QuestionAnswer(
|
||||||
|
index: json['index'],
|
||||||
|
question: json['question'],
|
||||||
|
targetAnswer: json['target_answer'],
|
||||||
|
duration: json['duration'],
|
||||||
|
type: json['type'],
|
||||||
|
options: json['options'] != null ? List<String>.from(json['options']) : null,
|
||||||
|
answer: json['answer'],
|
||||||
|
isCorrect: json['is_correct'],
|
||||||
|
timeSpent: (json['time_spent'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParticipantResult {
|
||||||
|
final String id;
|
||||||
|
final String sessionId;
|
||||||
|
final String quizId;
|
||||||
|
final String userId;
|
||||||
|
final String answeredAt;
|
||||||
|
final List<QuestionAnswer> answers;
|
||||||
|
final int totalScore;
|
||||||
|
final int totalCorrect;
|
||||||
|
|
||||||
|
ParticipantResult({
|
||||||
|
required this.id,
|
||||||
|
required this.sessionId,
|
||||||
|
required this.quizId,
|
||||||
|
required this.userId,
|
||||||
|
required this.answeredAt,
|
||||||
|
required this.answers,
|
||||||
|
required this.totalScore,
|
||||||
|
required this.totalCorrect,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ParticipantResult.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ParticipantResult(
|
||||||
|
id: json['id'],
|
||||||
|
sessionId: json['session_id'],
|
||||||
|
quizId: json['quiz_id'],
|
||||||
|
userId: json['user_id'],
|
||||||
|
answeredAt: json['answered_at'],
|
||||||
|
answers: (json['answers'] as List).map((e) => QuestionAnswer.fromJson(e)).toList(),
|
||||||
|
totalScore: json['total_score'],
|
||||||
|
totalCorrect: json['total_correct'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double get scorePercent => (totalCorrect / answers.length) * 100;
|
||||||
|
|
||||||
|
int get totalQuestions => answers.length;
|
||||||
|
|
||||||
|
String get name => "User $userId";
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
|
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
import 'package:quiz_app/data/models/base/base_model.dart';
|
import 'package:quiz_app/data/models/base/base_model.dart';
|
||||||
|
import 'package:quiz_app/data/models/history/participant_history_result.dart';
|
||||||
import 'package:quiz_app/data/providers/dio_client.dart';
|
import 'package:quiz_app/data/providers/dio_client.dart';
|
||||||
|
|
||||||
class AnswerService extends GetxService {
|
class AnswerService extends GetxService {
|
||||||
|
@ -26,4 +27,22 @@ class AnswerService extends GetxService {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<BaseResponseModel<ParticipantResult>?> getAnswerSession(String sessionId, String userId) async {
|
||||||
|
try {
|
||||||
|
final response = await _dio.post(APIEndpoint.quizAnswerSession, data: {
|
||||||
|
"session_id": sessionId,
|
||||||
|
"user_id": userId,
|
||||||
|
});
|
||||||
|
final parsedResponse = BaseResponseModel<ParticipantResult>.fromJson(
|
||||||
|
response.data,
|
||||||
|
(data) => ParticipantResult.fromJson(data),
|
||||||
|
);
|
||||||
|
|
||||||
|
return parsedResponse;
|
||||||
|
} on DioException catch (e) {
|
||||||
|
logC.e('Gagal mengirim jawaban: ${e.response?.data['message'] ?? e.message}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/data/services/answer_service.dart';
|
||||||
|
import 'package:quiz_app/feature/admin_result_page/controller/detail_participant_result_controller.dart';
|
||||||
|
|
||||||
|
class DetailParticipantResultBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => AnswerService());
|
||||||
|
Get.lazyPut(() => ParticipantResultController(Get.find<AnswerService>()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/routes/app_pages.dart';
|
||||||
import 'package:quiz_app/data/models/history/session_history.dart';
|
import 'package:quiz_app/data/models/history/session_history.dart';
|
||||||
import 'package:quiz_app/data/services/history_service.dart';
|
import 'package:quiz_app/data/services/history_service.dart';
|
||||||
|
|
||||||
|
@ -10,6 +11,8 @@ class AdminResultController extends GetxController {
|
||||||
SessionHistory? sessionHistory;
|
SessionHistory? sessionHistory;
|
||||||
RxBool isLoading = false.obs;
|
RxBool isLoading = false.obs;
|
||||||
|
|
||||||
|
String sessionId = "";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
loadData();
|
loadData();
|
||||||
|
@ -19,7 +22,7 @@ class AdminResultController extends GetxController {
|
||||||
void loadData() async {
|
void loadData() async {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
|
||||||
final sessionId = Get.arguments as String;
|
sessionId = Get.arguments as String;
|
||||||
final result = await _historyService.getSessionHistory(sessionId);
|
final result = await _historyService.getSessionHistory(sessionId);
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
@ -29,4 +32,9 @@ class AdminResultController extends GetxController {
|
||||||
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void goToDetailParticipants(String userId, String username) => Get.toNamed(
|
||||||
|
AppRoutes.quizMPLResultPage,
|
||||||
|
arguments: {"user_id": userId, "session_id": sessionId, "username": username},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/data/models/history/participant_history_result.dart';
|
||||||
|
import 'package:quiz_app/data/services/answer_service.dart';
|
||||||
|
|
||||||
|
class ParticipantResultController extends GetxController {
|
||||||
|
final AnswerService _answerService;
|
||||||
|
|
||||||
|
ParticipantResultController(this._answerService);
|
||||||
|
|
||||||
|
final Rx<ParticipantResult?> participantResult = Rx<ParticipantResult?>(null);
|
||||||
|
final RxBool isLoading = false.obs;
|
||||||
|
|
||||||
|
RxString participantName = "".obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
loadData();
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadData() async {
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
final args = Get.arguments;
|
||||||
|
participantName.value = args["username"];
|
||||||
|
final response = await _answerService.getAnswerSession(args["session_id"], args["user_id"]);
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
participantResult.value = response.data;
|
||||||
|
}
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double calculateScorePercent() {
|
||||||
|
if (participantResult.value == null) return 0;
|
||||||
|
return participantResult.value!.scorePercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTotalCorrect() {
|
||||||
|
return participantResult.value?.totalCorrect ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTotalQuestions() {
|
||||||
|
return participantResult.value?.totalQuestions ?? 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -167,6 +167,8 @@ class AdminResultPage extends GetView<AdminResultController> {
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
side: BorderSide(color: AppColors.accentBlue.withOpacity(0.2)),
|
side: BorderSide(color: AppColors.accentBlue.withOpacity(0.2)),
|
||||||
),
|
),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => controller.goToDetailParticipants(participant.id, participant.name),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -227,6 +229,7 @@ class AdminResultPage extends GetView<AdminResultController> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,238 +1,302 @@
|
||||||
// // Halaman detail untuk peserta
|
import 'package:flutter/material.dart';
|
||||||
// import 'package:flutter/material.dart';
|
import 'package:get/get.dart';
|
||||||
// import 'package:lucide_icons/lucide_icons.dart';
|
import 'package:lucide_icons/lucide_icons.dart';
|
||||||
// import 'package:quiz_app/app/const/colors/app_colors.dart';
|
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||||
// import 'package:quiz_app/app/const/text/text_style.dart';
|
import 'package:quiz_app/data/models/history/participant_history_result.dart';
|
||||||
// import 'package:quiz_app/feature/admin_result_page/view/admin_result_page.dart';
|
import 'package:quiz_app/feature/admin_result_page/controller/detail_participant_result_controller.dart';
|
||||||
|
|
||||||
// class ParticipantDetailPage extends StatelessWidget {
|
class ParticipantDetailPage extends GetView<ParticipantResultController> {
|
||||||
// final ParticipantResult participant;
|
const ParticipantDetailPage({
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
// const ParticipantDetailPage({
|
@override
|
||||||
// Key? key,
|
Widget build(BuildContext context) {
|
||||||
// required this.participant,
|
return Scaffold(
|
||||||
// }) : super(key: key);
|
backgroundColor: AppColors.background,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Detail Peserta'),
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
foregroundColor: AppColors.darkText,
|
||||||
|
elevation: 0,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(LucideIcons.arrowLeft),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Obx(() {
|
||||||
|
if (controller.isLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
// @override
|
final participant = controller.participantResult.value;
|
||||||
// Widget build(BuildContext context) {
|
if (participant == null) {
|
||||||
// return Scaffold(
|
return const Center(child: Text('Data peserta tidak tersedia.'));
|
||||||
// backgroundColor: AppColors.background,
|
}
|
||||||
// appBar: AppBar(
|
|
||||||
// title: Text('Detail ${participant.name}'),
|
|
||||||
// backgroundColor: Colors.white,
|
|
||||||
// foregroundColor: AppColors.darkText,
|
|
||||||
// elevation: 0,
|
|
||||||
// leading: IconButton(
|
|
||||||
// icon: const Icon(LucideIcons.arrowLeft),
|
|
||||||
// onPressed: () => Navigator.pop(context),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// body: Column(
|
|
||||||
// children: [
|
|
||||||
// _buildParticipantHeader(),
|
|
||||||
// Expanded(
|
|
||||||
// child: ListView.builder(
|
|
||||||
// padding: const EdgeInsets.all(16),
|
|
||||||
// itemCount: participant.answers.length,
|
|
||||||
// itemBuilder: (context, index) {
|
|
||||||
// return _buildAnswerCard(participant.answers[index], index + 1);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Widget _buildParticipantHeader() {
|
return Column(
|
||||||
// return Container(
|
children: [
|
||||||
// width: double.infinity,
|
_buildParticipantHeader(participant),
|
||||||
// padding: const EdgeInsets.all(16),
|
Expanded(
|
||||||
// decoration: BoxDecoration(
|
child: ListView.builder(
|
||||||
// color: Colors.white,
|
padding: const EdgeInsets.all(16),
|
||||||
// border: Border(
|
itemCount: participant.answers.length,
|
||||||
// bottom: BorderSide(
|
itemBuilder: (context, index) {
|
||||||
// color: AppColors.borderLight,
|
return _buildAnswerCard(participant.answers[index], index + 1);
|
||||||
// width: 1,
|
},
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
// ),
|
],
|
||||||
// child: Row(
|
);
|
||||||
// children: [
|
}),
|
||||||
// CircleAvatar(
|
);
|
||||||
// radius: 26,
|
}
|
||||||
// backgroundColor: AppColors.accentBlue,
|
|
||||||
// child: Text(
|
|
||||||
// participant.name.isNotEmpty ? participant.name[0].toUpperCase() : "?",
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 22,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: AppColors.primaryBlue,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 16),
|
|
||||||
// Expanded(
|
|
||||||
// child: Column(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
// children: [
|
|
||||||
// Text(
|
|
||||||
// participant.name,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 16,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: AppColors.darkText,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 4),
|
|
||||||
// Text(
|
|
||||||
// "Jumlah Soal: ${participant.totalQuestions}",
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 14,
|
|
||||||
// color: AppColors.softGrayText,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Container(
|
|
||||||
// padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: _getScoreColor(participant.scorePercent).withValues(alpha:0.1),
|
|
||||||
// borderRadius: BorderRadius.circular(16),
|
|
||||||
// border: Border.all(
|
|
||||||
// color: _getScoreColor(participant.scorePercent),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// child: Row(
|
|
||||||
// children: [
|
|
||||||
// Icon(
|
|
||||||
// LucideIcons.percent,
|
|
||||||
// size: 16,
|
|
||||||
// color: _getScoreColor(participant.scorePercent),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 6),
|
|
||||||
// Text(
|
|
||||||
// "${participant.scorePercent.toInt()}%",
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 16,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: _getScoreColor(participant.scorePercent),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Widget _buildAnswerCard(QuestionAnswer answer, int number) {
|
Widget _buildParticipantHeader(ParticipantResult participant) {
|
||||||
// return Container(
|
return Container(
|
||||||
// width: double.infinity,
|
width: double.infinity,
|
||||||
// margin: const EdgeInsets.only(bottom: 20),
|
padding: const EdgeInsets.all(16),
|
||||||
// padding: const EdgeInsets.all(16),
|
decoration: BoxDecoration(
|
||||||
// // decoration: _containerDecoration,
|
color: Colors.white,
|
||||||
// child: Column(
|
border: Border(
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
bottom: BorderSide(
|
||||||
// children: [
|
color: AppColors.borderLight,
|
||||||
// Row(
|
width: 1,
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
// children: [
|
),
|
||||||
// Container(
|
),
|
||||||
// width: 28,
|
child: Row(
|
||||||
// height: 28,
|
children: [
|
||||||
// decoration: BoxDecoration(
|
CircleAvatar(
|
||||||
// shape: BoxShape.circle,
|
radius: 26,
|
||||||
// color: answer.isCorrect ? AppColors.primaryBlue.withValues(alpha:0.1) : AppColors.scorePoor.withValues(alpha:0.1),
|
backgroundColor: AppColors.accentBlue,
|
||||||
// ),
|
child: Text(
|
||||||
// child: Center(
|
controller.participantName.value[0].toUpperCase(),
|
||||||
// child: Text(
|
style: const TextStyle(
|
||||||
// number.toString(),
|
fontSize: 22,
|
||||||
// style: TextStyle(
|
fontWeight: FontWeight.bold,
|
||||||
// fontSize: 13,
|
color: AppColors.primaryBlue,
|
||||||
// fontWeight: FontWeight.bold,
|
),
|
||||||
// color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
|
),
|
||||||
// ),
|
),
|
||||||
// ),
|
const SizedBox(width: 16),
|
||||||
// ),
|
Expanded(
|
||||||
// ),
|
child: Column(
|
||||||
// const SizedBox(width: 12),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// Expanded(
|
children: [
|
||||||
// child: Text(
|
Text(
|
||||||
// 'Soal $number: ${answer.question}',
|
controller.participantName.value,
|
||||||
// style: const TextStyle(
|
style: const TextStyle(
|
||||||
// fontSize: 16,
|
fontSize: 16,
|
||||||
// fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
// color: AppColors.darkText,
|
color: AppColors.darkText,
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
// ),
|
const SizedBox(height: 4),
|
||||||
// Icon(
|
Text(
|
||||||
// answer.isCorrect ? LucideIcons.checkCircle : LucideIcons.xCircle,
|
"Jumlah Soal: ${participant.totalQuestions}",
|
||||||
// color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
|
style: const TextStyle(
|
||||||
// size: 20,
|
fontSize: 14,
|
||||||
// ),
|
color: AppColors.softGrayText,
|
||||||
// ],
|
),
|
||||||
// ),
|
),
|
||||||
// const SizedBox(height: 12),
|
],
|
||||||
// Divider(color: AppColors.borderLight),
|
),
|
||||||
// const SizedBox(height: 12),
|
),
|
||||||
// _buildAnswerRow(
|
Container(
|
||||||
// label: "Jawaban Siswa:",
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
// answer: answer.userAnswer,
|
decoration: BoxDecoration(
|
||||||
// isCorrect: answer.isCorrect,
|
color: _getScoreColor(participant.scorePercent).withOpacity(0.1),
|
||||||
// ),
|
borderRadius: BorderRadius.circular(16),
|
||||||
// if (!answer.isCorrect) ...[
|
border: Border.all(
|
||||||
// const SizedBox(height: 10),
|
color: _getScoreColor(participant.scorePercent),
|
||||||
// _buildAnswerRow(
|
),
|
||||||
// label: "Jawaban Benar:",
|
),
|
||||||
// answer: answer.correctAnswer,
|
child: Row(
|
||||||
// isCorrect: true,
|
children: [
|
||||||
// ),
|
Icon(
|
||||||
// ],
|
LucideIcons.percent,
|
||||||
// ],
|
size: 16,
|
||||||
// ),
|
color: _getScoreColor(participant.scorePercent),
|
||||||
// );
|
),
|
||||||
// }
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
"${participant.scorePercent.toInt()}%",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: _getScoreColor(participant.scorePercent),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Widget _buildAnswerRow({
|
Widget _buildAnswerCard(QuestionAnswer answer, int number) {
|
||||||
// required String label,
|
return Container(
|
||||||
// required String answer,
|
width: double.infinity,
|
||||||
// required bool isCorrect,
|
margin: const EdgeInsets.only(bottom: 20),
|
||||||
// }) {
|
padding: const EdgeInsets.all(16),
|
||||||
// return Row(
|
decoration: BoxDecoration(
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
color: Colors.white,
|
||||||
// children: [
|
borderRadius: BorderRadius.circular(12),
|
||||||
// SizedBox(
|
boxShadow: [
|
||||||
// width: 110,
|
BoxShadow(
|
||||||
// child: Text(
|
color: Colors.grey.withOpacity(0.1),
|
||||||
// label,
|
blurRadius: 6,
|
||||||
// style: TextStyle(
|
offset: const Offset(0, 3),
|
||||||
// fontSize: 14,
|
),
|
||||||
// fontWeight: FontWeight.w500,
|
],
|
||||||
// color: AppColors.softGrayText,
|
),
|
||||||
// ),
|
child: Column(
|
||||||
// ),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// ),
|
children: [
|
||||||
// Expanded(
|
Row(
|
||||||
// child: Text(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// answer,
|
children: [
|
||||||
// style: TextStyle(
|
Container(
|
||||||
// fontSize: 15,
|
width: 28,
|
||||||
// fontWeight: FontWeight.w600,
|
height: 28,
|
||||||
// color: isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
|
decoration: BoxDecoration(
|
||||||
// ),
|
shape: BoxShape.circle,
|
||||||
// ),
|
color: answer.isCorrect ? AppColors.primaryBlue.withOpacity(0.1) : AppColors.scorePoor.withOpacity(0.1),
|
||||||
// ),
|
),
|
||||||
// ],
|
child: Center(
|
||||||
// );
|
child: Text(
|
||||||
// }
|
number.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'Soal $number: ${answer.question}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColors.darkText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
answer.isCorrect ? LucideIcons.checkCircle : LucideIcons.xCircle,
|
||||||
|
color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Divider(color: AppColors.borderLight),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildAnswerRow(
|
||||||
|
label: "Tipe Soal:",
|
||||||
|
answer: answer.type,
|
||||||
|
isCorrect: true,
|
||||||
|
),
|
||||||
|
_buildAnswerRow(
|
||||||
|
label: "Waktu Diberikan:",
|
||||||
|
answer: "${answer.duration} detik",
|
||||||
|
isCorrect: true,
|
||||||
|
),
|
||||||
|
_buildAnswerRow(
|
||||||
|
label: "Waktu Dihabiskan:",
|
||||||
|
answer: "${answer.timeSpent} detik",
|
||||||
|
isCorrect: true,
|
||||||
|
),
|
||||||
|
_buildAnswerRow(
|
||||||
|
label: "Jawaban Siswa:",
|
||||||
|
answer: answer.answer,
|
||||||
|
isCorrect: answer.isCorrect,
|
||||||
|
),
|
||||||
|
if (!answer.isCorrect) ...[
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildAnswerRow(
|
||||||
|
label: "Jawaban Benar:",
|
||||||
|
answer: answer.targetAnswer.toString(),
|
||||||
|
isCorrect: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (answer.options != null) ...[
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildOptions(answer.options!),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Color _getScoreColor(double score) {
|
Widget _buildOptions(List<String> options) {
|
||||||
// if (score >= 70) return AppColors.scoreGood;
|
return Column(
|
||||||
// if (score >= 60) return AppColors.scoreAverage;
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// return AppColors.scorePoor;
|
children: [
|
||||||
// }
|
const Text(
|
||||||
// }
|
"Pilihan Jawaban:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: AppColors.softGrayText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
...options.map((opt) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||||
|
child: Text(
|
||||||
|
"- $opt",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: AppColors.darkText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAnswerRow({
|
||||||
|
required String label,
|
||||||
|
required String answer,
|
||||||
|
required bool isCorrect,
|
||||||
|
}) {
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 110,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: AppColors.softGrayText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
answer,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getScoreColor(double score) {
|
||||||
|
if (score >= 70) return AppColors.scoreGood;
|
||||||
|
if (score >= 60) return AppColors.scoreAverage;
|
||||||
|
return AppColors.scorePoor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue