Team view: click-to-select contacts as an explicit subset (drives 'send email to selection') #53

Open
opened 2026-05-20 11:51:48 +00:00 by mAi · 2 comments
Collaborator

Trigger

m 2026-05-20 13:51:

In the Team view I want to select specific contacts just by clicking on them — that selection is more specific than the initial filters and that should be used for the 'send email to selection'.

Shape

Two-layer selection model on the team view (/admin/team or wherever the contact list lives):

  1. Filters define the visible set (existing functionality — office / role / search etc.).
  2. Click-to-select picks an explicit subset of the visible set. The selected rows are highlighted; a counter shows '3 of 12 selected' or similar.
  3. 'Send email to selection' action targets the explicit selection, NOT the filtered set. If no explicit selection, the action either: (a) targets all filtered rows as a fallback, or (b) is disabled until something is selected. Design call.

UX details

  • Selection is per-row, click anywhere on the row toggles select (except clicks on inner action buttons / links).
  • Standard keyboard support: Shift-click to range-select, Cmd/Ctrl-click to multi-select (web-table conventions).
  • Visual: existing .entity-table row-pointer pattern (the row-click contract from project CLAUDE.md > Frontend conventions) supports click-to-navigate; selection needs a distinct mechanism (checkbox column? click-to-select with a separate row-action area?). Design call — current row-click navigates to user detail; can't reuse without breaking that.
  • Selection survives filter changes: rows that match the new filter stay selected; rows that drop out are de-selected.
  • 'Clear selection' button visible when selection > 0.

Design calls

  1. Checkbox column (most explicit; obvious affordance) vs. row click w/ modifier-key (denser; collides with row-click-to-navigate). Recommend checkbox.
  2. Where the 'Send email to selection' button lives — sticky footer when selection > 0, or always-visible in the page header.
  3. Email composer surface — the existing broadcast modal (now using the unified modal primitive from t-paliad-217), or a fresh send-flow.
  4. Persistence of selection across page navigation — preserve until cleared, or wipe on navigation? Probably wipe.

Role

inventor → coder for the selection-model design call (checkbox-vs-click + send-flow surface). Coder shift after.

## Trigger m 2026-05-20 13:51: > In the Team view I want to select specific contacts just by clicking on them — that selection is more specific than the initial filters and that should be used for the 'send email to selection'. ## Shape Two-layer selection model on the team view (`/admin/team` or wherever the contact list lives): 1. **Filters** define the visible set (existing functionality — office / role / search etc.). 2. **Click-to-select** picks an explicit subset of the visible set. The selected rows are highlighted; a counter shows '3 of 12 selected' or similar. 3. **'Send email to selection'** action targets the explicit selection, NOT the filtered set. If no explicit selection, the action either: (a) targets all filtered rows as a fallback, or (b) is disabled until something is selected. Design call. ## UX details - Selection is per-row, click anywhere on the row toggles select (except clicks on inner action buttons / links). - Standard keyboard support: Shift-click to range-select, Cmd/Ctrl-click to multi-select (web-table conventions). - Visual: existing `.entity-table` row-pointer pattern (the row-click contract from project CLAUDE.md > Frontend conventions) supports click-to-navigate; selection needs a distinct mechanism (checkbox column? click-to-select with a separate row-action area?). Design call — current row-click navigates to user detail; can't reuse without breaking that. - Selection survives filter changes: rows that match the new filter stay selected; rows that drop out are de-selected. - 'Clear selection' button visible when selection > 0. ## Design calls 1. Checkbox column (most explicit; obvious affordance) vs. row click w/ modifier-key (denser; collides with row-click-to-navigate). Recommend checkbox. 2. Where the 'Send email to selection' button lives — sticky footer when selection > 0, or always-visible in the page header. 3. Email composer surface — the existing broadcast modal (now using the unified modal primitive from t-paliad-217), or a fresh send-flow. 4. Persistence of selection across page navigation — preserve until cleared, or wipe on navigation? Probably wipe. ## Role **inventor → coder** for the selection-model design call (checkbox-vs-click + send-flow surface). Coder shift after.
mAi self-assigned this 2026-05-20 11:51:48 +00:00
Author
Collaborator

Slice C shipped on branch mai/gauss/inventorcoder-team-admin — commit 02c7121.

