Skip to content

Update alert subscription UX#2015

Open
kiahna-tucker wants to merge 121 commits into
mainfrom
kiahna-tucker/uxr/notifications/ux-update/2026-06
Open

Update alert subscription UX#2015
kiahna-tucker wants to merge 121 commits into
mainfrom
kiahna-tucker/uxr/notifications/ux-update/2026-06

Conversation

@kiahna-tucker

@kiahna-tucker kiahna-tucker commented Jun 23, 2026

Copy link
Copy Markdown
Member

Issues

The issues directly below are advanced by this PR:
#1960

Changes

1960

  • changes...

Tests

Manually tested

  • scenarios you manually tested

Automated tests

  • unit testing covered

Playwright tests ran locally

  • Admin
  • Captures
  • Collections
  • HomePage
  • Login
  • Materialization

Screenshots

NOTE: Code that altered button label styling in the application was merged into this branch after screenshots were taken for this PR. The button labels in the Organization Notifications section of the Settings tab on the Admin page are in title case.

Table

Organization Notifications table

pr_screenshot-2015-alert_subscription_ux-table

Dialogs

Configure Notification Methods dialog | Initialized

pr_screenshot-2015-alert_subscription_ux-dialog-configure-initialized

Configure Notification Methods dialog | Subscription summary expanded

pr_screenshot-2015-alert_subscription_ux-dialog-configure-subscription_summary-expanded

Update Notification Methods dialog | Initialized

pr_screenshot-2015-alert_subscription_ux-dialog-update-initialized

Update Notification Methods dialog | Subscription summary expanded

pr_screenshot-2015-alert_subscription_ux-dialog-update-subscription_summary-expanded

Recipients section | No recipients found

pr_screenshot-2015-alert_subscription_ux-dialog-no_subscribers

Alert Types List Item Interactions

Standard alert type | Deselected

pr_screenshot-2015-alert_subscription_ux-dialog-alert_types-item-deselected

Standard alert type | Deselected | Hovered

pr_screenshot-2015-alert_subscription_ux-dialog-alert_types-item-deselected-hovered

Standard alert type | Selected

pr_screenshot-2015-alert_subscription_ux-dialog-alert_types-item-selected

Standard alert type | Selected | Hovered

pr_screenshot-2015-alert_subscription_ux-dialog-alert_types-item-selected-hovered

System alert type

A list item corresponding to a system alert type does not have a hover state since it cannot be deselected.

pr_screenshot-2015-alert_subscription_ux-dialog-alert_types-item-system_alert

Errors

Duplicate subscribed email detected | Subscriber summary expanded

pr_screenshot-2015-alert_subscription_ux-dialog-duplicate_email

Duplicate subscribed email detected | Subscriber summary collapsed

pr_screenshot-2015-alert_subscription_ux-dialog-duplicate_email-collapsed

Multiple email input errors detected | Subscriber summary expanded

pr_screenshot-2015-alert_subscription_ux-dialog-multiple_email_errors

Multiple email input errors detected | Subscriber summary collapsed

pr_screenshot-2015-alert_subscription_ux-dialog-multiple_email_errors-collapsed

@kiahna-tucker

Copy link
Copy Markdown
Member Author

Latest Claude code review:

This branch reworks PrefixAlerts from a single-subscription model to per-prefix, multi-recipient configuration with global (data-movement) settings. The logic is sound and ready to merge. Core flows were reviewed end-to-end:

  • Store state management (useAlertSubscriptionsStore) — mutable/immutable subscription tracking, deletion marking, and prefix-selection handling are internally consistent; subscription IDs align correctly between the immutable source and mutable working copies.
  • Save/delete path (useModifyAlertMetadata, useUpsertAlertSubscription, useUpsertAlertConfig) — correctly distinguishes create vs. update vs. delete, skips no-op writes, and cleans up stale subscriptions.
  • Validation (useEvaluateSubscriptionIneligibility) — empty-email and duplicate-email detection correctly operate on active (non-deleted) subscriptions only.
  • Global settings — the data-movement interval round-trips correctly between the stored format and the UI option set.

No correctness defects remain. Two low-severity, non-blocking items are worth noting:

  1. Alert-config pagination — configs are fetched as a single page rather than paged (useAllPages); prefixes beyond the default page size could miss their global settings. Fine if the per-tenant config count is small.
  2. Minor polish — one button handler returns a rejected promise with no catcher (harmless console noise; a bare return would be cleaner).

@kiahna-tucker

Copy link
Copy Markdown
Member Author

Latest Claude code review:

Code review summary

This branch refactors prefix alerts from a single-subscription model to per-prefix metadata supporting multiple recipients plus prefix-level global settings. The logic is sound and ready for review.

Areas verified as correct:

  • Store reducers (useAlertSubscriptionsStore) — immer-based updates for adding/deleting/toggling subscriptions and alert types correctly distinguish DB-backed rows (marked for deletion) from client-only rows (removed outright).
  • Save reconciliation (useModifyAlertMetadata) — the stale-subscription delete + upsert flow handles email changes, deletions, and no-op-identical subscriptions without duplicate or orphaned mutations.
  • Prefix-settings inheritance — longest-matching-prefix resolution for explicit vs. inherited (implicit) global settings is internally consistent, and the config-format round-trip (from/toUnconventionalTimeFormat) is symmetric.
  • Duplicate/empty-email validation — blank emails are excluded from duplicate detection and gated separately.
  • UI plumbing — PrefixedName fixedPrefix behavior and the per-prefix table/dialog wiring are consistent; no dead props or dangling references.

One minor, non-blocking item:

  • The alert-config query (PrefixAlerts/index.tsx) fetches a single page capped at first: 100 and does not follow pageInfo.hasNextPage. This is safe for any realistic number of prefix configs; if a tenant ever exceeds 100, the overflow would be silently dropped. Optional follow-up: log when hasNextPage is true, or paginate via useAllPages.

No correctness defects block merge.

@GregorShear

Copy link
Copy Markdown
Contributor

(and i'm also seeing now that if i save that acmeCo/nested/ dialog without making any changes, both parent configs and subscriptions are saved as new rows with the acmeCo/nested/ prefix)

Confirming that the config is no longer saved as a new row, but the subscription is

Screenshot 2026-07-16 at 2 28 16 PM

@kiahna-tucker

kiahna-tucker commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

Greg and I discussed the scenario highlighted above on VC which breaks down as follows:

Let's say an alert subscription exists for acmeCo/.

  1. Click the Configure Notifications button (which opens the Configure Notification Methods dialog prefilled with data related to acmeCo/).

  2. Enter characters in the prefix field that correspond to a nested prefix (e.g., acmeCo/anvils/) without direct subscriptions (i.e., alert_subscriptions database table does not contain a row corresponding to the nested prefix).

  3. Observe that the Recipients section remains unchanged, reflecting the subscribers to acmeCo/, and the Save CTA is enabled.

  4. Click the Save CTA.

This results in the creation of "superfluous" (or effectively duplicate) alert subscriptions for acmeCo/anvils/. To unsubscribe the recipient from those alerts for acmeCo/anvils/, the subscription at the acmeCo/anvils/ and acmeCo/ level would need to be removed.

That said, this UX quirk was presented on Jul. 10th and approved of but it is likely something we would like to address quickly. Given the prioritization of this feature and the approval, I would push for this quirk to be addressed in a rapid fast-follow.

@GregorShear

This comment was marked as resolved.

@kiahna-tucker

This comment was marked as resolved.

Comment thread src/components/tables/PrefixAlerts/index.tsx Outdated
@GregorShear

GregorShear commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

The email field loses focus if you backspace to the end.

also IIRC didn't we want to prevent editing the email of existing subscriptions?

Screen.Recording.2026-07-16.at.10.19.39.PM.mov

@kiahna-tucker

Copy link
Copy Markdown
Member Author

also IIRC didn't we want to prevent editing the email of existing subscriptions?

No, a user is free to "edit" the email of an existing subscription. Changing the email field input of an existing subscription results in the same server changes as removing the subscription in the client (i.e., clicking the X button in the top-right corner of subscriber summary) and adding a new one with the updated email. What I aimed to prevent was having more than one templated subscription in the client (i.e., a list item without a specified email).

The email field loses focus if you backspace to the end.

This is a known issue in production. Happy to investigate the defect and patch it, however I would advocate that this production bug should not be blocking.

Setting this error disables the email field and alert type checkboxes - you can still click "add recipient" but the UI that expands is just non-functional. I'd rather replace the entire contents of the dialog with some "init failed" message instead of presenting a half-broken UI.

Makes sense to me. Will update.

if (!fetching && !alertTypeResponse.fetching) {
if (!fetching && !alertConfigResponse.fetching) {
setInitializationError(error);
setSubscriptionMetadata(data?.alertSubscriptions ?? []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand why we're re-saving the urql cache to another store? couldn't we just interact with the query cache directly?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Query data would be prop drilled considerably to reach the components dependent upon it, needlessly increasing the sensitivity of higher order components. Actions within the store are able to reference relevant query data without the need for their callers to pass the data and, thus, increasing their sensitivity to changes to the data.

Happy to chat through refactor opportunities but I would push for this feature to get out the door given the prioritization.

Comment on lines 45 to 47
useUnmount(() => {
resetSubscriptionState();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels like a postgrest vestige. why do we need a globally scoped state to begin with? urql cache + an edit buffer that lives and dies with the dialog should be sufficient and much simpler, no?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Control Plane/UI Sync meeting on Monday, let's chat about how we would like to interact with GraphQL.

As for this PR, alert configs used to be fetched when the debounced prefix input changed yet still stored at that time. In light of the limitation of the alertConfigs endpoint that resulted in the need for the control-plane workaround, the timing of the call was adjusted but, yes, the data is still stored in a Zustand store. There are some benefits to that and, to my knowledge, the GraphQL query key issue is still present in the repo that would require the query data to be considerably prop drilled. I am not opposed to a refactor if you see a better approach to working with GraphQL data. However, given the urgency around this feature, I would like to land this ASAP and revisit at this point.

@GregorShear

Copy link
Copy Markdown
Contributor

One thing that would be worth doing (and is hopefully a small change) is replacing the hand-written gql queries with the codegen types

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

Labels

change:planned This is a planned change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants