Skip to content
Snippets Groups Projects
index.tsx 14.06 KiB
import { useEffect, useState } from 'react'
import format from 'date-fns/format'
import { addDays } from 'date-fns/fp'
import {
  Alert,
  Button,
  Table,
  TableBody,
  TextField,
  Typography,
} from '@mui/material'
import TableHeadMui from '@mui/material/TableHead'
import { styled } from '@mui/system'
import TableContainerMui from '@mui/material/TableContainer'
import TableRowMui from '@mui/material/TableRow'
import TableCellMui from '@mui/material/TableCell'

import Page from 'components/page'
import { Guest, Role } from 'interfaces'
import { useTranslation } from 'react-i18next'
import { useNavigate, useParams } from 'react-router-dom'
import SponsorInfoButtons from 'routes/components/sponsorInfoButtons'
import { DatePicker } from '@mui/lab'
import { Controller, SubmitHandler, useForm } from 'react-hook-form'
import { getRoleName, getRoleOuName, submitJsonOpts } from 'utils'
import { useFeatureContext } from 'contexts/featureContext'
import ConfirmDialog from '../../../../components/confirmDialog'
import useOus from '../../../../hooks/useOus'

interface GuestRoleInfoProps {
  guest: Guest
  reloadGuest: () => void
  reloadGuests: () => void
}

const endPeriodPost = (
  id: string,
  data: { end_date: Date },
  reloadGuests: () => void
) => {
  const payload = {
    end_date: format(data.end_date as Date, 'yyyy-MM-dd'),
  }
  console.log('submitting', JSON.stringify(payload))

  fetch(`/api/ui/v1/role/${id}`, submitJsonOpts('PATCH', payload))
    .then((res) => {
      if (!res.ok) {
        return null
      }
      return res.text()
    })
    .then((result) => {
      if (result !== null) {
        console.log('result', result)
        reloadGuests()
      }
    })
    .catch((error) => {
      console.log('error', error)
    })
}

type GuestRoleInfoParams = {
  pid: string
  id: string
}

type RoleFormData = {
  start_date: Date
  end_date: Date
  comments: string
  contact_person_unit: string
}
const TableHeadCell = styled(TableCellMui)({
  fontWeight: 'bold',
  border: 'none',
})

const TableHead = styled(TableHeadMui)(({ theme }) => ({
  borderTop: '0',
  borderLeft: '0',
  borderRight: '0',
  borderBottom: '3px solid',
  borderColor: theme.palette.secondary.main,
  borderRadius: '0',
}))

const TableRow = styled(TableRowMui)({
  borderTop: '0',
  borderLeft: '0',
  borderRight: '0',
  borderBottom: '0px solid',
  borderColor: 'black',
  borderRadius: '0',
})

const TableCell = styled(TableCellMui)({
  borderBottom: 'none',
})

const TableContainer = styled(TableContainerMui)({
  marginBottom: '1rem',
  borderRadius: '1%',
  borderStyle: 'solid',
  borderColor: 'black',
  borderWidth: '0.125rem',
  boxShadow: 'none',
  paddingLeft: '3.125rem',
  paddingRight: '3.125rem',
  paddingTop: '1.5625rem',
  paddingBottom: '1.5625rem',
})

