diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index c796419a19229cf1e307511961607b24b5c4224d..57ea2e9b44f0e00bb0741d1af6dde050bfb2f393 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -41,6 +41,9 @@ "contactInfo": "Contact information", "contactInfoBody": "A guest is only considered active if at least one identification number has been verified.", "contactInfoTableText": "Contact information and identification", + "consentInfoHead": "Consent information", + "choiceDate": "Choice date", + "consentName": "Consent type", "roleInfoHead": "Roles and periods", "roleInfoTableText": "Guest roles", "roleInfoBody": "You can only change roles that you have given." diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index ebb93f4671548bcf75940bf1a93ce001d37e63cf..de9c4afc8e0a4900efcb48b3f7056bb489eaed60 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -41,6 +41,9 @@ "contactInfo": "Kontaktinformasjon", "contactInfoBody": "For at ein gjest skal reknast som aktiv må minst ett identifikasjonsnummer vere godkjend.", "contactInfoTableText": "Kontaktinformasjon og identifikasjon", + "consentInfoHead": "Samtykkeinformasjon", + "choiceDate": "Valgdato", + "consentName": "Samtykketype", "roleInfoHead": "Roller og perioder", "roleInfoBody": "Du kan endre på tidsperioden, men kun på gjesteroller som du er vert for.", "roleInfoTableText": "Gjesteroller" diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index 5ba389a4f43115d47c1c352108ce4758cabe4901..01c367ba6d0d985542446ddddd312fecf3d46929 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -42,6 +42,9 @@ "contactInfo": "Kontaktinformasjon", "contactInfoBody": "For at en gjest skal regnes som aktiv må minst ett identifikasjonsnummer være godkjent.", "contactInfoTableText": "Kontaktinformasjon og identifikasjon", + "consentInfoHead": "Samtykkeinformasjon", + "choiceDate": "Valdato", + "consentName": "Samtykketype", "roleInfoTableText": "Gjesteroller", "roleInfoHead": "Roller og periodar", "roleInfoBody": "Du kan endre på tidsperioden, men berre på gjesteroller som du er vert for." diff --git a/frontend/src/components/consentLine/index.tsx b/frontend/src/components/consentLine/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b34118eacc81794e90b052dddd4c1ac7b6b89288 --- /dev/null +++ b/frontend/src/components/consentLine/index.tsx @@ -0,0 +1,52 @@ +import { Consent } from 'interfaces' +import { useTranslation } from 'react-i18next' +import { format } from 'date-fns' +import { TableCell, TableRow } from 'components/table' + +interface UserConsentLineProps { + consent: Consent +} +const UserConsentLine = ({ consent }: UserConsentLineProps) => { + const [, i18n] = useTranslation('common') + let name = '' + let choice = '' + switch (i18n.language) { + case 'nb': + name = + consent.type.name_nb || consent.type.name_nn || consent.type.name_en + choice = + consent.choice.text_nb || + consent.choice.text_nn || + consent.choice.text_en + break + case 'nn': + name = + consent.type.name_nn || consent.type.name_nb || consent.type.name_en + choice = + consent.choice.text_nn || + consent.choice.text_nb || + consent.choice.text_en + break + // en + default: + name = + consent.type.name_en || consent.type.name_nb || consent.type.name_nn + choice = + consent.choice.text_en || + consent.choice.text_nb || + consent.choice.text_nn + break + } + return ( + <TableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableCell align="left">{name}</TableCell> + <TableCell component="th" scope="row"> + {consent.consent_given_at + ? format(consent.consent_given_at, 'yyyy-MM-dd') + : null} + </TableCell> + <TableCell align="left">{choice}</TableCell> + </TableRow> + ) +} +export default UserConsentLine diff --git a/frontend/src/components/roleLine/index.tsx b/frontend/src/components/roleLine/index.tsx index 59cd2f9622d4a358fa3a2aa0f9094cf4041adc0b..13d3d1df10144dea665b0200bd986fd39fa9c624 100644 --- a/frontend/src/components/roleLine/index.tsx +++ b/frontend/src/components/roleLine/index.tsx @@ -1,4 +1,4 @@ -import { Button, TableRow, styled } from '@mui/material' +import { Button, TableRow } from '@mui/material' import { Role } from 'interfaces' import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' diff --git a/frontend/src/contexts/userContext.ts b/frontend/src/contexts/userContext.ts index ed0029453bb2f7ee438ed7338e42a8d442193dbe..c8918030b0ae3a40cb2f3b699a9f68374fce2686 100644 --- a/frontend/src/contexts/userContext.ts +++ b/frontend/src/contexts/userContext.ts @@ -24,6 +24,7 @@ export const UserContext = createContext<IUserContext>({ fnr: '', passport: '', roles: [], + consents: [], }, fetchUserInfo: noop, clearUserInfo: noop, diff --git a/frontend/src/interfaces/index.ts b/frontend/src/interfaces/index.ts index 04121b7ba73c675676d598ab9ebf478da6e510ac..c85f54fb3cb51e6f91b13b41775cd7b34b0057d8 100644 --- a/frontend/src/interfaces/index.ts +++ b/frontend/src/interfaces/index.ts @@ -64,6 +64,31 @@ export type FetchedRole = { max_days: number } +export type ConsentType = { + name_en: string + name_nb: string + name_nn: string +} + +export type ConsentChoice = { + text_en: string + text_nb: string + text_nn: string +} + +export type FetchedConsent = { + id: string + type: ConsentType + choice: ConsentChoice + consent_given_at: string +} +export type Consent = { + id: string + type: ConsentType + choice: ConsentChoice + consent_given_at: Date +} + export interface User { auth: boolean auth_type: string @@ -78,4 +103,5 @@ export interface User { fnr: string passport: string roles: FetchedRole[] + consents: FetchedConsent[] } diff --git a/frontend/src/providers/userProvider.tsx b/frontend/src/providers/userProvider.tsx index 3a17e9d8e231791ab137fa8685929ed0e55c9565..f63000911c476ab03806871d1a5b66518b36b77e 100644 --- a/frontend/src/providers/userProvider.tsx +++ b/frontend/src/providers/userProvider.tsx @@ -22,6 +22,7 @@ function UserProvider(props: UserProviderProps) { fnr: '', passport: '', roles: [], + consents: [], }) const getUserInfo = async () => { @@ -44,6 +45,7 @@ function UserProvider(props: UserProviderProps) { fnr: data.fnr, passport: data.passport, roles: data.roles, + consents: data.consents, }) } else { setUser({ @@ -60,6 +62,7 @@ function UserProvider(props: UserProviderProps) { fnr: '', passport: '', roles: [], + consents: [], }) } } catch (error) { @@ -77,6 +80,7 @@ function UserProvider(props: UserProviderProps) { fnr: '', passport: '', roles: [], + consents: [], }) } } @@ -108,6 +112,7 @@ function UserProvider(props: UserProviderProps) { fnr: '', passport: '', roles: [], + consents: [], }) } diff --git a/frontend/src/routes/landing/index.tsx b/frontend/src/routes/landing/index.tsx index 9fe40b7acf128abf10bbbc7790e4e7a8f599141b..97fbd4110a221d4d90814b947cb4d063e99a3a07 100644 --- a/frontend/src/routes/landing/index.tsx +++ b/frontend/src/routes/landing/index.tsx @@ -1,38 +1,42 @@ +import { Table, TableBody } from '@mui/material' import { - Paper, - Table, - TableBody, - TableCell, TableContainer, TableHead, + TableHeadCell, TableRow, -} from '@mui/material' + TableCell, +} from 'components/table' import Page from 'components/page' import { UserRoleLine } from 'components/roleLine' +import UserConsentLine from 'components/consentLine' import { useUserContext } from 'contexts' -import { FetchedRole, Role } from 'interfaces' +import { FetchedRole, Role, Consent, FetchedConsent } from 'interfaces' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Redirect } from 'react-router-dom' -import { parseRole } from 'utils' +import { parseRole, parseConsent } from 'utils' const GuestPage = () => { const { user } = useUserContext() const [t] = useTranslation('common') const [roles, setRoles] = useState<Role[]>([]) + const [consents, setConsents] = useState<Consent[]>([]) useEffect(() => { setRoles(user.roles.map((role: FetchedRole) => parseRole(role))) + setConsents(user.consents.map((cons: FetchedConsent) => parseConsent(cons))) }, []) return ( <Page> <h4>{t('guestInfo.contactInfo')}</h4> - <TableContainer component={Paper}> + <TableContainer> <Table sx={{ minWidth: 650 }} aria-label="simple table"> - <TableHead sx={{ backgroundColor: 'secondary.light' }}> + <TableHead sx={{ backgroundColor: 'none' }}> <TableRow> - <TableCell align="left">{t('guestInfo.contactInfo')}</TableCell> - <TableCell /> + <TableHeadCell align="left"> + {t('guestInfo.contactInfo')} + </TableHeadCell> + <TableHeadCell /> </TableRow> </TableHead> <TableBody> @@ -64,13 +68,13 @@ const GuestPage = () => { </Table> </TableContainer> <h4>{t('guestInfo.roleInfoHead')}</h4> - <TableContainer component={Paper}> + <TableContainer> <Table sx={{ minWidth: 650 }} aria-label="simple table"> - <TableHead sx={{ backgroundColor: 'secondary.light' }}> + <TableHead sx={{ backgroundColor: 'none' }}> <TableRow> - <TableCell align="left">{t('common:role')}</TableCell> - <TableCell align="left">{t('common:period')}</TableCell> - <TableCell align="left">{t('common:ou')}</TableCell> + <TableHeadCell align="left">{t('common:role')}</TableHeadCell> + <TableHeadCell align="left">{t('common:period')}</TableHeadCell> + <TableHeadCell align="left">{t('common:ou')}</TableHeadCell> </TableRow> </TableHead> <TableBody> @@ -80,6 +84,27 @@ const GuestPage = () => { </TableBody> </Table> </TableContainer> + <h4>{t('guestInfo.consentInfoHead')}</h4> + <TableContainer> + <Table sx={{ minWidth: 650 }} aria-label="simple table"> + <TableHead sx={{ backgroundColor: 'none' }}> + <TableRow> + <TableHeadCell align="left"> + {t('guestInfo.consentName')} + </TableHeadCell> + <TableHeadCell align="left"> + {t('guestInfo.choiceDate')} + </TableHeadCell> + <TableHeadCell align="left">{t('common:choice')}</TableHeadCell> + </TableRow> + </TableHead> + <TableBody> + {consents.map((cons) => ( + <UserConsentLine key={cons.id} consent={cons} /> + ))} + </TableBody> + </Table> + </TableContainer> </Page> ) } diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index 35eb41c832191ce2cb8e49150ad1db9b7c9ae60c..4e464dece2d70e49898636cfaf42072b08a9b95e 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -1,7 +1,14 @@ import validator from '@navikt/fnrvalidator' import { parseISO } from 'date-fns' import i18n from 'i18next' -import { FetchedIdentity, FetchedRole, Identity, Role } from 'interfaces' +import { + FetchedIdentity, + FetchedRole, + Identity, + Role, + Consent, + FetchedConsent, +} from 'interfaces' import { isValidPhoneNumber, parsePhoneNumber } from 'libphonenumber-js' const validEmailRegex = @@ -141,6 +148,15 @@ export function parseRole(role: FetchedRole): Role { } } +export function parseConsent(cons: FetchedConsent): Consent { + return { + id: cons.id, + type: cons.type, + choice: cons.choice, + consent_given_at: parseISO(cons.consent_given_at), + } +} + export function parseIdentity( identity: FetchedIdentity | null ): Identity | null { diff --git a/gregui/api/views/userinfo.py b/gregui/api/views/userinfo.py index f376fb0ad360d08257e4c52472ce80f06b19bd2d..2eaeb68678849b9e7ce647613d20c93f60c3fa69 100644 --- a/gregui/api/views/userinfo.py +++ b/gregui/api/views/userinfo.py @@ -1,8 +1,3 @@ -from typing import ( - Sequence, - Type, -) - from rest_framework.authentication import BaseAuthentication, SessionAuthentication from rest_framework.permissions import AllowAny, BasePermission from rest_framework.status import HTTP_403_FORBIDDEN @@ -48,6 +43,7 @@ class UserInfoView(APIView): "sponsor_id": None, "person_id": None, "roles": [], + "consents": [], "auth_type": auth_type, } @@ -102,6 +98,23 @@ class UserInfoView(APIView): "orgunit", "type", "sponsor" ) ] + person_consents = [ + { + "id": cons.id, + "type": { + "name_en": cons.type.name_en, + "name_nb": cons.type.name_nb, + "name_nn": cons.type.name_nn, + }, + "choice": { + "text_en": cons.choice.text_en, + "text_nb": cons.choice.text_nb, + "text_nn": cons.choice.text_nn, + }, + "consent_given_at": cons.consent_given_at, + } + for cons in person.consents.all() + ] content.update( { "person_id": person.id, @@ -113,6 +126,7 @@ class UserInfoView(APIView): "fnr": person.fnr and "".join((person.fnr.value[:-5], "*****")), "passport": person.passport and person.passport.value, "roles": person_roles, + "consents": person_consents, } ) return Response(content) diff --git a/gregui/tests/api/views/test_userinfo.py b/gregui/tests/api/views/test_userinfo.py index df7d3cb44888f333ac0939a4509c0ec88583fb0d..ff519513129d4ed76fd3d3da96281031eb0d7ae1 100644 --- a/gregui/tests/api/views/test_userinfo.py +++ b/gregui/tests/api/views/test_userinfo.py @@ -39,8 +39,9 @@ def test_userinfo_invited_get(client, invitation_link): "start_date": None, "end_date": "2050-10-15", "sponsor": {"first_name": "Sponsor", "last_name": "Bar"}, - } + }, ], + "consents": [], } @@ -64,6 +65,7 @@ def test_userinfo_sponsor_get(client, log_in, user_sponsor): "feide_id": "", "person_id": None, "roles": [], + "consents": [], "sponsor_id": 1, } @@ -80,6 +82,7 @@ def test_userinfo_guest_get(client, log_in, user_person): "sponsor_id": None, "person_id": 1, "roles": [], + "consents": [], "first_name": "Foo", "last_name": "Bar", "email": "foo@bar.com",