Coursemology/coursemology2

View on GitHub
client/app/bundles/course/reference-timelines/components/TimePopup/TimePopup.tsx

Summary

Maintainability
B
6 hrs
Test Coverage
import { Typography } from '@mui/material';
import moment from 'moment';
import {
  ItemWithTimeData,
  TimelineData,
} from 'types/course/referenceTimelines';

import { useAppDispatch } from 'lib/hooks/store';
import toast from 'lib/hooks/toast';
import useTranslation from 'lib/hooks/useTranslation';

import { useSetLastSaved } from '../../contexts';
import { createTime, deleteTime, updateTime } from '../../operations';
import translations from '../../translations';
import { DraftableTimeData } from '../../utils';
import SeriouslyAnchoredPopup from '../SeriouslyAnchoredPopup';

import TimePopupForm from './TimePopupForm';
import TimePopupTopBar from './TimePopupTopBar';

interface TimePopupProps {
  for?: Partial<DraftableTimeData>;
  assignedIn?: TimelineData;
  assignedTo?: ItemWithTimeData;
  anchorsOn?: HTMLElement;
  default?: boolean;
  gamified?: boolean;
  newTime?: boolean;
  onClose?: () => void;
}

const TimePopup = (props: TimePopupProps): JSX.Element => {
  const {
    anchorsOn: anchorElement,
    for: time,
    assignedIn: timeline,
    assignedTo: item,
  } = props;

  const { t } = useTranslation();

  const dispatch = useAppDispatch();

  const { startLoading, abortLoading, setLastSavedToNow } = useSetLastSaved();

  const handleCreateTime = (data: {
    startAt: moment.Moment;
    bonusEndAt?: moment.Moment;
    endAt?: moment.Moment;
  }): void => {
    if (!timeline || !item) return;

    startLoading();

    dispatch(
      createTime(timeline.id, item.id, {
        startAt: data.startAt.toISOString(),
        bonusEndAt: data.bonusEndAt?.toISOString(),
        endAt: data.endAt?.toISOString(),
      }),
    )
      .then(() => {
        props.onClose?.();
        setLastSavedToNow();
      })
      .catch((error) => {
        abortLoading();
        toast.error(error ?? t(translations.errorCreatingTime));
      });
  };

  const handleUpdateTime = (data: {
    startAt: moment.Moment;
    bonusEndAt?: moment.Moment;
    endAt?: moment.Moment;
  }): void => {
    if (!timeline || !item || !time?.id) return;

    startLoading();

    dispatch(
      updateTime(timeline.id, item.id, time.id, {
        startAt: data.startAt.toISOString(),
        bonusEndAt: data.bonusEndAt?.toISOString() ?? null,
        endAt: data.endAt?.toISOString() ?? null,
      }),
    )
      .then(() => {
        props.onClose?.();
        setLastSavedToNow();
      })
      .catch((error) => {
        abortLoading();
        toast.error(error ?? t(translations.errorUpdatingTime));
      });
  };

  const handleDeleteTime = (): void => {
    if (!timeline || !item || !time?.id) return;

    startLoading();

    dispatch(deleteTime(timeline.id, item.id, time.id))
      .then(() => {
        props.onClose?.();
        setLastSavedToNow();
      })
      .catch((error) => {
        abortLoading();
        toast.error(error ?? t(translations.errorDeletingTime));
      });
  };

  return (
    <SeriouslyAnchoredPopup
      anchorEl={anchorElement}
      anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
      classes={{
        paper: 'w-[36rem] shadow-xl border border-solid border-neutral-200',
      }}
      elevation={0}
      onClose={props.onClose}
      open={Boolean(anchorElement)}
    >
      <TimePopupTopBar
        default={timeline?.default}
        new={props.newTime}
        onClickClose={props.onClose}
        onClickDelete={handleDeleteTime}
      />

      <main className="space-y-4 px-4 pb-4">
        <section>
          <Typography className="text-neutral-500" variant="caption">
            {props.newTime
              ? t(translations.assigningToItem)
              : t(translations.assignedToItem)}
          </Typography>

          <Typography className="line-clamp-3">{item?.title}</Typography>
        </section>

        <section>
          <Typography className="text-neutral-500" variant="caption">
            {props.newTime
              ? t(translations.assigningInTimeline)
              : t(translations.assignedInTimeline)}
          </Typography>

          <Typography className="line-clamp-3">{timeline?.title}</Typography>
        </section>

        <TimePopupForm
          for={time}
          new={props.newTime}
          onSubmit={props.newTime ? handleCreateTime : handleUpdateTime}
          showsBonus={props.gamified}
        />
      </main>
    </SeriouslyAnchoredPopup>
  );
};

export default TimePopup;