Advertisement
Guest User

Survey Container

a guest
Jan 23rd, 2019
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 37.48 KB | None | 0 0
  1. import React from 'react'
  2. import {
  3. View,
  4. Dimensions,
  5. BackHandler,
  6. Platform
  7. } from 'react-native'
  8. import PropTypes from 'prop-types'
  9. import { connect } from 'react-redux'
  10. import Moment from 'moment'
  11. import _ from 'lodash'
  12. import { Styles } from '../../styles'
  13. import {
  14. getSurvey,
  15. createSurveyAnswer,
  16. getSurveyList,
  17. updateSurveyAnswer
  18. } from './store/actions'
  19. import {
  20. surveySelector,
  21. createSurveyAnswerSelector,
  22. updateSurveyAnswerSelector,
  23. surveyListSelector
  24. } from './store/selectors'
  25. import { OfflineNotifier, LoadingSurvey } from './components'
  26. import { SurveyHeader, SurveyStatus, InfoPopup } from './surveyComponents'
  27. import { validateNHS, validateSSN } from './regex'
  28. import {
  29. Survey,
  30. EQ5DSurvey,
  31. SurveyNavigationControl,
  32. EQ5DNavigationControl
  33. } from './'
  34. import translate from '../../content-config/content/main'
  35. import { getSelectors, validatePropsData } from '../../lib/store/dataHelpers'
  36. import { setLocalUser, setUser } from '../../store/common/actions'
  37. import { userSelector } from '../../store/common/selectors'
  38. import { deleteConsentData } from '../consent/store/actions'
  39. import Config from './config'
  40. import ErrorHandling from '../../utils/ErrorHandling'
  41. import { validateConditions } from './validateConditions'
  42. import { GoogleapisServiceProvider } from '../../lib/api/googleapis'
  43. import { isTokenError } from '../../utils'
  44.  
  45. const GoogleapisService = GoogleapisServiceProvider()
  46.  
  47. const screen = Dimensions.get('window')
  48. const dateFormat = 'YYYY-MM-DD HH:mm:ss'
  49. const defaultState = {
  50. surveyData: null,
  51. surveyStartedAt: null,
  52. tooltip: true,
  53. nextPage: null,
  54. pageCounter: 1,
  55. noOfPages: null,
  56. eq5d: null,
  57. loading: false,
  58. pageOrder: [],
  59. responses: {},
  60. resumeSurvey: false,
  61. error: null,
  62. surveyCompleted: false,
  63. leaveSurvey: false,
  64. goBack: false,
  65. errorSubmittingSurvey: false,
  66. getSurveyError: null,
  67. surveyId: null,
  68. thankYouScreenData: {},
  69. errorCode: null,
  70. multipleChoiceQuestions: [],
  71. showUnselectDragDropPopup: null
  72. }
  73.  
  74. const isLoadingProps = (props, state) => {
  75. const { surveyLoading, createSurveyAnswerLoading,
  76. surveyListLoading, updateSurveyAnswerLoading } = props
  77. const { goBack, loading } = state
  78.  
  79. // SurveyListLoading && goBack, need to be theer so that we check if the lsit
  80. // is loading only when we press the goback button, and not when we open a survey
  81. return !loading && (surveyLoading || createSurveyAnswerLoading || updateSurveyAnswerLoading ||
  82. !props.localUser.token || (surveyListLoading && goBack))
  83. }
  84.  
  85. class SurveyContainer extends ErrorHandling {
  86. constructor (props) {
  87. super(props)
  88. this.state = { ...defaultState }
  89. this.multipleChoicesMainPages = {}
  90. this.multipleChoicesMainPageAnswers = {}
  91. }
  92.  
  93. componentDidMount () {
  94. this.getSurveyFromBackend()
  95.  
  96. if (Platform.OS === 'android') {
  97. BackHandler.addEventListener('hardwareBackPress', this.onBackAndroid)
  98. }
  99. }
  100.  
  101. componentWillUnmount () {
  102. BackHandler.removeEventListener('hardwareBackPress', this.onBackAndroid)
  103. }
  104.  
  105. static getDerivedStateFromProps (props, state) {
  106. const { surveySuccess, createSurveyAnswerError, updateSurveyAnswerError, surveyError } = props
  107.  
  108. if (isLoadingProps(props, state)) return { loading: true }
  109.  
  110. if (surveySuccess && state.error) return { error: false, loading: false }
  111.  
  112. if ((createSurveyAnswerError || updateSurveyAnswerError) &&
  113. !isTokenError(createSurveyAnswerError) && !isTokenError(updateSurveyAnswerError))
  114. {
  115. return {
  116. loading: false,
  117. leaveSurvey: false,
  118. errorCode: null,
  119. errorSubmittingSurvey: true
  120. }
  121. }
  122. // if we retry call (token expired) we don't want to set loading to false
  123. if (surveyError && !isTokenError(surveyError)) return { loading: false, getSurveyError: true }
  124.  
  125. return null
  126. }
  127.  
  128. componentDidUpdate (prevProps, prevState) {
  129. const {
  130. surveySuccess,
  131. surveyError,
  132. appState
  133. } = this.props
  134.  
  135. if (prevProps.appState.match(/active|inactive/) && appState === 'background') {
  136. this.createResponseData(true)
  137. }
  138.  
  139. if (surveySuccess && validatePropsData('success', 'survey', prevProps, this.props)) {
  140. return this.onSurveySuccess(surveySuccess)
  141. }
  142.  
  143. if (surveySuccess &&
  144. (validatePropsData('success', 'createSurveyAnswer', prevProps, this.props) ||
  145. (validatePropsData('success', 'updateSurveyAnswer', prevProps, this.props)))) {
  146. this.onSurveyAnswerSuccess()
  147. }
  148.  
  149. if (surveyError && validatePropsData('error', 'survey', prevProps, this.props)) {
  150. return this.checkTokenError(surveyError, () => this.getSurveyFromBackend(), true)
  151. }
  152.  
  153. if ((validatePropsData('error', 'createSurveyAnswer', prevProps, this.props)) ||
  154. (validatePropsData('error', 'updateSurveyAnswer', prevProps, this.props))) {
  155. const { createSurveyAnswerError, updateSurveyAnswerError } = this.props
  156. return this.checkTokenError(createSurveyAnswerError || updateSurveyAnswerError,
  157. () => this.createOrUpdateSurveyAnswer(), false)
  158. }
  159.  
  160. this.onValidatePropsDataSurveysList(prevProps)
  161. }
  162.  
  163. checkTokenError (error, callback, surveyError) {
  164. if (isTokenError(error)) {
  165. if (this.retryCounter > 0) {
  166. this.retryCounter = 0
  167.  
  168. return this.setState({
  169. loading: false,
  170. getSurveyError: surveyError,
  171. errorSubmittingSurvey: true,
  172. errorCode: null,
  173. leaveSurvey: false
  174. })
  175. }
  176. this.refreshTokenAndRetry(() => callback()) // method found in ErrorHandling
  177. }
  178. }
  179.  
  180. onSurveyAnswerSuccess () {
  181. const surveyAnswerResponse = this.getSurveyAnswerResponse()
  182.  
  183. this.setState(prevState => ({
  184. loading: false,
  185. copyResponses: JSON.stringify(prevState.responses),
  186. surveyWithAnswerData: surveyAnswerResponse
  187. }))
  188.  
  189. // If survey completed
  190. if (surveyAnswerResponse.status === 'completed') this.onSurveyCompleted(surveyAnswerResponse)
  191.  
  192. // When navigating back or or putting survey in background
  193. if (this.state.goBack) this.retrieveSurveysList()
  194.  
  195. // call doesn't end when putting app in background
  196. // this.setState({ appState: AppState.currentState }) // TODO: ????
  197. }
  198.  
  199. getSurveyAnswerResponse () {
  200. const { createSurveyAnswerTimestamp, updateSurveyAnswerTimestamp,
  201. createSurveyAnswerSuccess, updateSurveyAnswerSuccess
  202. } = this.props
  203.  
  204. return createSurveyAnswerTimestamp > updateSurveyAnswerTimestamp
  205. ? createSurveyAnswerSuccess
  206. : updateSurveyAnswerSuccess
  207. }
  208.  
  209. onSurveyCompleted (surveyAnswerResponse) {
  210. this.retrieveSurveysList()
  211. this.setCompletedSurveyValue(surveyAnswerResponse)
  212. }
  213.  
  214. getSurveyFromBackend = () => {
  215. const { surveyId, localUser, getSurvey } = this.props
  216.  
  217. if (localUser && surveyId) {
  218. const { token, language } = localUser
  219. getSurvey(surveyId, {
  220. params: {
  221. accessToken: token,
  222. language
  223. }
  224. })
  225. }
  226. }
  227.  
  228. onValidatePropsDataSurveysList (prevProps) {
  229. const { goBack } = this.state
  230.  
  231. if ((validatePropsData('success', 'surveyList', prevProps, this.props) && goBack) ||
  232. validatePropsData('error', 'surveyList', prevProps, this.props)) {
  233. this.goToEdiary()
  234. }
  235. }
  236.  
  237. goToEdiary () { this.setState({ leaveSurvey: false }, () => this.props.goBack()) }
  238.  
  239. isResumeOrExpiredScreenShown = () => {
  240. const { data } = this.props
  241. const { resumeSurvey, surveyAnswerId, surveyData } = this.state
  242. const answerSurveysId = (data && data.answerId) || surveyAnswerId
  243.  
  244. return (surveyAnswerId && surveyData && !resumeSurvey) ||
  245. (answerSurveysId && !resumeSurvey)
  246. }
  247.  
  248. onBackAndroid = () => {
  249. const { surveyLoading, goBack } = this.props
  250. const { leaveSurvey } = this.state
  251. if (leaveSurvey || surveyLoading || this.isResumeOrExpiredScreenShown()) goBack()
  252. else this.onSurveyModalPressed(true)
  253. return true
  254. }
  255.  
  256. goBack = () => {
  257. const { responses, showPopupDate } = this.state
  258. const { fromNotifier, goToDashboard, goBack } = this.props
  259.  
  260. if (Object.keys(responses).length > 0 || showPopupDate) {
  261. this.leavePageCheckValidInput(() => this.createResponseData(true, 'goBack'))
  262. return
  263. }
  264. if (fromNotifier) return goToDashboard()
  265. goBack()
  266. }
  267.  
  268. toggleInfoPopup = (value, firstTime) => {
  269. const { surveyData } = this.state
  270.  
  271. if (firstTime && surveyData.showDragAndDropPopup === 'no') return
  272.  
  273. if (firstTime && surveyData.showDragAndDropPopup === 'yes') {
  274. return this.setState({ showInfoPopup: value, firstTimePopup: true, updateDragDropModalValue: true })
  275. }
  276. this.setState({ showInfoPopup: value })
  277. }
  278.  
  279. onCloseFirstTimePopup = () => {
  280. this.setState({ showInfoPopup: false, firstTimePopup: false })
  281. }
  282.  
  283. retrieveSurveysList () {
  284. const { localUser, getSurveyList } = this.props
  285. const { token, language } = localUser
  286.  
  287. getSurveyList({
  288. params: {
  289. accessToken: token,
  290. language
  291. }
  292. })
  293. }
  294.  
  295. setResumeSurveyValue = () => {
  296. this.setState({
  297. resumeSurvey: true,
  298. // We reset startedAt when we start again, an expired response
  299. surveyStartedAt: Moment().format(dateFormat)
  300. })
  301. }
  302.  
  303. setCompletedSurveyValue = surveyAnswerResponse => {
  304. this.setState({
  305. surveyCompleted: true,
  306. thankYouScreenData: surveyAnswerResponse
  307. })
  308. }
  309.  
  310. setError = value => this.setState({ error: value })
  311.  
  312. addResponse = (id, response, callback) => {
  313. const { pageOrder, pageCounter, responses, surveyData, multipleChoiceQuestions } = this.state
  314. const currentPage = pageOrder[pageCounter - 1]
  315. response.page = currentPage
  316. responses[id] = response
  317. const minPages = surveyData.pages[currentPage].minPages
  318. const pageInputType = surveyData.pages[pageOrder[pageCounter - 1]].pageInputType
  319. let lengthMultipleChoiceElements = 0
  320. if (this.multipleChoicesMainPageAnswers[currentPage]) {
  321. const mainPage = this.multipleChoicesMainPages[this.multipleChoicesMainPageAnswers[currentPage].mainPage]
  322. lengthMultipleChoiceElements = this.getLenghtMultipleChoiceOrMainArray(multipleChoiceQuestions, mainPage, currentPage)
  323. }
  324.  
  325. let mQuestions = multipleChoiceQuestions
  326. // Case multiple choice, and exclude multiple choice and radio types of answers
  327. if (pageInputType === 'multipleChoices' && typeof response.value === 'object') {
  328. const result = this.addReponseForMultipleChoice(response, lengthMultipleChoiceElements)
  329. mQuestions = result.mQuestions
  330. lengthMultipleChoiceElements = result.lengthMultipleChoiceElements
  331. }
  332.  
  333. this.setState({
  334. responses,
  335. multipleChoiceQuestions: mQuestions,
  336. noOfPages: minPages + pageCounter - 1 + lengthMultipleChoiceElements
  337. }, () => {
  338. if (callback) return callback()
  339. this.checkNextPage()
  340. })
  341. }
  342.  
  343. deleteSurveyConsentData = () => this.props.deleteConsentData('surveys_consent')
  344.  
  345. deleteResponse = id => { // uncheck answer
  346. const { responses, noOfPages, surveyData, pageOrder, pageCounter } = this.state
  347. const pageInputType = surveyData.pages[pageOrder[pageCounter - 1]].pageInputType
  348. delete responses[id]
  349. // Case multiple choice answers
  350. if (pageInputType === 'multipleChoices') {
  351. this.deleteDataMultipleChoice()
  352. return this.setState({
  353. responses,
  354. noOfPages: noOfPages - 1,
  355. multipleChoiceQuestions: []
  356. }, this.checkNextPage)
  357. }
  358. this.checkNextPage()
  359. }
  360.  
  361. setSurveyWithAnswers (survey, surveyAnswer) {
  362. const { responses, minPagesLeft, pageOrder, id, multipleChoiceQuestions = [],
  363. multipleChoicesMainPageAnswers, multipleChoicesMainPages } = surveyAnswer
  364. const length = pageOrder.length
  365. const { variablePrefix } = survey
  366. const multipleChoiceQuestionsLength = multipleChoiceQuestions.length > 0
  367. ? multipleChoiceQuestions.length : 0
  368.  
  369. this.multipleChoicesMainPageAnswers = multipleChoicesMainPageAnswers || []
  370. this.multipleChoicesMainPages = multipleChoicesMainPages || []
  371.  
  372. this.setState({
  373. surveyAnswerId: id,
  374. surveyData: survey,
  375. pageOrder,
  376. responses,
  377. multipleChoiceQuestions,
  378. noOfPages: length + minPagesLeft - 1 + multipleChoiceQuestionsLength,
  379. pageCounter: length,
  380. eq5d: variablePrefix === 'eq_5d',
  381. loading: false,
  382. copyResponses: JSON.stringify(responses),
  383. errorCode: null
  384. }, this.checkNextPage)
  385. }
  386.  
  387. setSurvey (survey) {
  388. const { pageOrder } = this.state
  389. const { pages, firstPage, variablePrefix } = survey
  390. pageOrder.length = 0
  391. pageOrder.push(survey.firstPage)
  392.  
  393. this.setState({
  394. surveyData: survey,
  395. surveyStartedAt: Moment().format(dateFormat),
  396. noOfPages: pages[firstPage].minPages,
  397. eq5d: variablePrefix === 'eq_5d',
  398. loading: false,
  399. nextPage: survey.firstPage,
  400. errorCode: null,
  401. responses: {},
  402. pageCounter: 1
  403. })
  404. }
  405.  
  406. onSurveySuccess (surveyResponse) {
  407. const { survey, surveyAnswer } = surveyResponse
  408. const expired = surveyAnswer && surveyAnswer.status === 'expired'
  409.  
  410. if (surveyAnswer && !expired) return this.setSurveyWithAnswers(survey, surveyAnswer)
  411. this.setSurvey(survey)
  412. }
  413.  
  414. onSurveyModalPressed = value => {
  415. const { surveyError } = this.props
  416.  
  417. if (surveyError) return this.props.goBack()
  418. this.setState({ leaveSurvey: value })
  419. }
  420.  
  421. swithThroughConditions (conditions, responses) {
  422. let conditionsPassed = true
  423. conditions.map(block => {
  424. // skip the other validations if there is a failed condition
  425. if (conditionsPassed) {
  426. const response = responses[block.inputId]
  427. conditionsPassed = validateConditions[block.condition](block, response)
  428. }
  429. })
  430. return conditionsPassed
  431. }
  432.  
  433. getNextPage (nextPage, responses) {
  434. let result = null
  435. let conditions = null
  436. const conditionals = nextPage.if ? nextPage.if : null
  437.  
  438. if (conditionals) {
  439. conditionals.map(cond => {
  440. // skip the other conditionals if a result has been found
  441. if (!result) {
  442. conditions = cond.conditions
  443. if (this.swithThroughConditions(conditions, responses)) result = cond
  444. }
  445. })
  446. if (result) return result.page
  447. if (nextPage.else) return nextPage.else
  448. return nextPage.page
  449. }
  450. return nextPage.page
  451. }
  452.  
  453. hasResponseData (data) {
  454. if (!data) return false
  455. return data.value !== null && data.value !== undefined && data.value !== ''
  456. }
  457.  
  458. hasItemDependencies (item, responses) {
  459. const { disableDependency } = item
  460.  
  461. if (!disableDependency) return false
  462.  
  463. let hasData = true
  464. disableDependency.map(dependency => {
  465. // if there is an undefined response the other dependencies are skipped
  466. if (hasData) {
  467. hasData = this.hasResponseData(responses[dependency])
  468. }
  469. })
  470. return hasData
  471. }
  472.  
  473. isRequiredItem (item) { return item.input && (item.required === 1) }
  474.  
  475. addSliderDefalutResponse (item) {
  476. const { id, field, parents, sliderInfo } = item
  477. const defaultVal = (sliderInfo && (sliderInfo.min || sliderInfo.min === 0)) ? sliderInfo.min : ''
  478. const response = {
  479. field: field,
  480. value: defaultVal,
  481. parents: parents,
  482. disabled: false
  483. }
  484. this.addResponse(id, response, () => {
  485. return true
  486. })
  487. }
  488.  
  489. validatePageData (pageData, responses) {
  490. const { items } = pageData
  491. let isValid = true
  492. let timburdenPreviousItemValue = null
  493.  
  494. items.map(item => {
  495. const { validation } = item
  496. if (item.value) timburdenPreviousItemValue = item.value
  497. // if an invalid response is found will skip the other checks
  498. if (item.sliderInfo && (item.sliderInfo.min || item.sliderInfo.min === 0) &&
  499. !(this.hasResponseData(responses[item.id]))) {
  500. return this.addSliderDefalutResponse(item)
  501. }
  502. if (isValid && this.isRequiredItem(item) &&
  503. (!(this.hasResponseData(responses[item.id]) || item.value || timburdenPreviousItemValue)) &&
  504. !this.hasItemDependencies(item, responses)) {
  505. if (validation === 'hours') {
  506. this.setState({ error: Config.surveysGeneralConfig.errorMessages['hours'] })
  507. } else this.setState({ error: Config.surveysGeneralConfig.errorMessages[item.input] })
  508. isValid = false
  509. }
  510. if (isValid && validation && validation !== 'month') {
  511. const value = (responses[item.id] && responses[item.id].value) || item.value
  512. if (!value && !this.isRequiredItem(item)) return // if no value, and field is optional => no need for validations
  513. if (validation === 'SSN') { // here should be added validation for popup if needed
  514. isValid = validateNHS(value) || validateSSN(value) //validate for UK and US
  515. if (!isValid) this.setState({ error: Config.surveysGeneralConfig.errorMessages['nhs'] })
  516. }
  517. }
  518. })
  519. return isValid
  520. }
  521.  
  522. checkNextPage () {
  523. const { pageOrder, pageCounter, surveyData, responses } = this.state
  524. const currentPage = pageOrder[pageCounter - 1]
  525. const nextPage = this.getNextPage(surveyData.pages[currentPage].nextPage, responses)
  526.  
  527. this.setState({ nextPage, error: null })
  528. return nextPage
  529. }
  530.  
  531. processString (string) { return string.replace(/\s/g, '').toLowerCase() }
  532.  
  533. interpretAsyncResults = ({results, status}, callback, zipCode) => {
  534. const { country } = this.props.localUser
  535. let avCountriesZipcode = false
  536. let zipCodeMatch = false
  537.  
  538. let userCountry = country
  539. if (country === 'UK') userCountry = 'GB'
  540. if (status !== 'OK') return callback(status, 'zipCode')
  541.  
  542. if (results[0] && results[0].address_components && (results[0].types[0] === 'postal_code')) {
  543. results[0].address_components.map(({ types, short_name }) => {
  544. if (types && (types[0] === 'postal_code') &&
  545. (this.processString(short_name) === this.processString(zipCode))) {
  546. zipCodeMatch = true
  547. }
  548. if (types && (types[0] === 'country') &&
  549. (userCountry === short_name)) {
  550. avCountriesZipcode = true
  551. }
  552. })
  553. if (avCountriesZipcode && zipCodeMatch) return callback(null)
  554. }
  555. this.setState({ error: translate('surveyItemErrors', 'zipcode') })
  556. return callback(status, 'zipCode')
  557. }
  558.  
  559. resolveValidationPromises = (promises, callback, zipCode) => {
  560. let noAsyncValidation = false
  561.  
  562. Promise.all(promises).then((results) => {
  563. results.forEach((response, index) => {
  564. if (response === 'noAsyncValidation') noAsyncValidation = true
  565. else return this.interpretAsyncResults(response, callback, zipCode)
  566.  
  567. if (noAsyncValidation && index === results.length - 1) {
  568. return callback(null, 'no_async_validation')
  569. }
  570. })
  571. })
  572. }
  573.  
  574. checkItemAsyncValidations = ({items}, responses, callback) => {
  575. const promises = []
  576. let zipCode = ''
  577.  
  578. items.forEach(({id, value, field}) => {
  579. const usedValue = (responses[id] && typeof responses[id].value === 'string')
  580. ? responses[id].value
  581. : value
  582.  
  583. if (field === 'zipCode') {
  584. const language = this.props.localUser.language
  585. promises.push(GoogleapisService.checkZipcode({ zipCode: usedValue, language }))
  586. zipCode = usedValue
  587. } else promises.push('noAsyncValidation')
  588. })
  589. this.resolveValidationPromises(promises, callback, zipCode)
  590. }
  591.  
  592. onPressPrevious = () => {
  593. const { pageOrder, pageCounter } = this.state
  594. const currentPageNo = pageCounter
  595.  
  596. pageOrder.pop()
  597.  
  598. currentPageNo >= 1 && this.setState({
  599. pageCounter: currentPageNo - 1,
  600. pageOrder: pageOrder,
  601. error: null
  602. }, () => this.checkNextPage)
  603. }
  604.  
  605. onPressNext = () => {
  606. const { pageOrder, pageCounter, surveyData, responses } = this.state
  607. const currentPage = pageOrder[pageCounter - 1]
  608. const pageData = surveyData.pages[currentPage]
  609.  
  610. if (this.pageHasAsyncValidation(pageData)) {
  611. return this.checkItemAsyncValidations(pageData, responses, (err, field) => {
  612. if (err && field === 'zipCode') {
  613. return this.setState({ error: translate('surveyItemErrors', 'zipcode') })
  614. }
  615. this.validateDataAndGetNextPage(pageData, responses)
  616. })
  617. }
  618. this.validateDataAndGetNextPage(pageData, responses)
  619. }
  620.  
  621. pageHasAsyncValidation (pageData) {
  622. const pageInputType = pageData.pageInputType || null
  623. return pageInputType && pageInputType === 'hasAsyncValidations'
  624. }
  625.  
  626. leavePageCheckValidInput = (callback) => {
  627. const { pageOrder, pageCounter, surveyData, responses } = this.state
  628. const pageData = surveyData.pages[pageOrder[pageCounter - 1]]
  629.  
  630. if (this.pageHasAsyncValidation(pageData)) {
  631. return this.checkItemAsyncValidations(pageData, responses, (err, field) => {
  632. if (err && field === 'zipCode') {
  633. const { id, value } = pageData.items[0]
  634. responses[id].value = value
  635. this.setState({ responses })
  636. }
  637. callback()
  638. })
  639. }
  640. callback()
  641. }
  642.  
  643. composeResponseData = intermediary => {
  644. const {
  645. pageOrder,
  646. pageCounter,
  647. surveyData,
  648. responses,
  649. surveyStartedAt,
  650. multipleChoiceQuestions
  651. } = this.state
  652. const { title, version, expiresIn } = surveyData
  653. const { surveyId, localUser } = this.props
  654. const { timezoneOffset } = localUser
  655. const currentPage = pageOrder[pageCounter - 1]
  656.  
  657. const newData = {
  658. title,
  659. version: version || null,
  660. expiresIn,
  661. surveyId,
  662. responses,
  663. pageOrder,
  664. timezoneOffset,
  665. multipleChoiceQuestions,
  666. multipleChoicesMainPages: this.multipleChoicesMainPages,
  667. multipleChoicesMainPageAnswers: this.multipleChoicesMainPageAnswers,
  668. startedAt: surveyStartedAt,
  669. minPagesLeft: surveyData.pages[currentPage].minPages,
  670. completionDate: !intermediary && Moment().format()
  671. }
  672.  
  673. return newData
  674. }
  675.  
  676. doNotCreadeOrUpdateSurveyAnswer (requestSource) {
  677. const {
  678. responses,
  679. copyResponses,
  680. showPopupDate
  681. } = this.state
  682. const {
  683. createSurveyAnswerLoading,
  684. updateSurveyAnswerLoading
  685. } = this.props
  686. const sameResponses = copyResponses === JSON.stringify(responses)
  687. const responseIsEmpty = JSON.stringify(responses) === '{}'
  688.  
  689. // Verify if not the same response, but if there is another call in progress
  690. return ((sameResponses ||
  691. responseIsEmpty ||
  692. createSurveyAnswerLoading ||
  693. updateSurveyAnswerLoading) &&
  694. !showPopupDate &&
  695. requestSource !== 'goBack' &&
  696. requestSource !== 'endSurvey')
  697. }
  698.  
  699. resetErrorSubmit = () => this.setState({ errorSubmittingSurvey: false })
  700.  
  701. update (...args) { this.props.updateSurveyAnswer(...args) }
  702.  
  703. createOrUpdateSurveyAnswer (intermediary) {
  704. const { surveyAnswerId, surveyWithAnswerData, updateDragDropModalValue } = this.state
  705. const { createSurveyAnswer, answerId, data, localUser } = this.props
  706.  
  707. const newData = this.composeResponseData(intermediary)
  708.  
  709. if (updateDragDropModalValue) {
  710. newData.showDragAndDropPopup = 'no'
  711. this.setState({ updateDragDropModalValue: false })
  712. }
  713.  
  714. const surveyWithAnswerId = surveyWithAnswerData && surveyWithAnswerData.surveyAnswerId
  715. const answerSurveysId = answerId || surveyAnswerId || surveyWithAnswerId
  716. const { token, language } = localUser
  717. const params = {params: {accessToken: token, language}}
  718.  
  719. answerSurveysId && token && (data.status !== 'expired' ||
  720. (surveyWithAnswerData && surveyWithAnswerData.status !== 'expired'))
  721. ? this.update(answerSurveysId, newData, params)
  722. : createSurveyAnswer(newData, params)
  723. }
  724.  
  725. createResponseData (intermediary, requestSource) {
  726. const { surveyData } = this.state
  727.  
  728. if (!surveyData) return
  729.  
  730. if (requestSource === 'goBack') this.setState({goBack: true})
  731. else this.setState({goBack: false})
  732.  
  733. if (this.doNotCreadeOrUpdateSurveyAnswer(requestSource)) return
  734.  
  735. this.createOrUpdateSurveyAnswer(intermediary)
  736. }
  737.  
  738. // HERE STARTS MULTIPLE CHOICE QUESTION LOGIC
  739. deleteMultipleChoice = () => { // 'don't know' answer
  740. const { noOfPages, multipleChoiceQuestions } = this.state
  741. const lengthMultipleChoiceElements = multipleChoiceQuestions.length
  742.  
  743. this.deleteDataMultipleChoice()
  744. this.setState({
  745. multipleChoiceQuestions: [],
  746. noOfPages: noOfPages - lengthMultipleChoiceElements
  747. })
  748. }
  749.  
  750. deleteDataMultipleChoice () {
  751. const { surveyData, pageOrder, pageCounter } = this.state
  752.  
  753. const currentPage = pageOrder[pageCounter - 1]
  754. // temporary fix becauase it's called every time uncheck a box on any question
  755. if (surveyData.pages[pageOrder[pageCounter - 1]].pageInputType === 'multipleChoices') {
  756. this.multipleChoicesMainPages[currentPage] = {}
  757. this.multipleChoicesMainPageAnswers = _.pickBy(this.multipleChoicesMainPageAnswers, value => value.mainPage !== currentPage)
  758. }
  759. }
  760.  
  761. addReponseForMultipleChoice = (response, lengthMultipleChoiceElements) => {
  762. const { pageOrder, pageCounter, responses, surveyData } = this.state
  763. const currentPage = pageOrder[pageCounter - 1]
  764.  
  765. const data = {
  766. responses,
  767. pageData: surveyData.pages[currentPage],
  768. nextPage: surveyData.pages[pageOrder[pageCounter - 1]].nextPage,
  769. lengthMultipleChoiceElements
  770. }
  771. const optionValues = surveyData.pages[pageOrder[pageCounter - 1]].items[0].values
  772. const { multipleChoicePickedValues } =
  773. this.getMultipleChoicePickedValues(data, response.value, optionValues, { isAddResponse: true })
  774. return {
  775. mQuestions: multipleChoicePickedValues,
  776. lengthMultipleChoiceElements: response.value.length
  777. }
  778. }
  779.  
  780. getLenghtMultipleChoiceOrMainArray = (multipleChoiceQuestions, mainPage, currentPage) => {
  781. let lengthMultipleChoiceElements = multipleChoiceQuestions.length
  782. let mainPageArray = Object.keys(mainPage)
  783. const mainPageLength = mainPageArray.length
  784. mainPageArray = mainPageArray.slice(mainPageArray.indexOf(currentPage) + 1, mainPageLength)
  785. if (mainPageLength > 3) lengthMultipleChoiceElements = mainPageArray.length
  786. return lengthMultipleChoiceElements
  787. }
  788.  
  789. updateStateAndCheckNextPage = (data) => this.setState(data, () => this.checkNextPage())
  790.  
  791. getMultipleChoicePickedValues = (data, pickedValues, allPossibleValues, isAddResponse) => {
  792. const { pageData } = data
  793. const { pageOrder, pageCounter } = this.state
  794.  
  795. const currentPage = pageOrder[pageCounter - 1]
  796.  
  797. const multipleChoicePickedValues = []
  798. allPossibleValues.map((allValue, index) => {
  799. pickedValues.map(pickedValue => {
  800. if (allValue === pickedValue) {
  801. multipleChoicePickedValues.push(pageData.multipleChoiceQuestions[index])
  802. }
  803. })
  804. })
  805.  
  806. this.refreshMainPageAnswers(multipleChoicePickedValues, currentPage)
  807. const isInPageOrder = pageOrder.findIndex(val => val === multipleChoicePickedValues[0])
  808.  
  809. const newLength = multipleChoicePickedValues.length
  810. if (isInPageOrder < 0 && !isAddResponse) {
  811. pageOrder.push(multipleChoicePickedValues[0])
  812. multipleChoicePickedValues.splice(0, 1)
  813. }
  814.  
  815. return { multipleChoicePickedValues, newLength }
  816. }
  817.  
  818. refreshMainPageAnswers (pickedValues, currentPage) {
  819. this.multipleChoicesMainPages[currentPage] = {}
  820. const newAnswers = {}
  821. newAnswers[currentPage] = { nextPage: pickedValues[0] }
  822. pickedValues.forEach((object, index) => {
  823. newAnswers[object] = { nextPage: pickedValues[index + 1] }
  824. if (!this.multipleChoicesMainPageAnswers[object]) {
  825. this.multipleChoicesMainPageAnswers[object] = { mainPage: currentPage }
  826. }
  827. })
  828. this.multipleChoicesMainPages[currentPage] = newAnswers
  829. }
  830.  
  831. updatePageProgressIfPickedValues = (data, pickedValues) => {
  832. const { nextPage } = data
  833. const { pageOrder, pageCounter, surveyData } = this.state
  834. const allPossibleValues = surveyData.pages[pageOrder[pageCounter - 1]].items[0].values
  835. const {
  836. multipleChoicePickedValues,
  837. newLength
  838. } = this.getMultipleChoicePickedValues(data, pickedValues, allPossibleValues)
  839.  
  840. const stateData = {
  841. multipleChoiceQuestions: multipleChoicePickedValues,
  842. pageCounter: pageCounter + 1,
  843. pageOrder,
  844. noOfPages: surveyData.pages[nextPage].minPages + pageCounter + newLength
  845. }
  846. this.updateStateAndCheckNextPage(stateData)
  847. }
  848.  
  849. updatePageProgressIfMultipleQuestion = (data) => {
  850. const { responses, nextPage, lengthMultipleChoiceElements } = data
  851. const { pageOrder, pageCounter } = this.state
  852. const pickedValues = responses[pageOrder[pageCounter - 1] + '_item1'] &&
  853. responses[pageOrder[pageCounter - 1] + '_item1'].value
  854. if (!pickedValues) return this.updatePageProgressAnyQuestion(nextPage, lengthMultipleChoiceElements)
  855. this.updatePageProgressIfPickedValues(data, pickedValues)
  856. }
  857.  
  858. updatePageProgressAnyQuestion = (nextPage, lengthMultipleChoiceElements) => {
  859. const { pageOrder, pageCounter, surveyData } = this.state
  860. pageOrder.push(nextPage)
  861. const stateData = {
  862. pageCounter: pageCounter + 1,
  863. pageOrder,
  864. noOfPages: surveyData.pages[nextPage].minPages + pageCounter + lengthMultipleChoiceElements
  865. }
  866. this.updateStateAndCheckNextPage(stateData)
  867. }
  868.  
  869. getLenghtAndUpdateMultipleChoice = (multipleChoiceQuestions, mainPage, currentPage) => {
  870. multipleChoiceQuestions.splice(0, 1)
  871. this.setState({ multipleChoiceQuestions })
  872. let lengthMultipleChoiceElements = multipleChoiceQuestions.length
  873. let mainPageArray = Object.keys(mainPage)
  874. const mainPageLength = mainPageArray.length
  875. mainPageArray = mainPageArray.slice(mainPageArray.indexOf(currentPage) + 2, mainPageLength)
  876. if (mainPageLength > 3) lengthMultipleChoiceElements = mainPageArray.length
  877. return lengthMultipleChoiceElements
  878. }
  879.  
  880. validateDataAndGetNextPage = (pageData, responses) => {
  881. const { multipleChoiceQuestions, pageOrder, pageCounter } = this.state
  882. const currentPage = pageOrder[pageCounter - 1]
  883. let lengthMultipleChoiceElements = 0
  884.  
  885. if (!this.validatePageData(pageData, responses)) return false
  886. let nextPage = this.getNextPage(pageData.nextPage, responses)
  887.  
  888. if (this.multipleChoicesMainPageAnswers[currentPage]) {
  889. const mainPage = this.multipleChoicesMainPages[this.multipleChoicesMainPageAnswers[currentPage].mainPage]
  890. if (mainPage[currentPage].nextPage) nextPage = mainPage[currentPage].nextPage
  891. lengthMultipleChoiceElements = this.getLenghtAndUpdateMultipleChoice(multipleChoiceQuestions, mainPage, currentPage)
  892. }
  893.  
  894. if (pageData.pageInputType === 'multipleChoices') { // new multiple choice page
  895. const data = { pageData, responses, nextPage, lengthMultipleChoiceElements }
  896. return this.updatePageProgressIfMultipleQuestion(data)
  897. }
  898. if (nextPage) return this.updatePageProgressAnyQuestion(nextPage, lengthMultipleChoiceElements)
  899. // the extra check of next page it's for not selecting any option yet
  900. this.createResponseData(false, 'endSurvey')
  901. }
  902. // HERE ENDS MULTIPLE CHOICE QUESTION LOGIC
  903.  
  904. renderHeader () {
  905. const { eq5d, pageCounter, noOfPages, surveyData } = this.state
  906. return (
  907. <SurveyHeader
  908. eq5d={eq5d}
  909. pageCounter={pageCounter}
  910. onBackPressed={this.onBackAndroid}
  911. noOfPages={noOfPages}
  912. surveyData={surveyData} />
  913. )
  914. }
  915.  
  916. renderSurveyPages () {
  917. const {
  918. pageOrder,
  919. pageCounter,
  920. surveyData,
  921. eq5d,
  922. responses,
  923. error,
  924. leaveSurvey,
  925. loading
  926. } = this.state
  927. const { navigate } = this.props
  928. const currentPage = pageOrder[pageCounter - 1]
  929. const firstPage = surveyData.firstPage
  930. const pages = surveyData.pages
  931. const pageData = currentPage && (pages[currentPage] || pages[firstPage] || {})
  932.  
  933. return eq5d
  934. ? <EQ5DSurvey
  935. goBack={this.goBack}
  936. leaveSurvey={leaveSurvey}
  937. onSurveyModalPressed={this.onSurveyModalPressed}
  938. addResponse={this.addResponse}
  939. deleteResponse={this.deleteResponse}
  940. id={currentPage}
  941. pageOrder={pageOrder}
  942. isLoading={loading}
  943. page={pageData}
  944. error={error}
  945. responses={responses}
  946. setError={this.setError} />
  947. : <Survey
  948. goBack={this.goBack}
  949. toggleInfoPopup={this.toggleInfoPopup}
  950. leaveSurvey={leaveSurvey}
  951. onSurveyModalPressed={this.onSurveyModalPressed}
  952. error={error}
  953. addResponse={this.addResponse}
  954. deleteResponse={this.deleteResponse}
  955. id={currentPage}
  956. pageOrder={pageOrder}
  957. isLoading={loading}
  958. navigate={navigate}
  959. deleteMultipleChoice={this.deleteMultipleChoice}
  960. page={pageData}
  961. responses={responses}
  962. setError={this.setError}
  963. createTimeburdenSpecialAnswer={this.createTimeburdenSpecialAnswer} />
  964. }
  965.  
  966. renderBottomControls () {
  967. const {
  968. pageCounter,
  969. surveyData,
  970. eq5d,
  971. nextPage,
  972. noOfPages,
  973. loading,
  974. errorSubmittingSurvey
  975. } = this.state
  976. const isLastPage = pageCounter === noOfPages && typeof nextPage !== 'string'
  977.  
  978. return eq5d
  979. ? <EQ5DNavigationControl
  980. eq5d={eq5d}
  981. isFirstPage={ pageCounter < 2 }
  982. isLastPage={isLastPage}
  983. loading={loading}
  984. errorSubmit={errorSubmittingSurvey}
  985. buttonInfo={surveyData}
  986. onPressNext={this.onPressNext}
  987. onPressPrevious={this.onPressPrevious} />
  988. : <SurveyNavigationControl
  989. eq5d={eq5d}
  990. isFirstPage={ pageCounter < 2 }
  991. isLastPage={isLastPage}
  992. loading={loading}
  993. resetErrorSubmit={this.resetErrorSubmit}
  994. errorSubmit={errorSubmittingSurvey}
  995. buttonInfo={surveyData}
  996. onPressNext={this.onPressNext}
  997. onPressPrevious={this.onPressPrevious} />
  998. }
  999.  
  1000. renderLoadingSurveyData () {
  1001. const { surveyLoading } = this.props
  1002. const { getSurveyError } = this.state
  1003. const { text, icon, error, button } = Config.loadingConfig.loading
  1004. let loadingMessage = text
  1005. if (getSurveyError) loadingMessage = error
  1006. if (surveyLoading) loadingMessage = text
  1007.  
  1008. return (
  1009. <LoadingSurvey
  1010. loadingImage={icon}
  1011. loadingError={getSurveyError && !surveyLoading}
  1012. button={button}
  1013. onButtonPressed={() => this.getSurveyFromBackend()}
  1014. message={loadingMessage} />
  1015. )
  1016. }
  1017.  
  1018. renderSurveyStatus () {
  1019. const { data, goBack, surveyError } = this.props
  1020. const {
  1021. pageCounter,
  1022. noOfPages,
  1023. surveyCompleted,
  1024. surveyData,
  1025. thankYouScreenData
  1026. } = this.state
  1027. const surveyDataInfo = Object.keys(data).length ? data : surveyData
  1028. const { surveyLoading } = this.props
  1029. console.log(this.state, { data })
  1030. return (
  1031. <SurveyStatus
  1032. loadingData={surveyLoading}
  1033. surveyError={surveyError}
  1034. goToSurvey={this.setResumeSurveyValue}
  1035. surveyCompleted={surveyCompleted}
  1036. pageCounter={pageCounter}
  1037. noOfPages={noOfPages}
  1038. goBack={goBack}
  1039. surveyData={surveyDataInfo}
  1040. thankYouScreen={thankYouScreenData} />
  1041. )
  1042. }
  1043.  
  1044. renderInfoPopup = () => {
  1045. const { showInfoPopup, firstTimePopup } = this.state
  1046.  
  1047. if (showInfoPopup) {
  1048. return (
  1049. <InfoPopup
  1050. firstTimePopup={firstTimePopup}
  1051. onCloseFirstTimePopup={this.onCloseFirstTimePopup}
  1052. onPopupShown={this.toggleInfoPopup} />
  1053. )
  1054. }
  1055. }
  1056.  
  1057. renderOfflineNotifier () {
  1058. return (
  1059. <OfflineNotifier
  1060. text={translate('general', 'noInternetConnection')}
  1061. />
  1062. )
  1063. }
  1064.  
  1065. renderSurveyContent () {
  1066. const { surveyLoading, surveyError } = this.props
  1067. const { eq5d, surveyData } = this.state
  1068. const backgroundColor = eq5d
  1069. ? Styles.mainColors.WHITE
  1070. : Styles.mainColors.SCREEN_DEFAULT_BACKGROUND_COLOR
  1071.  
  1072. return (
  1073. <View style={{ height: screen.height, backgroundColor }}>
  1074. {this.renderHeader()}
  1075. {this.renderOfflineNotifier()}
  1076. {(surveyLoading || surveyError || !surveyData) && this.renderLoadingSurveyData()}
  1077. {!surveyLoading && surveyData && this.renderSurveyPages()}
  1078. {this.renderInfoPopup()}
  1079. {!surveyLoading && surveyData && this.renderBottomControls()}
  1080. </View>
  1081. )
  1082. }
  1083.  
  1084. render () {
  1085. const { data } = this.props
  1086. const { resumeSurvey, surveyCompleted, surveyAnswerId, surveyData } = this.state
  1087. const answerSurveysId = (data && data.answerId) || surveyAnswerId
  1088.  
  1089. if ((surveyAnswerId && surveyData && !resumeSurvey) ||
  1090. (answerSurveysId && !resumeSurvey) || surveyCompleted) {
  1091. return this.renderSurveyStatus()
  1092. }
  1093.  
  1094. return this.renderSurveyContent()
  1095. }
  1096. }
  1097.  
  1098. const mapStateToProps = state => {
  1099. return {
  1100. ...getSelectors(surveySelector, 'surveySelector', state),
  1101. ...getSelectors(createSurveyAnswerSelector, 'createSurveyAnswerSelector', state),
  1102. ...getSelectors(updateSurveyAnswerSelector, 'updateSurveyAnswerSelector', state),
  1103. ...getSelectors(surveyListSelector, 'surveyListSelector', state),
  1104. localUser: userSelector(state) || {}
  1105. }
  1106. }
  1107.  
  1108. const mapDispatchToProps = {
  1109. getSurvey,
  1110. getSurveyList,
  1111. createSurveyAnswer,
  1112. updateSurveyAnswer,
  1113. deleteConsentData,
  1114. setLocalUser,
  1115. setUser
  1116. }
  1117.  
  1118. export default connect(mapStateToProps, mapDispatchToProps)(SurveyContainer)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement