Skip to content

Notifications: FE prep for real message content - #154

Open
romanivan-flamingo wants to merge 7 commits into
mainfrom
hotfix/notifications-real-content-fe
Open

Notifications: FE prep for real message content#154
romanivan-flamingo wants to merge 7 commits into
mainfrom
hotfix/notifications-real-content-fe

Conversation

@romanivan-flamingo

@romanivan-flamingo romanivan-flamingo commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

ClickUp: https://app.clickup.com/t/86ajn8hpg (parent PSA-loop task 86ajn8hmp)

FE side of "Notifications do not show the real message content". BE counterpart (merged): openframe-oss-lib#1741, openframe-saas-lib#707, openframe-saas-tenant#2552.

What this PR does

Content rendering (BE-independent):

  • Defensive markdown/HTML strip - stripNotificationMarkup() in notifications-helpers.ts removes tags, links/images (keeping their text), bold/italic/strike, code, headings, blockquotes and list markers from title/description. Applied in mapNotificationNode (drawer tiles, popups), the NATS live handler (live tiles + browser OS toast) and the /notifications table rows. Newlines are preserved so the full-text hover tooltip keeps its paragraph structure.
  • NATS createdAt - live notifications use the payload timestamp instead of stamping local Date.now(); local time remains the fallback.
  • Table rendering priority - the real title leads the first column; the context-derived kind label moved to the details column.

BE schema adoption (per the merged BE PRs):

  • schema.graphql - additive sync: ticketTitle on ticket-bearing contexts, nullable ticketId/ticketNumber/ticketTitle on ClientAiMessageContext.
  • Shared fragment (notification-fields.ts) selects ClientAiMessageContext { dialogId, clientTicketId: ticketId } (aliased - relay refuses to merge ID! with ID); the mapper folds it into meta.ticketId.
  • Navigation - CLIENT_AI_MESSAGE notifications with a ticket open the ticket's client chat tab; TICKET_ESCALATED_BY_USER (a first-class tile in the new Figma blocks) now selects ticketId and routes to Ticket Details.

Merge with main (ee035d5)

Main restructured the same area while this branch was open; the merge re-applies the changes onto the new structure (shared notification-fields.ts fragment, readNotificationNode read-once rows, generated-type mapper). The notification-settings surface deleted on main stays deleted.

Content suppression: dropped by BE before merge

Earlier commits of this branch carried a tenant-wide "Hide Message Content" toggle and its schema. The suppression parts of openframe-oss-lib#1741 were cut from the PR before it merged (verified against merge commit ce246608: no contentSuppressed, no updateNotificationContentSuppression, no redactor on main) - BE dropped notification settings "for now". All suppression traces are removed from this PR; enabled/typeSettings SDL stays as it is live on BE main.

Verification (QA, real data)

  • Real content across the table, drawer and popups: ticket number + title as the headline, message excerpts with host attribution, approval summaries, word-boundary truncation with ellipsis; no markdown artifacts in fresh records.
  • Escalated-ticket notification navigates to the ticket and auto-reads on open (unread 7 -> 6, record lands in History).
  • npm run relay, npm run generate-enums, npm run type-check, Biome - clean. No console errors.
  • Pending: live NATS popup / browser push spot-check with a fresh event; FCM/email verification on the BE side.

🤖 Generated with Claude Code

- Strip markdown/HTML artifacts from title/description defensively on FE
  (drawer tiles, /notifications table, browser OS toast); newlines are
  preserved so the full-text hover tooltip keeps paragraph structure
- Use the NATS payload createdAt for live notifications instead of local
  Date.now(), so live tiles match the stored record after reload
- Table: real title leads the first column; the context-derived kind
  label moved to the details column (it no longer masks real content)
- Route table rows through the shared strip/normalization instead of raw
  Relay edge fields

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@romanivan-flamingo
romanivan-flamingo requested a review from a team as a code owner August 10, 2026 11:53
@romanivan-flamingo
romanivan-flamingo requested a review from a team August 10, 2026 11:53
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Notification text is sanitized across mapped and live notification paths. Notification timestamps use normalized or payload values. The table displays the actual title and shows the notification type before the description.

Changes

Notification normalization and display

