Skip to content

Commit

Permalink
Show waiting list placement for multiple available pools
Browse files Browse the repository at this point in the history
  • Loading branch information
eikhr committed Sep 25, 2024
1 parent 8ae9420 commit 185d9a0
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 27 deletions.
9 changes: 6 additions & 3 deletions app/reducers/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -174,12 +175,14 @@ export const selectEventByIdOrSlug = createSelector(

export type PoolRegistrationWithUser = Overwrite<
ReadRegistration,
{ user: PublicUser }
{ user: PublicUserWithAbakusGroups }
>;
export type PoolWithRegistrations = Overwrite<
Optional<AuthPool, 'activationDate'>,
{ registrations: PoolRegistrationWithUser[] }
>;
> & {
isWaitingList?: boolean;
};
export const selectPoolsForEvent = createSelector(
selectEventById<DetailedEvent>,
selectPoolEntities,
Expand Down Expand Up @@ -243,7 +246,7 @@ export const selectMergedPool = createSelector(selectPoolsForEvent, (pools) => {
export const selectMergedPoolWithRegistrations = createSelector(
selectPoolsForEvent,
selectRegistrationEntities<ReadRegistration>,
selectUserEntities<PublicUser>,
selectUserEntities<PublicUserWithAbakusGroups>,
(pools, registrationEntities, userEntities) => {
if (pools.length === 0) return [];
return [
Expand Down
14 changes: 7 additions & 7 deletions app/routes/events/components/EventDetail/AttendeeSection.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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}
/>
))}
Expand Down
45 changes: 45 additions & 0 deletions app/routes/events/components/EventDetail/getWaitingListPosition.ts
Original file line number Diff line number Diff line change
@@ -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((pool) => !pool.isWaitingList);

const applicablePools = nonWaitingListPools.filter((pool) =>
isPermittedInPool(currentRegistration.user, pool),
);

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,
};
});
};
2 changes: 2 additions & 0 deletions app/routes/events/components/EventDetail/usePools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export function usePools(
registrations: waitingRegistrations,
registrationCount: waitingRegistrations.length,
permissionGroups: [],
isWaitingList: true,
},
]
: pools;
Expand All @@ -55,6 +56,7 @@ export function usePools(
name: 'Venteliste',
registrationCount: event.waitingRegistrationCount,
permissionGroups: [],
isWaitingList: true,
},
]
: pools;
Expand Down
52 changes: 38 additions & 14 deletions app/routes/events/components/RegistrationMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -246,8 +252,7 @@ const RegistrationMeta = ({
hasEnded,
useConsent,
isPriced,
registrationIndex,
hasSimpleWaitingList,
waitingListPosition,
photoConsents,
eventSemester,
skeleton,
Expand Down Expand Up @@ -282,21 +287,40 @@ const RegistrationMeta = ({
/>
)}
</>
) : hasSimpleWaitingList ? (
) : (
<TextWithIconWrapper
iconName="pause-circle-outline"
content={
<>
Din plass i ventelisten er{' '}
<strong>{registrationIndex + 1}</strong>
</>
waitingListPosition === undefined ? (
<>Du {hasEnded ? 'stod' : 'står'} på venteliste</>
) : typeof waitingListPosition === 'number' ? (
<>
Din plass i ventelisten er{' '}
<strong>{waitingListPosition}</strong>
</>
) : waitingListPosition.length === 1 ? (
<>
Din plass i ventelisten for{' '}
{waitingListPosition[0].poolName} er{' '}
<strong>{waitingListPosition[0].position}</strong>
</>
) : (
<>
Dine plasser i ventelistene er{' '}
{waitingListPosition.map(
({ poolName, position }, index) => (
<>
<strong>{position}</strong> for {poolName}
{index < waitingListPosition.length - 2
? ', '
: index < waitingListPosition.length - 1 && ' og '}
</>
),
)}
</>
)
}
/>
) : (
<TextWithIconWrapper
iconName="pause-circle-outline"
content={`Du ${hasEnded ? 'stod' : 'står'} på venteliste`}
/>
)}
<PresenceStatus
presence={registration.presence}
Expand Down
10 changes: 7 additions & 3 deletions app/store/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface User {
penalties: EntityId[];
icalToken: string;
abakusGroups: EntityId[];
allAbakusGroupIds: EntityId[];
isAbakusMember: boolean;
isAbakomMember: boolean;
pastMemberships: PastMembership[];
Expand Down Expand Up @@ -131,14 +132,17 @@ export type PublicUser = Pick<
| 'linkedinId'
>;

export type PublicUserWithAbakusGroups = Pick<User, 'abakusGroups'> &
export type PublicUserWithAbakusGroups = Pick<
User,
'abakusGroups' | 'allAbakusGroupIds'
> &
PublicUser;

export type PublicUserWithGroups = Pick<
User,
'abakusGroups' | 'pastMemberships' | 'memberships'
'pastMemberships' | 'memberships'
> &
PublicUser;
PublicUserWithAbakusGroups;

export type AdministrateUser = Pick<User, 'abakusGroups'> & PublicUser;

Expand Down

0 comments on commit 185d9a0

Please sign in to comment.