whisper-money/app
Víctor Falcón eea929ef9d refactor(drip): extract base mailable and job for the drip email family
The eight drip emails and their eight send jobs were near copy-paste. Every
mailable repeated the same constructor, $tries/$backoff, drip_from envelope,
markdown+userName content and RateLimited middleware, differing only in subject
and template (plus a reply-to on one and an extra view var on another). Every job
repeated the canReceiveEmails/hasReceivedEmail guards, the Mail::send call and the
UserMailLog::create block, differing only in the email type, the mailable, and a
handful of per-email eligibility checks.

Introduce two abstract bases:

- DripMail owns the shared mailable shape; subclasses declare dripSubject() and
  template(), with optional contentData() (PromoCode's promo code) and
  repliesToSender() (PaywallFollowUp) hooks.
- SendDripEmailJob owns the shared handle() flow; subclasses declare emailType()
  and buildMail(), with an optional shouldSend() hook for the per-email
  eligibility rules (pro-plan, onboarding, transactions, AI consent, ...).

Each mailable drops from ~68 to ~13 lines and each job from ~48 to ~18, with the
shared send/log logic now living in one place.

Behavior unchanged: subjects, from/reply-to, queue, rate limiting, templates,
view data and every eligibility guard are preserved. Full suite green
(1868 passing, 0 failing) and drip job/command/listener tests pass (71/71).
2026-07-04 20:28:20 +02:00
..
Actions fix(security): scope job-status endpoints to owner + feature-area fixes (#627) 2026-07-03 14:49:32 +02:00
Ai/Agents feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
Console/Commands feat(encryption): commands to warn and remove inactive encrypted-data accounts (#633) 2026-07-03 15:57:04 +00:00
Contracts feat(connections): manage which accounts a bank connection syncs (#558) 2026-06-18 16:22:49 +02:00
Enums feat(transactions): serve import dedup and account ledger from the backend (#631) 2026-07-03 16:49:59 +02:00
Events Add Budgeting Feature to Track and Manage Spending (#36) 2026-01-21 15:25:50 +01:00
Exceptions/Banking fix(banking): skip inaccessible EnableBanking accounts instead of failing the connection (#559) 2026-06-19 08:59:05 +02:00
Features refactor(ai): remove AiConsentSettings feature flag (#619) 2026-07-01 09:47:55 +02:00
Http perf(accounts): defer the account ledger prop on show (#632) 2026-07-03 17:40:08 +02:00
Jobs refactor(drip): extract base mailable and job for the drip email family 2026-07-04 20:28:20 +02:00
Listeners fix(discord): show old → new plan on plan change notification (#637) 2026-07-04 15:49:09 +00:00
Mail refactor(drip): extract base mailable and job for the drip email family 2026-07-04 20:28:20 +02:00
Models feat(ai): dismissable AI consent banner that stops after the first decision (#617) 2026-07-01 07:26:36 +00:00
Notifications fix: verify email via signed link without requiring login (#490) 2026-06-05 10:01:32 +02:00
Policies refactor(policies): extract HandlesUserOwnership trait (#478) 2026-06-03 17:43:30 +02:00
Providers fix: stop double-dispatching transaction listeners (N+1 insert into jobs) (#620) 2026-07-02 13:26:45 +00:00
Rules feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
Services fix(ai): surface learned-rule toast in edit modal and guard weak description keys (#635) 2026-07-04 11:00:15 +00:00