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 } 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 [showPeriodChange, setShowPeriodChange] = useState<Boolean>(false) const [showPeriodChangeFailed, setShowPeriodChangeFailed] = 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) } 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 { 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 } = { end_date: format(data.end_date as Date, 'yyyy-MM-dd'), start_date: format(data.start_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) { setShowPeriodChange(true) console.log('result', result) } // 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) => { setShowPeriodChangeFailed(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> </> )} {showPeriodChange && ( <Alert severity="success" sx={{ marginBottom: '1rem' }}> {t('guestInfo.periodChanged')} </Alert> )} {showPeriodChangeFailed && ( <Alert severity="error" sx={{ marginBottom: '1rem' }}> {t('error.changePeriodFailed')} </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> {displayContactAtUnit && ( <TableRow> <TableCell align="left" sx={{ fontWeight: 'bold' }}> {t('sponsor.contactPerson')} </TableCell> <TableCell align="left">{role.contact_person_unit}</TableCell> <TableCell align="left" /> </TableRow> )} {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> ) }