mirror of
https://github.com/vernu/textbee.git
synced 2026-07-30 17:07:46 -04:00
The old screen was one 425-line component with correctness bugs, not just a weak layout. Fixed defects: - Empty recipients were sent. handleSendBulkSMS mapped every parsed row into the payload, so a blank phone cell produced a message addressed to "". There was no filtering, no phone validation and no dedupe, so the same number listed twice was texted twice. - User-facing copy printed raw bytes: "max 1048576 bytes". - The row limit could be wrong. maxRows fell back to 50 while the subscription query was still loading, so an unlimited-plan user could be told their file was too big. The cap is now only enforced once the real limit is known. - Steps were gated with opacity-50 + pointer-events-none, which leaves controls in the tab order and unannounced. Locked steps now use `inert`. - Data came from raw useQuery(['devices']) + devices?.data instead of the typed useDevices(), the same shared-cache-key shape hazard that caused the devices?.filter crash in round 1. The new flow: upload (with parse summary and a preview table of the first 5 rows), map (auto-detected phone column plus live valid/invalid/duplicate counts), compose (clickable variable chips, SMS segment counter, preview that cycles through real recipients) and review (explicit count, an itemised list of skipped rows and why, nothing sent until confirmed). bulk-csv.ts holds the rules that decide who receives a message, as pure functions with 24 unit tests. Those tests caught a real bug during development: the short "to" hint substring-matched a column named "total", which would have silently auto-selected the wrong column. Short hints are now exact-match only. Sample CSV at /samples/bulk-sms-sample.csv using 555 reserved-for-fiction US and Canada numbers, so an unedited upload cannot text a real person, with an order-confirmation template that demonstrates multi-variable interpolation. E2e asserts the actual request payload contains exactly the two valid recipients from a file seeded with a blank, an invalid and a duplicate row, and that the parsed-CSV table does not reintroduce sideways scroll at 375px. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>