11import {
22 Address ,
33 Question ,
4- Quiz as QuizViewer ,
4+ Quiz as QuizContent ,
55} from "@courselit/common-models" ;
6- import {
7- actionCreators ,
8- AppDispatch ,
9- AppState ,
10- } from "@courselit/state-management" ;
116import { FetchBuilder } from "@courselit/utils" ;
127import { ChangeEvent , useState } from "react" ;
13- import { connect } from "react-redux" ;
148import {
159 TOAST_TITLE_ERROR ,
16- QUIZ_FAIL_MESSAGE ,
17- QUIZ_PASS_MESSAGE ,
1810 QUIZ_VIEWER_EVALUATE_BTN ,
1911 QUIZ_VIEWER_EVALUATE_BTN_LOADING ,
20- TOAST_TITLE_SUCCESS ,
21- } from "../../../ui-config/strings" ;
12+ TOAST_QUIZ_FAIL_MESSAGE ,
13+ TOAST_QUIZ_PASS_MESSAGE ,
14+ QUIZ_SCORE_PREFIX_MESSAGE ,
15+ } from "@/ui-config/strings" ;
2216import { Form , FormSubmit , useToast } from "@courselit/components-library" ;
2317
24- const { networkAction } = actionCreators ;
25-
2618interface QuizViewerProps {
2719 lessonId : string ;
28- content : QuizViewer ;
29- dispatch : AppDispatch ;
20+ content : QuizContent ;
3021 address : Address ;
3122}
3223
33- function QuizViewer ( { content, lessonId, dispatch, address } : QuizViewerProps ) {
24+ export default function QuizViewer ( {
25+ content,
26+ lessonId,
27+ address,
28+ } : QuizViewerProps ) {
3429 const { questions } = content ;
3530 const [ answers , setAnswers ] = useState < number [ ] [ ] > ( [
3631 ...content . questions . map ( ( item ) => [ ] ) ,
@@ -43,28 +38,27 @@ function QuizViewer({ content, lessonId, dispatch, address }: QuizViewerProps) {
4338 questionIndex : number ,
4439 optionIndex : number ,
4540 ) => {
46- const addOptionToQuestion = (
47- questionIndex : number ,
48- optionIndex : number ,
49- ) => {
50- answers [ questionIndex ] . push ( optionIndex ) ;
51- setAnswers ( [ ...answers ] ) ;
52- } ;
53-
54- const removeOptionFromQuestion = (
55- questionIndex : number ,
56- optionIndex : number ,
57- ) => {
58- const index = answers [ questionIndex ] . indexOf ( optionIndex ) ;
59- answers [ questionIndex ] . splice ( index , 1 ) ;
60- setAnswers ( [ ...answers ] ) ;
61- } ;
41+ const question = questions [ questionIndex ] ;
42+ const newAnswers = [ ...answers ] ;
6243
63- if ( checked ) {
64- addOptionToQuestion ( questionIndex , optionIndex ) ;
44+ if ( question . type === "single" ) {
45+ // For single choice, replace the entire array with the selected option
46+ newAnswers [ questionIndex ] = checked ? [ optionIndex ] : [ ] ;
6547 } else {
66- removeOptionFromQuestion ( questionIndex , optionIndex ) ;
48+ // For multiple choice, add/remove from the array
49+ if ( checked ) {
50+ if ( ! newAnswers [ questionIndex ] . includes ( optionIndex ) ) {
51+ newAnswers [ questionIndex ] . push ( optionIndex ) ;
52+ }
53+ } else {
54+ const index = newAnswers [ questionIndex ] . indexOf ( optionIndex ) ;
55+ if ( index > - 1 ) {
56+ newAnswers [ questionIndex ] . splice ( index , 1 ) ;
57+ }
58+ }
6759 }
60+
61+ setAnswers ( newAnswers ) ;
6862 } ;
6963
7064 const evaluate = async ( e : any ) => {
@@ -92,21 +86,20 @@ function QuizViewer({ content, lessonId, dispatch, address }: QuizViewerProps) {
9286 . build ( ) ;
9387
9488 try {
95- dispatch ( networkAction ( true ) ) ;
9689 setLoading ( true ) ;
9790 const response = await fetch . exec ( ) ;
9891
9992 if ( response . result ) {
10093 const { pass, score, passingGrade } = response . result ;
10194 if ( pass ) {
10295 toast ( {
103- title : TOAST_TITLE_SUCCESS ,
104- description : `${ QUIZ_PASS_MESSAGE } ${ score } points.` ,
96+ title : TOAST_QUIZ_PASS_MESSAGE ,
97+ description : `${ QUIZ_SCORE_PREFIX_MESSAGE } ${ score . toFixed ( 2 ) } points.` ,
10598 } ) ;
10699 } else {
107100 toast ( {
108- title : TOAST_TITLE_ERROR ,
109- description : `${ QUIZ_FAIL_MESSAGE } ${ score } points. Requires ${ passingGrade } points.` ,
101+ title : TOAST_QUIZ_FAIL_MESSAGE ,
102+ description : `${ QUIZ_SCORE_PREFIX_MESSAGE } ${ score . toFixed ( 2 ) } points. Requires ${ passingGrade } points.` ,
110103 variant : "destructive" ,
111104 } ) ;
112105 }
@@ -118,27 +111,32 @@ function QuizViewer({ content, lessonId, dispatch, address }: QuizViewerProps) {
118111 variant : "destructive" ,
119112 } ) ;
120113 } finally {
121- dispatch ( networkAction ( false ) ) ;
122114 setLoading ( false ) ;
123115 }
124116 } ;
125117
126118 return (
127119 < Form onSubmit = { evaluate } >
128120 { questions . map ( ( question : Question , questionIndex : number ) => (
129- < fieldset
130- className = "flex flex-col py-2 px-4 mb-4 border rounded border-slate-200"
131- key = { questionIndex }
132- >
133- < h2 className = "font-medium text-xl mb-2" >
134- { question . text }
121+ < fieldset className = "flex flex-col mb-8" key = { questionIndex } >
122+ < h2 className = "font-medium mb-2" >
123+ { questionIndex + 1 } . { question . text }
135124 </ h2 >
136125 { question . options . map ( ( option , index : number ) => (
137126 < div className = "flex items-center mb-2" key = { index } >
138127 < input
139- type = "checkbox"
128+ type = {
129+ question . type === "single"
130+ ? "radio"
131+ : "checkbox"
132+ }
140133 className = "mr-2"
141- checked = { option . correctAnswer }
134+ name = {
135+ question . type === "single"
136+ ? `question-${ questionIndex } `
137+ : undefined
138+ }
139+ checked = { answers [ questionIndex ] . includes ( index ) }
142140 onChange = { ( e : ChangeEvent < HTMLInputElement > ) =>
143141 setAnswerForQuestion (
144142 e . target . checked ,
@@ -165,13 +163,3 @@ function QuizViewer({ content, lessonId, dispatch, address }: QuizViewerProps) {
165163 </ Form >
166164 ) ;
167165}
168-
169- const mapStateToProps = ( state : AppState ) => ( {
170- address : state . address ,
171- } ) ;
172-
173- const mapDispatchToProps = ( dispatch : AppDispatch ) => ( {
174- dispatch : dispatch ,
175- } ) ;
176-
177- export default connect ( mapStateToProps , mapDispatchToProps ) ( QuizViewer ) ;
0 commit comments