Skip to content
Snippets Groups Projects
CustomPopup.tsx 7.8 KiB
Newer Older
import {
  Button,
  FormControl,
  FormControlLabel,
  FormGroup,
  NativeSelect,
  Switch,
  TextField,
} from '@mui/material'
import { DateTime } from 'luxon'
import React, { useEffect, useState } from 'react'
Bastien DUMONT's avatar
Bastien DUMONT committed
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
import { Link } from 'react-router-dom'
Bastien DUMONT's avatar
Bastien DUMONT committed
import { getAxiosXSRFHeader } from '../../axios.config'
import { useCustomPopup } from '../../hooks/useCustomPopup'
import { usePartnersIssue } from '../../hooks/usePartnersIssue'
import { useWhoAmI } from '../../hooks/useWhoAmI'
Bastien DUMONT's avatar
Bastien DUMONT committed
import { ICustomPopup, PopupDuration } from '../../models/customPopup.model'
import {
Bastien DUMONT's avatar
Bastien DUMONT committed
  Option,
  durationEnum,
  durationType,
Bastien DUMONT's avatar
Bastien DUMONT committed
  mapDuration,
Bastien DUMONT's avatar
Bastien DUMONT committed
} from '../../models/durationOptions.model'
import { convertStringToEditorState } from '../../utils/editorStateManagement'
import { getFilenameFromPath } from '../../utils/imagesUrlsGetter'
import ImagePicker from '../ImagePicker/ImagePicker'
Bastien DUMONT's avatar
Bastien DUMONT committed
import Loader from '../Loader/Loader'
import CustomEditor from '../Newsletter/CustomEditor'
import { links } from '../Routes/Router'
Bastien DUMONT's avatar
Bastien DUMONT committed
import './popups.scss'
import { PopupWrapper } from './Wrapper'
  {
    value: durationEnum.hours,
    label: 'Heures',
  },
  {
    value: durationEnum.days,
    label: 'Jours',
  },
  {
    value: durationEnum.infinite,
    label: 'Indéterminée',
  },
]

export const CustomPopup: React.FC = () => {
  const { partnersIssue } = usePartnersIssue()
  const { customPopup } = useCustomPopup()

  const isPartnerNotificationOn = Boolean(
    partnersIssue.data &&
      (partnersIssue.data.enedis_failure ||
        partnersIssue.data.egl_failure ||
        partnersIssue.data.grdf_failure)
  )

  if (isPartnerNotificationOn) {
    return (
      <PopupWrapper>
        <p className="singlePopupWarning">
          La pop-up personnalisée ne peut pas être activée en même temps que la{' '}
          <Link to={links.partnersIssue.path}>
            pop-up maintenance des partenaires
          </Link>
        </p>
      </PopupWrapper>
    )
  }

  if (customPopup.isLoading) {
    return <Loader />
  }

  if (customPopup.error) {
    return <p>Une erreur est survenue</p>
  }

  if (customPopup.data) {
    return (
      <PopupWrapper>
        <CustomPopupForm initialData={customPopup.data} />
      </PopupWrapper>
    )
  }
}

