Skip to content

feat(#2147): add reminder UI — email checkbox and settings dialog fields#11

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

feat(#2147): add reminder UI — email checkbox and settings dialog fields#11
b-mackenzie-alexander merged 4 commits into
mainfrom
feat/doc-2147-reminder-ui

Conversation

@vic-aibuilder
Copy link
Copy Markdown
Collaborator

@vic-aibuilder vic-aibuilder commented Apr 18, 2026

Summary

  • Add ownerReminderDigest checkbox to DocumentEmailCheckboxes (Email tab) so senders can opt out of the reminder digest
  • Add reminderEnabled toggle and reminderIntervalDays interval input (1–30 days) to the envelope settings dialog General tab, immediately after the Expiration field
  • Both reminder fields are disabled when the document has already been sent or when no expiration date is set (with explanatory tooltip)
  • Extend ZDocumentMetaCreateSchema, ZEnvelopeLiteSchema, and ZEnvelopeEditorSchema to carry the new fields through the tRPC layer
  • Fix null → undefined coercion for reminderIntervalDays in embedded authoring routes

Test plan

  • Open document settings dialog → General tab → confirm reminder toggle appears after Expiration field
  • Confirm toggle is disabled when no expiration date is set, with tooltip explaining why
  • Enable expiration, then enable reminder toggle → confirm interval input appears
  • Confirm both fields are disabled after document is sent
  • Open settings dialog → Email tab → confirm "Send me a reminder digest" checkbox appears
  • Confirm npm run build passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Automatic Reminders in envelope settings with configurable interval (1–30 days).
    • UI: “Automatic Reminders” switch and conditional “Reminder Interval (days)” input; interval required when reminders enabled.
    • Reminder controls disabled for sent envelopes or when no expiration period is set.
    • Reminder settings persist on envelope create/update.
  • Behavior Changes

    • Owner reminder digest email option added and default changed to off.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds automatic reminder fields to document/envelope schemas and editor UI: introduces reminderEnabled and reminderIntervalDays with cross-field Zod validation, surfaces a switch and conditional numeric input in the envelope settings, persists these fields in create/update payloads, and adds an owner reminder digest checkbox and default change.

Changes

Cohort / File(s) Summary
Document meta & envelope schemas
packages/lib/types/document-meta.ts, packages/lib/types/envelope.ts
Added reminderEnabled?: boolean and reminderIntervalDays?: number (int, 1–30) to document meta; split fields into ZDocumentMetaFieldsSchema and applied superRefine to require reminderIntervalDays and envelopeExpirationPeriod when reminders enabled.
Editor envelope schema
packages/lib/types/envelope-editor.ts
Extended ZEditorEnvelopeSchema.documentMeta to include reminderEnabled and reminderIntervalDays.
Envelope editor UI & form
apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
Form schema now uses superRefine; added meta.reminderEnabled switch (disabled if sent or no expiration) and conditional numeric meta.reminderIntervalDays input; default values and submit payload persist these fields.
Create / Update envelope routes
apps/remix/app/routes/embed+/v2+/authoring+/envelope.create._index.tsx, apps/remix/app/routes/embed+/v2+/authoring+/envelope.edit.$id.tsx
Mapped envelope.documentMeta.reminderIntervalDays into create/update request payloads (included explicitly or undefined).
Email settings & UI
packages/ui/components/document/document-email-checkboxes.tsx, packages/lib/types/document-email.ts
Added OwnerReminderDigest checkbox to UI; changed ownerReminderDigest default from truefalse in schema and DEFAULT_DOCUMENT_EMAIL_SETTINGS.
Distribute envelope request schema
packages/trpc/server/envelope-router/distribute-envelope.types.ts
Switched meta validation to use ZDocumentMetaFieldsSchema (same picked fields) to align with field/schema refactor.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I hopped through schemas, switches, and fields,

Tucked tiny reminders where logic yields.
A switch, a number, a checkbox so neat,
Envelopes will nudge — no missed beats! ✨📨

🚥 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 'feat(#2147): add reminder UI — email checkbox and settings dialog fields' directly and accurately describes the main changes: adding reminder UI elements (email checkbox and settings dialog fields).
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-reminder-ui

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

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Thank you for creating your first Pull Request and for being a part of the open signing revolution! 💚🚀

Feel free to hop into our community in Discord

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`:
- Around line 805-835: When the reminder toggle (meta.reminderEnabled) is
flipped, update the form state so disabling clears meta.reminderIntervalDays and
enabling sets a sensible default if it's currently undefined; call
form.setValue('meta.reminderIntervalDays', undefined) when meta.reminderEnabled
becomes false, and when it becomes true call
form.setValue('meta.reminderIntervalDays', 7) only if
form.getValues('meta.reminderIntervalDays') is undefined (also respect
envelopeHasBeenSent if the toggle should be inert when sent). Implement this in
the toggle's onChange handler or a useEffect watching
form.watch('meta.reminderEnabled'), referencing meta.reminderEnabled and
meta.reminderIntervalDays to locate the logic.
- Around line 789-798: The Switch's disabled prop uses non-reactive
form.getValues('meta.envelopeExpirationPeriod'), so replace that with a watched
value: call form.watch('meta.envelopeExpirationPeriod') at the component level
(similar to the existing emailSettings watch near the emailSettings extraction)
and use that watched expirationPeriod in the disabled expression alongside
envelopeHasBeenSent; update both occurrences where
getValues('meta.envelopeExpirationPeriod') is used so the Switch (and any other
UI) reacts immediately when the user toggles expiration.
🪄 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: f6e9667a-7f87-4aa6-8e00-4a7530979595

📥 Commits

Reviewing files that changed from the base of the PR and between e21637c and ec93742.

📒 Files selected for processing (7)
  • apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
  • apps/remix/app/routes/embed+/v2+/authoring+/envelope.create._index.tsx
  • apps/remix/app/routes/embed+/v2+/authoring+/envelope.edit.$id.tsx
  • packages/lib/types/document-meta.ts
  • packages/lib/types/envelope-editor.ts
  • packages/lib/types/envelope.ts
  • packages/ui/components/document/document-email-checkboxes.tsx

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`:
- Around line 152-159: The current superRefine only checks
data.meta.reminderIntervalDays but doesn't stop reminders when no expiration is
set; update the superRefine validator (the same block using superRefine) to also
check data.meta.reminderEnabled && !data.meta.expiresAt (or the exact expiration
field name used in the schema, e.g., data.meta.expiration) and call ctx.addIssue
with a custom ZodIssueCode and a message like "Expiration is required when
reminders are enabled", targeting the expiration path ['meta',
'<expirationFieldName>']; keep the existing interval check so both expiration
and reminderIntervalDays are required when reminderEnabled is true.
- Around line 814-844: The reminder interval input remains editable when the
expiration is unset; update the rendering/disabled logic in the
EnvelopeEditorSettingsDialog so the interval input respects expiration presence.
Specifically, change the condition around the FormField (currently using
form.watch('meta.reminderEnabled')) to also require a set expiration
(form.watch('meta.expiresAt')) or alternately add to the Input's disabled prop:
disabled={envelopeHasBeenSent || !form.watch('meta.expiresAt')}; target the
FormField with name "meta.reminderIntervalDays" and the Input element to ensure
the interval is either hidden or disabled when expiresAt is not present.
🪄 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: c6435da8-52ba-42e2-9b7b-7cdb387d19a4

📥 Commits

Reviewing files that changed from the base of the PR and between ec93742 and 4b1aec2.

📒 Files selected for processing (1)
  • apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx

Comment on lines +152 to +159
}).superRefine((data, ctx) => {
if (data.meta.reminderEnabled && !data.meta.reminderIntervalDays) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: msg`Reminder interval is required when reminders are enabled`.id,
path: ['meta', 'reminderIntervalDays'],
});
}
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

Enforce expiration as a required precondition for reminders

Line [153] validates interval presence but does not prevent meta.reminderEnabled = true when no expiration is set. This allows an invalid reminder configuration to be submitted.

🔧 Proposed fix
 }).superRefine((data, ctx) => {
-  if (data.meta.reminderEnabled && !data.meta.reminderIntervalDays) {
+  if (data.meta.reminderEnabled && !data.meta.envelopeExpirationPeriod) {
+    ctx.addIssue({
+      code: z.ZodIssueCode.custom,
+      message: msg`Set an expiration date to enable automatic reminders`.id,
+      path: ['meta', 'reminderEnabled'],
+    });
+  }
+
+  if (data.meta.reminderEnabled && data.meta.reminderIntervalDays == null) {
     ctx.addIssue({
       code: z.ZodIssueCode.custom,
       message: msg`Reminder interval is required when reminders are enabled`.id,
       path: ['meta', 'reminderIntervalDays'],
     });
   }
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`
around lines 152 - 159, The current superRefine only checks
data.meta.reminderIntervalDays but doesn't stop reminders when no expiration is
set; update the superRefine validator (the same block using superRefine) to also
check data.meta.reminderEnabled && !data.meta.expiresAt (or the exact expiration
field name used in the schema, e.g., data.meta.expiration) and call ctx.addIssue
with a custom ZodIssueCode and a message like "Expiration is required when
reminders are enabled", targeting the expiration path ['meta',
'<expirationFieldName>']; keep the existing interval check so both expiration
and reminderIntervalDays are required when reminderEnabled is true.

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.

♻️ Duplicate comments (2)
apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx (2)

154-161: ⚠️ Potential issue | 🟠 Major

Enforce expiration as a required precondition for reminders in schema validation.

Line [155] only enforces interval presence, so meta.reminderEnabled = true can still be submitted with no expiration period.

🔧 Proposed fix
   .superRefine((data, ctx) => {
-    if (data.meta.reminderEnabled && !data.meta.reminderIntervalDays) {
+    if (data.meta.reminderEnabled && !data.meta.envelopeExpirationPeriod) {
+      ctx.addIssue({
+        code: z.ZodIssueCode.custom,
+        message: msg`Set an expiration date to enable automatic reminders`.id,
+        path: ['meta', 'envelopeExpirationPeriod'],
+      });
+    }
+
+    if (data.meta.reminderEnabled && data.meta.reminderIntervalDays == null) {
       ctx.addIssue({
         code: z.ZodIssueCode.custom,
         message: msg`Reminder interval is required when reminders are enabled`.id,
         path: ['meta', 'reminderIntervalDays'],
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`
around lines 154 - 161, The current .superRefine only requires
reminderIntervalDays when data.meta.reminderEnabled is true but does not require
an expiration field; update the superRefine callback to also check that
data.meta.expirationDays (or the project’s expiration field, e.g.,
data.meta.expirationDate) is present/valid when data.meta.reminderEnabled ===
true and, if missing, call ctx.addIssue with code z.ZodIssueCode.custom, a
helpful message (e.g., "Expiration is required when reminders are enabled"), and
path ['meta','expirationDays'] (or ['meta','expirationDate'] to match the actual
field name) so both reminderIntervalDays and the expiration field are enforced
as required preconditions for reminders.

813-843: ⚠️ Potential issue | 🟠 Major

Make reminder interval rendering/disabled state depend on expiration.

Line [813] and Line [829] allow editing meta.reminderIntervalDays after expiration is unset.

🔧 Proposed fix
-                      {form.watch('meta.reminderEnabled') && (
+                      {form.watch('meta.reminderEnabled') && envelopeExpirationPeriod && (
                         <FormField
                           control={form.control}
                           name="meta.reminderIntervalDays"
                           render={({ field }) => (
@@
                                 <Input
                                   type="number"
                                   min={1}
                                   max={30}
                                   className="bg-background"
-                                  disabled={envelopeHasBeenSent}
+                                  disabled={envelopeHasBeenSent || !envelopeExpirationPeriod}
                                   value={field.value ?? ''}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`
around lines 813 - 843, The reminder interval input currently only checks
form.watch('meta.reminderEnabled') and envelopeHasBeenSent; update the render
condition and disabled state so the field also depends on whether an expiration
is set. Change the conditional that renders meta.reminderIntervalDays to
something like form.watch('meta.reminderEnabled') &&
Boolean(form.watch('meta.expiration')) (or the actual expiration field name used
in this form), and set the Input's disabled prop to envelopeHasBeenSent ||
!Boolean(form.watch('meta.expiration')), ensuring meta.reminderIntervalDays
cannot be edited when expiration is unset or the envelope has been sent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`:
- Around line 154-161: The current .superRefine only requires
reminderIntervalDays when data.meta.reminderEnabled is true but does not require
an expiration field; update the superRefine callback to also check that
data.meta.expirationDays (or the project’s expiration field, e.g.,
data.meta.expirationDate) is present/valid when data.meta.reminderEnabled ===
true and, if missing, call ctx.addIssue with code z.ZodIssueCode.custom, a
helpful message (e.g., "Expiration is required when reminders are enabled"), and
path ['meta','expirationDays'] (or ['meta','expirationDate'] to match the actual
field name) so both reminderIntervalDays and the expiration field are enforced
as required preconditions for reminders.
- Around line 813-843: The reminder interval input currently only checks
form.watch('meta.reminderEnabled') and envelopeHasBeenSent; update the render
condition and disabled state so the field also depends on whether an expiration
is set. Change the conditional that renders meta.reminderIntervalDays to
something like form.watch('meta.reminderEnabled') &&
Boolean(form.watch('meta.expiration')) (or the actual expiration field name used
in this form), and set the Input's disabled prop to envelopeHasBeenSent ||
!Boolean(form.watch('meta.expiration')), ensuring meta.reminderIntervalDays
cannot be edited when expiration is unset or the envelope has been sent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 90787816-e094-4dfc-92ed-ab49c53770c8

📥 Commits

Reviewing files that changed from the base of the PR and between 4b1aec2 and c747eff.

📒 Files selected for processing (1)
  • apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx

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.

♻️ Duplicate comments (2)
apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx (2)

813-847: ⚠️ Potential issue | 🟡 Minor

Interval input should also respect expiration state.

When envelopeExpirationPeriod is cleared after reminders were enabled, the interval input remains visible and editable while the switch is disabled. This creates an inconsistent UX where the user can edit a field that will fail server-side validation.

🔧 Proposed fix
-                      {form.watch('meta.reminderEnabled') && (
+                      {form.watch('meta.reminderEnabled') && envelopeExpirationPeriod && (
                         <FormField
                           control={form.control}
                           name="meta.reminderIntervalDays"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`
around lines 813 - 847, The reminder interval input (FormField with name
"meta.reminderIntervalDays") must also respect the envelope expiration state:
change the visibility/availability check from only
form.watch('meta.reminderEnabled') to require both the reminder toggle and a
valid expiration (e.g., form.watch('meta.reminderEnabled') &&
!!form.watch('meta.envelopeExpirationPeriod')), or alternatively keep it visible
but set the Input disabled when envelopeExpirationPeriod is missing; ensure you
reference envelopeHasBeenSent and the existing field name
"meta.reminderIntervalDays" so the field is either hidden or disabled when the
expiration period is cleared to prevent client-side editing that would fail
server validation.

154-162: 🧹 Nitpick | 🔵 Trivial

Add client-side validation for expiration requirement.

The superRefine only validates reminderIntervalDays when reminderEnabled is true, but doesn't validate that envelopeExpirationPeriod is set. This creates a mismatch with the server-side schema (ZDocumentMetaCreateSchema in document-meta.ts), which validates both conditions.

While the UI disables the switch when no expiration is set (line 804), adding the client-side validation would provide consistent error feedback and catch edge cases.

🔧 Proposed fix
   .superRefine((data, ctx) => {
+    if (data.meta.reminderEnabled && !data.meta.envelopeExpirationPeriod) {
+      ctx.addIssue({
+        code: z.ZodIssueCode.custom,
+        message: msg`Set an expiration date to enable automatic reminders`.id,
+        path: ['meta', 'reminderEnabled'],
+      });
+    }
+
     if (data.meta.reminderEnabled && !data.meta.reminderIntervalDays) {
       ctx.addIssue({
         code: z.ZodIssueCode.custom,
         message: msg`Reminder interval is required when reminders are enabled`.id,
         path: ['meta', 'reminderIntervalDays'],
       });
     }
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`
around lines 154 - 162, The superRefine on the Zod schema only enforces
reminderIntervalDays when data.meta.reminderEnabled is true; update the same
superRefine block in envelope-editor-settings-dialog.tsx to also require
data.meta.envelopeExpirationPeriod when reminderEnabled is true by adding a
ctx.addIssue with code z.ZodIssueCode.custom, a clear message (e.g. "Expiration
period is required when reminders are enabled"), and path
['meta','envelopeExpirationPeriod'] so the client-side validation matches the
server ZDocumentMetaCreateSchema behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx`:
- Around line 813-847: The reminder interval input (FormField with name
"meta.reminderIntervalDays") must also respect the envelope expiration state:
change the visibility/availability check from only
form.watch('meta.reminderEnabled') to require both the reminder toggle and a
valid expiration (e.g., form.watch('meta.reminderEnabled') &&
!!form.watch('meta.envelopeExpirationPeriod')), or alternatively keep it visible
but set the Input disabled when envelopeExpirationPeriod is missing; ensure you
reference envelopeHasBeenSent and the existing field name
"meta.reminderIntervalDays" so the field is either hidden or disabled when the
expiration period is cleared to prevent client-side editing that would fail
server validation.
- Around line 154-162: The superRefine on the Zod schema only enforces
reminderIntervalDays when data.meta.reminderEnabled is true; update the same
superRefine block in envelope-editor-settings-dialog.tsx to also require
data.meta.envelopeExpirationPeriod when reminderEnabled is true by adding a
ctx.addIssue with code z.ZodIssueCode.custom, a clear message (e.g. "Expiration
period is required when reminders are enabled"), and path
['meta','envelopeExpirationPeriod'] so the client-side validation matches the
server ZDocumentMetaCreateSchema behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 03590a4f-1f10-47ba-8318-1ed051bce1eb

📥 Commits

Reviewing files that changed from the base of the PR and between c747eff and 27d32af.

📒 Files selected for processing (4)
  • apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
  • packages/lib/types/document-email.ts
  • packages/lib/types/document-meta.ts
  • packages/trpc/server/envelope-router/distribute-envelope.types.ts

vic-aibuilder and others added 4 commits April 21, 2026 10:40
…ialog fields

- Add ownerReminderDigest checkbox to DocumentEmailCheckboxes
- Add reminderEnabled toggle and reminderIntervalDays interval input to
  envelope settings dialog General tab (disabled when no expiration set
  or document already sent)
- Extend ZDocumentMetaCreateSchema, ZEnvelopeLiteSchema, and
  ZEnvelopeEditorSchema to include reminder fields
- Fix null→undefined coercion for reminderIntervalDays in embedded
  authoring routes

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

- Replace form.getValues() with form.watch() for envelopeExpirationPeriod so
  the reminder switch disables reactively when the expiration period is cleared
- Add superRefine cross-field validation requiring reminderIntervalDays when
  reminderEnabled is true, preventing silent misconfiguration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… opt-in default

- Extract ZDocumentMetaFieldsSchema so .pick() callers remain unbroken
- Add .superRefine() to ZDocumentMetaCreateSchema: requires reminderIntervalDays
  and envelopeExpirationPeriod when reminderEnabled is true
- Change ownerReminderDigest default from true to false (opt-in, not opt-out)
- Replace Number() coercion with parseInt() in interval input handler
- Update distribute-envelope.types.ts to use ZDocumentMetaFieldsSchema.pick()

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

♻️ Duplicate comments (1)
apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx (1)

154-162: ⚠️ Potential issue | 🟠 Major

Enforce expiration/reminder consistency in the form layer.

Line [154]-Line [162] validates interval only, but Line [813] still renders the interval whenever reminderEnabled is true. If expiration is cleared, reminders can remain logically “on” in form state and submit into a backend validation failure.

🔧 Suggested fix
   .superRefine((data, ctx) => {
-    if (data.meta.reminderEnabled && !data.meta.reminderIntervalDays) {
+    if (data.meta.reminderEnabled && !data.meta.envelopeExpirationPeriod) {
+      ctx.addIssue({
+        code: z.ZodIssueCode.custom,
+        message: msg`Set an expiration date to enable automatic reminders`.id,
+        path: ['meta', 'reminderEnabled'],
+      });
+    }
+
+    if (data.meta.reminderEnabled && data.meta.reminderIntervalDays == null) {
       ctx.addIssue({
         code: z.ZodIssueCode.custom,
         message: msg`Reminder interval is required when reminders are enabled`.id,
         path: ['meta', 'reminderIntervalDays'],
       });
@@
-                      {form.watch('meta.reminderEnabled') && (
+                      {form.watch('meta.reminderEnabled') && envelopeExpirationPeriod && (
                         <FormField
@@
-                                  disabled={envelopeHasBeenSent}
+                                  disabled={envelopeHasBeenSent || !envelopeExpirationPeriod}

Also applies to: 813-847

🤖 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/types/document-email.ts`:
- Line 75: The change flips the default of ownerReminderDigest to false, which
breaks opt-out semantics; instead restore the legacy default of true for
ownerReminderDigest in the email settings schema so that missing/partial/invalid
emailSettings continue to enable digests unless explicitly set to false (update
the default for ownerReminderDigest wherever it’s defined in the
document-email.ts schema and any duplicate declaration around the second
occurrence so both places use true), ensuring the opt-out behavior remains
intact.
🪄 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: 8c2c45c9-a9f2-4d3e-9e22-ef6103ef29e6

📥 Commits

Reviewing files that changed from the base of the PR and between 27d32af and 9cd32e8.

📒 Files selected for processing (9)
  • apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
  • apps/remix/app/routes/embed+/v2+/authoring+/envelope.create._index.tsx
  • apps/remix/app/routes/embed+/v2+/authoring+/envelope.edit.$id.tsx
  • packages/lib/types/document-email.ts
  • packages/lib/types/document-meta.ts
  • packages/lib/types/envelope-editor.ts
  • packages/lib/types/envelope.ts
  • packages/trpc/server/envelope-router/distribute-envelope.types.ts
  • packages/ui/components/document/document-email-checkboxes.tsx

'Whether to send a digest email to the document owner when reminders are sent to unsigned recipients. Aggregates all pending documents for the team into a single email.',
)
.default(true),
.default(false),
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

Default flip likely breaks “opt-out” semantics and legacy behavior

Line 75 and Line 118 switch ownerReminderDigest to default false, so any document with missing/partial/invalid emailSettings now silently disables digest emails. That is a backward-incompatible behavior shift and appears to conflict with the PR objective wording (“allow senders to opt out”).

Suggested fix (preserve opt-out behavior)
-      .default(false),
+      .default(true),
...
-  ownerReminderDigest: false,
+  ownerReminderDigest: true,

Also applies to: 118-118

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/types/document-email.ts` at line 75, The change flips the
default of ownerReminderDigest to false, which breaks opt-out semantics; instead
restore the legacy default of true for ownerReminderDigest in the email settings
schema so that missing/partial/invalid emailSettings continue to enable digests
unless explicitly set to false (update the default for ownerReminderDigest
wherever it’s defined in the document-email.ts schema and any duplicate
declaration around the second occurrence so both places use true), ensuring the
opt-out behavior remains intact.

@b-mackenzie-alexander b-mackenzie-alexander merged commit d5ab4af into main Apr 21, 2026
11 of 12 checks passed
@b-mackenzie-alexander b-mackenzie-alexander deleted the feat/doc-2147-reminder-ui branch April 21, 2026 22:27
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