From 9d259cae06fe010cf1ca08c08530b502ce26a3c4 Mon Sep 17 00:00:00 2001 From: Silas Berger Date: Tue, 2 Sep 2025 12:00:17 +0200 Subject: [PATCH 1/8] Remove user table from user page. --- src/pages/user/index.tsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/pages/user/index.tsx b/src/pages/user/index.tsx index 967eb9683..0d131b6cf 100644 --- a/src/pages/user/index.tsx +++ b/src/pages/user/index.tsx @@ -171,22 +171,6 @@ const UserPage = observer(() => { )} )} - - {userStore.current?.hasElevatedAccess && ( -
-

User Tabelle

-
- -
-
- )} -

Account

- {current?.hasElevatedAccess && ( <>
Admin
From 618bd1dba25d61791bace9561567bd2044145a12 Mon Sep 17 00:00:00 2001 From: Silas Berger Date: Tue, 2 Sep 2025 13:08:27 +0200 Subject: [PATCH 2/8] Add student group search. --- .../Admin/StudentGroupPanel/index.tsx | 64 ++++++++++++++----- .../StudentGroupPanel/styles.module.scss | 40 ++++++++++++ 2 files changed, 89 insertions(+), 15 deletions(-) diff --git a/src/components/Admin/StudentGroupPanel/index.tsx b/src/components/Admin/StudentGroupPanel/index.tsx index 12ed680bc..de5280091 100644 --- a/src/components/Admin/StudentGroupPanel/index.tsx +++ b/src/components/Admin/StudentGroupPanel/index.tsx @@ -4,36 +4,70 @@ import styles from './styles.module.scss'; import { observer } from 'mobx-react-lite'; import { useStore } from '@tdev-hooks/useStore'; import Button from '@tdev-components/shared/Button'; -import { mdiPlusCircleOutline } from '@mdi/js'; +import { mdiMagnify, mdiPlusCircleOutline, mdiRestore } from '@mdi/js'; import StudentGroup from '@tdev-components/StudentGroup'; import _ from 'es-toolkit/compat'; -import scheduleMicrotask from '@tdev-components/util/scheduleMicrotask'; import { action } from 'mobx'; +import Icon from '@mdi/react'; const StudentGroupPanel = observer(() => { const userStore = useStore('userStore'); const groupStore = useStore('studentGroupStore'); const current = userStore.current; + const [searchFilter, setSearchFilter] = React.useState(''); + const [searchRegex, setSearchRegex] = React.useState(new RegExp(searchFilter, 'i')); + + React.useEffect(() => { + setSearchRegex(new RegExp(searchFilter, 'i')); + }, [searchFilter]); + if (!current?.hasElevatedAccess) { return null; } return (
-
+ +
{_.orderBy( - groupStore.managedStudentGroups.filter((g) => !g.parentId), + groupStore.managedStudentGroups + .filter((g) => !g.parentId) + .filter((user) => searchRegex.test(user.searchTerm)), ['_pristine.name', 'createdAt'], ['asc', 'desc'] ).map((group) => ( diff --git a/src/components/Admin/StudentGroupPanel/styles.module.scss b/src/components/Admin/StudentGroupPanel/styles.module.scss index 32dbc1a66..6faf16241 100644 --- a/src/components/Admin/StudentGroupPanel/styles.module.scss +++ b/src/components/Admin/StudentGroupPanel/styles.module.scss @@ -1,3 +1,43 @@ +.controls { + display: flex; + flex-direction: row; + gap: 1em; + width: 100%; + margin-bottom: 0.5em; + flex-wrap: wrap; + + .searchBox { + display: flex; + align-items: center; + grid-area: 0.5em; + flex-grow: 1; + border: 1px solid var(--ifm-color-secondary); + border-radius: var(--ifm-global-radius); + padding: 0 0.2em; + + .searchInput { + display: flex; + align-items: stretch; + width: 100%; + + input[type='text'] { + flex-grow: 1; + border: none; + outline: none; + padding: 0.5em; + border-radius: 4px; + background-color: transparent; + width: 100%; + + &::placeholder { + color: inherit; + opacity: 0.5; + } + } + } + } +} + .studentGroups { display: flex; flex-direction: row; From c50022116161d77d9d7785a7a49d5db8c5708df4 Mon Sep 17 00:00:00 2001 From: Silas Berger Date: Tue, 2 Sep 2025 15:30:10 +0200 Subject: [PATCH 3/8] Add group membership badges. --- .../StudentGroup/AddMembersPopup/AddUser.tsx | 11 ++++++-- .../AddMembersPopup/styles.module.scss | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/components/StudentGroup/AddMembersPopup/AddUser.tsx b/src/components/StudentGroup/AddMembersPopup/AddUser.tsx index 45d7b6913..aca0ea7d4 100644 --- a/src/components/StudentGroup/AddMembersPopup/AddUser.tsx +++ b/src/components/StudentGroup/AddMembersPopup/AddUser.tsx @@ -47,11 +47,16 @@ const AddUser = observer((props: _AddMembersPopupPropsInternal) => { )} title={user.email} > -
- +
+
{' '} {user.nameShort} - +
+
+ {user.studentGroups.map((group) => ( + {group.name} + ))} +
-
- {user.studentGroups.map((group) => ( - {group.name} - ))} +
+
+ {user.studentGroups.map((group) => ( + {group.name} + ))} +
+
+
+ ); +}; const AddUser = observer((props: _AddMembersPopupPropsInternal) => { const userStore = useStore('userStore'); @@ -17,7 +59,10 @@ const AddUser = observer((props: _AddMembersPopupPropsInternal) => { setSearchRegex(new RegExp(searchFilter, 'i')); }, [searchFilter]); - const group = props.studentGroup; + const group: StudentGroup = props.studentGroup; + const users = userStore.users.filter((user) => searchRegex.test(user.searchTerm)); + const usersInParentGroup = users.filter((user) => group.parent?.userIds.has(user.id)); + const otherUsers = users.filter((user) => !group.parent?.userIds.has(user.id)); return ( <> @@ -36,42 +81,17 @@ const AddUser = observer((props: _AddMembersPopupPropsInternal) => { />
- {userStore.users - .filter((user) => searchRegex.test(user.searchTerm)) - .map((user, idx) => ( -
-
-
- {' '} - {user.nameShort} -
-
-
- {user.studentGroups.map((group) => ( - {group.name} - ))} -
-
-
-
-
-
- ))} + {usersInParentGroup.map((user, idx) => ( + + ))} + {otherUsers.map((user, idx) => ( + 0} + /> + ))}
From de882f6aab46c55e00abe2c0fc28e392885c6a01 Mon Sep 17 00:00:00 2001 From: Silas Berger Date: Tue, 2 Sep 2025 17:34:44 +0200 Subject: [PATCH 6/8] Rework membership popup styling. --- .../StudentGroup/AddMembersPopup/AddUser.tsx | 2 +- .../StudentGroup/AddMembersPopup/index.tsx | 2 +- .../AddMembersPopup/styles.module.scss | 85 +++++++++++-------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/components/StudentGroup/AddMembersPopup/AddUser.tsx b/src/components/StudentGroup/AddMembersPopup/AddUser.tsx index c3ce39fa6..00a591477 100644 --- a/src/components/StudentGroup/AddMembersPopup/AddUser.tsx +++ b/src/components/StudentGroup/AddMembersPopup/AddUser.tsx @@ -69,7 +69,7 @@ const AddUser = observer((props: _AddMembersPopupPropsInternal) => {

Benutzer:in hinzufügen

-
+
{ ref={popupRef} >
-
+

{props.studentGroup.name}

diff --git a/src/components/StudentGroup/AddMembersPopup/styles.module.scss b/src/components/StudentGroup/AddMembersPopup/styles.module.scss index 83bb9921b..50811acfc 100644 --- a/src/components/StudentGroup/AddMembersPopup/styles.module.scss +++ b/src/components/StudentGroup/AddMembersPopup/styles.module.scss @@ -1,6 +1,11 @@ .wrapper { overflow-y: auto; - max-height: 80vh; + height: 80vh; + width: 40em; + + @media screen and (max-width: 768px) { + width: 95vw; + } } .tabs { @@ -31,7 +36,7 @@ } } -.addUserCardTitle { +.addMembersPopupTitle { display: flex; flex-direction: row; align-items: center; @@ -42,6 +47,45 @@ } } +.addUserCardBody { + padding-top: 0.5em; + + .addUserListItem { + display: flex; + flex-direction: row; + width: 100%; + text-wrap: nowrap; + + .userInfo { + display: flex; + flex-direction: row; + align-items: center; + flex-grow: 0; + flex-shrink: 0; + gap: 0.2em; + } + + .groupMembership { + display: flex; + width: 100%; + flex-grow: 1; + flex-shrink: 1; + justify-content: flex-end; + align-items: center; + overflow: hidden; + + .groupMembershipBadges { + margin: 0 0.5em; + gap: 0.2em; + display: flex; + flex-direction: row; + align-items: center; + overflow-x: auto; + } + } + } +} + .importFromList { padding: 1em; @@ -55,37 +99,10 @@ } } -.addUserListItem { - display: flex; - flex-direction: row; +.textInput { width: 100%; - text-wrap: nowrap; - - .userInfo { - display: flex; - flex-direction: row; - align-items: center; - flex-grow: 0; - flex-shrink: 0; - gap: 0.2em; - } - - .groupMembership { - display: flex; - width: 100%; - flex-grow: 1; - flex-shrink: 1; - justify-content: flex-end; - align-items: center; - overflow: hidden; - - .groupMembershipBadges { - margin: 0 0.5em; - gap: 0.2em; - display: flex; - flex-direction: row; - align-items: center; - overflow-x: auto; - } - } + border: 1px solid var(--ifm-color-secondary); + border-radius: var(--ifm-global-radius); + padding: 0.5em 1em; + margin-bottom: 0.5em; } From 816b887194b1a4f6739eb93913b08349bd840840 Mon Sep 17 00:00:00 2001 From: Silas Berger Date: Tue, 2 Sep 2025 17:49:25 +0200 Subject: [PATCH 7/8] Refactor. --- .../AddMembersPopup/styles.module.scss | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/StudentGroup/AddMembersPopup/styles.module.scss b/src/components/StudentGroup/AddMembersPopup/styles.module.scss index 50811acfc..83da21979 100644 --- a/src/components/StudentGroup/AddMembersPopup/styles.module.scss +++ b/src/components/StudentGroup/AddMembersPopup/styles.module.scss @@ -13,6 +13,14 @@ --ifm-tabs-padding-horizontal: 0.6em; } +.textInput { + width: 100%; + border: 1px solid var(--ifm-color-secondary); + border-radius: var(--ifm-global-radius); + padding: 0.5em 1em; + margin-bottom: 0.5em; +} + .list { > :nth-child(2n) { background-color: var(--ifm-color-secondary-lightest); @@ -98,11 +106,3 @@ max-height: 500px; } } - -.textInput { - width: 100%; - border: 1px solid var(--ifm-color-secondary); - border-radius: var(--ifm-global-radius); - padding: 0.5em 1em; - margin-bottom: 0.5em; -} From 53ffb34d3c7b4824142a3629bba522a6561a1d61 Mon Sep 17 00:00:00 2001 From: Silas Berger Date: Tue, 16 Sep 2025 09:10:55 +0200 Subject: [PATCH 8/8] Improve group filtering. --- .../Admin/StudentGroupPanel/index.tsx | 51 ++++++++++++++++--- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/components/Admin/StudentGroupPanel/index.tsx b/src/components/Admin/StudentGroupPanel/index.tsx index de5280091..0dbb03988 100644 --- a/src/components/Admin/StudentGroupPanel/index.tsx +++ b/src/components/Admin/StudentGroupPanel/index.tsx @@ -64,15 +64,50 @@ const StudentGroupPanel = observer(() => {
- {_.orderBy( - groupStore.managedStudentGroups + {(() => { + const matches = groupStore.managedStudentGroups .filter((g) => !g.parentId) - .filter((user) => searchRegex.test(user.searchTerm)), - ['_pristine.name', 'createdAt'], - ['asc', 'desc'] - ).map((group) => ( - - ))} + .map((group) => { + let matchPriority = 0; + + if (searchRegex) { + const nameMatch = searchRegex.test(group.name); + const studentMatch = group.students?.some( + (s) => searchRegex.test(s.name) || searchRegex.test(s.email) + ); + const descriptionMatch = searchRegex.test(group.description ?? ''); + + if (nameMatch) { + matchPriority = 1; + } else if (studentMatch) { + matchPriority = 2; + } else if (descriptionMatch) { + matchPriority = 3; + } else { + // We have a search filter and this doesn't match it. + return null; + } + } + + return { + group: group, + matchPriority + }; + }) + .filter((group) => !!group); // Non-matched groups are null - filter them out. + + return _.orderBy( + matches, + ['matchPriority', '_pristine.name', 'createdAt'], + ['asc', 'asc', 'desc'] + ).map((match) => ( + + )); + })()}
);