| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- import OLButton from '@/shared/components/ol/ol-button'
- import OLForm from '@/shared/components/ol/ol-form'
- import OLFormCheckbox from '@/shared/components/ol/ol-form-checkbox'
- import OLFormGroup from '@/shared/components/ol/ol-form-group'
- import OLIconButton from '@/shared/components/ol/ol-icon-button'
- import { OLToast } from '@/shared/components/ol/ol-toast'
- import { OLToastContainer } from '@/shared/components/ol/ol-toast-container'
- import useTutorial from '@/shared/hooks/promotions/use-tutorial'
- import { memo, useCallback, useEffect, useMemo, useState } from 'react'
- import { sendMB } from '@/infrastructure/event-tracking'
- import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
- import { useTranslation } from 'react-i18next'
- import { useTutorialContext } from '@/shared/context/tutorial-context'
- type EditorSurveyPage = 'ease-of-use' | 'meets-my-needs' | 'thank-you'
- export default memo(function EditorSurvey() {
- return (
- <OLToastContainer className="editor-survey-toast">
- <EditorSurveyContent />
- </OLToastContainer>
- )
- })
- const TUTORIAL_KEY = 'editor-popup-ux-survey'
- const EditorSurveyContent = () => {
- const [easeOfUse, setEaseOfUse] = useState<number | null>(null)
- const [meetsMyNeeds, setMeetsMyNeeds] = useState<number | null>(null)
- const [page, setPage] = useState<EditorSurveyPage>('ease-of-use')
- const { inactiveTutorials } = useTutorialContext()
- const hasCompletedSurvey = inactiveTutorials.includes(TUTORIAL_KEY)
- const newEditor = useIsNewEditorEnabled()
- const { t } = useTranslation()
- const {
- tryShowingPopup: tryShowingSurvey,
- showPopup: showSurvey,
- dismissTutorial: dismissSurvey,
- completeTutorial: completeSurvey,
- } = useTutorial(TUTORIAL_KEY, {
- name: TUTORIAL_KEY,
- })
- useEffect(() => {
- if (!hasCompletedSurvey) {
- tryShowingSurvey()
- }
- }, [hasCompletedSurvey, tryShowingSurvey])
- const onSubmit = useCallback(() => {
- sendMB('editor-survey-submit', {
- easeOfUse,
- meetsMyNeeds,
- newEditor,
- })
- setPage('thank-you')
- completeSurvey({ event: 'promo-click', action: 'complete' })
- }, [easeOfUse, meetsMyNeeds, completeSurvey, newEditor])
- if (!showSurvey && page !== 'thank-you') {
- return null
- }
- if (page === 'ease-of-use') {
- return (
- <OLToast
- className="editor-survey-question-toast"
- content={
- <EditorSurveyQuestion
- questionText={t('overleaf_is_easy_to_use')}
- name="ease-of-use"
- buttonText={t('next')}
- onButtonClick={() => setPage('meets-my-needs')}
- value={easeOfUse}
- onValueChange={setEaseOfUse}
- onDismiss={dismissSurvey}
- />
- }
- type="info"
- />
- )
- }
- if (page === 'meets-my-needs') {
- return (
- <OLToast
- className="editor-survey-question-toast"
- content={
- <EditorSurveyQuestion
- questionText={t('overleafs_functionality_meets_my_needs')}
- name="meets-my-needs"
- buttonText={t('submit_title')}
- onButtonClick={onSubmit}
- value={meetsMyNeeds}
- onValueChange={setMeetsMyNeeds}
- onDismiss={dismissSurvey}
- />
- }
- type="info"
- />
- )
- }
- return <OLToast type="info" content={t('thank_you')} autoHide delay={3000} />
- }
- const EditorSurveyQuestion = ({
- onDismiss,
- name,
- questionText,
- buttonText,
- onButtonClick,
- value,
- onValueChange,
- }: {
- onDismiss: () => void
- name: string
- questionText: string
- buttonText: string
- onButtonClick: () => void
- value: number | null
- onValueChange: (newValue: number) => void
- }) => {
- const { t } = useTranslation()
- const options = useMemo(
- () => [
- {
- value: 1,
- description: t('strongly_disagree'),
- },
- {
- value: 2,
- description: t('disagree'),
- },
- {
- value: 3,
- description: t('neither_agree_nor_disagree'),
- },
- {
- value: 4,
- description: t('agree'),
- },
- {
- value: 5,
- description: t('strongly_agree'),
- },
- ],
- [t]
- )
- const onChange = useCallback(
- (event: React.ChangeEvent<HTMLInputElement>) => {
- const newValue = event.target.value
- if (newValue) {
- onValueChange(Number(newValue))
- }
- },
- [onValueChange]
- )
- return (
- <div className="editor-survey-question">
- <div className="editor-survey-question-top-line">
- <div>{t('your_feedback_matters_answer_two_quick_questions')}</div>
- <OLIconButton
- variant="ghost"
- size="sm"
- icon="close"
- accessibilityLabel={t('close')}
- onClick={onDismiss}
- />
- </div>
- <div>
- <label className="editor-survey-question-label" htmlFor={name}>
- {questionText}
- </label>
- <OLForm className="editor-survey-question-form">
- <OLFormGroup className="editor-survey-question-options">
- {options.map(({ value: optionValue, description }) => (
- <OLFormCheckbox
- key={optionValue}
- name={name}
- type="radio"
- value={optionValue}
- id={optionValue.toString()}
- label={optionValue}
- checked={optionValue === value}
- onChange={onChange}
- aria-label={`${optionValue} ${description}`}
- />
- ))}
- </OLFormGroup>
- </OLForm>
- <div className="editor-survey-option-labels">
- <div>{t('strongly_disagree')}</div>
- <div>{t('strongly_agree')}</div>
- </div>
- </div>
- <OLButton
- size="sm"
- variant="secondary"
- onClick={onButtonClick}
- disabled={!value}
- >
- {buttonText}
- </OLButton>
- </div>
- )
- }
|