Skip to content

feat(#2147): render REMINDER_SENT events in document activity feed#15

Merged
b-mackenzie-alexander merged 4 commits into
mainfrom
feat/doc-2147-activity-feed
Apr 21, 2026
Merged

feat(#2147): render REMINDER_SENT events in document activity feed#15
b-mackenzie-alexander merged 4 commits into
mainfrom
feat/doc-2147-activity-feed

Conversation

@b-mackenzie-alexander
Copy link
Copy Markdown
Owner

@b-mackenzie-alexander b-mackenzie-alexander commented Apr 21, 2026

Summary

  • Adds a REMINDER_SENT match case to the document activity feed component using the MailOpen icon, consistent with the existing DOCUMENT_OPENED pattern
  • Makes recipientId nullable in ZDocumentAuditLogEventReminderSentSchema to support digest entries (no specific recipient)
  • Writes a DocumentAuditLog per envelope in the digest handler so digest sends appear in the activity feed
  • Adds digest-specific label in formatDocumentAuditLogAction: renders "Reminder digest sent to owner" when recipientId is null, and "Reminder sent to [name]" for per-recipient entries

Depends on: PR #14 (Person 2, merged) and PR #11 (Person 4, merged)

Test plan

  • Send a document with reminders enabled and wait for the sweep to fire — confirm "Reminder sent to [name]" appears in the activity feed for each recipient
  • Confirm "Reminder digest sent to owner" appears for digest entries
  • Confirm no new imports were added to the activity feed component beyond what was already present
  • npm run build passes

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added tracking for reminder digest notifications in activity logs with a dedicated icon in recent activity.
  • Improvements

    • Updated recent activity display to show a mail-opened icon for reminder sent events.
    • Enhanced email subject formatting in reminder digest notifications.
  • Documentation

    • Completed and documented remainder feature implementation milestones.

Luba Kaper and others added 4 commits April 21, 2026 19:37
Adds a match case for DOCUMENT_AUDIT_LOG_TYPE.REMINDER_SENT in the
recent activity component, using the MailOpen icon consistent with
the existing DOCUMENT_OPENED pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ctivity feed

- Make recipientId nullable in ZDocumentAuditLogEventReminderSentSchema
  so digest entries (no specific recipient) can be represented
- Write a DocumentAuditLog per envelope in the digest handler so digest
  sends appear in the document activity feed
- Add digest-specific label in formatDocumentAuditLogAction: renders
  "Reminder digest sent to owner" when recipientId is null

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

Reminder email functionality is enhanced with audit logging for owner digest emails. The email subject template switches from Lingui plural() to manual string formatting, the handler adds audit log creation for reminder sends, the audit log schema allows nullable recipient IDs for digest scenarios, and the UI now displays reminder audit log events in the activity feed.

Changes

Cohort / File(s) Summary
Documentation
ROADMAP.md
Checklist entries marked complete across all feature sections with notes on final implementation details including handler grouping, filtering behavior, i18n subject implementation, and cross-field validation patterns.
Audit Logging Support
packages/lib/types/document-audit-logs.ts, packages/lib/utils/document-audit-logs.ts
Audit log schema updated to allow recipientId to be nullable; formatting utility now returns "Reminder digest sent to owner" message when recipientId is null, maintaining previous behavior for non-null cases.
Email Handler
packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts
Replaced Lingui plural() with manual pluralization in subject; removed teamId/userId/status filtering from envelope query; added background job task to create audit logs with REMINDER_SENT type and null recipient fields.
UI Display
apps/remix/app/components/general/document/document-page-view-recent-activity.tsx
Added new match case for REMINDER_SENT audit log type to display mail-opened icon, matching the icon used for DOCUMENT_OPENED events.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

apps: web

Poem

🐰 Hop, hop! A reminder is sent with a chime,
The audit log records the moment in time,
Owner digests now tracked, documented with care,
Mail icons on dashboards float through the air! ✉️

🚥 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 accurately summarizes the primary change: adding rendering of REMINDER_SENT events to the document activity feed component.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/doc-2147-activity-feed

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
ROADMAP.md (1)

173-183: 🧹 Nitpick | 🔵 Trivial

Person 5 checklist items should be marked complete.

This PR implements the REMINDER_SENT activity feed rendering, but the checklist items remain unchecked. The implementation in document-page-view-recent-activity.tsx satisfies these requirements:

  • Uses MailOpen icon (line 145)
  • Labels handled by formatDocumentAuditLogAction (already supports both per-recipient and digest cases)
  • No new imports needed
  • Same styling as adjacent cases
📝 Proposed update
-- [ ] Read `document-page-view-recent-activity.tsx` fully — understand existing match/render pattern
-- [ ] Identify where new audit log types are rendered (the `match` block over `auditLog.type`)
-- [ ] Add a render case for `DOCUMENT_AUDIT_LOG_TYPE.REMINDER_SENT`:
-  - [ ] Use `MailOpen` icon (already imported at line 6)
-  - [ ] Label: "Reminder sent to [recipient name]" for per-recipient entries
-  - [ ] Label: "Reminder digest sent to owner" for digest entries (`recipientId` null)
-  - [ ] Same `DateTime` relative formatting used by adjacent cases
-  - [ ] Same `text-slate-400` + icon layout used by adjacent cases
-- [ ] Verify no new imports needed beyond what's already in the file
-- [ ] Confirm the feed correctly shows both recipient reminder events and digest events
-- [ ] Confirm `npm run build` passes
+- [x] Read `document-page-view-recent-activity.tsx` fully — understand existing match/render pattern
+- [x] Identify where new audit log types are rendered (the `match` block over `auditLog.type`)
+- [x] Add a render case for `DOCUMENT_AUDIT_LOG_TYPE.REMINDER_SENT`:
+  - [x] Use `MailOpen` icon (already imported at line 6)
+  - [x] Label: "Reminder sent to [recipient name]" for per-recipient entries
+  - [x] Label: "Reminder digest sent to owner" for digest entries (`recipientId` null)
+  - [x] Same `DateTime` relative formatting used by adjacent cases
+  - [x] Same `text-slate-400` + icon layout used by adjacent cases
+- [x] Verify no new imports needed beyond what's already in the file
+- [x] Confirm the feed correctly shows both recipient reminder events and digest events
+- [ ] Confirm `npm run build` passes
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ROADMAP.md` around lines 173 - 183, Mark the Person 5 checklist items as
complete by checking the boxes for the tasks related to implementing
REMINDER_SENT in the activity feed; specifically update the checklist entries to
checked where they reference document-page-view-recent-activity.tsx, the
addition of a render case for DOCUMENT_AUDIT_LOG_TYPE.REMINDER_SENT, use of the
MailOpen icon, label handling via formatDocumentAuditLogAction (covers
per-recipient and digest/recipientId null), application of the same DateTime
relative formatting and text-slate-400 + icon layout, and confirmation that no
new imports were added and npm run build passes.
apps/remix/app/components/general/document/document-page-view-recent-activity.tsx (1)

153-158: 🧹 Nitpick | 🔵 Trivial

Minor optimization: formatDocumentAuditLogAction is called twice.

The function is invoked once for the title attribute and again for the content. Consider memoizing or extracting to a variable.

♻️ Proposed fix
             {documentAuditLogs.map((auditLog, auditLogIndex) => {
+              const auditLogAction = formatDocumentAuditLogAction(i18n, auditLog, userId);
               return (
                 <li key={auditLog.id} className="relative flex gap-x-4">
                   {/* ... icon rendering ... */}

                   <p
                     className="flex-auto truncate py-0.5 text-xs leading-5 text-muted-foreground dark:text-muted-foreground/70"
-                    title={formatDocumentAuditLogAction(i18n, auditLog, userId).description}
+                    title={auditLogAction.description}
                   >
-                    {formatDocumentAuditLogAction(i18n, auditLog, userId).description}
+                    {auditLogAction.description}
                   </p>
                   {/* ... time rendering ... */}
                 </li>
               );
             })}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/remix/app/components/general/document/document-page-view-recent-activity.tsx`
around lines 153 - 158, Extract the repeated call to
formatDocumentAuditLogAction into a single variable and reuse it for both title
and content; for example, in the DocumentPageViewRecentActivity component
compute const formattedAction = useMemo(() => formatDocumentAuditLogAction(i18n,
auditLog, userId), [i18n, auditLog, userId]) (or a plain const if memoization
isn't needed) and then use formattedAction.description for both the title prop
and the paragraph contents to avoid calling formatDocumentAuditLogAction twice.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts`:
- Around line 113-115: Replace the manual English-only pluralization in the
email subject with Lingui's plural macro: import plural from
'@lingui/core/macro' and change the subject construction that currently uses
`${count} document${count === 1 ? '' : 's'} awaiting signatures in
"${teamName}"` to use an ICU plural via plural(count, { one: "# document
awaiting signatures in \"{teamName}\"", other: "# documents awaiting signatures
in \"{teamName}\"" }) (keeping the `count` and `teamName` variables) inside the
i18n._(msg`...`) call so translations use language-specific plural rules; update
the import and the subject assignment where `subject`, `count`, and `teamName`
are defined in send-owner-reminder-digest-email.handler.ts.

---

Outside diff comments:
In
`@apps/remix/app/components/general/document/document-page-view-recent-activity.tsx`:
- Around line 153-158: Extract the repeated call to formatDocumentAuditLogAction
into a single variable and reuse it for both title and content; for example, in
the DocumentPageViewRecentActivity component compute const formattedAction =
useMemo(() => formatDocumentAuditLogAction(i18n, auditLog, userId), [i18n,
auditLog, userId]) (or a plain const if memoization isn't needed) and then use
formattedAction.description for both the title prop and the paragraph contents
to avoid calling formatDocumentAuditLogAction twice.

In `@ROADMAP.md`:
- Around line 173-183: Mark the Person 5 checklist items as complete by checking
the boxes for the tasks related to implementing REMINDER_SENT in the activity
feed; specifically update the checklist entries to checked where they reference
document-page-view-recent-activity.tsx, the addition of a render case for
DOCUMENT_AUDIT_LOG_TYPE.REMINDER_SENT, use of the MailOpen icon, label handling
via formatDocumentAuditLogAction (covers per-recipient and digest/recipientId
null), application of the same DateTime relative formatting and text-slate-400 +
icon layout, and confirmation that no new imports were added and npm run build
passes.
🪄 Autofix (Beta)

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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d8932f28-ce79-45ec-822f-55290d7f38db

📥 Commits

Reviewing files that changed from the base of the PR and between d5ab4af and 4f39ab0.

📒 Files selected for processing (5)
  • ROADMAP.md
  • apps/remix/app/components/general/document/document-page-view-recent-activity.tsx
  • packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts
  • packages/lib/types/document-audit-logs.ts
  • packages/lib/utils/document-audit-logs.ts

Comment on lines 113 to 115
subject: i18n._(
msg`Reminder: ${plural(count, { one: '# document', other: '# documents' })} awaiting signatures in "${teamName}"`,
msg`Reminder: ${count} document${count === 1 ? '' : 's'} awaiting signatures in "${teamName}"`,
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

How to use Lingui plural macro for i18n pluralization in TypeScript?

💡 Result:

To use the Lingui plural macro for i18n pluralization in TypeScript, import it from "@lingui/core/macro" (or "@lingui/macro" in older versions) and use it with a numeric value and an object specifying plural forms based on your source language's rules (typically 'one' and 'other' for English). Basic usage: import { plural } from "@lingui/core/macro"; const count = 5; const message = plural(count, { one: "# book", other: "# books" }); // Renders as "5 books" (macro transforms to i18n._ under the hood with ICU MessageFormat) The '#' placeholder represents the number. Use template literals for variables: const message = plural(count, { one: ${name} has # friend, other: ${name} has # friends }); For JSX/React, use the Plural component: import { Plural } from "@lingui/react/macro"; Nest inside t macro for explicit IDs/comments: import { t, plural } from "@lingui/core/macro"; const message = t${plural(count, { one: "# book", other: "# books" })}; Lingui uses CLDR plural rules automatically for target languages during translation extraction. Only 'other' is required; use source language forms in code (e.g., English: one/other; Czech: one/few/many/other). Setup requires Lingui CLI for extraction (lingui extract) and compilation (lingui compile). Ensure Babel/SWC macro support is configured.

Citations:


🏁 Script executed:

# First, let's check if the file exists and view the relevant section
if [ -f "packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts" ]; then
  echo "=== File found, showing lines 100-130 ==="
  sed -n '100,130p' "packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts"
else
  echo "File not found at expected path. Searching for it..."
  find . -name "send-owner-reminder-digest-email.handler.ts" -type f
fi

Repository: b-mackenzie-alexander/documenso

Length of output: 1055


🏁 Script executed:

# Let's also check the imports in the file to understand the i18n pattern
head -50 packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts 2>/dev/null || echo "File check failed"

Repository: b-mackenzie-alexander/documenso

Length of output: 1809


🏁 Script executed:

# Search for plural usage in the codebase
rg "plural" --type ts --type tsx -A 3 -B 1 | head -100

Repository: b-mackenzie-alexander/documenso

Length of output: 102


🏁 Script executed:

# Also search for other email handlers that might have similar pluralization issues
find packages -name "*.handler.ts" -o -name "*.email.ts" | head -20

Repository: b-mackenzie-alexander/documenso

Length of output: 1605


🏁 Script executed:

# Check for any existing uses of msg with i18n._()
rg "i18n\._\(" --type ts --type tsx -A 2 | head -100

Repository: b-mackenzie-alexander/documenso

Length of output: 102


🏁 Script executed:

# Search for plural usage without type flags
rg "plural" -A 3 -B 1 --max-count 50 2>/dev/null | head -80

Repository: b-mackenzie-alexander/documenso

Length of output: 6616


🏁 Script executed:

# Search for i18n._ usage with msg
rg "i18n\._\(" -A 2 --max-count 30 2>/dev/null | head -60

Repository: b-mackenzie-alexander/documenso

Length of output: 4325


🏁 Script executed:

# Check what's imported from `@lingui` in other email files
rg "@lingui" packages/lib/jobs/definitions/emails/ --max-count 20

Repository: b-mackenzie-alexander/documenso

Length of output: 1295


🏁 Script executed:

# Search for actual plural() function usage if it exists
rg "plural\(" --max-count 20 -B 2 -A 2

Repository: b-mackenzie-alexander/documenso

Length of output: 5481


🏁 Script executed:

# Look at a similar email handler to understand the pattern
head -50 packages/lib/jobs/definitions/emails/send-owner-recipient-expired-email.handler.ts

Repository: b-mackenzie-alexander/documenso

Length of output: 1540


🏁 Script executed:

# Check the full context of the problematic line including more context
sed -n '105,125p' packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts

Repository: b-mackenzie-alexander/documenso

Length of output: 576


🏁 Script executed:

# Check imports in files using plural()
rg "import.*plural" --max-count 10 -B 1 -A 1

Repository: b-mackenzie-alexander/documenso

Length of output: 1738


🏁 Script executed:

# Also check the ROADMAP to understand the status of this task
cat ROADMAP.md | grep -A 5 -B 5 "plural()"

Repository: b-mackenzie-alexander/documenso

Length of output: 608


Use Lingui plural() macro to fix pluralization for non-English locales.

The manual pluralization ${count === 1 ? '' : 's'} only works for English. Languages like Russian, Japanese, Arabic, and others have different plural rules (1 singular form, 2+ plural forms, or more complex variants). This breaks translations for all non-English locales.

Import plural from @lingui/core/macro and replace the ternary with Lingui's ICU MessageFormat:

Proposed fix
-import { msg } from '@lingui/core/macro';
+import { msg, plural } from '@lingui/core/macro';
 // ...
       subject: i18n._(
-        msg`Reminder: ${count} document${count === 1 ? '' : 's'} awaiting signatures in "${teamName}"`,
+        plural(count, {
+          one: `Reminder: # document awaiting signatures in "${teamName}"`,
+          other: `Reminder: # documents awaiting signatures in "${teamName}"`,
+        }),
       ),

