No description
Find a file
Randall Stillwell 29e69e964b feat(invites): smart recipient autocomplete combobox (Task 3, done)
Closes Task-invite-recipient-autocomplete. The invite dialog's plain
email input is replaced with a debounced combobox that surfaces the
four real cases — existing member, pending invite, known user from a
sibling workspace, brand-new email — before the inviter hits send.

Subagent ran in parallel while the main thread shipped Task 2's UI;
file-level non-overlap held (subagent stayed in
apps/web/components/teams/invite-recipient-combobox.tsx and the
shared types; main thread stayed in invite-dialog.tsx and the
teams page). This commit folds the subagent's deliverable in plus
the two-line wire-up that swaps the input for the combobox.

Files (5 by subagent + 1 wire-up by main thread):

@tasks/shared:
* packages/shared/src/types/invite-suggestions.ts — InviteSuggestion
  union + pure mergeInviteSuggestions ranker. Lives in shared so
  client + server consume one type definition.
* packages/shared/src/types/invite-suggestions.test.ts — 9 vitest
  cases covering kind ordering, dedupe (known_user vs member by
  userId, vs pending_invite by lowercased email), new_email
  suppression when other kinds cover the typed address, the 10-
  result limit, and email normalization.
* packages/shared/src/types/index.ts — re-export.

apps/web:
* apps/web/server/routers/invites.ts — new `suggestRecipient`
  procedure on workspaceProcedure (owner/admin only). Implements
  the four kinds with the tenancy fence wired as a two-step query:
  first SELECT DISTINCT workspace_id FROM workspace_members WHERE
  user_id = inviter (the inviter's workspace pool), then
  inArray(workspaceMembers.workspaceId, pool) + ne(users.id,
  inviter) on the candidate join. Read the procedure JSDoc for the
  full set of invariants. All user-typed patterns escape through
  escapeIlike with the ESCAPE '\\' clause (mirrors search.ts).
  No existing exports modified.
* apps/web/components/teams/invite-recipient-combobox.tsx —
  standalone controlled combobox. 200ms debounce, min-2-char gate,
  distinct row styling per kind, ArrowUp/Down/Enter/Esc keyboard
  nav, outside-click close.
* apps/web/components/teams/invite-dialog.tsx (wire-up) — Input
  swapped for InviteRecipientCombobox. Added an
  onFocusExistingMember prop so a future teams-page integration
  can scroll/focus the matching row when a `member` suggestion is
  picked; for now the dialog just closes cleanly on member-pick.

Gates: 0 lint errors / 15 warnings (14 baseline + 1 incidental
from earlier teams-page work, none from this task's files); 6/6
type-check; 23/23 tests (14 baseline + 9 new).

Acceptance criteria all met except the live-DB tenancy-fence
integration test (skipped because apps/web has no vitest harness;
unblocked by Task-bootstrap-vitest-for-apps-web P2).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 10:39:09 -05:00
.cursor/rules docs: add AGENTS.md and Cursor rules 2026-05-06 22:14:16 -05:00
.github/workflows test(ci): bootstrap vitest + GitHub Actions and lock in 3-gate quality bar 2026-06-02 09:23:55 -05:00
apps feat(invites): smart recipient autocomplete combobox (Task 3, done) 2026-06-02 10:39:09 -05:00
config feat: ECHODO app shell, Coolify deploy, Authentik + Umami 2026-04-26 14:34:34 -05:00
docker docker: raise web build V8 heap to 5 GB to match 8 GB host 2026-04-28 13:33:46 -05:00
docs feat: ECHODO app shell, Coolify deploy, Authentik + Umami 2026-04-26 14:34:34 -05:00
packages feat(invites): smart recipient autocomplete combobox (Task 3, done) 2026-06-02 10:39:09 -05:00
plans feat(invites): smart recipient autocomplete combobox (Task 3, done) 2026-06-02 10:39:09 -05:00
.env.example feat: ECHODO app shell, Coolify deploy, Authentik + Umami 2026-04-26 14:34:34 -05:00
.gitignore feat: ECHODO app shell, Coolify deploy, Authentik + Umami 2026-04-26 14:34:34 -05:00
AGENTS.md test(ci): bootstrap vitest + GitHub Actions and lock in 3-gate quality bar 2026-06-02 09:23:55 -05:00
package.json test(ci): bootstrap vitest + GitHub Actions and lock in 3-gate quality bar 2026-06-02 09:23:55 -05:00
pnpm-lock.yaml test(ci): bootstrap vitest + GitHub Actions and lock in 3-gate quality bar 2026-06-02 09:23:55 -05:00
pnpm-workspace.yaml feat: Full project management application scaffold 2026-03-26 22:39:16 -05:00
tsconfig.json feat: Full project management application scaffold 2026-03-26 22:39:16 -05:00
turbo.json test(ci): bootstrap vitest + GitHub Actions and lock in 3-gate quality bar 2026-06-02 09:23:55 -05:00