diff --git a/app/reducers/events.ts b/app/reducers/events.ts index c5efb5ffca..7c2c5d7c6d 100644 --- a/app/reducers/events.ts +++ b/app/reducers/events.ts @@ -32,6 +32,7 @@ import type { AdministrateUser, AdministrateUserWithGrade, PublicUser, + PublicUserWithAbakusGroups, } from 'app/store/models/User'; import type { AnyAction } from 'redux'; import type { Optional, Overwrite } from 'utility-types'; @@ -174,7 +175,7 @@ export const selectEventByIdOrSlug = createSelector( export type PoolRegistrationWithUser = Overwrite< ReadRegistration, - { user: PublicUser } + { user: PublicUserWithAbakusGroups } >; export type PoolWithRegistrations = Overwrite< Optional, @@ -243,7 +244,7 @@ export const selectMergedPool = createSelector(selectPoolsForEvent, (pools) => { export const selectMergedPoolWithRegistrations = createSelector( selectPoolsForEvent, selectRegistrationEntities, - selectUserEntities, + selectUserEntities, (pools, registrationEntities, userEntities) => { if (pools.length === 0) return []; return [ diff --git a/app/routes/events/components/EventDetail/AttendeeSection.tsx b/app/routes/events/components/EventDetail/AttendeeSection.tsx index 2d09ef3b1b..e2179ea498 100644 --- a/app/routes/events/components/EventDetail/AttendeeSection.tsx +++ b/app/routes/events/components/EventDetail/AttendeeSection.tsx @@ -1,8 +1,10 @@ import { Flex } from '@webkom/lego-bricks'; import moment from 'moment-timezone'; +import { useMemo } from 'react'; import Attendance from 'app/components/UserAttendance/Attendance'; import { useIsLoggedIn } from 'app/reducers/auth'; import { selectRegistrationsFromPools } from 'app/reducers/events'; +import { getWaitingListPosition } from 'app/routes/events/components/EventDetail/getWaitingListPosition'; import RegistrationMeta, { RegistrationMetaSkeleton, } from 'app/routes/events/components/RegistrationMeta'; @@ -39,11 +41,10 @@ export const AttendeeSection = ({ ); const currentMoment = moment(); - const hasSimpleWaitingList = - pools.filter((p) => p.name != 'Venteliste').length <= 1; - const waitingListIndex = - currentRegistration && - currentPool?.registrations.indexOf(currentRegistration); + const waitingListPosition = useMemo( + () => getWaitingListPosition(currentRegistration, currentPool, pools), + [currentRegistration, currentPool, pools], + ); // The UserGrid is expanded when there's less than 5 minutes till activation const minUserGridRows = @@ -78,8 +79,7 @@ export const AttendeeSection = ({ hasEnded={moment(event.endTime).isBefore(currentMoment)} registration={currentRegistration} isPriced={event.isPriced} - registrationIndex={waitingListIndex ?? 0} - hasSimpleWaitingList={hasSimpleWaitingList} + waitingListPosition={waitingListPosition} skeleton={showSkeleton} /> ))} diff --git a/app/routes/events/components/EventDetail/getWaitingListPosition.ts b/app/routes/events/components/EventDetail/getWaitingListPosition.ts new file mode 100644 index 0000000000..a87f3197f2 --- /dev/null +++ b/app/routes/events/components/EventDetail/getWaitingListPosition.ts @@ -0,0 +1,45 @@ +import type { + PoolRegistrationWithUser, + PoolWithRegistrations, +} from 'app/reducers/events'; +import type { PublicUserWithAbakusGroups } from 'app/store/models/User'; + +const isPermittedInPool = ( + user: PublicUserWithAbakusGroups, + pool: PoolWithRegistrations, +) => { + return pool.permissionGroups.some((permissionGroup) => + user.allAbakusGroupIds.some( + (userGroup) => userGroup === permissionGroup.id, + ), + ); +}; + +export const getWaitingListPosition = ( + currentRegistration?: PoolRegistrationWithUser, + waitingList?: PoolWithRegistrations, + pools?: PoolWithRegistrations[], +) => { + if (!currentRegistration || !waitingList || !pools) return undefined; + + const nonWaitingListPools = pools.filter((p) => p.name !== 'Venteliste'); + + const applicablePools = pools.filter((p) => + isPermittedInPool(currentRegistration.user, p), + ); + + if (applicablePools.length === 0) return undefined; + if (nonWaitingListPools.length === 1) { + return waitingList.registrations.indexOf(currentRegistration) + 1; + } + return applicablePools.map((p) => { + const applicableWaitingListRegistrations = waitingList.registrations.filter( + (r) => isPermittedInPool(r.user, p), + ); + return { + poolName: p.name, + position: + applicableWaitingListRegistrations.indexOf(currentRegistration) + 1, + }; + }); +}; diff --git a/app/routes/events/components/RegistrationMeta.tsx b/app/routes/events/components/RegistrationMeta.tsx index 8edc8c4598..d1cddd3734 100644 --- a/app/routes/events/components/RegistrationMeta.tsx +++ b/app/routes/events/components/RegistrationMeta.tsx @@ -23,11 +23,17 @@ import type { import type { PoolRegistrationWithUser } from 'app/reducers/events'; import type { Presence } from 'app/store/models/Registration'; +type WaitingListPosition = + | number + | { + poolName: string; + position: number; + }[]; + type Props = { registration?: PoolRegistrationWithUser; isPriced: boolean; - registrationIndex: number; - hasSimpleWaitingList: boolean; + waitingListPosition?: WaitingListPosition; useConsent: boolean; hasOpened: boolean; hasEnded: boolean; @@ -246,8 +252,7 @@ const RegistrationMeta = ({ hasEnded, useConsent, isPriced, - registrationIndex, - hasSimpleWaitingList, + waitingListPosition, photoConsents, eventSemester, skeleton, @@ -282,14 +287,30 @@ const RegistrationMeta = ({ /> )} - ) : hasSimpleWaitingList ? ( + ) : waitingListPosition ? ( - Din plass i ventelisten er{' '} - {registrationIndex + 1} - + typeof waitingListPosition === 'number' ? ( + <> + Din plass i ventelisten er{' '} + {waitingListPosition} + + ) : ( + <> + Din plass i ventelisten{' '} + {waitingListPosition.map( + ({ poolName, position }, index) => ( + <> + for {poolName} er {position} + {index < waitingListPosition.length - 2 + ? ', ' + : index < waitingListPosition.length - 1 && ' og '} + + ), + )} + + ) } /> ) : ( diff --git a/app/store/models/User.ts b/app/store/models/User.ts index 782c7fd73f..56505f557c 100644 --- a/app/store/models/User.ts +++ b/app/store/models/User.ts @@ -42,6 +42,7 @@ interface User { penalties: EntityId[]; icalToken: string; abakusGroups: EntityId[]; + allAbakusGroupIds: EntityId[]; isAbakusMember: boolean; isAbakomMember: boolean; pastMemberships: PastMembership[]; @@ -131,14 +132,17 @@ export type PublicUser = Pick< | 'linkedinId' >; -export type PublicUserWithAbakusGroups = Pick & +export type PublicUserWithAbakusGroups = Pick< + User, + 'abakusGroups' | 'allAbakusGroupIds' +> & PublicUser; export type PublicUserWithGroups = Pick< User, - 'abakusGroups' | 'pastMemberships' | 'memberships' + 'pastMemberships' | 'memberships' > & - PublicUser; + PublicUserWithAbakusGroups; export type AdministrateUser = Pick & PublicUser;