diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 210cf747a4a6e2fe7356537050bad51f05098ad1..cb7264abda8a53669751faa3fb4c6d7e4e010a49 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -61,20 +61,25 @@ "role": "Guest role", "period": "Period", "ou": "Organisation", + "department": "Department", "choice": "Choices", "registerText": "Register new guest", - "waitingGuests": "Guests waiting for your confirmation", - "waitingGuestsDescription": "See the table below for guests ", + "waitingGuests": "Guests waiting for confirmation", + "waitingGuestsDescription": "Confirm your guests that <1>registered manually</1> here. Guests with FEIDE-user do not need confirmation.", "noWaitingGuests": "No waiting guests", - "activeGuests": "Your active guests", - "activeGuestsDescription": "Make changes to your existing guests", + "activeGuests": "Confirmed guests", + "activeGuestsDescription": "Make changes to guest roles.", "noActiveGuests": "No active guests", "sentInvitations": "Sent invitations", "sentInvitationsDescription": "Invitations awaiting response from guest.", "noInvitations": "No invitations", "status": "Status", - "active": "Active", - "expired": "Expired", + "statusText": { + "active": "Active", + "expired": "Expired", + "waitingForGuest": "Waiting for guest", + "waitingForSponsor": "Needs confirmation" + }, "details": "Details", "nationalIdNumber": "National ID number", "validation": { diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index f8fc3a000306d744f00c9bb9292f2bb8f9dfb0a5..6268284c72a524fd2152add353e5c1b67c5e9760 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -61,20 +61,25 @@ "role": "Gjesterolle", "period": "Periode", "ou": "Organisasjon", + "department": "Avdeling", "choice": "Valg", "registerText": "Registrer ny gjest", - "waitingGuests": "Dine gjester som venter på godkjenning", - "waitingGuestsDescription": "Under er en oversikt over kontoer som venter på godkjenning", + "waitingGuests": "Gjester som venter på godkjenning", + "waitingGuestsDescription": "Her godkjenner du gjester som har <1>registrert seg manuelt</1>. Gjester som har FEIDE-bruker trenger ikke godkjenning.", "noWaitingGuests": "Ingen gjester til godkjenning", - "activeGuests": "Dine aktive gjester", - "activeGuestsDescription": "Her kan du endre på eksisterende gjester", + "activeGuests": "Godkjente gjester", + "activeGuestsDescription": "Her kan du endre på gjesteroller", "noActiveGuests": "Ingen aktive gjester", "sentInvitations": "Sendte invitasjoner", "sentInvitationsDescription": "Invitasjoner som venter på at gjesten skal ferdigstille registreringen.", "noInvitations": "Ingen invitasjoner", "status": "Status", - "active": "Aktiv", - "expired": "Utgått", + "statusText": { + "active": "Aktiv", + "expired": "Utgått", + "waitingForGuest": "Venter på gjest", + "waitingForSponsor": "Trenger godkjenning" + }, "details": "Detaljer", "nationalIdNumber": "Fødselsnummer/D-nummer", "validation": { diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index ca630977cb0948a3ad697718ac5d0c54dad6857e..b186583d1eab10289d1add8a535041e02392d443 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -62,20 +62,25 @@ "role": "Gjesterolle", "period": "Periode", "ou": "Organisasjon", + "department": "Avdeling", "choice": "Valg", "registerText": "Registrer ny gjest", - "waitingGuests": "Dine gjester som venter på godkjenning", - "waitingGuestsDescription": "Under er en oversikt over kontoer som venter på godkjenning", + "waitingGuests": "Gjester som venter på godkjenning", + "waitingGuestsDescription": "Her godkjenner du gjester som har <1>registrert seg manuelt</1>. Gjester som har FEIDE-bruker trenger ikke godkjenning.", "noWaitingGuests": "Ingen gjester til godkjenning", - "activeGuests": "Dine aktive gjester", - "activeGuestsDescription": "Her kan du endre på eksisterende gjester", + "activeGuests": "Godkjente gjester", + "activeGuestsDescription": "Her kan du endre på gjesteroller.", "noActiveGuests": "Ingen aktive gjester", "sentInvitations": "Sendte invitasjonar", "sentInvitationsDescription": "Invitasjonar som venter på at gjesten skal ferdigstille registreringa.", "noInvitations": "Ingen invitasjonar", "status": "Status", - "active": "Aktiv", - "expired": "Utgått", + "statusText": { + "active": "Aktiv", + "expired": "Utgått", + "waitingForGuest": "Venter på gjest", + "waitingForSponsor": "Trenger godkjenning" + }, "details": "Detaljer", "nationalIdNumber": "Fødselsnummer/D-nummer", "validation": { diff --git a/frontend/src/routes/components/sponsorGuestButtons.tsx b/frontend/src/routes/components/sponsorGuestButtons.tsx index 021703014090bae566c63ea0be96f2298712f2fd..b713ff98ce6fb280fc0cb257286d58557ab69d38 100644 --- a/frontend/src/routes/components/sponsorGuestButtons.tsx +++ b/frontend/src/routes/components/sponsorGuestButtons.tsx @@ -1,5 +1,5 @@ import PersonIcon from '@mui/icons-material/Person' -import { Box, IconButton, Theme } from '@mui/material' +import { Box, IconButton, styled, Theme } from '@mui/material' import PersonAddIcon from '@mui/icons-material/PersonAdd' import React from 'react' import { useTranslation } from 'react-i18next' @@ -10,6 +10,13 @@ interface SponsorGuestButtonsProps { registerNewGuestActive?: boolean } +const StyledIconButton = styled(IconButton)({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + fontSize: '22px', +}) + export default function SponsorGuestButtons(props: SponsorGuestButtonsProps) { const { yourGuestsActive, registerNewGuestActive } = props const { t } = useTranslation(['common']) @@ -30,22 +37,20 @@ export default function SponsorGuestButtons(props: SponsorGuestButtonsProps) { flexDirection: 'row', justifyContent: 'space-evenly', marginBottom: '2rem', + fontSize: '22px', }} > - <IconButton + <StyledIconButton onClick={goToOverview} sx={{ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - typography: 'caption', + color: () => (yourGuestsActive ? 'primary.main' : ''), textDecorationLine: () => (yourGuestsActive ? 'underline' : ''), }} > <PersonIcon - fontSize="large" sx={{ - borderRadius: '2rem', + fontSize: '80px', + borderRadius: '4rem', borderStyle: 'solid', borderColor: (theme: Theme) => yourGuestsActive @@ -59,22 +64,19 @@ export default function SponsorGuestButtons(props: SponsorGuestButtonsProps) { }} /> {t('yourGuests')} - </IconButton> + </StyledIconButton> - <IconButton + <StyledIconButton onClick={goToRegister} sx={{ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - typography: 'caption', + color: () => (registerNewGuestActive ? 'primary.main' : ''), textDecorationLine: () => (registerNewGuestActive ? 'underline' : ''), }} > <PersonAddIcon - fontSize="large" sx={{ - borderRadius: '2rem', + fontSize: '80px', + borderRadius: '4rem', borderStyle: 'solid', borderColor: (theme: Theme) => registerNewGuestActive @@ -88,7 +90,7 @@ export default function SponsorGuestButtons(props: SponsorGuestButtonsProps) { }} /> {t('registerNewGuest')} - </IconButton> + </StyledIconButton> </Box> ) } diff --git a/frontend/src/routes/sponsor/frontpage/index.tsx b/frontend/src/routes/sponsor/frontpage/index.tsx index 1353b459c703c3edaffcbc172fbc62aa97c01028..678c03598fb145e5cdb921f43f6f8f30186a23e0 100644 --- a/frontend/src/routes/sponsor/frontpage/index.tsx +++ b/frontend/src/routes/sponsor/frontpage/index.tsx @@ -12,13 +12,15 @@ import { AccordionDetails, Button, } from '@mui/material' -import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import NotificationsActiveIcon from '@mui/icons-material/NotificationsActive' +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward' +import { styled } from '@mui/system' import Page from 'components/page' -import { useTranslation } from 'react-i18next' +import { useTranslation, Trans } from 'react-i18next' import { Link } from 'react-router-dom' import { Guest, Role } from 'interfaces' -import { isBefore, format } from 'date-fns' +import { isBefore } from 'date-fns' import SponsorGuestButtons from '../../components/sponsorGuestButtons' @@ -29,40 +31,118 @@ interface GuestProps { interface PersonLineProps { person: Guest role: Role - showStatusColumn?: boolean } -const PersonLine = ({ person, role, showStatusColumn }: PersonLineProps) => { +interface StatusProps { + status: string +} + +interface GuestTableProps { + guests: Guest[] + emptyText: string +} + +interface FrontPageProps { + guests: Guest[] +} + +const StyledTableRow = styled(TableRow)(({ theme }) => ({ + borderTop: '0', + borderLeft: '0', + borderRight: '0', + borderBottom: '2px solid', + borderColor: theme.palette.primary.main, + borderRadius: '0', +})) + +const StyledAccordion = styled(Accordion)({ + borderStyle: 'none', + boxShadow: 'none', +}) + +const StyledAccordionSummary = styled(AccordionSummary)({ + borderStyle: 'solid', + borderColor: 'black', + borderWidth: '2px', + borderRadius: '1%', +}) + +const StyledTableHeadCell = styled(TableCell)({ + fontWeight: 'bold', +}) + +const StyledTableHead = styled(TableHead)(({ theme }) => ({ + borderTop: '0', + borderLeft: '0', + borderRight: '0', + borderBottom: '3px solid', + borderColor: theme.palette.secondary.main, + borderRadius: '0', +})) + +const Status = ({ status }: StatusProps) => { + const { t } = useTranslation('common') + switch (status) { + case 'active': + return ( + <TableCell sx={{ color: 'success.main' }} align="left"> + {t('statusText.active')} + </TableCell> + ) + case 'expired': + return ( + <TableCell sx={{ color: 'error.main' }} align="left"> + {t('statusText.expired')} + </TableCell> + ) + case 'waitingForGuest': + return ( + <TableCell sx={{ color: 'blue' }} align="left"> + {t('statusText.waitingForGuest')} + </TableCell> + ) + case 'waitingForSponsor': + return ( + <TableCell sx={{ color: 'blue' }} align="left"> + {t('statusText.waitingForSponsor')} + </TableCell> + ) + default: + return ( + <TableCell sx={{ color: 'error.main' }} align="left"> + {t('statusText.waiting')} + </TableCell> + ) + } +} + +const PersonLine = ({ person, role }: PersonLineProps) => { const [t, i18n] = useTranslation(['common']) const today = new Date() today.setHours(0, 0, 0, 0) + let status = '' + + if (!person.registered) { + status = 'waitingForGuest' + } else if (person.registered && !person.verified) { + status = 'waitingForSponsor' + } else if (person.registered && person.verified) { + if (!isBefore(role.end_date, today)) { + status = 'active' + } else { + status = 'expired' + } + } + return ( - <TableRow - key={`${person.first} ${person.last}`} - sx={{ '&:last-child td, &:last-child th': { border: 0 } }} - > + <StyledTableRow key={`${person.first} ${person.last}`}> <TableCell component="th" scope="row"> {`${person.first} ${person.last}`} </TableCell> <TableCell align="left"> {i18n.language === 'en' ? role.name_en : role.name_nb} </TableCell> - - {showStatusColumn && - (!isBefore(role.end_date, today) ? ( - <TableCell sx={{ color: 'green' }} align="left"> - {t('common:active')} - </TableCell> - ) : ( - <TableCell sx={{ color: 'red' }} align="left"> - {t('common:expired')} - </TableCell> - ))} - - <TableCell align="left"> - {role.start_date ? format(role.start_date, 'yyyy-MM-dd') : null} -{' '} - {format(role.end_date, 'yyyy-MM-dd')} - </TableCell> + <Status status={status} /> <TableCell align="left"> {i18n.language === 'en' ? role.ou_en : role.ou_nb} </TableCell> @@ -76,15 +156,47 @@ const PersonLine = ({ person, role, showStatusColumn }: PersonLineProps) => { {t('common:details')} </Button> </TableCell> - </TableRow> + </StyledTableRow> ) } -PersonLine.defaultProps = { - showStatusColumn: false, +const GuestTable = ({ guests, emptyText }: GuestTableProps) => { + const { t } = useTranslation('common') + + return ( + <TableContainer + component={Paper} + sx={{ boxShadow: 'none', borderRadius: '0px' }} + > + <Table sx={{ minWidth: 650 }} aria-label="simple table"> + <StyledTableHead> + <TableRow> + <StyledTableHeadCell>{t('common:name')}</StyledTableHeadCell> + <StyledTableHeadCell>{t('common:role')}</StyledTableHeadCell> + <StyledTableHeadCell>{t('common:status')}</StyledTableHeadCell> + <StyledTableHeadCell>{t('common:department')}</StyledTableHeadCell> + <StyledTableHeadCell /> + </TableRow> + </StyledTableHead> + <TableBody> + {guests.length > 0 ? ( + guests.map((person) => + person.roles.map((role) => ( + <PersonLine role={role} person={person} /> + )) + ) + ) : ( + <TableRow> + <TableCell> {emptyText}</TableCell> + </TableRow> + )} + </TableBody> + </Table> + </TableContainer> + ) } -const WaitingForGuestRegistration = ({ persons }: GuestProps) => { +const InvitedGuests = ({ persons }: GuestProps) => { const [activeExpanded, setActiveExpanded] = useState(false) // Show guests that have not responded to the invite yet @@ -95,53 +207,20 @@ const WaitingForGuestRegistration = ({ persons }: GuestProps) => { } const [t] = useTranslation(['common']) return ( - <Accordion + <StyledAccordion expanded={activeExpanded} onChange={() => { setActiveExpanded(!activeExpanded) }} > - <AccordionSummary expandIcon={<ExpandMoreIcon />}> + <StyledAccordionSummary expandIcon={<ArrowUpwardIcon color="primary" />}> <h2>{t('common:sentInvitations')}</h2> - </AccordionSummary> + </StyledAccordionSummary> <AccordionDetails> <p>{t('common:sentInvitationsDescription')}</p> - <TableContainer component={Paper}> - <Table sx={{ minWidth: 650 }} aria-label="simple table"> - <TableHead sx={{ backgroundColor: 'secondary.light' }}> - <TableRow> - <TableCell>{t('common:name')}</TableCell> - <TableCell align="left">{t('common:role')}</TableCell> - <TableCell align="left">{t('common:period')}</TableCell> - <TableCell align="left">{t('common:ou')}</TableCell> - <TableCell align="left">{t('common:choice')}</TableCell> - </TableRow> - </TableHead> - <TableBody> - {guests.length > 0 ? ( - guests.map((person) => - person.roles ? ( - person.roles.map((role) => ( - <PersonLine role={role} person={person} /> - )) - ) : ( - <></> - ) - ) - ) : ( - <></> - )} - - <TableRow> - <TableCell> - {guests.length > 0 ? '' : t('common:noActiveGuests')} - </TableCell> - </TableRow> - </TableBody> - </Table> - </TableContainer> + <GuestTable guests={guests} emptyText={t('common:noInvitations')} /> </AccordionDetails> - </Accordion> + </StyledAccordion> ) } @@ -155,60 +234,20 @@ const ActiveGuests = ({ persons }: GuestProps) => { } const [t] = useTranslation(['common']) return ( - <Accordion + <StyledAccordion expanded={activeExpanded} onChange={() => { setActiveExpanded(!activeExpanded) }} > - <AccordionSummary expandIcon={<ExpandMoreIcon />}> + <StyledAccordionSummary expandIcon={<ArrowUpwardIcon color="primary" />}> <h2>{t('common:activeGuests')}</h2> - </AccordionSummary> + </StyledAccordionSummary> <AccordionDetails> <p>{t('common:activeGuestsDescription')}</p> - <TableContainer component={Paper}> - <Table sx={{ minWidth: 650 }} aria-label="simple table"> - <TableHead sx={{ backgroundColor: 'secondary.light' }}> - <TableRow> - <TableCell>{t('common:name')}</TableCell> - <TableCell align="left">{t('common:role')}</TableCell> - - <TableCell align="left">{t('common:status')}</TableCell> - - <TableCell align="left">{t('common:period')}</TableCell> - <TableCell align="left">{t('common:ou')}</TableCell> - <TableCell align="left">{t('common:choice')}</TableCell> - </TableRow> - </TableHead> - <TableBody> - {guests.length > 0 ? ( - guests.map((person) => - person.roles ? ( - person.roles.map((role) => ( - <PersonLine - role={role} - person={person} - showStatusColumn - /> - )) - ) : ( - <></> - ) - ) - ) : ( - <></> - )} - - <TableRow> - <TableCell> - {guests.length > 0 ? '' : t('common:noActiveGuests')} - </TableCell> - </TableRow> - </TableBody> - </Table> - </TableContainer> + <GuestTable guests={guests} emptyText={t('common:noActiveGuests')} /> </AccordionDetails> - </Accordion> + </StyledAccordion> ) } @@ -223,67 +262,43 @@ const WaitingGuests = ({ persons }: GuestProps) => { const [t] = useTranslation(['common']) return ( - <Accordion + <StyledAccordion expanded={waitingExpanded} onChange={() => { setWaitingExpanded(!waitingExpanded) }} - sx={{ border: 'none' }} > - <AccordionSummary expandIcon={<ExpandMoreIcon />}> - <h2>{t('common:waitingGuests')}</h2> - </AccordionSummary> + <StyledAccordionSummary expandIcon={<ArrowUpwardIcon color="primary" />}> + <h2> + {t('common:waitingGuests')}{' '} + {guests.length > 0 && ( + <NotificationsActiveIcon + sx={{ color: 'error.main', fontSize: '26px' }} + /> + )} + </h2> + </StyledAccordionSummary> <AccordionDetails> - <p>{t('common:waitingGuestsDescription')}</p> - - <TableContainer component={Paper}> - <Table sx={{ minWidth: 650 }} aria-label="simple table"> - <TableHead sx={{ backgroundColor: 'secondary.light' }}> - <TableRow> - <TableCell>{t('common:name')}</TableCell> - <TableCell align="left">{t('common:role')}</TableCell> - <TableCell align="left">{t('common:period')}</TableCell> - <TableCell align="left">{t('common:ou')}</TableCell> - <TableCell align="left">{t('common:choice')}</TableCell> - </TableRow> - </TableHead> - <TableBody> - {guests.length > 0 ? ( - guests.map((person) => - person.roles ? ( - person.roles.map((role) => ( - <PersonLine role={role} person={person} /> - )) - ) : ( - <></> - ) - ) - ) : ( - <></> - )} - <TableRow> - <TableCell> - {guests.length > 0 ? '' : t('common:noWaitingGuests')} - </TableCell> - </TableRow> - </TableBody> - </Table> - </TableContainer> + <p> + <Trans i18nKey="common:waitingGuestsDescription"> + Her godkjenner du gjester som har <b>registrert seg manuelt</b>. + Gjester som har FEIDE-bruker trenger ikke godkjenning. + </Trans> + </p> + <GuestTable guests={guests} emptyText={t('common:noWaitingGuests')} /> </AccordionDetails> - </Accordion> + </StyledAccordion> ) } -interface FrontPageProps { - guests: Guest[] -} - function FrontPage({ guests }: FrontPageProps) { return ( <Page> <SponsorGuestButtons yourGuestsActive /> - <WaitingForGuestRegistration persons={guests} /> + <InvitedGuests persons={guests} /> + <br /> <WaitingGuests persons={guests} /> + <br /> <ActiveGuests persons={guests} /> </Page> )