export const CustomPopupForm = ({
  initialData,
}: {
  initialData: ICustomPopup
}) => {
  const { data: user } = useWhoAmI()
  const { saveCustomPopup } = useCustomPopup()

  const isPopupOutdated = (date: string) =>
    DateTime.local() >= DateTime.fromISO(date)

  const isOutdated = isPopupOutdated(initialData.endDate)
  const isEnabled = isOutdated ? false : initialData.popupEnabled
Bastien DUMONT's avatar
Bastien DUMONT committed
  const [popupDuration, setPopupDuration] = useState<PopupDuration>({
    type: durationEnum.infinite,
    duration: 5,
  })
Bastien DUMONT's avatar
Bastien DUMONT committed
  const [customPopup, setCustomPopup] = useState<ICustomPopup>({
    popupEnabled: isEnabled,
    title: initialData.title,
    image: initialData.image || 'bullhorn',
    description: initialData.description,
    endDate: isOutdated
      ? DateTime.local().plus({ days: 365 }).toISO()
      : initialData.endDate,
  const toggleCustomPopup = (value: boolean): void => {
    setCustomPopup(prev => ({
      ...prev,
      popupEnabled: value,
    }))
  const handlePopupChange = (
    value: string,
    field: 'title' | 'image' | 'description'
  ) => {
Bastien DUMONT's avatar
Bastien DUMONT committed
    setCustomPopup(prev => ({
Bastien DUMONT's avatar
Bastien DUMONT committed
      ...prev,
      [field]: value,
Bastien DUMONT's avatar
Bastien DUMONT committed
    }))
  }

  const handleSave = async (): Promise<void> => {
    if (user) {
      saveCustomPopup(
        {
          ...customPopup,
          image: getFilenameFromPath(customPopup.image),
        },
Bastien DUMONT's avatar
Bastien DUMONT committed
        getAxiosXSRFHeader(user.xsrftoken)
      )
    }
  }

  const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
Bastien DUMONT's avatar
Bastien DUMONT committed
    setPopupDuration(prev => ({
      ...prev,
      type: event.target.value as durationType,
    }))
  }

Bastien DUMONT's avatar
Bastien DUMONT committed
  /** Handles duration change */
  useEffect(() => {
    const now = DateTime.local()
    const newDate =
      popupDuration.type !== durationEnum.infinite
        ? now.plus({ [popupDuration.type]: popupDuration.duration })
        : now.plus({ years: 1 })

Bastien DUMONT's avatar
Bastien DUMONT committed
    setCustomPopup(prev => ({
      ...prev,
      endDate: newDate.toISO() ?? '',
    }))
  }, [popupDuration])

  /**
   * Returns "Popup expirée" OR "Temps restant : ..."
   */
  const getRemainingDuration = (date: string) => {
    if (isOutdated) {
      return <p className="endDate">Popup expirée</p>
    }
    return (
      <p className="endDate">
        Temps d&apos;activation restant :<br />
        {DateTime.fromISO(date)
          .diffNow(['days', 'hours', 'minutes', 'seconds'])
          .set({ second: 0 })
          .toHuman()}
      </p>
    )
  }

Bastien DUMONT's avatar
Bastien DUMONT committed
  return (
    <>
      <div className="customPopup">
        <h3>Affichage de pop-up personnalisée</h3>
        <FormGroup style={{ flexDirection: 'row' }}>
          <FormControlLabel
            label="Pop-up active"
            labelPlacement="top"
            control={
              <Switch
                checked={customPopup.popupEnabled}
                onChange={event => toggleCustomPopup(event.target.checked)}
              />
            }
          />
          {customPopup.popupEnabled &&
            initialData.endDate &&
            getRemainingDuration(initialData.endDate)}
        </FormGroup>
        <div className="popupEndDate">
          <h4>Durée</h4>
          <div>
            <FormControl style={{ flexDirection: 'row', gap: '1rem' }}>
              <NativeSelect
                inputProps={{
                  name: 'age',
                  id: 'uncontrolled-native',
                }}
                onChange={event => handleSelectChange(event)}
                value={popupDuration.type}
              >
                {OPTIONS.map(option => (
                  <option key={option.value} value={option.value}>
                    {option.label}
                  </option>
                ))}
              </NativeSelect>
              {popupDuration.type !== 'infinite' && (
Bastien DUMONT's avatar
Bastien DUMONT committed
                <TextField
                  style={{ width: '6rem' }}
                  inputProps={{
                    inputMode: 'numeric',
                    pattern: '[0-9]*',
                  }}
                  id="outlined-number"
                  type="number"
                  label={mapDuration[popupDuration.type]}
                  InputLabelProps={{
                    shrink: true,
                  }}
                  value={popupDuration.duration}
                  onChange={e =>
                    setPopupDuration(prev => ({
                      ...prev,
                      duration: Number(e.target.value),
                    }))
              )}
            </FormControl>
          </div>
        </div>
        <h4>Image</h4>
        <div>
          <ImagePicker
            imageURL={`/assets/ecogesture/${customPopup.image}.png`}
            handleChange={handlePopupChange}
          />
        </div>

        <h4>Contenu</h4>
        <div className="popupTitle">
          <TextField
            type="text"
            placeholder="Titre de la popup"
            fullWidth
            label="Titre"
            value={customPopup.title}
            onChange={event => handlePopupChange(event.target.value, 'title')}
          />
        </div>

        <div className="popupDescription">
          <CustomEditor
            baseState={convertStringToEditorState(customPopup.description)}
            handleChange={value => handlePopupChange(value, 'description')}
            type="custom_popup"
          />
        </div>
      </div>
      <div className="buttons">
        <Button variant="outlined" onClick={() => setCustomPopup(initialData)}>
          Annuler
        </Button>
        <Button onClick={handleSave} disabled={!user}>
          Sauvegarder
        </Button>