export default function GuestRoleInfo({
  guest,
  reloadGuest,
  reloadGuests,
}: GuestRoleInfoProps) {
  const { pid, id } = useParams<GuestRoleInfoParams>()
  const { ous } = useOus()
  const [t, i18n] = useTranslation('common')
  const { displayContactAtUnit, displayComment } = useFeatureContext()
  const navigate = useNavigate()
  const [showRoleChange, setShowRoleChange] = useState<Boolean>(false)
  const [showRoleChangeFailed, setShowRoleChangeFailed] =
    useState<Boolean>(false)
  const [badEndDate, setBadEndDate] = useState<Boolean>(false)
  const [role, setRole] = useState<Role>({
    id: '',
    name_nb: '',
    name_en: '',
    ou_nb: '',
    ou_en: '',
    start_date: new Date(),
    end_date: new Date(),
    max_days: 365,
    contact_person_unit: null,
    comments: null,
    sponsor_name: '',
    ou_id: -1,
  })
  // Prepare min and max date values
  const today = new Date()
  const todayPlusMaxDays = addDays(role.max_days)(today)

  // Make a function for use with onClick of the end role button
  const endPeriod = (role_id: string) => () => {
    // Set the role to have ended yesterday so that the change is immediate.
    // If the role was set to end today, the change will not be active
    // until the next date since the end date is inclusive
    const newEndDate = addDays(-1)(today)
    if (role.start_date !== undefined && role.start_date > newEndDate) {
      setBadEndDate(true)
      setShowRoleChange(false)
      setShowRoleChangeFailed(false)
    } else {
      role.end_date = newEndDate
      endPeriodPost(role_id, { end_date: newEndDate }, reloadGuests)
      // Go back to guest overview page
      navigate(`/sponsor/guest/${guest.pid}`, {
        state: {
          cancelledRoleName:
            i18n.language === 'en' ? role.name_en : role.name_nb,
        },
      })
    }
  }

  const {
    register,
    control,
    handleSubmit,
    setValue,
    reset,
    formState: { isDirty, isValid },
  } = useForm<RoleFormData>({ mode: 'onTouched' })

  // A sponsor can only edit roles belonging to departments where he is a sponsor.
  // Look at the unit where the role is registered and see if that unit is present
  // in the list of units where a sponsor can assign roles to determine if
  // he can edit this role
  const allowEdit =
    ous === undefined ? false : ous.some((value) => value.id === role.ou_id)

  // Submit function for the save button
  const submit: SubmitHandler<RoleFormData> = (data) => {
    const payload: {
      start_date?: string
      end_date: string
      contact_person_unit?: string
      comments?: string
    } = {
      end_date: format(data.end_date as Date, 'yyyy-MM-dd'),
      start_date: format(data.start_date as Date, 'yyyy-MM-dd'),
      contact_person_unit: data.contact_person_unit,
      comments: data.comments,
    }

    console.log('submitting', JSON.stringify(payload))
    fetch(`/api/ui/v1/role/${id}`, submitJsonOpts('PATCH', payload))
      .then((res) => {
        if (!res.ok) {
          return null
        }
        return res.text()
      })
      .then((result) => {
        if (result !== null) {
          setShowRoleChange(true)
          setBadEndDate(false)
          setShowRoleChangeFailed(false)
          console.log('result', result)
        } else {
          setShowRoleChangeFailed(true)
          setShowRoleChange(false)
          setBadEndDate(false)
        }
        // Reload the guest so that the information on the overview page is updated
        reloadGuests()
        reloadGuest()
        // A submit does not clear the dirty state, so resetting values in the form
        // to the values just submitted to clear the dirty-flag
        reset(data)
      })
      .catch((error) => {
        setShowRoleChangeFailed(true)
        console.log('error', error)
      })
  }

  const onSubmit = handleSubmit(submit)
  const [showEndRoleConfirmationDialog, setShowEndRoleConfirmationDialog] =
    useState(false)

  // Find the role info relevant for this page
  const getRoleInfo = () => {
    const roleInfo = guest.roles.filter((ro) => ro.id.toString() === id)[0]
    if (roleInfo) {
      setRole(roleInfo)
      // Set values of date fields to current dates if role exists
      setValue('end_date', roleInfo.end_date)
      // The start date can be undefined for existing roles that have been imported
      if (roleInfo.start_date !== undefined) {
        setValue('start_date', roleInfo.start_date)
      }
    }
  }
  useEffect(() => {
    getRoleInfo()
  }, [guest])

  return (
    <Page>
      <SponsorInfoButtons
        to={`/sponsor/guest/${pid}`}
        name={`${guest.first} ${guest.last}`}
      />
      {allowEdit && (
        <>
          <Typography sx={{ marginBottom: '1rem' }} variant="h2">
            {t('sponsor.roleInfoHead')}
          </Typography>
          <Typography sx={{ marginBottom: '1rem' }} variant="body1">
            {t('sponsor.roleInfoText')}
          </Typography>
        </>
      )}
      {showRoleChange && (
        <Alert severity="success" sx={{ marginBottom: '1rem' }}>
          {t('guestInfo.roleChanged')}
        </Alert>
      )}
      {showRoleChangeFailed && (
        <Alert severity="error" sx={{ marginBottom: '1rem' }}>
          {t('error.changeRoleFailed')}
        </Alert>
      )}
      {badEndDate && (
        <Alert
          sx={{ fontSize: '1rem', marginTop: '1rem', marginBottom: '1rem' }}
          severity="error"
        >
          {t('error.badEndDate')}
        </Alert>
      )}
      <form onSubmit={onSubmit}>
        <TableContainer>
          <Table sx={{ minWidth: 650 }} aria-label="simple table">
            <TableHead sx={{ backgroundColor: 'none' }}>
              <TableRow>
                <TableHeadCell align="left">
                  {t('sponsor.details')}
                </TableHeadCell>
              </TableRow>
            </TableHead>
            <TableBody>
              <TableRow>
                <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                  {t('common:role')}
                </TableCell>
                <TableCell>{getRoleName(role)}</TableCell>
              </TableRow>
              <TableRow>
                <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                  {t('common:period')}
                </TableCell>
                <TableCell align="left">
                  <Controller
                    name="start_date"
                    control={control}
                    defaultValue={role.start_date}
                    render={({ field: { onChange, value } }) => (
                      <DatePicker
                        mask="____-__-__"
                        label={t('input.roleStartDate')}
                        value={value}
                        maxDate={todayPlusMaxDays}
                        inputFormat="yyyy-MM-dd"
                        onChange={onChange}
                        renderInput={(params) => <TextField {...params} />}
                        disabled={!allowEdit}
                      />
                    )}
                  />
                </TableCell>
                <TableCell align="left">
                  <Controller
                    name="end_date"
                    control={control}
                    defaultValue={role.end_date}
                    render={({ field: { onChange, value } }) => (
                      <DatePicker
                        mask="____-__-__"
                        label={t('input.roleEndDate')}
                        maxDate={todayPlusMaxDays}
                        value={value}
                        inputFormat="yyyy-MM-dd"
                        onChange={onChange}
                        renderInput={(params) => <TextField {...params} />}
                        disabled={!allowEdit}
                      />
                    )}
                  />
                </TableCell>
              </TableRow>
              <TableRow>
                <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                  {t('common:ou')}
                </TableCell>
                <TableCell align="left">{getRoleOuName(role)}</TableCell>
                <TableCell align="left" />
              </TableRow>
              <TableRow>
                <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                  {t('common:host')}
                </TableCell>
                <TableCell align="left">{role.sponsor_name}</TableCell>
                <TableCell align="left" />
              </TableRow>
              {allowEdit && displayContactAtUnit && (
                <TableRow>
                  <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                    {t('sponsor.contactPerson')}
                  </TableCell>
                  <TableCell>
                    <TextField
                      id="contact_person"
                      defaultValue={role.contact_person_unit}
                      label={`${t('input.contactPersonUnit')}`}
                      {...register(`contact_person_unit`)}
                    />
                  </TableCell>
                </TableRow>
              )}
              {allowEdit && displayComment && (
                <TableRow>
                  <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                    {t('input.comment')}
                  </TableCell>
                  <TableCell>
                    <TextField
                      id="comment"
                      label={`${t('input.comment')}`}
                      multiline
                      rows={2}
                      defaultValue={role.comments}
                      {...register(`comments`)}
                    />
                  </TableCell>
                </TableRow>
              )}
              {!allowEdit && displayContactAtUnit && (
                <TableRow>
                  <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                    {t('sponsor.contactPerson')}
                  </TableCell>
                  <TableCell align="left">{role.contact_person_unit}</TableCell>
                  <TableCell align="left" />
                </TableRow>
              )}
              {!allowEdit && displayComment && (
                <TableRow>
                  <TableCell align="left" sx={{ fontWeight: 'bold' }}>
                    {t('input.comment')}
                  </TableCell>
                  <TableCell align="left">{role.comments}</TableCell>
                  <TableCell align="left" />
                </TableRow>
              )}
            </TableBody>
          </Table>
        </TableContainer>
        <Button
          variant="contained"
          color="success"
          type="submit"
          disabled={!isDirty || !isValid || !allowEdit}
        >
          {t('button.save')}
        </Button>{' '}
        {/* If the role has already expired the role end button is disabled */}
        <Button
          aria-label={t('sponsor.endNow')}
          color="primary"
          disabled={role.end_date < today || !allowEdit}
          onClick={() => setShowEndRoleConfirmationDialog(true)}
        >
          {t('sponsor.endNow')}
        </Button>
        {typeof id === 'string' ? (
          <ConfirmDialog
            title={t('endRoleDialog.title')}
            open={showEndRoleConfirmationDialog}
            setOpen={setShowEndRoleConfirmationDialog}
            onConfirm={endPeriod(id)}
          >
            {t('endRoleDialog.text')}
          </ConfirmDialog>
        ) : (
          <></>
        )}
      </form>
    </Page>
  )
}