The # placeholder automatically renders the count. Lingui handles language-specific plural rules during translation compilation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
subject: i18n._(
msg`Reminder: ${plural(count, { one: '# document', other: '# documents' })} awaiting signatures in "${teamName}"`,
msg`Reminder: ${count} document${count === 1 ? '' : 's'} awaiting signatures in "${teamName}"`,
),
subject: i18n._(
plural(count, {
one: `Reminder: # document awaiting signatures in "${teamName}"`,
other: `Reminder: # documents awaiting signatures in "${teamName}"`,
}),
),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.ts`
around lines 113 - 115, Replace the manual English-only pluralization in the
email subject with Lingui's plural macro: import plural from
'@lingui/core/macro' and change the subject construction that currently uses
`${count} document${count === 1 ? '' : 's'} awaiting signatures in
"${teamName}"` to use an ICU plural via plural(count, { one: "# document
awaiting signatures in \"{teamName}\"", other: "# documents awaiting signatures
in \"{teamName}\"" }) (keeping the `count` and `teamName` variables) inside the
i18n._(msg`...`) call so translations use language-specific plural rules; update
the import and the subject assignment where `subject`, `count`, and `teamName`
are defined in send-owner-reminder-digest-email.handler.ts.

@b-mackenzie-alexander b-mackenzie-alexander merged commit e583efc into main Apr 21, 2026
13 of 14 checks passed
@b-mackenzie-alexander b-mackenzie-alexander deleted the feat/doc-2147-activity-feed branch April 21, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants