QuestionnaireModal.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import { useCallback } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import {
  4. Modal, ModalHeader, ModalBody, ModalFooter,
  5. } from 'reactstrap';
  6. import { apiv3Put } from '~/client/util/apiv3-client';
  7. import { toastSuccess, toastError } from '~/client/util/toastr';
  8. import { IAnswer } from '~/interfaces/questionnaire/answer';
  9. import { IQuestionnaireOrderHasId } from '~/interfaces/questionnaire/questionnaire-order';
  10. import { useCurrentUser } from '~/stores/context';
  11. import { useQuestionnaireModal } from '~/stores/modal';
  12. import loggerFactory from '~/utils/logger';
  13. import Question from './Question';
  14. const logger = loggerFactory('growi:QuestionnaireModal');
  15. type QuestionnaireModalProps = {
  16. questionnaireOrder: IQuestionnaireOrderHasId
  17. }
  18. const QuestionnaireModal = ({ questionnaireOrder }: QuestionnaireModalProps): JSX.Element => {
  19. const { data: currentUser } = useCurrentUser();
  20. const lang = currentUser?.lang;
  21. const { data: questionnaireModalData, close: closeQuestionnaireModal } = useQuestionnaireModal();
  22. const isOpened = questionnaireModalData?.openedQuestionnaireId === questionnaireOrder._id;
  23. const inputNamePrefix = 'question-';
  24. const { t } = useTranslation();
  25. const sendAnswer = useCallback(async(answers: IAnswer[]) => {
  26. try {
  27. await apiv3Put('/questionnaire/answer', {
  28. questionnaireOrderId: questionnaireOrder._id,
  29. answers,
  30. });
  31. toastSuccess(
  32. <>
  33. <div className="font-weight-bold">{t('questionnaire.thank_you_for_answering')}</div>
  34. <div className="pt-2">{t('questionnaire.additional_feedback')}</div>
  35. </>,
  36. {
  37. autoClose: 3000,
  38. closeButton: true,
  39. },
  40. );
  41. }
  42. catch (e) {
  43. logger.error(e);
  44. toastError(t('questionnaire.failed_to_send'));
  45. }
  46. }, [questionnaireOrder._id, t]);
  47. const submitHandler = useCallback(async(event) => {
  48. event.preventDefault();
  49. const answers: IAnswer[] = questionnaireOrder.questions.map((question) => {
  50. const answerValue = event.target[`${inputNamePrefix + question._id}`].value;
  51. return { question: question._id, value: answerValue };
  52. });
  53. sendAnswer(answers);
  54. closeQuestionnaireModal();
  55. }, [closeQuestionnaireModal, questionnaireOrder.questions, sendAnswer]);
  56. const skipBtnClickHandler = useCallback(async() => {
  57. try {
  58. apiv3Put('/questionnaire/skip', {
  59. questionnaireOrderId: questionnaireOrder._id,
  60. });
  61. toastSuccess(t('questionnaire.skipped'));
  62. }
  63. catch (e) {
  64. logger.error(e);
  65. toastError(t('questionnaire.failed_to_skip'));
  66. }
  67. closeQuestionnaireModal();
  68. }, [closeQuestionnaireModal, questionnaireOrder._id, t]);
  69. // No showing toasts since not important
  70. const closeBtnClickHandler = useCallback(async() => {
  71. closeQuestionnaireModal();
  72. try {
  73. await apiv3Put('/questionnaire/deny', {
  74. questionnaireOrderId: questionnaireOrder._id,
  75. });
  76. }
  77. catch (e) {
  78. logger.error(e);
  79. }
  80. }, [closeQuestionnaireModal, questionnaireOrder._id]);
  81. const questionnaireOrderTitle = lang === 'en_US' ? questionnaireOrder.title.en_US : questionnaireOrder.title.ja_JP;
  82. return (<Modal
  83. size="lg"
  84. isOpen={isOpened}
  85. toggle={closeBtnClickHandler}
  86. >
  87. <form onSubmit={submitHandler}>
  88. <ModalHeader
  89. tag="h4"
  90. toggle={closeBtnClickHandler}
  91. className="bg-primary text-light">
  92. <span>{t('questionnaire.give_us_feedback')}</span>
  93. </ModalHeader>
  94. <ModalBody className="my-4">
  95. <div className="container">
  96. <h3 className="grw-modal-head">{questionnaireOrderTitle}</h3>
  97. <div className="row mt-4">
  98. <div className="col-6"></div>
  99. <div className="col-1 p-0 pr-2 font-weight-bold text-center align-items-center">{t('questionnaire.no_answer')}</div>
  100. <div className="col-5 d-flex justify-content-between align-items-center pl-2">
  101. <span className="font-weight-bold">{t('questionnaire.disagree')}</span>
  102. <span className="font-weight-bold">{t('questionnaire.agree')}</span>
  103. </div>
  104. </div>
  105. {questionnaireOrder.questions?.map((question) => {
  106. return <Question question={question} inputNamePrefix={inputNamePrefix} key={question._id}/>;
  107. })}
  108. </div>
  109. </ModalBody>
  110. <ModalFooter>
  111. {currentUser?.admin
  112. && <a href="" className="mr-auto d-flex align-items-center"><i className="material-icons mr-1">settings</i>{t('questionnaire.settings')}</a>}
  113. <>
  114. <button type="button" className="btn btn-outline-secondary mr-3" onClick={skipBtnClickHandler}>{t('questionnaire.dont_show_again')}</button>
  115. <button type="submit" className="btn btn-primary">{t('questionnaire.answer')}</button>
  116. </>
  117. </ModalFooter>
  118. </form>
  119. </Modal>);
  120. };
  121. export default QuestionnaireModal;