Bulk team email — send to filtered selection from /team page #7

Open
opened 2026-05-07 10:37:57 +00:00 by mAi · 0 comments
Collaborator

Goal

Let a user (likely a project lead or admin) send an email to a filtered subset of team members directly from the Team view. Replaces the current "copy emails into your mail client" flow which is friction-heavy and error-prone.

m's framing (2026-05-07):

"send emails to specific teams or selections of teams. Maybe just a button in the Teams view that allows sending an email to all filtered members. We should be able to filter by (select) projects, or locations etc..."

Sketch

Two surfaces:

  • /team page — gain filter chips: projects (multi-select), HLC offices (Munich / Düsseldorf / Amsterdam / London / Paris / Milan / Hamburg), profession (after issue #6 ships) or current project_teams.role. Existing per-row layout stays.
  • "E-Mail an Auswahl" button — appears once a filter selects ≥1 member. Click opens a compose modal: To-list preview, subject, body, send. On send → backend uses existing MailService to dispatch.

Existing infrastructure to reuse

  • MailService (internal/services/mail_service.go) — already wired to SMTP via env vars (SMTP_HOST, SMTP_USERNAME, etc.). Used for invitations + reminders today. Bulk-send fits naturally as a new method.
  • /team pagefrontend/src/team.tsx + client/team.ts — has the rendered list of users. Filter chips can hook into existing render path.
  • /admin/email-templates — paliad already has email templates (visible in routing per admin-email-templates.tsx). Bulk-send could reuse template rendering, with placeholders.
  • paliad.partner_unit_events / paliad.project_events — audit table candidates for "broadcast sent" event.

Open design questions (for m)

Sender / privacy

  1. From / Reply-To semantics. Three candidates:
    • (a) From: paliad@hlc.com, Reply-To: sender's email (clean infra signature, replies route correctly)
    • (b) From: sender's email directly (requires DKIM / SPF for sender's domain — risky to forge)
    • (c) From: paliad@hlc.com, body explicitly names the sender, reply via paliad inbox
    • Recommend (a). m signs off.
  2. Recipient disclosure. Each member gets their own personalised email (BCC-equivalent — they don't see each other), OR a single email with all recipients in To: (people see who else got it)? Recommend per-recipient (privacy + per-name placeholders work).
  3. Authoring permission. Who can use the bulk-send button?
    • Any project member?
    • Project lead only?
    • global_admin only?
    • Recommend: project lead OR global_admin in v1.

Compose UX

  1. Subject / body input. Simple text inputs (subject + plain text body)? Markdown? Rich-text? Recommend plain text + Markdown rendering for v1 (paragraph breaks, basic links). No attachments in v1.
  2. Placeholders. Per-recipient personalisation: {{name}}, {{first_name}}, {{role_on_project}}, etc. Recommend a small fixed set with documentation in the compose modal.
  3. Templates. Reuse existing paliad.email_templates? Or freeform-only at v1 with templates as follow-up?
  4. Confirmation step. Compose modal shows "Empfänger (N): Anna, Bert, Conny, +12 more" with the full list expandable, then send button. Hard-block send if N=0 or message body empty.

Filtering

  1. Filter axes for v1. m mentioned project + location. Other candidates:
    • profession (after #6 lands)
    • project_teams.role (lead / associate / pa / ...)
    • office (paliad.users.office — per CLAUDE.md "Munich, Düsseldorf, Amsterdam, London, Paris, Milan, Hamburg")
    • direct vs derived membership
    • active vs archived projects
    • Pick the v1 set; rest deferred.
  2. Filter scope source. "Filter by projects" — does that mean the user picks one or many projects from a multi-select, and the recipient pool = union of those projects' effective teams? Or does the filter narrow within an already-rendered team table?
  3. Filter persistence. Should saved filters become re-usable distribution lists? Out of scope for v1; flag for issue #5 (Custom Views — a saved view of the team page IS effectively a saved distribution list).

Audit + compliance

  1. Audit trail. Log every broadcast in paliad.project_events (or new paliad.email_broadcasts table)? Recommend a dedicated table + admin viewer (/admin/broadcasts) with subject + sender + recipient list + timestamp. Compliance for HLC requires this.
  2. Opt-out / unsubscribe. Internal HLC firm-wide emails to authenticated colleagues — strict CAN-SPAM doesn't apply to first-party transactional. Recommend no opt-out at v1 (it's a workplace tool); revisit if external counsel / experts get included.
  3. Rate limit. Cap N recipients per broadcast (e.g. 100)? Cap broadcasts per user per day? Recommend soft cap of 100 + admin-edit-able config.

Backend / deliverability

  1. Send queue vs synchronous. Synchronous send-and-wait blocks the request. Async queue (one job per recipient) is more robust but heavier. Recommend: synchronous + per-recipient MailService.Send in a goroutine pool with timeout, log failures.
  2. Bounce handling. SMTP bounces — surface to sender, retry once, then mark recipient as failed and stash for /admin/broadcasts review. Recommend for v1: log bounces, don't auto-retry.

Out of scope (v1)

  • Cross-project broadcast across unrelated matters.
  • Newsletter-style scheduled sends.
  • WhatsApp / Telegram / Slack channels.
  • File attachments.
  • Template editor on the bulk-send page (use existing /admin/email-templates surface).
  • Per-recipient localisation (DE/EN body switch). v1 sends one body to everyone.
  • "Reply all" thread tracking on incoming bounces.

References

  • internal/services/mail_service.go — current SMTP integration (invites + reminders)
  • frontend/src/team.tsx + client/team.ts — current /team page
  • frontend/src/admin-email-templates.tsx + edit — existing template surface
  • paliad.users.office — for HLC-office filter
  • Issue #5 (data display model) — saved views are a natural superset; this issue stays narrow on the "send email" interaction

Worker brief

  • Role: fixer or coder (depending on which open-question batch m locks). NOT cronus per memory.
  • Branch convention: mai/<worker>/feat-bulk-team-email
  • Held until m signs off the open questions above.
## Goal Let a user (likely a project lead or admin) send an email to a filtered subset of team members directly from the Team view. Replaces the current "copy emails into your mail client" flow which is friction-heavy and error-prone. m's framing (2026-05-07): > "send emails to specific teams or selections of teams. Maybe just a button in the Teams view that allows sending an email to all filtered members. We should be able to filter by (select) projects, or locations etc..." ## Sketch Two surfaces: - **`/team` page** — gain filter chips: projects (multi-select), HLC offices (Munich / Düsseldorf / Amsterdam / London / Paris / Milan / Hamburg), profession (after issue #6 ships) or current `project_teams.role`. Existing per-row layout stays. - **"E-Mail an Auswahl" button** — appears once a filter selects ≥1 member. Click opens a compose modal: To-list preview, subject, body, send. On send → backend uses existing `MailService` to dispatch. ## Existing infrastructure to reuse - **`MailService`** (`internal/services/mail_service.go`) — already wired to SMTP via env vars (`SMTP_HOST`, `SMTP_USERNAME`, etc.). Used for invitations + reminders today. Bulk-send fits naturally as a new method. - **`/team` page** — `frontend/src/team.tsx` + `client/team.ts` — has the rendered list of users. Filter chips can hook into existing render path. - **`/admin/email-templates`** — paliad already has email templates (visible in routing per `admin-email-templates.tsx`). Bulk-send could reuse template rendering, with placeholders. - **`paliad.partner_unit_events`** / **`paliad.project_events`** — audit table candidates for "broadcast sent" event. ## Open design questions (for m) ### Sender / privacy 1. **From / Reply-To semantics.** Three candidates: - (a) From: `paliad@hlc.com`, Reply-To: sender's email (clean infra signature, replies route correctly) - (b) From: sender's email directly (requires DKIM / SPF for sender's domain — risky to forge) - (c) From: `paliad@hlc.com`, body explicitly names the sender, reply via paliad inbox - Recommend (a). m signs off. 2. **Recipient disclosure.** Each member gets their own personalised email (BCC-equivalent — they don't see each other), OR a single email with all recipients in To: (people see who else got it)? Recommend per-recipient (privacy + per-name placeholders work). 3. **Authoring permission.** Who can use the bulk-send button? - Any project member? - Project lead only? - global_admin only? - Recommend: project lead OR global_admin in v1. ### Compose UX 4. **Subject / body input.** Simple text inputs (subject + plain text body)? Markdown? Rich-text? Recommend plain text + Markdown rendering for v1 (paragraph breaks, basic links). No attachments in v1. 5. **Placeholders.** Per-recipient personalisation: `{{name}}`, `{{first_name}}`, `{{role_on_project}}`, etc. Recommend a small fixed set with documentation in the compose modal. 6. **Templates.** Reuse existing `paliad.email_templates`? Or freeform-only at v1 with templates as follow-up? 7. **Confirmation step.** Compose modal shows "Empfänger (N): Anna, Bert, Conny, +12 more" with the full list expandable, then send button. Hard-block send if N=0 or message body empty. ### Filtering 8. **Filter axes for v1.** m mentioned project + location. Other candidates: - profession (after #6 lands) - project_teams.role (lead / associate / pa / ...) - office (paliad.users.office — per CLAUDE.md "Munich, Düsseldorf, Amsterdam, London, Paris, Milan, Hamburg") - direct vs derived membership - active vs archived projects - Pick the v1 set; rest deferred. 9. **Filter scope source.** "Filter by projects" — does that mean the user picks one or many projects from a multi-select, and the recipient pool = union of those projects' effective teams? Or does the filter narrow within an already-rendered team table? 10. **Filter persistence.** Should saved filters become re-usable distribution lists? Out of scope for v1; flag for issue #5 (Custom Views — a saved view of the team page IS effectively a saved distribution list). ### Audit + compliance 11. **Audit trail.** Log every broadcast in `paliad.project_events` (or new `paliad.email_broadcasts` table)? Recommend a dedicated table + admin viewer (`/admin/broadcasts`) with subject + sender + recipient list + timestamp. Compliance for HLC requires this. 12. **Opt-out / unsubscribe.** Internal HLC firm-wide emails to authenticated colleagues — strict CAN-SPAM doesn't apply to first-party transactional. Recommend no opt-out at v1 (it's a workplace tool); revisit if external counsel / experts get included. 13. **Rate limit.** Cap N recipients per broadcast (e.g. 100)? Cap broadcasts per user per day? Recommend soft cap of 100 + admin-edit-able config. ### Backend / deliverability 14. **Send queue vs synchronous.** Synchronous send-and-wait blocks the request. Async queue (one job per recipient) is more robust but heavier. Recommend: synchronous + per-recipient `MailService.Send` in a goroutine pool with timeout, log failures. 15. **Bounce handling.** SMTP bounces — surface to sender, retry once, then mark recipient as failed and stash for /admin/broadcasts review. Recommend for v1: log bounces, don't auto-retry. ## Out of scope (v1) - Cross-project broadcast across unrelated matters. - Newsletter-style scheduled sends. - WhatsApp / Telegram / Slack channels. - File attachments. - Template editor on the bulk-send page (use existing /admin/email-templates surface). - Per-recipient localisation (DE/EN body switch). v1 sends one body to everyone. - "Reply all" thread tracking on incoming bounces. ## References - `internal/services/mail_service.go` — current SMTP integration (invites + reminders) - `frontend/src/team.tsx` + `client/team.ts` — current /team page - `frontend/src/admin-email-templates.tsx` + edit — existing template surface - `paliad.users.office` — for HLC-office filter - Issue #5 (data display model) — saved views are a natural superset; this issue stays narrow on the "send email" interaction ## Worker brief - Role: fixer or coder (depending on which open-question batch m locks). NOT cronus per memory. - Branch convention: `mai/<worker>/feat-bulk-team-email` - Held until m signs off the open questions above.
mAi self-assigned this 2026-05-07 10:37:57 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#7
No description provided.