PHP's default \s is ASCII-only, so the duplicate check missed rows differing
only by a non-breaking space (U+00A0) or other Unicode whitespace — common in
bank statement descriptions — whereas the old client-side check (JS \s) caught
them. Normalize the full Unicode whitespace set before keying, then trim.
Also harden the endpoint's tests: Unicode-whitespace matching, the date-range
boundary (an out-of-range existing row must not match), and request validation.
Migrate the last two flows off the deprecated client-side IndexedDB/Dexie
"offline-first" model so they read from the backend instead of the local DB.
- Import duplicate detection now hits POST /api/transactions/check-duplicates,
which matches on day + amount (integer cents) + normalized description
server-side, replacing the sync()-then-read-Dexie approach that silently
missed duplicates beyond the local cache window.
- The account detail transaction list is now served as an Inertia prop from
AccountController@show (eager-loading category + labels) instead of fetching
every transaction from /api/sync/transactions and filtering client-side,
which could show an incomplete list for accounts with long history.
- The import preview's "existing transactions" panel reads from
GET /api/transactions?account_id=…&per_page=10 (index gains an account_id
filter and a clamped per_page) instead of Dexie.
- TransactionList renders only from the provided prop; the /api/sync fetch,
the Dexie-backed search, and the refreshKey refetch are removed (search is
now in-memory; create triggers a scoped Inertia reload).
Tests cover the new endpoint (matching, normalization, account scoping, IDOR),
the account_id filter, and the account-show transactions prop.