Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • andretol/greg
1 result
Show changes
Commits on Source (15)
Showing
with 202 additions and 451 deletions
......@@ -7,7 +7,7 @@ PIP ?= pip -q
POETRY ?= poetry
PYLINT ?= pylint -sn
PYTEST ?= pytest -v -s --no-header
PYTHON ?= python3.9
PYTHON ?= python3.10
VENV ?= venv
mypy = $(MYPY) --config-file mypy.ini
......
FROM harbor.uio.no/library/docker.io-node:14-alpine as build-stage
FROM harbor.uio.no/library/docker.io-node:16-alpine as build-stage
LABEL org.opencontainers.image.authors="bnt-int@usit.uio.no"
LABEL no.uio.contact="bnt-int@usit.uio.no"
......
This diff is collapsed.
......@@ -21,7 +21,6 @@
"@types/react": "^17.0.33",
"@types/react-dom": "^17.0.10",
"@types/react-helmet": "^6.1.4",
"@types/react-router-dom": "^5.3.2",
"date-fns": "^2.24.0",
"fetch-intercept": "^2.4.0",
"http-proxy-middleware": "^2.0.1",
......@@ -37,7 +36,7 @@
"react-helmet": "^6.1.0",
"react-hook-form": "^7.17.5",
"react-i18next": "^11.11.4",
"react-router-dom": "^5.3.0",
"react-router-dom": "^6.3.0",
"react-scripts": "4.0.3",
"typeface-roboto": "^1.1.13",
"typescript": "^4.4.4",
......
import React from 'react'
import { Route, RouteProps, useLocation, useHistory } from 'react-router-dom'
import { setCookie } from 'utils'
import { useUserContext } from 'contexts'
import { useLocation, Navigate } from 'react-router-dom'
interface IProtectedRoute extends RouteProps {
children: React.ReactNode
}
function ProtectedRoute({ children, ...rest }: IProtectedRoute) {
// Simple protected route component
const isAuthenticated = () => {
const { user } = useUserContext()
if ( !user.auth ) {
if (!user.auth) {
const location = useLocation()
const history = useHistory()
setCookie('redirect', location.pathname)
history.push('/')
return false
}
return (
<Route {...rest}>
{children}
</Route>
)
return true
}
export default ProtectedRoute
const RequireAuth = ({ children }: { children: JSX.Element }) =>
isAuthenticated() ? children : <Navigate to="/" />
export default RequireAuth
......@@ -2,7 +2,7 @@ import { FetchedGuest, FetchedRole, Guest } from 'interfaces'
import { useEffect, useState } from 'react'
import { parseRole, parseIdentity } from 'utils'
const useGuest = (pid: string) => {
const useGuest = (pid: string ) => {
const [guestInfo, setGuest] = useState<Guest>({
pid: '',
first: '',
......
......@@ -2,7 +2,7 @@ import PersonIcon from '@mui/icons-material/Person'
import { Box, IconButton } from '@mui/material'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
import ArrowForwardIcon from '@mui/icons-material/ArrowForward'
......@@ -15,10 +15,10 @@ export default function OverviewGuestButton(
) {
const { backArrow } = properties
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const goToGuestOverview = () => {
history.push('/guestregister')
navigate('/guestregister')
}
return (
......@@ -33,7 +33,7 @@ export default function OverviewGuestButton(
{backArrow !== undefined ? (
<ArrowBackIcon
onClick={() => {
history.push(backArrow)
navigate(backArrow)
}}
/>
) : (
......
......@@ -3,7 +3,7 @@ import { Box, IconButton, styled, Theme } from '@mui/material'
import PersonAddIcon from '@mui/icons-material/PersonAdd'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
interface SponsorGuestButtonsProps {
yourGuestsActive?: boolean
......@@ -20,14 +20,14 @@ const StyledIconButton = styled(IconButton)({
export default function SponsorGuestButtons(props: SponsorGuestButtonsProps) {
const { yourGuestsActive, registerNewGuestActive } = props
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const goToOverview = () => {
history.push('/sponsor')
navigate('/sponsor')
}
const goToRegister = () => {
history.push('/register')
navigate('/register')
}
return (
......
import React from 'react'
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock'
import { render, screen } from 'test-utils'
import AdapterDateFns from '@mui/lab/AdapterDateFns'
import { LocalizationProvider } from '@mui/lab'
import { BrowserRouter } from 'react-router-dom'
import { render, screen } from 'test-utils'
import { FeatureContext } from 'contexts'
import GuestRegister from './index'
......@@ -44,11 +46,13 @@ test('Field showing values correctly', async () => {
)
render(
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
<BrowserRouter>
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
</BrowserRouter>
)
await screen.findByDisplayValue(testData.person.first_name)
......@@ -88,11 +92,13 @@ test('Gender and birth date suggestions not showing if no national ID given', as
Promise.resolve<any>(JSON.stringify(testData))
)
render(
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
<BrowserRouter>
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
</BrowserRouter>
)
// Wait a bit so that all the values are showing
......@@ -121,11 +127,13 @@ test('Gender and birth date suggestions not overriding existing values', async (
Promise.resolve<any>(JSON.stringify(testData))
)
render(
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
<BrowserRouter>
<FeatureContext.Provider value={allFeaturesOn}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRegister />
</LocalizationProvider>
</FeatureContext.Provider>
</BrowserRouter>
)
// In this a date of birth was already set, and it should not have been overridden by a suggestion
......
import React, { Suspense, useContext, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import {
CountryCode,
getCountries,
......@@ -67,7 +67,7 @@ type InvitationData = {
*/
export default function GuestRegister() {
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const { showGenderFieldForGuest } = useContext(FeatureContext)
const guestRegisterRef = useRef<GuestRegisterCallableMethods>(null)
......@@ -369,7 +369,7 @@ export default function GuestRegister() {
// Go back to the invite page. The invitation ID does not need
// to be specified here, since the user has already been
// logged in and can be identified without the invitation ID
history.push('/invite')
navigate('/invite')
}
return (
......
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import { Button, Typography } from '@mui/material'
import { useUserContext } from 'contexts'
const GuestSuccessStep = () => {
const { t } = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const { getUserInfo } = useUserContext()
// Refetch user info after submission, so that the front page doesn't redirect us
......@@ -36,7 +36,7 @@ const GuestSuccessStep = () => {
marginTop: '2rem',
}}
onClick={() => {
history.push('/')
navigate('/')
}}
>
{t('button.backToFrontPage')}
......
import { useEffect } from 'react'
import { Switch, Route, useHistory } from 'react-router-dom'
import { Routes, Route, useNavigate } from 'react-router-dom'
import { styled } from '@mui/system'
import { CssBaseline } from '@mui/material'
......@@ -22,7 +22,7 @@ import LogoutInviteSession from 'routes/invite/logout'
import Footer from 'routes/components/footer'
import Header from 'routes/components/header'
import NotFound from 'routes/components/notFound'
import ProtectedRoute from 'components/protectedRoute'
import RequireAuth from 'components/requireAuth'
const AppWrapper = styled('div')({
display: 'flex',
......@@ -34,14 +34,14 @@ const AppWrapper = styled('div')({
export default function App() {
const { user, clearUserInfo } = useUserContext()
const history = useHistory()
const navigate = useNavigate()
useEffect(() => {
if (user.auth) {
const redirect = getCookie('redirect')
if (redirect) {
deleteCookie('redirect')
history.push(redirect)
navigate(redirect)
}
}
}, [user.fetched])
......@@ -67,23 +67,31 @@ export default function App() {
<CssBaseline />
<AppWrapper>
<Header />
<Switch>
<Route exact path="/">
<FrontPage />
</Route>
<ProtectedRoute path="/sponsor">
<Sponsor />
</ProtectedRoute>
<ProtectedRoute path="/register">
<Register />
</ProtectedRoute>
<Route path="/invite/logout" component={LogoutInviteSession} />
<Route path="/invite/" component={Invite} />
<Route path="/guestregister" component={GuestRegister} />
<Route>
<NotFound />
</Route>
</Switch>
<Routes>
<Route path="/" element={<FrontPage />} />
<Route
path="/sponsor/*"
element={
<RequireAuth>
<Sponsor />
</RequireAuth>
}
/>
<Route
path="/register/*"
element={
<RequireAuth>
<Register />
</RequireAuth>
}
/>
<Route path="/invite/logout" element={<LogoutInviteSession />} />
<Route path="/invite/" element={<Invite />} />
<Route path="/guestregister" element={<GuestRegister />} />
<Route element={<NotFound />} />
</Routes>
<Footer />
</AppWrapper>
</>
......
import { useEffect, useState } from 'react'
import { Redirect } from 'react-router-dom'
import { Navigate } from 'react-router-dom'
import { useCookies } from 'react-cookie'
import { Box, CircularProgress } from '@mui/material'
......@@ -20,7 +20,7 @@ export default function LogoutInviteSession() {
}, [])
if (loggedOut) {
return <Redirect to="/" />
return <Navigate to="/" />
}
return (
......
......@@ -13,7 +13,7 @@ import { useUserContext } from 'contexts'
import { FetchedRole, Role, Consent, FetchedConsent } from 'interfaces'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Redirect } from 'react-router-dom'
import { Navigate } from 'react-router-dom'
import { parseRole, parseConsent } from 'utils'
const GuestPage = () => {
......@@ -123,11 +123,11 @@ function LandingPage() {
const { user } = useUserContext()
if (user.sponsor_id) {
return <Redirect to="/sponsor" />
return <Navigate to="/sponsor" />
}
if (user.person_id) {
if (!user.registration_completed_date) {
return <Redirect to="/guestregister" />
return <Navigate to="/guestregister" />
}
return <GuestPage />
}
......
import { Link, useParams, useHistory } from 'react-router-dom'
import { Link, useParams, useNavigate } from 'react-router-dom'
import Page from 'components/page'
import RoleLine from 'components/roleLine'
......@@ -95,7 +95,7 @@ export default function GuestInfo({
}: GuestInfoProps) {
const { pid } = useParams<GuestInfoParams>()
const [t] = useTranslation(['common'])
const history = useHistory()
const navigate = useNavigate()
const [confirmCancelDialogOpen, setConfirmCancelDialogOpen] = useState(false)
const [emailUpdateError, setEmailUpdateError] =
useState<ServerErrorReportData>()
......@@ -196,7 +196,7 @@ export default function GuestInfo({
if (result !== null) {
// The invite for the guest has been cancelled, send the user back to the sponsor front page
reloadGuests()
history.push('/sponsor')
navigate('/sponsor')
}
})
.catch((error) => {
......@@ -365,9 +365,12 @@ export default function GuestInfo({
<TableHeadCell align="left">{t('common:host')}</TableHeadCell>
<TableHeadCell align="left" />
</TableRow>
{guest.roles.map((role) => (
<RoleLine key={role.id} pid={pid} role={role} />
))}
{guest.roles.map((role) => {
if (pid === undefined) {
return <></>
}
return <RoleLine key={role.id} pid={pid} role={role} />
})}
</TableBody>
</Table>
<Button
......
......@@ -3,10 +3,9 @@ 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 { BrowserRouter } from 'react-router-dom'
import GuestRoleInfo from './index'
import { waitFor } from '../../../../test-utils'
......@@ -54,14 +53,12 @@ const guest: Guest = {
}
test('Button state correct on load', async () => {
const history = createMemoryHistory()
render(
<Router history={history}>
<BrowserRouter>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<GuestRoleInfo guest={guest} reloadGuest={() => {}} />
</LocalizationProvider>
</Router>
</BrowserRouter>
)
await waitFor(
......
......@@ -11,7 +11,7 @@ import TableCellMui from '@mui/material/TableCell'
import Page from 'components/page'
import { Guest, Role } from 'interfaces'
import { useTranslation } from 'react-i18next'
import { useHistory, useParams } from 'react-router-dom'
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'
......@@ -103,7 +103,7 @@ export default function GuestRoleInfo({
const { pid, id } = useParams<GuestRoleInfoParams>()
const [t] = useTranslation('common')
const { displayContactAtUnit, displayComment } = useFeatureContext()
const history = useHistory()
const navigate = useNavigate()
const [role, setRole] = useState<Role>({
id: '',
name_nb: '',
......@@ -122,15 +122,15 @@ export default function GuestRoleInfo({
const todayPlusMaxDays = addDays(role.max_days)(today)
// Make a function for use with onClick of the end role button
const endPeriod = () => {
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)
role.end_date = newEndDate
endPeriodPost(id, { end_date: newEndDate })
endPeriodPost(role_id, { end_date: newEndDate })
// Go back to guest overview page
history.push(`/sponsor/guest/${guest.pid}`)
navigate(`/sponsor/guest/${guest.pid}`)
}
const {
......@@ -314,14 +314,18 @@ export default function GuestRoleInfo({
>
{t('sponsor.endNow')}
</Button>
<ConfirmDialog
title={t('endRoleDialog.title')}
open={showEndRoleConfirmationDialog}
setOpen={setShowEndRoleConfirmationDialog}
onConfirm={endPeriod}
>
{t('endRoleDialog.text')}
</ConfirmDialog>
{typeof id === 'string' ? (
<ConfirmDialog
title={t('endRoleDialog.title')}
open={showEndRoleConfirmationDialog}
setOpen={setShowEndRoleConfirmationDialog}
onConfirm={endPeriod(id)}
>
{t('endRoleDialog.text')}
</ConfirmDialog>
) : (
<></>
)}
</form>
</Page>
)
......
import useGuest from 'hooks/useGuest'
import { Route, useParams } from 'react-router-dom'
import { Route, Routes, useParams } from 'react-router-dom'
import { submitJsonOpts } from 'utils'
import GuestInfo from './guestInfo'
import GuestRoleInfo from './guestRoleInfo'
......@@ -15,6 +15,9 @@ type GuestRoutesProps = {
function GuestRoutes({ reloadGuests }: GuestRoutesProps) {
const { pid } = useParams<GuestInfoParams>()
if (typeof pid === 'undefined') {
return <></>
}
const { guestInfo, reloadGuestInfo } = useGuest(pid)
const updateEmail = (
......@@ -59,23 +62,32 @@ function GuestRoutes({ reloadGuests }: GuestRoutesProps) {
}
return (
<>
<Route path="/sponsor/guest/:pid/roles/:id">
<GuestRoleInfo guest={guestInfo} reloadGuest={reloadGuestInfo} />
</Route>
<Route exact path="/sponsor/guest/:pid/newrole">
<NewGuestRole guest={guestInfo} reloadGuestInfo={reloadGuestInfo} />
</Route>
<Route exact path="/sponsor/guest/:pid">
<GuestInfo
guest={guestInfo}
updateEmail={updateEmail}
resend={resend}
reloadGuests={reloadGuests}
reloadGuest={reloadGuestInfo}
/>
</Route>
</>
<Routes>
<Route
path="/roles/:id"
element={
<GuestRoleInfo guest={guestInfo} reloadGuest={reloadGuestInfo} />
}
/>
<Route
path="/newrole"
element={
<NewGuestRole guest={guestInfo} reloadGuestInfo={reloadGuestInfo} />
}
/>
<Route
path=""
element={
<GuestInfo
guest={guestInfo}
updateEmail={updateEmail}
resend={resend}
reloadGuests={reloadGuests}
reloadGuest={reloadGuestInfo}
/>
}
/>
</Routes>
)
}
......
import { useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { Link, useHistory, useParams } from 'react-router-dom'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { format } from 'date-fns'
import { addDays } from 'date-fns/fp'
import { DatePicker } from '@mui/lab'
......@@ -110,11 +110,16 @@ function NewGuestRole({ guest, reloadGuestInfo }: NewGuestRoleProps) {
const { displayContactAtUnit, displayComment } = useFeatureContext()
const { pid } = useParams<GuestInfoParams>()
const history = useHistory()
// Hack to make typecheck happy. The only way for pid to be undefined
// is if the Route is set up wrong, which should brake everything any way
if(typeof(pid) === "undefined"){
return <></>
}
const navigate = useNavigate()
const onSubmit = handleSubmit(async () => {
await postRole(getValues(), pid)
reloadGuestInfo()
history.push(`/sponsor/guest/${pid}`)
navigate(`/sponsor/guest/${pid}`)
})
const { ous } = useOus()
......@@ -172,7 +177,7 @@ function NewGuestRole({ guest, reloadGuestInfo }: NewGuestRoleProps) {
}
return (
<MenuItem key={ou.id.toString()} value={ou.id}>
{name} ({ou.id})
{name} ({ou.orgreg_id})
</MenuItem>
)
}
......
import { Route } from 'react-router-dom'
import { Route, Routes } from 'react-router-dom'
import FrontPage from 'routes/sponsor/frontpage'
import useGuests from 'hooks/useGuests'
......@@ -8,14 +8,13 @@ function Sponsor() {
const { guests, reloadGuests } = useGuests()
return (
<>
<Route path="/sponsor/guest/:pid">
<GuestRoutes reloadGuests={reloadGuests} />
</Route>
<Route exact path="/sponsor">
<FrontPage guests={guests} />
</Route>
</>
<Routes>
<Route
path="guest/:pid/*"
element={<GuestRoutes reloadGuests={reloadGuests} />}
/>
<Route path="" element={<FrontPage guests={guests} />} />
</Routes>
)
}
......