Layer / File(s) Summary
Notification markup normalization
src/graphql/notifications/notifications-helpers.ts
Adds stripNotificationMarkup and applies it to mapped notification titles and descriptions.
Notification data path normalization
src/app/(app)/notifications/components/notifications-section.tsx, src/app/components/notifications/notifications-data-provider.tsx
Notification rows use mapped text and normalized timestamps. Live notifications sanitize text and prefer the payload timestamp.
Notification column display
src/app/(app)/notifications/components/notifications-columns.tsx
The title column renders the notification title. The details column renders the type before the description.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: aliaska-varieva

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the frontend changes that prepare notifications to display real message content.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/notifications-real-content-fe

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@romanivan-flamingo romanivan-flamingo self-assigned this Aug 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/graphql/notifications/notifications-helpers.ts`:
- Line 241: Update the normalization regex in the notification helper to avoid
treating underscores adjacent to word characters as italic delimiters,
preserving identifiers such as job_status_failed while still stripping valid
underscore-delimited markup. Add coverage for both identifier text and
snake_case content.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8db850b4-b5d8-4213-a528-b6ac9f56b540

📥 Commits

Reviewing files that changed from the base of the PR and between 93cbeda and 5edc59e.

📒 Files selected for processing (4)
  • src/app/(app)/notifications/components/notifications-columns.tsx
  • src/app/(app)/notifications/components/notifications-section.tsx
  • src/app/components/notifications/notifications-data-provider.tsx
  • src/graphql/notifications/notifications-helpers.ts

Comment thread src/graphql/notifications/notifications-helpers.ts Outdated
romanivan-flamingo and others added 6 commits August 12, 2026 16:39
…nt ticket nav)

BE counterpart (open PRs): openframe-oss-lib#1741, openframe-saas-lib#707,
openframe-saas-tenant#2552.

- schema.graphql: additive sync with the BE PRs - ticketTitle on ticket-bearing
  contexts, nullable ticketId/ticketNumber on ClientAiMessageContext,
  NotificationSettings.contentSuppressed, updateNotificationContentSuppression
  mutation (refetch via fetch-schema once BE deploys)
- drawer/section fragments select ClientAiMessageContext dialogId + ticketId
  (aliased clientTicketId - relay refuses to merge ID! with ID); the mapper
  folds it into meta.ticketId
- CLIENT_AI_MESSAGE notifications with a ticket now navigate to the ticket's
  client chat tab (previously no action)
- Notification settings modal: tenant-wide "Hide Message Content" switch,
  visible to ADMIN/OWNER only, saved via the dedicated mutation and only when
  the value changed

Deployment ordering: FE must not deploy before the BE PRs - the new field
selections fail GraphQL validation against the old schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s schema

BE main renamed NotificationSettings.pushEnabled to enabled and added
typeSettings (per-type toggles subtask); the old field no longer exists, so
the settings query - and with it the new suppression toggle - would fail
GraphQL validation. Minimal migration: SDL synced from the BE PR
(NotificationSettingGroup enum, NotificationTypeSetting type/input, new
updateNotificationSettings signature), query/mutations select enabled, the
modal state renamed. typeSettings is intentionally not sent - omitting it
keeps the stored group overrides; the per-group checkboxes UI stays in the
sibling subtask.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… structure

Resolution notes:
- schema.graphql: main's freshly fetched SDL as base, with the merged BE PR
  additions re-applied (ticketTitle on six ticket-bearing contexts, nullable
  ticketId/ticketNumber/ticketTitle on ClientAiMessageContext,
  NotificationSettings.contentSuppressed, updateNotificationContentSuppression)
- fragments: main moved the shared node selection into notification-fields.ts;
  the ClientAiMessageContext selection (dialogId + aliased clientTicketId) now
  lives there, drawer/section relay files taken from main as-is
- notifications-helpers: main's generated-type mapper signature kept, the
  hand-written NotificationNodeShape dropped; markup strip stays
- notifications-section rows: main's read-once readNotificationNode structure
  kept, with stripNotificationMarkup applied to the row title/description
- settings surface: main deleted the notification-settings modal, its relay
  files and the profile-card entry point; the deletion is accepted, including
  our suppression-toggle UI and master-switch migration - that UI returns as a
  follow-up per the new Figma design attached to ClickUp 86ajn8hpg

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…_USER

The new Figma notification blocks include a first-class escalated-ticket tile,
and the context already exists in the schema, but it was neither selected in
the shared fragment nor mapped in navigation - the tile rendered its text yet
was not clickable and never auto-read on the ticket page, unlike every
neighboring ticket tile. Select its ticketId (dialogId stays unselected for
the same nullability-merge reason as TicketReopenedContext) and route it to
Ticket Details.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The suppression parts of openframe-oss-lib#1741 (contentSuppressed on
NotificationSettings, updateNotificationContentSuppression) were removed from
the PR before it merged - verified against the merge commit ce246608 - so the
hand-patched SDL described an API that does not exist. enabled/typeSettings
stay: they are live on BE main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e mapper

- schema.graphql taken from main wholesale - it is now freshly introspected
  from the deployed backend and already carries everything the fragments
  select (ticketTitle on six contexts, ClientAiMessageContext
  ticketId/ticketNumber, TicketEscalatedByUserContext) with canonical
  docstring formatting; correctly has no content-suppression fields
- mapper: main made context nullable (context-less rows on the new emission
  path) - kept its optional chaining and re-applied clientTicketId in the
  ticket-id chain plus the markup strip on title/description

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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