Skip to content

Commit

Permalink
Merge pull request #30 from NUMde/update-open-choice
Browse files Browse the repository at this point in the history
Updating Choice- and Ope-Choice Handling
  • Loading branch information
mbastian93 committed Jul 22, 2021
2 parents 1d7f469 + 91e10c2 commit 1fb3f56
Show file tree
Hide file tree
Showing 8 changed files with 735 additions and 688 deletions.
1,076 changes: 484 additions & 592 deletions src/assets/files/questionnaire.js

Large diffs are not rendered by default.

237 changes: 173 additions & 64 deletions src/components/modal/questionnaireModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,16 +322,28 @@ class QuestionnaireModal extends Component {
return returnValue
}

/**
* compares two different valueCoding Objects
*/
compareCoding = (val1, val2) => {

if (val1 && val2) return val1.system === val2.system && val1.code === val2.code

return false
}

// creating questionnaire items
/*-----------------------------------------------------------------------------------*/

/**
* renders a choice-type ui-element (if its dependencies check out)
* supports the drop-down extension (without repeat)
* @param {QuestionnaireItem} item questionnaire item
*/
createChoices = item => {
// checks the dependencies of the item and renders it (if the dependencies check out)
return this.getRenderStatusOfItem(item) ? (

<View>
{/* title */}
<Text
Expand All @@ -341,72 +353,168 @@ class QuestionnaireModal extends Component {
{item.text}
</Text>

{ item.extension &&
item.extension[0].valueCodeableConcept &&
item.extension[0].valueCodeableConcept.coding &&
item.extension[0].valueCodeableConcept.coding[0].code === "drop-down" ?
<View>
<Picker
selectedValue={this.props.questionnaireItemMap[item.linkId].answer}
onValueChange={value => {this.props.actions.setAnswer({linkId: item.linkId, answer: value})}}
>
{item.answerOption.map((answerOption, index) => {
return <Picker.Item
label={answerOption.valueString}
value={answerOption.valueString}
key={index}
/>
})}
</Picker>
</View> :
<View>
{item.answerOption.map((answerOption, index) => {
return (
<CheckBox
title={this.getItemTitle(answerOption)}
checkedIcon='dot-circle-o'
uncheckedIcon='circle-o'
onPress={() =>
this.props.actions.setAnswer({
linkId: item.linkId,
answer: answerOption.valueCoding || answerOption.valueString || answerOption.valueInteger,
})
}
onIconPress={() =>
this.props.actions.setAnswer({
linkId: item.linkId,
answer: answerOption.valueCoding || answerOption.valueString || answerOption.valueInteger,
})
}
checkedColor={config.theme.colors.primary}
uncheckedColor={config.theme.colors.accent1}
checked={
this.compareCoding(exportService.getCorrectlyFormattedAnswer(this.props.questionnaireItemMap[item.linkId]), answerOption.valueCoding) ||
exportService.getCorrectlyFormattedAnswer(this.props.questionnaireItemMap[item.linkId]) === answerOption.valueString ||
exportService.getCorrectlyFormattedAnswer(this.props.questionnaireItemMap[item.linkId]) === answerOption.valueInteger
}
key={`${item.linkId}.a_${index}`}
containerStyle={{...localStyle.choice, marginLeft: this.calculateIndent(item.linkId)}}
textStyle={localStyle.choiceText}
/>
)
})}
</View>}
{/* renders all answer options */}


{/* checks if the drop-down extension is available. */}
{/* if yes, it will render it. */}
{/* if not, the default way is chosen. */}
{
item.extension &&
item.extension[0].valueCodeableConcept &&
item.extension[0].valueCodeableConcept.coding &&
item.extension[0].valueCodeableConcept.coding[0].code === "drop-down" ?
(
<View>
{/* renders the drop-down */}
<Picker
selectedValue={this.props.questionnaireItemMap[item.linkId].answer}
onValueChange={value => {this.props.actions.setAnswer({linkId: item.linkId, answer: value})}}
>
{item.answerOption.map((answerOption, index) => {
return <Picker.Item
label={answerOption.valueString}
value={answerOption.valueString}
key={index}
/>
})}
</Picker>
</View>
)
:
(
<View>
{/* repeat: false */}
{!item.repeats && (
<View>
{/* renders a list of answers */}
{item.answerOption.map((answerOption, index) => {
return !answerOption.isOpenQuestionAnswer && (
<CheckBox
uncheckedIcon='circle-o'
checkedIcon='dot-circle-o'
key={`${item.linkId}.a_${index}`}
textStyle={localStyle.choiceText}
title={this.getItemTitle(answerOption)}
checkedColor={config.theme.colors.primary}
uncheckedColor={config.theme.colors.accent1}
containerStyle={{...localStyle.choice, marginLeft: this.calculateIndent(item.linkId)}}
onPress={() =>
{
this.props.actions.setAnswer({
linkId: item.linkId,
answer: answerOption.valueCoding || answerOption.valueString || answerOption.valueInteger,
})

this.removeOpenAnswer(item)
}
}
onIconPress={() =>
{
this.props.actions.setAnswer({
linkId: item.linkId,
answer: answerOption.valueCoding || answerOption.valueString || answerOption.valueInteger,
})

this.removeOpenAnswer(item)
}
}
checked={
this.compareCoding(exportService.getCorrectlyFormattedAnswer(this.props.questionnaireItemMap[item.linkId]), answerOption.valueCoding) ||
exportService.getCorrectlyFormattedAnswer(this.props.questionnaireItemMap[item.linkId]) === answerOption.valueString ||
exportService.getCorrectlyFormattedAnswer(this.props.questionnaireItemMap[item.linkId]) === answerOption.valueInteger
}
/>
)
})}
</View>
)}

{/* repeat: true */}
{item.repeats && (
<View>
{item.answerOption.map((answerOption, index) => {
return !answerOption.isOpenQuestionAnswer && (
<CheckBox
title={this.getItemTitle(answerOption)}
checkedColor={config.theme.colors.primary}
uncheckedColor={config.theme.colors.accent1}
onPress={() =>
{
this.props.actions.setAnswer({
linkId: item.linkId,
answer: answerOption.valueCoding || answerOption.valueString || answerOption.valueInteger,
openAnswer: true,
})
}
}
onIconPress={() =>
{
this.props.actions.setAnswer({
linkId: item.linkId,
answer: answerOption.valueCoding || answerOption.valueString || answerOption.valueInteger,
openAnswer: true,
})
}
}
checked={
(this.props.questionnaireItemMap[item.linkId].answer && answerOption.valueCoding && this.props.questionnaireItemMap[item.linkId].answer.some( c => c.code === answerOption.valueCoding.code && c.system === answerOption.valueCoding.system ))
||
(this.props.questionnaireItemMap[item.linkId].answer && this.props.questionnaireItemMap[item.linkId].answer.includes( answerOption.valueString ))
||
(this.props.questionnaireItemMap[item.linkId].answer && this.props.questionnaireItemMap[item.linkId].answer.includes( answerOption.valueInteger ))
}
key={`${item.linkId}.a_${index}`}
containerStyle={{...localStyle.choice, marginLeft: this.calculateIndent(item.linkId)}}
textStyle={localStyle.choiceText}
/>)
})}
</View>
)}

{/* if type: 'open-choice' */}
{item.type === "open-choice" && (
<Input
placeholder={item.repeats ? config.text.survey.additionalAnswer : config.text.survey.alternativeAnswer}
value = {this.procureOpenAnswer(item)}
accessibilityHint={config.text.accessibility.questionnaire.textFieldHint}
onChangeText={(text) =>
{
// sets the answer
this.props.actions.setAnswer({
linkId: item.linkId,
answer: text,
isOpenAnswer: true,
isAdditionalAnswer: item.repeats
})
}
}
/>
)}
</View>
)
}
</View>
) : null
}

/**
* compares two different valueCoding Objects
*/
compareCoding = (val1, val2) => {

if (val1 && val2) return val1.system === val2.system && val1.code === val2.code
removeOpenAnswer = item => {
if(item.type !== 'open-choice') return
let a = this.props.questionnaireItemMap[item.linkId].answerOption.filter(e => e.isOpenQuestionAnswer)[0]
a.answer = null
}

return false
procureOpenAnswer = item => {
if(item.type !== 'open-choice') return
return this.props.questionnaireItemMap[item.linkId].answerOption.filter(e => e.isOpenQuestionAnswer)[0].answer
}

checkIfOpenAnswerWasChosen = (item) => {

let answer = this.props.questionnaireItemMap[item.linkId].answer

this.props.questionnaireItemMap[item.linkId].answerOption.some(
e => {
return e.valueString === answer || e.valueInteger === answer || e.valueDecimal === answer || e.valueDate === answer
}
)
}

/**
Expand Down Expand Up @@ -692,6 +800,7 @@ class QuestionnaireModal extends Component {

// creates a radio-item
case 'choice':
case 'open-choice':
return this.createChoices(item)

// creates a checkbox
Expand All @@ -703,8 +812,8 @@ class QuestionnaireModal extends Component {
return this.createDatePicker(item)

// creates a group of checkboxes, at least one must be checked
case 'open-choice':
return this.createOpenChoices(item)
// case 'open-choice':
// return this.createOpenChoices(item)

// creates the inputs for decimals and integers (and numerical sliders)
// this also utilizes the decimal-pad or the num-pad
Expand Down Expand Up @@ -1011,7 +1120,7 @@ const localStyle = StyleSheet.create({
textAlign: "center",
flexDirection: "row",
justifyContent: "space-between",
},
},

modalTitle: {
fontSize: 24,
Expand Down
6 changes: 4 additions & 2 deletions src/config/textConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ export default {

/** strings of the survey-screen */
"survey":{
"subTitle":"",
"logout":"logout",
"subTitleCheckIn":"",
"title":"Questionnaire",
"isLoading":"loading...",
"yourAnswer":"your answer",
Expand All @@ -181,12 +183,12 @@ export default {
"noUserTitle":"User not found",
"sendFinished":"send questionnaire",
"send":"Send Questionnaire Button Title",
"subTitle":"",
"additionalAnswer": "zusätzliche Antwort",
"alternativeAnswer": "alternative Antwort",
"surveySubTitle":"To be completed till: ",
"surveyTitle":"Your current questionnaire",
"inputPlaceholder":"please enter your answer",
"noQuestionnaireTitle":"Questionnaire not found",
"subTitleCheckIn":"",
"loadingQuestionnaire":"requesting\nquestionnaire",
"inputPlaceholderTime":"please enter the date here",
"sessionTimeout":"Your user could not re recognized",
Expand Down
1 change: 1 addition & 0 deletions src/screens/checkIn/checkInContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ class CheckInContainer extends Component {
* checks if the questionnaire was completed and if true triggers the export
*/
exportAndUploadQuestionnaireResponse = () => {

// if the questionnaire was NOT completed
if(this.props.questionnaireItemMap && !this.props.questionnaireItemMap.done) {
// shows a message remembering the user to complete the questionnaire
Expand Down
22 changes: 20 additions & 2 deletions src/screens/checkIn/checkInReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,20 @@ const valuesHandlers = {
}
// if its just a single-value answer
else {
// just updates the answer value
questionnaireItemMap[values.answer.linkId].answer = values.answer.answer

// nulls an empty string
if(typeof values.answer.answer === 'string' && !values.answer.answer) values.answer.answer = null

// writes the open-answer value in a separate variable
if(values.answer.isOpenAnswer) {
let answer = questionnaireItemMap[values.answer.linkId].answerOption.filter(e => e.isOpenQuestionAnswer)[0]
answer.answer = values.answer.answer
}

if(!values.answer.isAdditionalAnswer) {
// just updates the answer value
questionnaireItemMap[values.answer.linkId].answer = values.answer.answer
}
}

// updates the questionnaireItemMap to reflect the state of the questionnaire
Expand Down Expand Up @@ -406,6 +418,12 @@ const traverseItem = (item, questionnaireItemMap) => {
type: item.type || 'ignore',
required: item.required || false
}

// adds another answer object in case we have an open-choice
if(item.type === 'open-choice' && !questionnaireItemMap[item.linkId].answerOption.some(e => e.isOpenQuestionAnswer)) {
questionnaireItemMap[item.linkId].answerOption.push({isOpenQuestionAnswer:true, answer: null})
}

// sets the started value to false if the item is category
if(item.linkId.length === 1) {
questionnaireItemMap[item.linkId].started = false
Expand Down
2 changes: 2 additions & 0 deletions src/screens/checkIn/surveyScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ class SurveyScreen extends Component {
)}
</ListItem>
))}


</View>
)
return <View></View>
Expand Down
Loading

0 comments on commit 1fb3f56

Please sign in to comment.