Skip to content

Commit

Permalink
Merge pull request #25 from weining-li/issue/19-prevent-incomplete-su…
Browse files Browse the repository at this point in the history
…bmission

issue/19-option to prevent submitting with incomplete questions
  • Loading branch information
clpetersonucf authored May 27, 2021
2 parents 311ec7f + ebf5f03 commit 941dc53
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 28 deletions.
11 changes: 10 additions & 1 deletion src/components/creator/creator-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import CreatorTutorial from './creator-tutorial';
import CreatorHintsModal from './creator-hints-modal';
import CreatorFakeoutModal from './creator-fakeout-modal'
import CreatorBankModal from './creator-bank-modal'
import CreatorSubmissionSettingsModal from './creator-submission-settings-modal'
import CreatorErrorModal from './creator-error-modal'

const CreatorApp = (props) => {
Expand Down Expand Up @@ -127,6 +128,7 @@ const CreatorApp = (props) => {
options: {
legend: global.state.legend,
enableQuestionBank: global.state.enableQuestionBank,
requireAllQuestions: global.state.requireAllQuestions,
numAsk: global.state.numAsk
}
}
Expand Down Expand Up @@ -163,6 +165,10 @@ const CreatorApp = (props) => {
dispatch({type: 'toggle_bank_modal', payload: {}})
}

const toggleSubmissionSettings = () => {
dispatch({type: 'toggle_submission_settings_modal', payload: {}})
}

const toggleHintModal = () => {
dispatch({type: 'toggle_hint_modal'})
}
Expand All @@ -184,14 +190,17 @@ const CreatorApp = (props) => {
enableQuestionBank={global.state.enableQuestionBank}
numAsk={global.state.numAsk}
questionCount={global.state.items.length}></CreatorBankModal>
<CreatorSubmissionSettingsModal
requireAllQuestions={global.state.requireAllQuestions}></CreatorSubmissionSettingsModal>
<CreatorFakeoutModal
fakes={global.state.items[global.state.currentIndex].fakes}></CreatorFakeoutModal>

<CreatorErrorModal></CreatorErrorModal>
<header className="creator-header">
<input value={global.state.title} onChange={handleTitleUpdate} placeholder="Give Your Widget a Title"/>
<button className="toggleLegend" onClick={toggleLegend}>Legend</button>
<button className="toggleBank" onClick={toggleBank}>Question Bank</button>
<button className="toggleSubmissionSettings" onClick={toggleSubmissionSettings}>Submission Settings</button>
</header>
<QuestionSelect questions={global.state.items}></QuestionSelect>
<section className="content-container">
Expand Down
45 changes: 45 additions & 0 deletions src/components/creator/creator-submission-settings-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, {useContext} from 'react'
import { store } from '../../creator-store'

const CreatorSubmissionSettingsModal = (props) => {

const global = useContext(store)
const dispatch = global.dispatch

const handleToggleSubmissionSettings = (event) => {
dispatch({type:'toggle_require_all_questions', payload: event.target.value == 'true' ? true : false})
}

const dismiss = () => {
dispatch({type: 'toggle_submission_settings_modal'})
}

return (
<div className='modal-wrapper' style={{display: global.state.showSubmissionSettingsModal ? 'block' : 'none'}}>
<div className='modal creator'>
<h3>Submission Settings</h3>
<span className="select-wrapper">
<span className="strong">Require all questions?</span>
<span className="pref-select">
<input type="radio" name="question-bank-toggle" value={true} onChange={handleToggleSubmissionSettings} checked={props.requireAllQuestions == true}/>
<span className={`radio-overlay ${props.requireAllQuestions == true ? 'selected' : ''}`}></span>
Yes
</span>
<span className="pref-select">
<input type="radio" name="question-bank-toggle" value={false} onChange={handleToggleSubmissionSettings} checked={props.requireAllQuestions == false}/>
<span className={`radio-overlay left ${props.requireAllQuestions == false ? 'selected' : ''}`}></span>
No
</span>
</span>
<span className="select-wrapper">
If enabled, students must respond to all questions before submission by sorting at least one token per question. Empty responses are not allowed.
</span>

<button onClick={dismiss}>Okay</button>
</div>
<div className='modal-bg'></div>
</div>
)
}

export default CreatorSubmissionSettingsModal
11 changes: 8 additions & 3 deletions src/components/player/player-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ const PlayerApp = (props) => {
const emptyQuestionCheck = () => {

let isEmpty = false
let i = 0;

for (let item of global.state.items) {
if (item.sorted.length <= 0) {
if (item.sorted.length <= 0) {
dispatch({type: 'select_question', payload: i})
isEmpty = true
break
}
i++
}
return isEmpty
}
Expand Down Expand Up @@ -81,7 +84,9 @@ const PlayerApp = (props) => {

return(
<div className="player-container">
<WarningModal submitForScoring={submitForScoring}></WarningModal>
<WarningModal
submitForScoring={submitForScoring}
requireAllQuestions={props.qset.options.requireAllQuestions}></WarningModal>
<PlayerTutorial></PlayerTutorial>
<header className="player-header">
<span className="title">{global.state.title}</span>
Expand All @@ -94,7 +99,7 @@ const PlayerApp = (props) => {
<p>{questionText}</p>
<div className={'hint-text ' +
`${(
global.state.items[global.state.currentIndex]?.attemptsUsed > 0 &&
global.state.items[global.state.currentIndex]?.attemptsUsed > 0 &&
global.state.items[global.state.currentIndex]?.attemptsUsed < global.state.items[global.state.currentIndex]?.attempts &&
global.state.items[global.state.currentIndex]?.responseState != 'correct' &&
global.state.items[global.state.currentIndex]?.responseState != 'incorrect-no-attempts' &&
Expand Down
10 changes: 5 additions & 5 deletions src/components/player/question-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const QuestionSelect = (props) => {

const global = useContext(store)
const dispatch = global.dispatch

const [state, setState] = useState({paginateMin: 0, paginateMax: 8, visibleQuestions: []})

const currentIndex = global.state.currentIndex
Expand All @@ -14,7 +14,7 @@ const QuestionSelect = (props) => {
let questionList = global.state.items.map((item, index) => {
return <button className={`select-btn ${currentIndex == index ? 'selected' : ''}`} key={index} onClick={() => {selectQuestion(index)}}>{index + 1}</button>
})

// if the list of questions gets too long, we have to start computing the subset to display
if (questionList.length > 10) {
if (currentIndex < state.paginateMin) {
Expand All @@ -23,7 +23,7 @@ const QuestionSelect = (props) => {
else if (currentIndex > state.paginateMax) {
setState(state => ({...state, paginateMin: currentIndex - 8, paginateMax: currentIndex}))
}

setState(state => ({...state, visibleQuestions: [
...questionList.slice(state.paginateMin, currentIndex),
...questionList.slice(currentIndex, state.paginateMax + 1)
Expand All @@ -37,7 +37,7 @@ const QuestionSelect = (props) => {
const selectQuestion = (index) => {
dispatch({type: 'select_question', payload: index})
}

return (
<div className="question-select">
<button className={`select-btn paginate-up ${global.state.items.length > 10 ? 'show' : ''} ${currentIndex > 0 ? '': 'disabled'}`} onClick={() => {selectQuestion(currentIndex - 1)}} disabled={currentIndex <= 0}><span className="icon-arrow-up2"></span></button>
Expand All @@ -47,4 +47,4 @@ const QuestionSelect = (props) => {
)
}

export default QuestionSelect
export default QuestionSelect
22 changes: 16 additions & 6 deletions src/components/player/warning-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {useContext} from 'react'
import { store } from '../../player-store'

const WarningModal = (props) => {

const global = useContext(store)
const dispatch = global.dispatch

Expand All @@ -14,11 +13,22 @@ const WarningModal = (props) => {
<div className='warning-wrapper' style={{display: global.state.showWarning ? 'block' : 'none'}}>
<div className='warning'>
<span className='dev-warning'>You still have unfinished questions.</span>
<h3>Are you sure you want to submit?</h3>
<div className='warning-submit-holder'>
<button onClick={toggle}>No</button>
<button onClick={props.submitForScoring}>Yes</button>
</div>
{props.requireAllQuestions ?
<div>
<h3>Please answer all questions before submitting.</h3>
<div className='warning-submit-holder'>
<button onClick={toggle}>Ok</button>
</div>
</div>
:
<div>
<h3>Are you sure you want to submit?</h3>
<div className='warning-submit-holder'>
<button onClick={toggle}>No</button>
<button onClick={props.submitForScoring}>Yes</button>
</div>
</div>
}
</div>
<div className='warning-bg'>
</div>
Expand Down
15 changes: 11 additions & 4 deletions src/creator-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const init = {
showHintModal: false,
showFakeoutModal: false,
showBankModal: false,
showSubmissionSettingsModal: false,
showErrorModal: false,
errors: [],
selectedTokenIndex: -1,
Expand All @@ -33,6 +34,7 @@ const init = {
],
numAsk: 1,
enableQuestionBank: false,
requireAllQuestions: false,
showLegend: false,
legendColorPickerTarget: -1,
onboarding: true,
Expand Down Expand Up @@ -63,7 +65,8 @@ const importFromQset = (qset) => {
items: items,
legend: qset.options.legend,
numAsk: qset.options.numAsk,
enableQuestionBank: qset.options.enableQuestionBank
enableQuestionBank: qset.options.enableQuestionBank,
requireAllQuestions: qset.options.requireAllQuestions ? qset.options.requireAllQuestions : false // this value will not exist for older qsets
}
}

Expand Down Expand Up @@ -134,7 +137,7 @@ const questionItemReducer = (items, action) => {
...item,
attempts: parseInt(action.payload.pref)
}

}
else return item
})
Expand Down Expand Up @@ -288,7 +291,7 @@ const StateProvider = ( { children } ) => {
return {...state, requireInit: false}
case 'init-existing':
let imported = importFromQset(action.payload.qset)
return {...state, title: action.payload.title, items: imported.items, legend: imported.legend, numAsk: imported.numAsk, enableQuestionBank: imported.enableQuestionBank, requireInit: false, onboarding: false, showTokenTutorial: false}
return {...state, title: action.payload.title, items: imported.items, legend: imported.legend, numAsk: imported.numAsk, enableQuestionBank: imported.enableQuestionBank, requireAllQuestions: imported.requireAllQuestions, requireInit: false, onboarding: false, showTokenTutorial: false}
case 'dismiss_tutorial':
return {...state, showTutorial: false}
case 'toggle_token_tutorial':
Expand All @@ -309,7 +312,7 @@ const StateProvider = ( { children } ) => {
case 'phrase_input_to_token':
return {...state, items: questionItemReducer(state.items, action), showTokenTutorial: false}
case 'phrase_token_to_input':
return {...state, items: questionItemReducer(state.items, action), selectedTokenIndex: action.payload.phraseIndex == state.selectedTokenIndex ? -1 : state.selectedTokenIndex }
return {...state, items: questionItemReducer(state.items, action), selectedTokenIndex: action.payload.phraseIndex == state.selectedTokenIndex ? -1 : state.selectedTokenIndex }
case 'fakeout_token_to_input':
return {...state, items: questionItemReducer(state.items, action), selectedFakeoutIndex: action.payload.fakeoutIndex == state.selectedFakeoutIndex ? -1 : state.selectedFakeoutIndex }
case 'phrase_token_type_select':
Expand Down Expand Up @@ -341,12 +344,16 @@ const StateProvider = ( { children } ) => {
return {...state, showFakeoutModal: !state.showFakeoutModal}
case 'toggle_bank_modal':
return {...state, showBankModal: !state.showBankModal}
case 'toggle_submission_settings_modal':
return {...state, showSubmissionSettingsModal: !state.showSubmissionSettingsModal}
case 'toggle_error_modal':
return {...state, errors: action.payload.error, showErrorModal: !state.showErrorModal}
case 'update_num_ask':
return {...state, numAsk: action.payload}
case 'toggle_ask_limit':
return {...state, enableQuestionBank: action.payload}
case 'toggle_require_all_questions':
return {...state, requireAllQuestions: action.payload}
default:
throw new Error('Base reducer: this action type was not defined')
}
Expand Down
5 changes: 3 additions & 2 deletions src/creator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ div.startupTooltip {
}

.toggleLegend,
.toggleBank {
.toggleBank,
.toggleSubmissionSettings {
float: right;
padding: 8px 28px;
outline: none;
Expand All @@ -85,7 +86,7 @@ div.startupTooltip {
border: solid 1px $colorAccentHover;
font-weight: bold;

&.toggleBank {
&.toggleBank, &.toggleSubmissionSettings {
margin-right: 10px;
}

Expand Down
1 change: 1 addition & 0 deletions src/demo.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@
}
],
"enableQuestionBank": false,
"requireAllQuestions": true,
"numAsk": 1
}
}
Expand Down
16 changes: 9 additions & 7 deletions src/player-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const init = {
showTutorial: true,
showWarning: false,
numAsk: 1,
enableQuestionBank: "no",
enableQuestionBank: false,
requireAllQuestions: false,
questionsAsked: []
}

Expand Down Expand Up @@ -44,7 +45,7 @@ const importFromQset = (qset) => {

let fakes = item.options.fakes.map((token) => { return {...token, status: 'unsorted', fakeout: true, id: createTokenKey()}})
let reals = item.answers[0].options.phrase.map((token) => { return {...token, status: 'unsorted', fakeout: false, id: createTokenKey()} })

return {
question: item.questions[0].text,
answer: item.answers[0].text,
Expand All @@ -63,6 +64,7 @@ const importFromQset = (qset) => {
legend: qset.options.legend,
numAsk: qset.options.numAsk,
enableQuestionBank: qset.options.enableQuestionBank,
requireAllQuestions: qset.options.requireAllQuestions ? qset.options.requireAllQuestions : false,
questionsAsked: []
}
}
Expand All @@ -84,9 +86,9 @@ const calcResponseState = (item) => {
state = 'pending'
}
else state = 'ready'
}
}
break

case 'pending':
if (item.fakeout.length == 0 && item.phrase.length == 0) {
state = 'ready'
Expand Down Expand Up @@ -142,7 +144,7 @@ const tokenSortedPhraseReducer = (list, action) => {
...list.slice(0, action.payload.tokenIndex),
...list.slice(action.payload.tokenIndex + 1)
]

return sorted.map((token) => ({
...token,
reqPositionUpdate: true
Expand Down Expand Up @@ -179,13 +181,13 @@ const tokenSortedPhraseReducer = (list, action) => {
},
...list.slice(action.payload.targetIndex)
]

return sorted.map((token) => ({
...token,
reqPositionUpdate: true
})
)
}
}
case 'response_token_rearrange':
let target = action.payload.targetIndex
if (action.payload.originIndex < target) target--
Expand Down

0 comments on commit 941dc53

Please sign in to comment.