Frontend-only on /team (the contact list where the existing broadcast button already lives). 5 files / +382 LoC:

  • frontend/src/team.tsx — master “Alle sichtbaren auswählen” checkbox row above the team-list.
  • frontend/src/client/team.ts — module-scoped selectedUserIDs Set; per-row checkbox; Shift-click range expansion via renderedUserIDs DOM-order snapshot; sticky footer (renderSelectionFooter) with live counter + Auswahl aufheben + E-Mail an Auswahl button; selectedRecipients() mirrors the role-resolution rules of displayedRecipients() and feeds the existing openBroadcastModal unchanged; tri-state master checkbox (syncMasterCheckbox).
  • frontend/src/styles/global.css.team-card[data-selected] lime highlight, .team-card-select cell, .team-selection-footer z-index 150 (above mobile bottom-nav at 100, below modal overlays at 1000+).
  • frontend/src/client/i18n.ts — +10 keys (team.selection.*) × DE + EN.

Design picks honoured (all (R) per design §8 + head approval):

  • Q1 surface = /team (not /admin/team — broadcast already lives here)
  • Q2 mechanism = checkbox column (not modifier-key)
  • Q3 button placement = sticky footer when selection > 0
  • Q4 filter-change → drop-out de-selects (pruneSelectionToVisible runs every render())
  • Q5 empty-selection fallback = existing top-bar broadcast button untouched, still targets the filtered set
  • Q6 persistence = wipe on navigation (Set is module-scoped in-memory)

bun run build clean (2543 i18n keys, data-i18n scan clean). No backend changes. Awaiting head + m — also see #48 for Slice A.

Blocked sibling: Slice B (#49 Add User) parked pending m’s decision on provisioning SUPABASE_SERVICE_ROLE_KEY (design §3.3 / trade-off #3).

Slice C shipped on branch `mai/gauss/inventorcoder-team-admin` — commit [02c7121](https://mgit.msbls.de/m/paliad/commit/02c7121). Frontend-only on `/team` (the contact list where the existing broadcast button already lives). 5 files / +382 LoC: - `frontend/src/team.tsx` — master “Alle sichtbaren auswählen” checkbox row above the team-list. - `frontend/src/client/team.ts` — module-scoped `selectedUserIDs` Set; per-row checkbox; Shift-click range expansion via `renderedUserIDs` DOM-order snapshot; sticky footer (`renderSelectionFooter`) with live counter + `Auswahl aufheben` + `E-Mail an Auswahl` button; `selectedRecipients()` mirrors the role-resolution rules of `displayedRecipients()` and feeds the existing `openBroadcastModal` unchanged; tri-state master checkbox (`syncMasterCheckbox`). - `frontend/src/styles/global.css` — `.team-card[data-selected]` lime highlight, `.team-card-select` cell, `.team-selection-footer` z-index 150 (above mobile bottom-nav at 100, below modal overlays at 1000+). - `frontend/src/client/i18n.ts` — +10 keys (`team.selection.*`) × DE + EN. Design picks honoured (all (R) per design §8 + head approval): - Q1 surface = `/team` (not `/admin/team` — broadcast already lives here) - Q2 mechanism = checkbox column (not modifier-key) - Q3 button placement = sticky footer when selection > 0 - Q4 filter-change → drop-out de-selects (`pruneSelectionToVisible` runs every `render()`) - Q5 empty-selection fallback = existing top-bar broadcast button untouched, still targets the filtered set - Q6 persistence = wipe on navigation (Set is module-scoped in-memory) bun run build clean (2543 i18n keys, data-i18n scan clean). No backend changes. Awaiting head + m — also see #48 for Slice A. Blocked sibling: Slice B (#49 Add User) parked pending m’s decision on provisioning `SUPABASE_SERVICE_ROLE_KEY` (design §3.3 / trade-off #3).
Author
Collaborator

Shipped via gauss on mai/gauss/inventorcoder-team-admin — merged into main; commit https://mgit.msbls.de/m/paliad/commit/111c7c3. Live after next Dokploy deploy. (m/paliad#49 Add User slice still parked pending m's credential go-ahead.)

Shipped via gauss on `mai/gauss/inventorcoder-team-admin` — merged into main; commit https://mgit.msbls.de/m/paliad/commit/111c7c3. Live after next Dokploy deploy. (m/paliad#49 Add User slice still parked pending m's credential go-ahead.)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#53
No description provided.