feat(enclave): typeahead UX for group Add member #145

Merged
nrupard merged 1 commit from feat/lc-83-add-member-typeahead into main 2026-05-18 21:35:10 +02:00
Owner

Follow-up to PR #144. The first cut at the group-member UI took a raw user id in a text input, which is unusable - nobody knows their own UUID, let alone someone else's. This rewires it to use the same typeahead UX the enclave invite flow and DM start use.

UX:

  • Per-group Add member collapsible now contains a search input ("Search by username or display name...") and a popover that fills via hx-get.
  • Each result row shows the avatar, display_name, and @username. State is computed server-side: Add button (addable), In group pill (already a member of the group), Not in enclave pill (must be invited to the enclave first).
  • Clicking Add POSTs the row, swapping it in-place with an Added confirmation. The bad-input branch (race: user left the enclave between fetch and click) returns an inline red row.

Wire:

  • New GET /enclave/{id}/groups/{gid}/members/search?q= reuses db::auth::search_users + a per-group id set to compute row state. Returns GroupMemberSearchFragment.
  • Existing POST /enclave/{id}/groups/{gid}/members now returns GroupMemberRowResult (HTML row) instead of a redirect; mirrors POST /enclave/{id}/invite.
  • GroupMemberCandidate, GroupMemberCandidateState, and the two template structs live in views/enclave.rs next to their invite counterparts.
  • Templates: enclave/group_member_search.html, enclave/group_member_row_result.html. Both follow the existing enclave/invite_search.html / enclave/invite_row_result.html shape so the avatar partial is reused unchanged.

Validated: just check clean (server + desktop + clippy both feature sets + fmt). routes_user_groups integration suite 3/3 passing. Existing test updated from SEE_OTHER to OK because the add response shape moved from redirect to row fragment.

Follow-up to PR #144. The first cut at the group-member UI took a raw user id in a text input, which is unusable - nobody knows their own UUID, let alone someone else's. This rewires it to use the same typeahead UX the enclave invite flow and DM start use. UX: - Per-group `Add member` collapsible now contains a search input ("Search by username or display name...") and a popover that fills via `hx-get`. - Each result row shows the avatar, display_name, and `@username`. State is computed server-side: `Add` button (addable), `In group` pill (already a member of the group), `Not in enclave` pill (must be invited to the enclave first). - Clicking `Add` POSTs the row, swapping it in-place with an `Added` confirmation. The bad-input branch (race: user left the enclave between fetch and click) returns an inline red row. Wire: - New `GET /enclave/{id}/groups/{gid}/members/search?q=` reuses `db::auth::search_users` + a per-group id set to compute row state. Returns `GroupMemberSearchFragment`. - Existing `POST /enclave/{id}/groups/{gid}/members` now returns `GroupMemberRowResult` (HTML row) instead of a redirect; mirrors `POST /enclave/{id}/invite`. - `GroupMemberCandidate`, `GroupMemberCandidateState`, and the two template structs live in `views/enclave.rs` next to their invite counterparts. - Templates: `enclave/group_member_search.html`, `enclave/group_member_row_result.html`. Both follow the existing `enclave/invite_search.html` / `enclave/invite_row_result.html` shape so the avatar partial is reused unchanged. Validated: `just check` clean (server + desktop + clippy both feature sets + fmt). `routes_user_groups` integration suite 3/3 passing. Existing test updated from `SEE_OTHER` to `OK` because the add response shape moved from redirect to row fragment.
feat(enclave): typeahead UX for group "Add member"
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 1m4s
8e6cc6d7b9
Follow-up to LC-83 UI (PR #144). The first cut took a raw user_id in an open text input, which is unusable: nobody knows their own UUID, let alone someone else's. Switch to the same typeahead pattern enclave invite and DM start use: search by username or display_name, candidates render inline with an "Add" button, already-in-group rows render an "In group" pill, and users who are not members of the enclave render "Not in enclave" so admins do not waste a click on someone they would have to invite first.

New endpoint `GET /enclave/{id}/groups/{gid}/members/search?q=` reuses `db::auth::search_users` plus a per-group membership set to compute the per-row state. `POST /enclave/{id}/groups/{gid}/members` now returns a row fragment (`GroupMemberRowResult`) the typeahead swaps in via `hx-swap="outerHTML"`, mirroring `POST /enclave/{id}/invite`. The bad-input branch (user not in enclave) returns an inline red row instead of erroring, since that case is now a normal interaction (admin clicked Add on a Not-in-enclave row that became one between fetch and click).

The settings template swaps the previous `<input name="user_id">` for the typeahead input + popover; existing "Member chips" row above the popover already shows current membership, so the loop is closed without a manual reload. Test updated from `SEE_OTHER` to `OK` because the response shape moved from redirect to row fragment.
nrupard deleted branch feat/lc-83-add-member-typeahead 2026-05-18 21:35:10 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!145
No description provided.