whisper-money/app/Jobs
Víctor Falcón 9a458b1031
feat(ai): manage AI consent outside onboarding with live backfill (#591)
## Summary

Lets existing (non-onboarding) users discover and manage AI
categorization, gated behind a new Pennant feature flag. When a flagged
paid user grants consent, every uncategorized transaction is categorized
in the background with live, on-screen feedback.

Enable per user: `php artisan feature:enable AiConsentSettings
user@example.com`

## What's included

**Feature flag**
- New `AiConsentSettings` Pennant flag (off by default), exposed to the
frontend via the shared `features` prop.

**Billing settings (`/settings/billing`)**
- When the flag is active, a section to grant or revoke AI consent
(checkbox → `POST`/`DELETE /ai/consent`).

**Transactions page**
- A consent prompt rendered as the first row of the transactions table
(new `DataTable` `topRow` slot), styled with the shared gradient
`AiSparkleIcon`. Only shown to paid users with the flag who haven't
consented yet.
- An inline "Enable AI" button records consent without leaving the page.

**Backfill on consent**
- Granting consent dispatches a queued
`CategorizeUncategorizedTransactionsJob` that categorizes all of the
user's uncategorized transactions, **most recent first**, recording
progress in the cache.
- The transactions page polls a new status endpoint (`GET
/ai/categorization/{jobId}/status`): visible uncategorized rows show a
spinner + pulse while the backfill runs, categories stream in via
partial reloads, and a toast tracks `processed / total` until
completion.

## Notes / decisions

- **Polling, not websockets** — the project has no broadcasting
configured; this mirrors the existing `ReEvaluateTransactionRules` job +
status-endpoint pattern.
- The backfill job is kept separate from
`CategorizeOnboardingTransactionsJob` so the onboarding pass stays
progress-free (small, commented duplication).
- Reuses the existing `AiCategorizationGate` (config kill-switch + pro
plan + active consent) — free users and disabled-AI never dispatch the
job.
- Not included (YAGNI): cancelling an in-flight backfill on revoke;
persisting the spinner across full page reloads.

## Testing

- New Pest coverage: feature-flag exposure, billing/transactions consent
props, consent-triggered dispatch + gating, the status endpoint
(200/404), job progress recording, and recent-first ordering.
- `php artisan test --exclude-testsuite=Browser` passes (one unrelated,
pre-existing `DashboardTest` failure on `main`).
- Pint, Prettier and ESLint clean.
2026-06-25 10:50:35 +02:00
..
Drip feat(drip): email users stuck on the paywall a day after onboarding (#562) 2026-06-19 14:11:12 +00:00
ApplySingleAutomationRuleJob.php fix: batch automation rule application (#435) 2026-05-26 10:45:37 +02:00
AssignHistoricalTransactionsToBudget.php feat: Load transactions history on budget created (#72) 2026-01-22 11:10:15 +01:00
CategorizeOnboardingTransactionsJob.php feat(ai): manage AI consent outside onboarding with live backfill (#591) 2026-06-25 10:50:35 +02:00
CategorizeUncategorizedTransactionsJob.php feat(ai): manage AI consent outside onboarding with live backfill (#591) 2026-06-25 10:50:35 +02:00
GenerateHistoricalLoanBalancesJob.php feat(loans): backfill historical balances on loan creation (#322) 2026-04-24 13:09:34 +01:00
GenerateHistoricalRealEstateBalancesJob.php feat(real-estate): auto-calculate revaluation % and generate historical balances (#253) 2026-04-15 12:18:33 +00:00
GenerateRuleSuggestionsJob.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
ReEvaluateTransactionRulesJob.php feat(transactions): re-add select all matching filters to bulk actions bar (#169) 2026-03-01 10:27:43 +00:00
SendDailyBankTransactionsSyncedEmailJob.php feat(settings): let users disable bank transactions email (#472) 2026-06-02 12:24:39 +02:00
SendUpdateEmailJob.php Support soft-deleted users with reusable emails (#316) 2026-04-22 11:41:41 +01:00
SyncAllBankingConnectionsJob.php refactor: centralize duplicated provider & locale keys into enums (#543) 2026-06-16 13:43:14 +00:00
SyncBankingConnectionJob.php fix(banking): handle EnableBanking expired sessions as reconnect, not error (#557) 2026-06-18 13:48:13 +00:00
SyncBinanceHistoricalBalancesJob.php feat: Add Binance integration (#131) 2026-02-18 15:23:46 +01:00