Skip to content

Recipients tab: list, search and unreachable persons #191

Description

@Silthus

Part of #181. Spec: #186. Slice 5 of 11 from #185.

Question

How do we ship this slice as one draft PR on PostHog/posthog behind workflows-audience?

Delivers. The Recipients tab becomes Audience's default: one row per recipient from the listing API, address search, and the "N persons can't be reached" line that opens the persons list already filtered to persons with no email property.

Spec. #186, approved by Michael on 2026-10-01.

Blocked by. #187, #190

Acceptance criteria

  • /audience opens Recipients. Columns: Recipient (with a Suppressed tag), All marketing, Topics (explicit subscribed/unsubscribed topics as tags, "No preference on any topic" otherwise), Persons (name, count or "No person"), Last sent (header says it covers 30 days).
  • Cursor paging, 50 rows a page. The search box sends search to the API, debounced.
  • Above the table, one muted line: "N persons can't be reached because they have no email property". It is hidden at 0. The link opens urls.persons() with a #q= DataTableNode over an ActorsQuery filtered by { type: 'person', key: 'email', operator: 'is_not_set' }.
  • Loading, empty ("No recipients yet"), no-match and error states are different screens. The error state asks to narrow the search.
  • A row opens /audience/recipients/<email>.
  • The frontend uses the generated messaging_recipients types. Stories at full width and ~520px, with screenshots.
  • A draft PR on PostHog/posthog with proof (tests plus screenshots for visual work), an adversarial review, and green CI. Never ready, never queued.

Tracking events

  • audience unreachable persons opened (count).

Test seam

Jest test through the real recipients logic and the real unreachablePersonsUrl() with the API mocked at the HTTP layer: typing a search issues a request with search, the next page sends the returned cursor, and the unreachable link decodes to an ActorsQuery with the email is_not_set filter. The persons scene itself is not mocked away from the URL contract: decode the q hash the way personsSceneLogic does.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions