diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 99536259bc4e2a14251790482c5a1c2467cec8fc..2575a064fca9c0d304ad11d8675e5a9e07a74088 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -89,6 +89,7 @@ "firstName": "First name", "lastName": "Last name", "dateOfBirth": "Date of birth", + "endDate": "End date", "name": "Name", "role": "Guest role", "period": "Period", diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index ea1da2b58d275a54f7ef73bb4bc1999438ab855c..d17983e0af5c8ea7c0f063f985503a2d31ee6fc2 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -89,6 +89,7 @@ "firstName": "Fornavn", "lastName": "Etternavn", "dateOfBirth": "Fødselsdato", + "endDate": "Sluttdato", "name": "Navn", "role": "Gjesterolle", "period": "Periode", diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index e9af6cd8101ad3a8eee3b2d6652a5a0ce7edd9e4..4cafdbd06dd4dd473bc470578cff20e927babb10 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -89,6 +89,7 @@ "firstName": "Fornamn", "lastName": "Etternamn", "dateOfBirth": "Fødselsdato", + "endDate": "Sluttdato", "name": "Namn", "role": "Gjesterolle", "period": "Periode", diff --git a/frontend/src/components/page/page.tsx b/frontend/src/components/page/page.tsx index 8f6637ecb7e82b4b6ced51363e5d42f563f73593..299c98fdae4e461bfa89063ad6b915218682a708 100644 --- a/frontend/src/components/page/page.tsx +++ b/frontend/src/components/page/page.tsx @@ -8,6 +8,7 @@ import { appInst } from 'appConfig' interface IPage { children: React.ReactNode header?: string + pageWidth?: boolean } let faviconPath = '' @@ -30,7 +31,7 @@ switch (appInst) { } export default function Page(props: IPage) { - const { header, children } = props + const { header, children, pageWidth } = props const { i18n, t } = useTranslation() const appTitle = t('common:header:applicationTitle') @@ -42,7 +43,7 @@ export default function Page(props: IPage) { <title>{header}</title> </Helmet> <Container - maxWidth="md" + maxWidth={pageWidth ? 'lg' : 'md'} sx={{ paddingBottom: '2rem', paddingTop: '2rem' }} > {header ? <Typography variant="h1">{header}</Typography> : <></>} @@ -54,4 +55,5 @@ export default function Page(props: IPage) { Page.defaultProps = { header: null, + pageWidth: false, } diff --git a/frontend/src/routes/sponsor/frontpage/index.tsx b/frontend/src/routes/sponsor/frontpage/index.tsx index c992c5a8c7c927591a5343ecbbcfd1069dcccb4e..5036a661d31f45322f58c90ee27f3a2b7c930fc1 100644 --- a/frontend/src/routes/sponsor/frontpage/index.tsx +++ b/frontend/src/routes/sponsor/frontpage/index.tsx @@ -1,27 +1,27 @@ -import React, { useState } from 'react' +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward' +import NotificationsActiveIcon from '@mui/icons-material/NotificationsActive' import { + Accordion, + AccordionDetails, + AccordionSummary, + Button, + Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, - Paper, - Accordion, - AccordionSummary, - AccordionDetails, - Button, Typography, } from '@mui/material' -import NotificationsActiveIcon from '@mui/icons-material/NotificationsActive' -import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward' import { styled } from '@mui/system' +import { useState } from 'react' import Page from 'components/page' -import { useTranslation, Trans } from 'react-i18next' -import { Link } from 'react-router-dom' +import { differenceInDays, format, isBefore } from 'date-fns' import { Guest, Role } from 'interfaces' -import { differenceInDays, isBefore } from 'date-fns' +import { Trans, useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' import { getRoleName, getRoleOuName } from 'utils' import SponsorGuestButtons from '../../components/sponsorGuestButtons' @@ -42,6 +42,7 @@ interface StatusProps { interface GuestTableProps { guests: Guest[] emptyText: string + marginWidth: number } interface FrontPageProps { @@ -60,6 +61,7 @@ const StyledTableRow = styled(TableRow)({ const StyledAccordion = styled(Accordion)({ borderStyle: 'none', boxShadow: 'none', + margin: 'auto', }) const StyledAccordionSummary = styled(AccordionSummary)({ @@ -165,6 +167,41 @@ const Status = ({ person, role }: StatusProps) => { } } +const sortByDate = ( + guestRole: { guest: Guest; role: Role }[] +): { guest: Guest; role: Role }[] => + guestRole.sort( + (a, b) => a.role.end_date.getTime() - b.role.end_date.getTime() + ) + +const sortByStatus = (guests: Guest[]): { guest: Guest; role: Role }[] => { + let activeRoleAndPerson: { guest: Guest; role: Role }[] = [] + let expiringRoleAndPerson: { guest: Guest; role: Role }[] = [] + let expiredRoleAndPerson: { guest: Guest; role: Role }[] = [] + let status + + guests.forEach((guest) => + guest.roles.forEach((role) => { + ;[status] = calculateStatus(guest, role) + if (status === 'active') { + activeRoleAndPerson.push({ guest, role }) + } else if (status === 'expiring') { + expiringRoleAndPerson.push({ guest, role }) + } else { + expiredRoleAndPerson.push({ guest, role }) + } + }) + ) + + activeRoleAndPerson = sortByDate(activeRoleAndPerson) + expiringRoleAndPerson = sortByDate(expiringRoleAndPerson) + expiredRoleAndPerson = sortByDate(expiredRoleAndPerson) + + return expiringRoleAndPerson + .concat(activeRoleAndPerson) + .concat(expiredRoleAndPerson) +} + const PersonLine = ({ person, role }: PersonLineProps) => { const [t] = useTranslation(['common']) @@ -175,6 +212,7 @@ const PersonLine = ({ person, role }: PersonLineProps) => { </TableCell> <TableCell align="left">{getRoleName(role)}</TableCell> <Status person={person} role={role} /> + <TableCell align="left"> {format(role.end_date, 'yyyy-MM-dd')}</TableCell> <TableCell align="left">{getRoleOuName(role)}</TableCell> <TableCell align="left"> <Button @@ -190,35 +228,38 @@ const PersonLine = ({ person, role }: PersonLineProps) => { ) } -const GuestTable = ({ guests, emptyText }: GuestTableProps) => { +const GuestTable = ({ guests, emptyText, marginWidth }: GuestTableProps) => { const { t } = useTranslation('common') return ( <TableContainer component={Paper} - sx={{ boxShadow: 'none', borderRadius: '0rem' }} + sx={{ + boxShadow: 'none', + borderRadius: '0rem', + minWidth: marginWidth, + }} > - <Table sx={{ minWidth: 650 }} aria-label="simple table"> + <Table sx={{ minWidth: marginWidth }} aria-label="simple table"> <StyledTableHead> <TableRow> <StyledTableHeadCell>{t('common:name')}</StyledTableHeadCell> <StyledTableHeadCell>{t('common:role')}</StyledTableHeadCell> <StyledTableHeadCell>{t('common:status')}</StyledTableHeadCell> + <StyledTableHeadCell>{t('common:endDate')}</StyledTableHeadCell> <StyledTableHeadCell>{t('common:department')}</StyledTableHeadCell> <StyledTableHeadCell /> </TableRow> </StyledTableHead> <TableBody> {guests.length > 0 ? ( - guests.map((person) => - person.roles.map((role) => ( - <PersonLine - key={`${person.first} ${person.last} ${role.id}`} - role={role} - person={person} - /> - )) - ) + sortByStatus(guests).map((personRole) => ( + <PersonLine + key={`${personRole.guest.first} ${personRole.guest.last} ${personRole.role.id}`} + role={personRole.role} + person={personRole.guest} + /> + )) ) : ( <StyledTableRow> <TableCell> {emptyText}</TableCell> @@ -261,7 +302,11 @@ const InvitedGuests = ({ persons }: GuestProps) => { > {t('common:sentInvitationsDescription')} </Typography> - <GuestTable guests={guests} emptyText={t('common:noInvitations')} /> + <GuestTable + guests={guests} + emptyText={t('common:noInvitations')} + marginWidth={650} + /> </AccordionDetails> </StyledAccordion> ) @@ -297,7 +342,11 @@ const ActiveGuests = ({ persons }: GuestProps) => { > {t('common:activeGuestsDescription')} </Typography> - <GuestTable guests={guests} emptyText={t('common:noActiveGuests')} /> + <GuestTable + guests={guests} + emptyText={t('common:noActiveGuests')} + marginWidth={1000} + /> </AccordionDetails> </StyledAccordion> ) @@ -342,7 +391,11 @@ const WaitingGuests = ({ persons }: GuestProps) => { Gjester som har FEIDE-bruker trenger ikke godkjenning. </Trans> </Typography> - <GuestTable guests={guests} emptyText={t('common:noWaitingGuests')} /> + <GuestTable + guests={guests} + emptyText={t('common:noWaitingGuests')} + marginWidth={650} + /> </AccordionDetails> </StyledAccordion> ) @@ -350,7 +403,7 @@ const WaitingGuests = ({ persons }: GuestProps) => { function FrontPage({ guests }: FrontPageProps) { return ( - <Page> + <Page pageWidth> <SponsorGuestButtons yourGuestsActive /> <InvitedGuests persons={guests} /> <br />