diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index ac2696ce7f38a425b78093dd6995b0535391c949..8ce73823bc3069b9a0d3dc4cee1b10f0614c56b1 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -39,6 +39,10 @@ "modifyEnd": "Change end date", "endNow": "End role" }, + "endRoleDialog": { + "title": "End role", + "text": "Ending the role will cause the guest to lose access if there are no other active roles registered. Are you sure you want to end the role?" + }, "guestInfo": { "contactInfo": "Contact information", "contactInfoBody": "A guest is only considered active if at least one identification number has been verified.", diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index d52017d7623683cc7ab78538e047ed910757b7bf..204f50746421e8dc8c0b042d1017ab7f3e4224b4 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -39,6 +39,10 @@ "modifyEnd": "Endre sluttdato", "endNow": "Avslutt rolle" }, + "endRoleDialog": { + "title": "Avslutt rolle", + "text": "Dersom du avslutter rollen vil gjesten miste tilgang om det ikke eksisterer andre aktive roller. Vil du avslutte rollen?" + }, "guestInfo": { "contactInfo": "Kontaktinformasjon", "contactInfoBody": "For at en gjest skal regnes som aktiv må minst ett identifikasjonsnummer være godkjent.", diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index 21d8b5546127bb6f6df6c093e61450a9869b255d..42a7248ef2d6316f87687d951eb16ddc22c221e5 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -39,6 +39,10 @@ "modifyEnd": "Endre sluttdato", "endNow": "Avslutt rolle" }, + "endRoleDialog": { + "title": "Avslutt rolle", + "text": "Dersom du avsluttar rolla vil gjesten miste tilgang om det ikkje eksisterer andre aktive roller. Vil du avslutte rolla?" + }, "guestInfo": { "contactInfo": "Kontaktinformasjon", "contactInfoBody": "For at ein gjest skal reknast som aktiv må minst ett identifikasjonsnummer vere godkjend.", diff --git a/frontend/src/routes/sponsor/guest/guestRoleInfo/index.test.tsx b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cd5098c9513ec4c03b6a2907a1efd09b04920887 --- /dev/null +++ b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.test.tsx @@ -0,0 +1,68 @@ +import React from 'react' +import { render, screen } from 'test-utils' +import AdapterDateFns from '@mui/lab/AdapterDateFns' +import { LocalizationProvider } from '@mui/lab' +// eslint-disable-next-line import/no-extraneous-dependencies +import { createMemoryHistory } from 'history' +import { Guest } from 'interfaces' +import parse from 'date-fns/parse' +import { Router } from 'react-router-dom' +import GuestRoleInfo from './index' +import { waitFor } from '../../../../test-utils' + +const guest: Guest = { + pid: '100', + first: 'Test', + last: 'Tester', + email: 'test@example.org', + mobile: '(+47)97543991', + fnr: null, + passport: null, + feide_id: null, + active: true, + registered: true, + verified: true, + roles: [ + { + id: '200', + ou_en: 'English organizational unit name', + ou_nb: 'Norsk navn organisasjonsenhet', + name_en: 'Guest role', + name_nb: 'Gjesterolle', + start_date: parse('2021-08-10', 'yyyy-MM-dd', new Date()), + end_date: parse('2021-08-16', 'yyyy-MM-dd', new Date()), + contact_person_unit: 'Test contact person', + max_days: 100, + comments: 'Test comment', + }, + { + id: '201', + ou_en: 'English organizational unit name', + ou_nb: 'Norsk navn organisasjonsenhet', + name_en: 'Test role', + name_nb: 'Testrolle', + start_date: parse('2021-09-06', 'yyyy-MM-dd', new Date()), + end_date: parse('2021-09-20', 'yyyy-MM-dd', new Date()), + contact_person_unit: 'Test contact person', + max_days: 100, + comments: 'Test comment', + }, + ], +} + +test('Button state correct on load', async () => { + const history = createMemoryHistory() + + render( + <Router history={history}> + <LocalizationProvider dateAdapter={AdapterDateFns}> + <GuestRoleInfo guest={guest} reloadGuest={() => {}} /> + </LocalizationProvider> + </Router> + ) + + await waitFor(() => { + expect(screen.getByText('button.save')).toBeDisabled() + expect(screen.getByText('sponsor.endNow')).toBeEnabled() + }) +}) diff --git a/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx index 50506eccfe7ad8ab6880a0314e55084becc6ba5a..deea70407c726efbdd1266dfcb12678ba585c0f9 100644 --- a/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx +++ b/frontend/src/routes/sponsor/guest/guestRoleInfo/index.tsx @@ -11,16 +11,19 @@ import TableCellMui from '@mui/material/TableCell' import Page from 'components/page' import { Guest, Role } from 'interfaces' import { useTranslation } from 'react-i18next' -import { useParams } from 'react-router-dom' +import { useHistory, 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' interface GuestRoleInfoProps { guest: Guest + reloadGuest: () => void } + const endPeriodPost = (id: string, data: { end_date: Date }) => { const payload = { end_date: format(data.end_date as Date, 'yyyy-MM-dd'), @@ -93,10 +96,14 @@ const TableContainer = styled(TableContainerMui)({ paddingBottom: '1.5625rem', }) -export default function GuestRoleInfo({ guest }: GuestRoleInfoProps) { +export default function GuestRoleInfo({ + guest, + reloadGuest, +}: GuestRoleInfoProps) { const { pid, id } = useParams<GuestRoleInfoParams>() const [t] = useTranslation('common') const { displayContactAtUnit, displayComment } = useFeatureContext() + const history = useHistory() const [role, setRole] = useState<Role>({ id: '', name_nb: '', @@ -114,11 +121,25 @@ export default function GuestRoleInfo({ guest }: GuestRoleInfoProps) { const todayPlusMaxDays = addDays(role.max_days)(today) // Make a function for use with onClick of the end role button - const endPeriod = () => () => { - role.end_date = today - endPeriodPost(id, { end_date: today }) + const endPeriod = () => { + // 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) + role.end_date = newEndDate + endPeriodPost(id, { end_date: newEndDate }) + // Go back to guest overview page + history.push(`/sponsor/guest/${guest.pid}`) } + const { + control, + handleSubmit, + setValue, + reset, + formState: { isDirty, isValid }, + } = useForm<RoleFormData>({ mode: 'onChange' }) + // Submit function for the save button const submit: SubmitHandler<RoleFormData> = (data) => { const payload: { start_date?: string; end_date: string } = { @@ -138,19 +159,20 @@ export default function GuestRoleInfo({ guest }: GuestRoleInfoProps) { if (result !== null) { console.log('result', result) } + // Reload the guest so that the information on the overview page is updated + 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) => { console.log('error', error) }) } - const { - control, - handleSubmit, - setValue, - formState: { isDirty, isValid }, - } = useForm<RoleFormData>({ mode: 'onChange' }) const onSubmit = handleSubmit(submit) + const [showEndRoleConfirmationDialog, setShowEndRoleConfirmationDialog] = + useState(false) // Find the role info relevant for this page const getRoleInfo = () => { @@ -268,9 +290,23 @@ export default function GuestRoleInfo({ guest }: GuestRoleInfoProps) { > {t('button.save')} </Button>{' '} - <Button color="primary" onClick={endPeriod()}> + {/* 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} + onClick={() => setShowEndRoleConfirmationDialog(true)} + > {t('sponsor.endNow')} </Button> + <ConfirmDialog + title={t('endRoleDialog.title')} + open={showEndRoleConfirmationDialog} + setOpen={setShowEndRoleConfirmationDialog} + onConfirm={endPeriod} + > + {t('endRoleDialog.text')} + </ConfirmDialog> </form> </Page> ) diff --git a/frontend/src/routes/sponsor/guest/index.tsx b/frontend/src/routes/sponsor/guest/index.tsx index 49a092dd5ea76030576489ef922838f921279813..37e46d6fa7e40ff5bd4024a71b7d810a2873846d 100644 --- a/frontend/src/routes/sponsor/guest/index.tsx +++ b/frontend/src/routes/sponsor/guest/index.tsx @@ -56,7 +56,7 @@ function GuestRoutes({ reloadGuests }: GuestRoutesProps) { return ( <> <Route path="/sponsor/guest/:pid/roles/:id"> - <GuestRoleInfo guest={guestInfo} /> + <GuestRoleInfo guest={guestInfo} reloadGuest={reloadGuestInfo} /> </Route> <Route exact path="/sponsor/guest/:pid/newrole"> <NewGuestRole guest={guestInfo} reloadGuestInfo={reloadGuestInfo} />