Skip to content

feat(inbox): inbox messaging + UI polish, client form a11y, integration rate-limit fix#267

Merged
jeroenrinzema merged 10 commits into
mainfrom
feat/inbox-polish
Jun 24, 2026
Merged

feat(inbox): inbox messaging + UI polish, client form a11y, integration rate-limit fix#267
jeroenrinzema merged 10 commits into
mainfrom
feat/inbox-polish

Conversation

@jeroenrinzema

Copy link
Copy Markdown
Contributor

Summary

Brings the inbox messaging system to main along with a batch of UI polish and small fixes. Builds on the inbox feature work (originally PR #238) plus follow-up refinements, and a final polish commit.

Inbox

  • Full inbox messaging: send/schedule/manage messages for organizations and users; client + management API endpoints, async pubsub delivery across push (APNS/FCM/WebPush), email, and SMS; scheduler, RBAC permissions, DB migrations, and console UI.
  • Preview now renders the notification-center card (matching the campaign preview) instead of the standalone InboxFrame, passing through the sent time with violet accent styling.
  • InboxNotificationCenter gains a time prop so previews show the actual sent time rather than always "now".
  • Expand chevron in inbox-detail-table is now a real Button with an accessible label; row-click propagation stopped.
  • SentAt mapped into the inbox message OAPI response.

Settings → Clients / Access (a11y + fixes)

  • Associate form labels with controls (htmlFor/id); group selectable card sets with role="group" + aria-label; label the row-actions menu.
  • Fix integration setup validation: rate_limit is an override object ({ limit, interval }) matching the registered form fields, not a scalar. The scalar schema rejected every submit, silently no-op'ing the create button.
  • Re-export GrantConstraints; cap client search at 100 results.

Router

  • Un-nest org events/scheduled out of the removed OrganizationDetailEventsLayout wrapper; drop unused imports.

Test plan

  • Create an integration with a rate limit override — create button now submits.
  • Inbox message preview shows the notification-center card with sent time.
  • Tab through Settings → Clients forms; labels/controls associated.

Introduce a full inbox feature allowing messages to be sent, scheduled,
and managed for both organizations and users. Includes client and
management API endpoints, pubsub consumers for async delivery across
push (APNS, FCM, WebPush), email, and SMS channels, a scheduler for
timed messages, RBAC permissions, database migrations, and a console UI
for viewing inbox messages.
Add dedicated channel preview frames (email, phone, push, inbox) and an
inbox channel preview, message expanded row, and inbox channel metadata.
Extract reusable events layouts for organizations and users, and shared
date-time helpers. Refine inbox detail/scheduled tables, datetime editing,
broadcast scheduling, and campaign template setup. Tidy inbox scheduler
and pubsub consumers for organization/user delivery.
Bring the inbox branch up to date with main, which had already integrated
the inbox feature (PR #238) plus subsequent fixes, the client-API auth/RBAC
overhaul, access-policies, new providers, and a codebase-wide react-hook-form
+ zod form-validation refactor.

Conflict resolution:
- Backend inbox pipeline (consumers, scheduler, store, client controllers,
  OAPI specs + generated code, migrations, tests): took main's version. Main's
  design is the tested, integrated baseline (JetStream Msg-Id dedup, sent_at
  guard, empty-recipient guard). The local refinement commit's parallel
  backend changes (enriched lifecycle payload, transactional publish-before-
  commit, email-subject guard) are superseded by main's equivalents.
- Frontend inbox UI (preview.tsx, inbox-detail-table.tsx): kept the local
  refined versions (dedicated Frame preview components incl. the inbox channel
  preview, message expanded row). Added an inbox template branch to preview.tsx
  so the generic Preview renders main's new inbox campaign channel via
  InboxNotificationCenter.
- Org/User detail nav: kept both the inbox tab (local) and the scheduled tab
  (main); added missing CalendarClock import.
- scheduled-detail-table.tsx and CreateBroadcastDialog.tsx: took main's version
  to adopt its react-hook-form + zod validation architecture. The local split
  date/time picker UX for these two forms is set aside in favor of main's
  validated forms.
- apikeys removed on main (replaced by auth_methods); en.json took main's
  superset of i18n keys.

Verified: go build ./..., go vet on inbox packages, and console tsc --noEmit
all pass.
… rate-limit validation

Inbox:
- Preview renders the notification-center card (matching the campaign
  preview) instead of the standalone InboxFrame; pass through the sent
  time and apply the violet accent styling.
- Add a `time` prop to InboxNotificationCenter so previews can show the
  actual sent time rather than always "now".
- Wrap the expand chevron in inbox-detail-table in a real Button with an
  accessible label and stop row-click propagation.
- Map SentAt into the inbox message OAPI response.

Settings → Clients / Access (a11y + fixes):
- Associate form labels with controls via htmlFor/id; group selectable
  card sets with role="group" + aria-label; label the row actions menu.
- Fix integration setup validation: rate_limit is an override object
  ({ limit, interval }) matching the registered form fields, not a
  scalar. The scalar schema rejected every submit, silently no-op'ing
  the create button.
- Re-export GrantConstraints; cap client search at 100 results.

Router: un-nest org events/scheduled from the removed
OrganizationDetailEventsLayout wrapper; drop unused imports.
The lists_test.go referenced oapi.Draft / oapi.Ready, which don't
exist in the generated oapi package. The correct constants are
oapi.ListStateDraft and oapi.ListStateReady, fixing the test build
failure that broke both the lint and tests CI jobs.
EmailPreviewContent referenced emailLabels, which is declared in the
sibling Preview component, causing a ReferenceError at runtime (TS2304).
Pass the labels in as a prop instead.

The SMS template branch read data.text, but TextTemplateData exposes
{ body }, so the preview rendered empty. Read data.body.

Also narrow each template branch to its concrete *TemplateData type so
the union-member property accesses (code/subject/from/title/body) type-
check instead of relying on the untyped union.
InboxFrame was exported from the preview barrel but never consumed; the
inbox preview path uses InboxNotificationCenter. Remove the dead module
and its barrel re-exports.
The client list has no pagination, so lowering the fetch limit to 100
silently dropped clients for projects with 100-200 of them. Restore the
limit to 200 and note pagination as the proper follow-up.
Add timezone-robust unit tests for the pure date/time helpers
(parse/split/combine and ISO conversions), asserting round-trip and
structural properties so they pass regardless of the host timezone.
Replace the magic literal int16(3) default priority with a named
DefaultInboxPriority constant that documents its tie to the OpenAPI
schema default.

Add pure (non-DB) unit tests for InboxMessage.OAPI()/InboxMessages.OAPI()
and normalizeInboxMessageParams, covering the newly-mapped sent_at field
and the default priority.
@jeroenrinzema jeroenrinzema merged commit 32e1ec8 into main Jun 24, 2026
6 checks passed
@jeroenrinzema jeroenrinzema deleted the feat/inbox-polish branch June 24, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant