feat: add daily digest mail#55
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughAdds a due-tasks daily digest: DB migration and dedupe log, timezone-aware scheduling/utilities and tests, server-side task query and email rendering, runner with deduplication and Netlify cron trigger, notification type/payload updates, and user settings UI with auto-timezone detection. ChangesDue Tasks Digest Feature
Sequence Diagram(s)sequenceDiagram
participant NetlifyFunction as Netlify Function
participant DigestRunner as runDueTasksDigest
participant Database as Database
participant EmailService as Email Service
NetlifyFunction->>DigestRunner: trigger hourly
DigestRunner->>Database: query eligible users by schedule
loop for each candidate user
DigestRunner->>Database: insert claim into due_tasks_digest_log (on conflict do nothing)
alt claim inserted
DigestRunner->>Database: queryDueTasksForUser(dayStart, dayEnd)
DigestRunner->>DigestRunner: buildDigestEmail
DigestRunner->>EmailService: send email
DigestRunner->>Database: keep claim (sent_at set) / or update as recorded
else already claimed
DigestRunner->>DigestRunner: skip user
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
✅ Deploy Preview for pno-project-y ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
src/lib/due-tasks-digest/timezone.test.ts (1)
52-73: ⚡ Quick winAdd a regression test for high positive-offset timezones.
Please add a
getDayBoundsInUtccase for a zone likePacific/Kiritimati(UTC+14) to lock in 24-hour bounds and prevent this class of day-rollover regression.🤖 Prompt for 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. In `@src/lib/due-tasks-digest/timezone.test.ts` around lines 52 - 73, Add a regression unit test to timezone.test.ts that calls getDayBoundsInUtc with a high positive-offset zone like "Pacific/Kiritimati" (UTC+14) for a sample date (e.g., "2026-06-08") and assert it returns a 24-hour UTC window (start and end exactly 24 hours apart). Specifically, add an it block named something like "returns a 24-hour window for Pacific/Kiritimati" that calls getDayBoundsInUtc("2026-06-08", "Pacific/Kiritimati") and checks start.toISOString() and end.toISOString() against the expected ISO strings that reflect the +14 offset (start should be 14 hours behind the local midnight in UTC, end should be start + 24 hours). Ensure the test follows the same pattern as the existing cases for "Europe/Berlin" and "America/New_York".
🤖 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/components/settings/AccountDigestScheduleSection.tsx`:
- Around line 33-42: The updateSchedule function currently sends a
shallow-replaced digestSchedule built from the render-time schedule ({
...schedule, ...patch }), which can cause back-to-back edits to clobber each
other; change updateSchedule to merge the patch into the latest cached
preferences (not the render-time schedule) before calling updatePreferences —
e.g., read the current preferences from the client cache (or accept an updater
callback) and produce digestSchedule =
deepMerge(currentPreferences.digestSchedule ?? {}, patch) so you send a merged
object; alternatively, implement deep-merge logic server-side for digestSchedule
persistence so that partial patches only update nested fields instead of
replacing the whole object (refer to updateSchedule, updatePreferences,
digestSchedule, and schedule).
In `@src/db/schema.ts`:
- Around line 329-334: The digestScheduleSchema currently allows invalid
timezones and clock values; update the schema so timezone uses a runtime
validation against real IANA zone names (e.g., refine timezone by checking
Intl.supportedValuesOf?.('timeZone') includes the value or by attempting new
Intl.DateTimeFormat(undefined, { timeZone }) inside a try/catch) and tighten
workdayStart by replacing the loose regex with a strict one that enforces 00–23
hours and 00–59 minutes (e.g., validate with a pattern like
^(?:[01]\d|2[0-3]):[0-5]\d$ or an equivalent refine). Apply these changes on the
digestScheduleSchema (fields: timezone and workdayStart) so only valid zones and
HH:MM times are accepted before persistence.
In `@src/lib/due-tasks-digest/build-digest-email.ts`:
- Around line 26-33: groupTasksByProject currently keys the Map by
task.projectName which merges distinct projects with the same name; change it to
key the Map by task.projectId (use DueTaskRow.projectId) while still
storing/pushing the full DueTaskRow objects, and when returning the grouped
entries sort them by the projectName for display (e.g., sort by
groups.get(key)[0].projectName or otherwise use the first task's projectName) so
you preserve unique project grouping but keep human-readable ordering; update
references inside groupTasksByProject accordingly.
In `@src/lib/due-tasks-digest/run.ts`:
- Around line 122-125: The dedup check using hasDigestBeenSent before sending is
racy because the insert that marks a digest as sent occurs after delivery;
change to an atomic “claim before send” pattern: implement a
markDigestSentIfNotExists that performs a single DB insert/upsert using the
unique constraint on (userId, localDate) (INSERT ... ON CONFLICT DO NOTHING or
equivalent) and returns whether the insert succeeded; call this before sending
and skip sending if the insert failed, and if sending later fails, remove the
claim (delete the row) or use a transactional/compensating approach so failed
sends don't leave a permanent mark; replace the current hasDigestBeenSent +
post-send insert flow (referenced in hasDigestBeenSent and the post-send insert
block) with this atomic claim-check to eliminate the race.
- Around line 50-51: The code currently calls
userPreferencesSchema.parse(row.preferences ?? {}) which will throw on malformed
data and abort the whole run; replace parse with
userPreferencesSchema.safeParse(row.preferences ?? {}) (or try/catch around
parse) in the candidate-loading logic, check safeParseResult.success and on
failure log the error/context and fall back to an empty preferences object
before calling mergeUserNotifications(preferences) so one bad row.preferences
cannot crash run.ts.
In `@src/lib/due-tasks-digest/timezone.ts`:
- Around line 105-109: The nextLocalDate calculation currently uses
Date.UTC(year, month - 1, day + 1, 12) which can push the local date two days
forward in +12/+13/+14 zones; fix getDayBoundsInUtc by computing the local date
for the current day using getLocalDateString(new Date(Date.UTC(year, month - 1,
day, 12)), timeZone), then derive the next calendar day by adding one day in the
timezone-aware local-date string (not by shifting the UTC anchor), and pass that
next local date + "00:00" to zonedDateTimeToUtc; update the code paths around
getLocalDateString and zonedDateTimeToUtc (references: getDayBoundsInUtc,
getLocalDateString, zonedDateTimeToUtc) so the increment is done in local-date
space to avoid creating a 48-hour window in extreme east timezones.
---
Nitpick comments:
In `@src/lib/due-tasks-digest/timezone.test.ts`:
- Around line 52-73: Add a regression unit test to timezone.test.ts that calls
getDayBoundsInUtc with a high positive-offset zone like "Pacific/Kiritimati"
(UTC+14) for a sample date (e.g., "2026-06-08") and assert it returns a 24-hour
UTC window (start and end exactly 24 hours apart). Specifically, add an it block
named something like "returns a 24-hour window for Pacific/Kiritimati" that
calls getDayBoundsInUtc("2026-06-08", "Pacific/Kiritimati") and checks
start.toISOString() and end.toISOString() against the expected ISO strings that
reflect the +14 offset (start should be 14 hours behind the local midnight in
UTC, end should be start + 24 hours). Ensure the test follows the same pattern
as the existing cases for "Europe/Berlin" and "America/New_York".
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e41e37cb-d7aa-48fe-ac40-2abde76b1ba5
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
drizzle/0006_thankful_warstar.sqldrizzle/meta/0006_snapshot.jsondrizzle/meta/_journal.jsonnetlify/functions/due-tasks-digest.mtspackage.jsonsrc/components/settings/AccountDigestScheduleSection.tsxsrc/components/settings/AccountNotificationSection.tsxsrc/components/settings/UserSettingsForm.tsxsrc/db/schema.tssrc/hooks/use-auto-detect-digest-timezone.tssrc/lib/digest-timezones.tssrc/lib/due-tasks-digest/build-digest-email.tssrc/lib/due-tasks-digest/query-due-tasks.tssrc/lib/due-tasks-digest/run.tssrc/lib/due-tasks-digest/timezone.test.tssrc/lib/due-tasks-digest/timezone.tssrc/lib/notifications/payloads.tssrc/lib/notifications/types.tssrc/routes/_signed-in.tsx
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
🎉 This PR is included in version 0.9.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary by CodeRabbit
New Features
Tests
Chores
UX