From 69f83da9b2bdd69ddc760c2d90211869752fe286 Mon Sep 17 00:00:00 2001 From: Guilhem Date: Thu, 22 Oct 2026 17:11:52 +0200 Subject: [PATCH] scroll and search the group drawer's member lists (#11945) * feat: scroll and search the group drawer's member lists Co-Authored-By: Claude Opus 5.5 (1M context) * fix: stop the add member picker loading after a failed username load Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- .../src/lib/components/GroupEditor.svelte | 240 ++++++++++++------ 1 file changed, 169 insertions(+), 71 deletions(-) diff --git a/frontend/src/lib/components/GroupEditor.svelte b/frontend/src/lib/components/GroupEditor.svelte index 2c256207e1..44d7dc5eb1 100644 --- a/frontend/src/lib/components/GroupEditor.svelte +++ b/frontend/src/lib/components/GroupEditor.svelte @@ -97,7 +97,10 @@ ) let group: Group | undefined let instance_group: InstanceGroup | undefined = $state() - let usernames: string[] = $state([]) + // Every username of the workspace, for the Add member picker: loaded on its first open, + // since it grows with the workspace and most opens of this editor add no one. + let usernames: string[] | undefined = $state() + let usernamesLoading = $state(false) let groupNames: string[] = $state([]) let loaded = $state(false) let reloadHistory = $state(0) @@ -140,9 +143,41 @@ } async function loadUsernames(): Promise { - usernames = await UserService.listUsernames({ workspace: $operatingWorkspace! }) + if (usernames !== undefined || usernamesLoading) return + usernamesLoading = true + try { + usernames = await UserService.listUsernames({ workspace: $operatingWorkspace! }) + } catch (e) { + sendUserToast(`Could not load users: ${e?.body ?? e}`, true) + } finally { + usernamesLoading = false + } } + // From this many rows a table no longer fits its 320px box: it scrolls, and a search shows. + // Both follow every row, not the matching ones, so typing never resizes the drawer. + const MANY_ROWS = 8 + let memberSearch = $state('') + const memberQuery = $derived( + draft.members.length >= MANY_ROWS ? memberSearch.trim().toLowerCase() : '' + ) + const shownMembers = $derived( + draft.members + .filter((m) => !memberQuery || m.member_name.toLowerCase().includes(memberQuery)) + .sort((a, b) => a.member_name.localeCompare(b.member_name)) + ) + + let emailSearch = $state('') + const instanceEmails = $derived(instance_group?.emails ?? []) + const emailQuery = $derived( + instanceEmails.length >= MANY_ROWS ? emailSearch.trim().toLowerCase() : '' + ) + const shownEmails = $derived( + instanceEmails + .filter((e) => !emailQuery || e.toLowerCase().includes(emailQuery)) + .sort((a, b) => a.localeCompare(b)) + ) + async function loadGroupNames(): Promise { groupNames = (await GroupService.listGroupNames({ workspace: $operatingWorkspace! })) ?? [] } @@ -156,7 +191,6 @@ } async function load() { - loadAside(loadUsernames) if (isNew) { loadAside(loadGroupNames) writeVerdict = true @@ -410,69 +444,87 @@