diff --git a/app/components/UserAttendance/AttendanceModalContent.module.css b/app/components/UserAttendance/AttendanceModalContent.module.css index 5da826a313..40ddc9f0b3 100644 --- a/app/components/UserAttendance/AttendanceModalContent.module.css +++ b/app/components/UserAttendance/AttendanceModalContent.module.css @@ -124,3 +124,27 @@ transition: opacity var(--linear-fast); opacity: 0.4; } + +.groupFilters { + position: relative; + flex-shrink: 0; + overflow: hidden; +} + +.collapsedIndicator { + position: absolute; + right: 0; + left: 0; + bottom: 0; + height: 50%; + background: linear-gradient(to bottom, transparent, var(--color-white) 90%); +} + +.groupFilterButton { + cursor: pointer; + + &.selected { + background-color: var(--lego-font-color); + color: var(--inverted-font-color); + } +} diff --git a/app/components/UserAttendance/AttendanceModalContent.tsx b/app/components/UserAttendance/AttendanceModalContent.tsx index 47aa06299d..09cbf6f6d3 100644 --- a/app/components/UserAttendance/AttendanceModalContent.tsx +++ b/app/components/UserAttendance/AttendanceModalContent.tsx @@ -2,18 +2,22 @@ import { Flex } from '@webkom/lego-bricks'; import cx from 'classnames'; import { flatMap } from 'lodash'; import { Send } from 'lucide-react'; -import { useState, useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import { TextInput } from 'app/components/Form'; import { ProfilePicture } from 'app/components/Image'; +import { GroupFilter } from 'app/components/UserAttendance/GroupFilter'; import EmptyState from '../EmptyState'; import styles from './AttendanceModalContent.module.css'; import type { EntityId } from '@reduxjs/toolkit'; -import type { PublicUser } from 'app/store/models/User'; +import type { + PublicUser, + PublicUserWithAbakusGroups, +} from 'app/store/models/User'; export type AttendanceModalRegistration = { id: EntityId; - user: PublicUser; + user: PublicUser | PublicUserWithAbakusGroups; pool?: EntityId; }; @@ -66,18 +70,28 @@ const AttendanceModalContent = ({ selectedPool, isMeeting, }: Props) => { - const [filter, setFilter] = useState(''); + const [search, setSearch] = useState(''); + const [groupFilter, setGroupFilter] = useState(null); const amendedPools = useMemo(() => generateAmendedPools(pools), [pools]); const registrations = useMemo( + () => amendedPools[selectedPool]?.registrations, + [amendedPools, selectedPool], + ); + + const filteredRegistrations = useMemo( () => - amendedPools[selectedPool]?.registrations.filter((registration) => { - return registration.user.fullName - .toLowerCase() - .includes(filter.toLowerCase()); - }), - [filter, amendedPools, selectedPool], + registrations.filter( + (registration) => + registration.user.fullName + .toLowerCase() + .includes(search.toLowerCase()) && + (groupFilter && 'abakusGroups' in registration.user + ? registration.user.abakusGroups.includes(groupFilter) + : true), + ), + [registrations, search, groupFilter], ); return ( @@ -86,13 +100,21 @@ const AttendanceModalContent = ({ type="text" prefix="search" placeholder="Søk etter navn" - onChange={(e) => setFilter(e.target.value)} + onChange={(e) => setSearch(e.target.value)} className={styles.searchInput} /> + {!isMeeting && ( + + )} +
    - {registrations.length > 0 ? ( - registrations?.map((registration) => ( + {filteredRegistrations.length > 0 ? ( + filteredRegistrations?.map((registration) => (
  • void; +}; + +export const GroupFilter = ({ + registrations, + groupFilter, + setGroupFilter, +}: Props) => { + const currentUser = useAppSelector(selectCurrentUser); + const allGroups = useAppSelector(selectAllGroups); + const [hovered, setHovered] = useState(false); + const expanded = hovered || groupFilter !== null; + + const topGroups = useMemo( + () => + allGroups + .filter( + (group) => + currentUser?.abakusGroups.includes(group.id) && + !excludeFilterGroups.includes(group.id), + ) + .map( + (group) => + [ + group, + registrations.filter( + (reg) => + 'abakusGroups' in reg.user && + reg.user.abakusGroups.includes(group.id), + ).length, + ] as const, + ) + .sort((a, b) => b[1] - a[1]), + [allGroups, currentUser?.abakusGroups, registrations], + ); + + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {topGroups.map(([group]) => ( + + groupFilter === group.id + ? setGroupFilter(null) + : setGroupFilter(group.id) + } + className={cx( + styles.groupFilterButton, + groupFilter === group.id && styles.selected, + )} + > + {group.name} + + ))} + {!expanded &&
    } + + ); +};