Money: transaction entry with swipe-to-save, glow bloom and rolling numbers #1198

Open
opened 2026-10-06 10:00:55 +00:00 by kayg · 5 comments
Owner

Owner decision (2026-10-06): transaction entry with swipe-to-save and rolling numbers

Reference (private, the owner's chosen design, take it and make it better): ~/calternal-private/research/transaction-entry-2026-10-06.md (bottom note: a swipe-to-pay slider — knob drag with a luminous fill; past the threshold the keypad and slider dismiss into a radial glow bloom; a circle draws and resolves into a checkmark) plus the ranked research in the same file and frames in ~/calternal-private/research/transaction-entry/. Do not copy any brand, artwork or assets; recreate the interaction in calternal's design language.

Depends on #1180 (per-budget money format, exact amount parser, Torph counters). Build on its branch.

Flow

  • Entry surface: phone = shared bottom sheet; desktop = shared overlay. Amount first and focused immediately (phone: a custom accessible numeric keypad with 44 px targets + physical keyboard; desktop: the amount field). Payee, Category, Date, Account as compact chips/rows under the amount, prefilled from context (current Account/Category, today); changing any of them never loses the draft.
  • Typing: digits roll in by place value with Torph (only changed places move; up for increase, down for decrease; currency sign and separators stay attached; ~120–180 ms; never trail the latest value).
  • Swipe to save: a shared slider control: dragging the knob grows a glowing fill tinted by the selected Category's colour (glass + glow tokens); past the threshold it commits; releasing early springs back. Desktop: click-drag the same control, or ⌘↩ / Ctrl+Enter; keyboard: hold Enter ~400 ms with a fill progress (prevents accidental saves). Vibration API pulse at threshold where supported.
  • Success moment: keypad and slider dismiss into a radial glow bloom in the Category colour; a circle draws and resolves into a check; THEN the consequences animate — the Account balance and the Category's Available roll down by place value and Ready to assign updates (~180–240 ms each, staggered slightly). Optimistic: the transaction appears in the register immediately; the server confirms in the background; on failure the row shows Retry inline.
  • Undo: inline "Saved · Undo" for ~5 s (no toast, per #1142).
  • Reduced motion: no bloom/roll/draw travel; a calm cross-fade to the check and final values. Screen readers: one announcement "Saved 450.00 to Groceries", values remain exact.

Evidence

e2e like a User on phone (touch drag, early release, threshold, keypad) and desktop (drag, ⌘↩, held Enter), exact amounts in INR/EUR/JPY budgets, Undo, reduced motion, failure + Retry. Screen recordings (WebM) of the full flow at 390 and 1440, light + dark, plus frame-step captures of the bloom and check, reviewed at ACTUAL size. Performance: 60 fps during drag and bloom on the perf-test VM (no layout reads per frame; transform/opacity only). A Money numbers review follows.

## Owner decision (2026-10-06): transaction entry with swipe-to-save and rolling numbers Reference (private, the owner's chosen design, take it and make it better): ~/calternal-private/research/transaction-entry-2026-10-06.md (bottom note: a swipe-to-pay slider — knob drag with a luminous fill; past the threshold the keypad and slider dismiss into a radial glow bloom; a circle draws and resolves into a checkmark) plus the ranked research in the same file and frames in ~/calternal-private/research/transaction-entry/. Do not copy any brand, artwork or assets; recreate the interaction in calternal's design language. Depends on #1180 (per-budget money format, exact amount parser, Torph counters). Build on its branch. ### Flow - **Entry surface:** phone = shared bottom sheet; desktop = shared overlay. Amount first and focused immediately (phone: a custom accessible numeric keypad with 44 px targets + physical keyboard; desktop: the amount field). Payee, Category, Date, Account as compact chips/rows under the amount, prefilled from context (current Account/Category, today); changing any of them never loses the draft. - **Typing:** digits roll in by place value with Torph (only changed places move; up for increase, down for decrease; currency sign and separators stay attached; ~120–180 ms; never trail the latest value). - **Swipe to save:** a shared slider control: dragging the knob grows a glowing fill tinted by the selected Category's colour (glass + glow tokens); past the threshold it commits; releasing early springs back. Desktop: click-drag the same control, or ⌘↩ / Ctrl+Enter; keyboard: hold Enter ~400 ms with a fill progress (prevents accidental saves). Vibration API pulse at threshold where supported. - **Success moment:** keypad and slider dismiss into a radial glow bloom in the Category colour; a circle draws and resolves into a check; THEN the consequences animate — the Account balance and the Category's Available roll down by place value and Ready to assign updates (~180–240 ms each, staggered slightly). Optimistic: the transaction appears in the register immediately; the server confirms in the background; on failure the row shows Retry inline. - **Undo:** inline "Saved · Undo" for ~5 s (no toast, per #1142). - **Reduced motion:** no bloom/roll/draw travel; a calm cross-fade to the check and final values. Screen readers: one announcement "Saved 450.00 to Groceries", values remain exact. ### Evidence e2e like a User on phone (touch drag, early release, threshold, keypad) and desktop (drag, ⌘↩, held Enter), exact amounts in INR/EUR/JPY budgets, Undo, reduced motion, failure + Retry. Screen recordings (WebM) of the full flow at 390 and 1440, light + dark, plus frame-step captures of the bloom and check, reviewed at ACTUAL size. Performance: 60 fps during drag and bloom on the perf-test VM (no layout reads per frame; transform/opacity only). A Money numbers review follows.
Author
Owner

Starting #1198 on job/txentry-1198 from the #1180 base 2f005fc5168d767d050dd8a0cecd033b9c9e72a7. Fetched origin and merged origin/dev; it was already up to date. I am reviewing the shared Money entry, amount, surface, motion, and slider primitives before implementing the interaction.

Starting #1198 on `job/txentry-1198` from the #1180 base `2f005fc5168d767d050dd8a0cecd033b9c9e72a7`. Fetched `origin` and merged `origin/dev`; it was already up to date. I am reviewing the shared Money entry, amount, surface, motion, and slider primitives before implementing the interaction.
Author
Owner

Finding for #1198: the MoneyCategoryView schema has no Category colour field, and the transaction POST accepts no retry identity. The current add flow also waits for the POST before refreshing the register. I am using the existing deterministic tagColor palette for the glow, adding a User-scoped Idempotency-Key receipt to transaction create, and binding Undo to that create receipt plus the unchanged row. This follows DESIGN §59 and keeps Budget Markdown as the source of truth.

Finding for #1198: the MoneyCategoryView schema has no Category colour field, and the transaction POST accepts no retry identity. The current add flow also waits for the POST before refreshing the register. I am using the existing deterministic `tagColor` palette for the glow, adding a User-scoped `Idempotency-Key` receipt to transaction create, and binding Undo to that create receipt plus the unchanged row. This follows DESIGN §59 and keeps Budget Markdown as the source of truth.
Author
Owner

Finding: cargo clippy -p calternal-server --all-targets -- -D warnings could not compile the embedded frontend because apps/web/build/ does not exist. The RustEmbed derive then reports missing Frontend::get methods. I will create the production web build, rerun the required server gates, and remove generated build output during cleanup.

Finding: `cargo clippy -p calternal-server --all-targets -- -D warnings` could not compile the embedded frontend because `apps/web/build/` does not exist. The `RustEmbed` derive then reports missing `Frontend::get` methods. I will create the production web build, rerun the required server gates, and remove generated build output during cleanup.
Author
Owner

Finding: review showed the first keyed-create implementation reserved its receipt after the Markdown write. Two concurrent requests using the same User-scoped operation ID in different budgets could both write before the Index rejected one. The route now reserves a Pending file plan and inverse before filesystem I/O, then completes the receipt after the checked write; retries recover that stable plan. I also corrected the OpenAPI header annotations so they describe create and delete only.

The Money test binary compiled, but its latest test run could not exercise fixtures: calternal-fs rejected temporary roots because free space was 28,938,240,000 bytes and its five-percent reserve was 31,695,649,792 bytes. The host reported only 32 GB free while the server clippy build occupied 4.9 GB. The earlier Money test run passed before the Pending-plan refinement.

Finding: review showed the first keyed-create implementation reserved its receipt after the Markdown write. Two concurrent requests using the same User-scoped operation ID in different budgets could both write before the Index rejected one. The route now reserves a Pending file plan and inverse before filesystem I/O, then completes the receipt after the checked write; retries recover that stable plan. I also corrected the OpenAPI header annotations so they describe create and delete only. The Money test binary compiled, but its latest test run could not exercise fixtures: `calternal-fs` rejected temporary roots because free space was 28,938,240,000 bytes and its five-percent reserve was 31,695,649,792 bytes. The host reported only 32 GB free while the server clippy build occupied 4.9 GB. The earlier Money test run passed before the Pending-plan refinement.
Author
Owner

Finding with evidence: the new amount morph test showed that TextInput switched between two conditional branches when Torph became active. That replaced the native input after the first valid keystroke and lost its focus/caret identity. The Entry Amount now keeps one input mounted and toggles only the aria-hidden visual overlay; a focused regression test covers this transition.

Finding with evidence: the new amount morph test showed that `TextInput` switched between two conditional branches when Torph became active. That replaced the native input after the first valid keystroke and lost its focus/caret identity. The Entry Amount now keeps one input mounted and toggles only the aria-hidden visual overlay; a focused regression test covers this transition.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#1198
No description provided.