diff --git a/ui-admin/src/forms/FormPreview.tsx b/ui-admin/src/forms/FormPreview.tsx index d3f0de8c1..c184293a7 100644 --- a/ui-admin/src/forms/FormPreview.tsx +++ b/ui-admin/src/forms/FormPreview.tsx @@ -11,7 +11,8 @@ import { Profile, surveyJSModelFromFormContent, useForceUpdate, - useI18n + useI18n, + getSurveyJsAnswerList } from '@juniper/ui-core' import { FormPreviewOptions } from './FormPreviewOptions' @@ -63,6 +64,7 @@ export const FormPreview = (props: FormPreviewProps) => {
{ proxyProfile: surveyModel.getVariable('proxyProfile'), isGovernedUser: surveyModel.getVariable('isGovernedUser') }} + forceUpdate={forceUpdate} onChange={({ ignoreValidation, showInvisibleElements, profile, proxyProfile, isGovernedUser }) => { surveyModel.ignoreValidation = ignoreValidation surveyModel.showInvisibleElements = showInvisibleElements diff --git a/ui-admin/src/forms/FormPreviewOptions.tsx b/ui-admin/src/forms/FormPreviewOptions.tsx index 8cd75ca9a..504d73a81 100644 --- a/ui-admin/src/forms/FormPreviewOptions.tsx +++ b/ui-admin/src/forms/FormPreviewOptions.tsx @@ -1,9 +1,13 @@ -import React from 'react' -import { Profile } from '@juniper/ui-core' -import InfoPopup from '../components/forms/InfoPopup' -import { TextInput } from '../components/forms/TextInput' +import React, { useEffect, useState } from 'react' +import { Answer, Profile } from '@juniper/ui-core' +import InfoPopup from 'components/forms/InfoPopup' +import { TextInput } from 'components/forms/TextInput' +import { Button } from 'components/forms/Button' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faClipboard } from '@fortawesome/free-solid-svg-icons' type FormPreviewOptions = { + answers: Answer[] ignoreValidation: boolean showInvisibleElements: boolean locale: string @@ -14,12 +18,30 @@ type FormPreviewOptions = { type FormPreviewOptionsProps = { value: FormPreviewOptions + forceUpdate: () => void onChange: (newValue: FormPreviewOptions) => void } /** Controls for configuring the form editor's preview tab. */ export const FormPreviewOptions = (props: FormPreviewOptionsProps) => { - const { value, onChange } = props + const { value, forceUpdate, onChange } = props + const [copyTriggered, setCopyTriggered] = useState(false) + + //this effect is necessary because the answers typed into the preview do not persist to the model + //until a component re-render is triggered, hence the forceUpdate call in the copy button onClick. + //however, just forcing a re-render is not enough. we also need to ensure that everything happens + //in the right order, so we copy the latest answers to the clipboard only after the re-render. + useEffect(() => { + if (copyTriggered) { + const filteredAnswers = value.answers.filter(a => a.questionStableId !== 'qualified') + filteredAnswers.map(a => { + // @ts-ignore + delete a.format + }) + navigator.clipboard.writeText(JSON.stringify(filteredAnswers)) + setCopyTriggered(false) + } + }, [copyTriggered]) return (
@@ -136,6 +158,12 @@ export const FormPreviewOptions = (props: FormPreviewOptionsProps) => { unboldLabel={true} value={value.proxyProfile?.familyName ?? ''} />
+
diff --git a/ui-admin/src/study/surveys/PreEnrollShortcutModal.tsx b/ui-admin/src/study/surveys/PreEnrollShortcutModal.tsx new file mode 100644 index 000000000..7dd557883 --- /dev/null +++ b/ui-admin/src/study/surveys/PreEnrollShortcutModal.tsx @@ -0,0 +1,139 @@ +import React, { useState, useEffect } from 'react' +import Api, { + VersionedForm +} from 'api/api' +import Modal from 'react-bootstrap/Modal' +import { Button, IconButton } from 'components/forms/Button' +import { StudyEnvContextT } from 'study/StudyEnvironmentRouter' +import { TextInput } from 'components/forms/TextInput' +import { faClipboard } from '@fortawesome/free-solid-svg-icons' +import { Checkbox } from 'components/forms/Checkbox' +import queryString from 'query-string' +import { useConfig } from 'providers/ConfigProvider' + +type ReferralSource = { + referringSite: string +} + +type PreEnrollQueryParams = { + skipPreEnroll?: boolean + referralSource?: ReferralSource + preFilledAnswers?: string +} + +/** component for selecting versions of a form */ +export default function PreEnrollShortcutModal({ + studyEnvContext, workingForm, onDismiss +}: { + studyEnvContext: StudyEnvContextT, + workingForm: VersionedForm, + onDismiss: () => void +}) { + const zoneConfig = useConfig() + + const currentPortalEnv = studyEnvContext.portal.portalEnvironments.find(env => + env.environmentName === studyEnvContext.currentEnv.environmentName) + const portalUrl= Api.getParticipantLink(currentPortalEnv!.portalEnvironmentConfig, zoneConfig.participantUiHostname, + studyEnvContext.portal.shortcode, currentPortalEnv!.environmentName) + + const [shortcutUrl, setShortcutUrl] = useState(portalUrl) + const [queryParams, setQueryParams] = useState({}) + const [skipPreEnroll, setSkipPreEnroll] = useState(false) + const [referralSource, setReferralSource] = useState() + const [preFilledAnswers, setPrefilledAnswers] = useState() + + useEffect(() => { + const params = { + ...queryParams, + referralSource: queryParams.referralSource ? JSON.stringify(queryParams.referralSource) : undefined, + preFilledAnswers: queryParams.preFilledAnswers ? JSON.stringify(queryParams.preFilledAnswers) : undefined + } + + const allParamsEmpty = Object.values(params).every(v => v === undefined) + setShortcutUrl(allParamsEmpty ? portalUrl : `${portalUrl}?${queryString.stringify(params)}`) + }, [queryParams]) + + return + + {workingForm.name} - shortcuts + + +
+

+ Participants can be directed to your website with pre-configured enrollment options. + After configuring the options below, copy and paste the URL to + direct participants to the portal with your selected options. +

+
+ { + const newReferralSource = e ? { referringSite: e } : undefined + setReferralSource(newReferralSource) + setQueryParams(prev => ({ + ...prev, + referralSource: newReferralSource + })) + }}/> +
+ +
+ { + setPrefilledAnswers(e) + setQueryParams(prev => ({ + ...prev, + preFilledAnswers: e + })) + }}/> +
+ + { + setSkipPreEnroll(!skipPreEnroll) + setQueryParams(prev => ({ + ...prev, + skipPreEnroll: !skipPreEnroll ? true : undefined + })) + }}/> + +
+
+ Configured Portal URL +
+ setShortcutUrl(e)}/> + navigator.clipboard.writeText(shortcutUrl || 'error: could not create url')}/> +
+
+
+ +
+ + + +
+} diff --git a/ui-admin/src/study/surveys/SurveyEditorView.tsx b/ui-admin/src/study/surveys/SurveyEditorView.tsx index c02eaacf7..199a2b0d8 100644 --- a/ui-admin/src/study/surveys/SurveyEditorView.tsx +++ b/ui-admin/src/study/surveys/SurveyEditorView.tsx @@ -19,6 +19,7 @@ import FormHistoryModal from './FormHistoryModal' import useLanguageSelectorFromParam from 'portal/languages/useLanguageSelector' import Select from 'react-select' import InfoPopup from 'components/forms/InfoPopup' +import PreEnrollShortcutModal from './PreEnrollShortcutModal' type SurveyEditorViewProps = { studyEnvContext: StudyEnvContextT @@ -52,6 +53,7 @@ const SurveyEditorView = (props: SurveyEditorViewProps) => { const [showLoadedDraftModal, setShowLoadedDraftModal] = useState(!!getDraft({ formDraftKey: FORM_DRAFT_KEY })) const [showDiscardDraftModal, setShowDiscardDraftModal] = useState(false) const [showAdvancedOptions, setShowAdvancedOptions] = useState(false) + const [showPreEnrollShortcuts, setShowPreEnrollShortcuts] = useState(false) const [showVersionSelector, setShowVersionSelector] = useState(false) const [showErrors, setShowErrors] = useState(false) @@ -228,6 +230,12 @@ const SurveyEditorView = (props: SurveyEditorViewProps) => { Download form JSON + { (currentForm as Survey).surveyType === 'PRE_ENROLL' &&
  • + +
  • } { showVersionSelector && { }} onDismiss={() => setShowAdvancedOptions(false)}/> } + { showPreEnrollShortcuts && setShowPreEnrollShortcuts(false)}/> + }