feat(#2147): render REMINDER_SENT events in document activity feed#15
Conversation
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>
📝 WalkthroughWalkthroughReminder email functionality is enhanced with audit logging for owner digest emails. The email subject template switches from Lingui Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 | 🔵 TrivialPerson 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.tsxsatisfies these requirements:
- Uses
MailOpenicon (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 | 🔵 TrivialMinor optimization:
formatDocumentAuditLogActionis called twice.The function is invoked once for the
titleattribute 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
📒 Files selected for processing (5)
ROADMAP.mdapps/remix/app/components/general/document/document-page-view-recent-activity.tsxpackages/lib/jobs/definitions/emails/send-owner-reminder-digest-email.handler.tspackages/lib/types/document-audit-logs.tspackages/lib/utils/document-audit-logs.ts
| 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}"`, | ||
| ), |
There was a problem hiding this comment.
🧩 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
fiRepository: 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 -100Repository: 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 -20Repository: 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 -100Repository: 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 -80Repository: 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 -60Repository: 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 20Repository: 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 2Repository: 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.tsRepository: 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.tsRepository: 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 1Repository: 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.
| 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.
Summary
REMINDER_SENTmatch case to the document activity feed component using theMailOpenicon, consistent with the existingDOCUMENT_OPENEDpatternrecipientIdnullable inZDocumentAuditLogEventReminderSentSchemato support digest entries (no specific recipient)DocumentAuditLogper envelope in the digest handler so digest sends appear in the activity feedformatDocumentAuditLogAction: renders "Reminder digest sent to owner" whenrecipientIdis null, and "Reminder sent to [name]" for per-recipient entriesDepends on: PR #14 (Person 2, merged) and PR #11 (Person 4, merged)
Test plan
npm run buildpasses🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Improvements
Documentation