Files
kvitt/docs/ux/flows.md
T
jacobh2 6da6b1b023 UX sprint U2: design-spec.md, screen catalogue, strings.csv (owner approved)
Kalas (muted) design spec with final tokens, components and every screen S-1 to
S-22 in all states; 80-frame screen catalogue with 155 PNGs in docs/ux/screens/;
strings.csv with D-017 module prefixes (558 rows, en and sv); colour tokens
renamed to the landed contract names slate and amber; fixed category ids;
proposed-decisions.md with the final §9.7 hex values and UX-9. Supersedes
tokens.md, components.md, screens.md and the U1 Kalas PNGs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2TeboHB8AtnzTrt3bWFzY
2026-10-18 08:27:51 +02:00

111 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Kvitt: flows
Status: U1 (2026-10-17), updated in U2 (2026-10-18) for the owner's decisions (category suggestion UX-3 with no caption and "most often" history; 14 default categories UX-8). Step-by-step flows with screen ids (`ia.md`, `design-spec.md` §4). Rules referenced by spec section; nothing here redefines them. Data from the shared mock household (Jacob = owner/admin, Anna = partner; today lördag 17 oktober 2026).
Notation: **User** does · *System* does · `S-n` screen · ⟶ navigation.
---
## F-1 First run as admin (US-1, FR-1, FR-2, FR-5)
1. **Jacob** runs `kvitt-server bootstrap` on the Pi (outside the app); it prints the code `7K3M-QX9D-2HPA` and the link `https://icehack.asuscomm.com:8443/i/7K3MQX9D2HPA` (AC-1.1).
2. **Jacob** opens the link on the phone. The landing page (AC-2.5) offers "Ladda ner Kvitt" (first time) and "Öppna i Kvitt".
3. **Jacob** installs the APK (Android asks once to allow installs from the browser; outside the app), then taps "Öppna i Kvitt" ⟶ `S-1` with server and code filled (AC-2.6). Without a link: `S-1` empty, he types the code (server prefilled).
4. **Jacob** types his name in "Ditt namn" and taps "Para ihop". *Progress "Parar ihop…"; pairs with the phone model as device name ("Galaxy S26").* (`S-1` always has "Ditt namn" on a first pairing because AC-2.4 sends the display name in the accept call and the app cannot know the invite kind beforehand; the server ignores it for `device` invites. The field is hidden in the "Para ihop telefonen igen" variant.)
5. *Full sync (AC-2.9)* ⟶ `S-4` loading state "Hämtar era utgifter…" ⟶ the system notification-permission prompt.
6. *Partner not yet joined* ⟶ `S-4` shows the `S-3` content "Väntar på din partner" (AC-5.1).
7. **Jacob** taps "Bjud in partner" ⟶ `S-21`, a `partner` invite is created and shown: code in groups of 4, link, "Gäller till tis 20 okt. 14:02".
8. **Jacob** taps "Dela" ⟶ Android share sheet ⟶ sends the message to Anna (SMS, Signal, …). Back ⟶ `S-3` now shows the expiry.
9. When Anna has joined (F-2), the next sync replaces `S-3` with `S-4` empty state "Skapa ert första konto" ⟶ **Jacob** taps "Nytt konto" ⟶ `S-6` "Vardag", 50/50 ⟶ Spara ⟶ `S-4` with the account and the FAB.
Error branches: `invite_invalid` / network / `rate_limited` inline on `S-1` (`design-spec.md` S-1). Lost every phone later: `kvitt-server pair-device` (AC-1.6) then F-1 steps 2–5.
## F-2 Join as spouse (US-2, AC-2.4, AC-2.10)
1. **Anna** receives Jacob's message, taps the link ⟶ landing page ⟶ "Ladda ner Kvitt" ⟶ installs (Android "Install unknown apps" for her browser, once) ⟶ "Öppna i Kvitt" ⟶ `S-1` filled.
2. **Anna** types her name in "Ditt namn" ("Anna") and taps "Para ihop" ⟶ *pairs, creates the member user*.
3. ⟶ `S-2`: "Du går med i Jacobs hushåll", her name prefilled, avatar with initials. **Anna** taps "Lägg till bild" ⟶ photo picker ⟶ picks a photo ⟶ *upload with progress*. Taps "Fortsätt".
4. *Full sync* ⟶ `S-4` loading ⟶ notification permission prompt ⟶ `S-4` empty "Skapa ert första konto" (or Jacob's accounts, if he created any after she joined).
5. On Jacob's phone the next sync (or push-triggered sync) removes `S-3`.
## F-3 Add an expense online (US-3, AC-8.6, UX-3) — the ten-second flow
1. **Jacob** opens Kvitt ⟶ `S-4`.
2. Taps FAB "Lägg till utgift" ⟶ `S-7` with amount focused, keypad up; defaults: Konto Vardag (last used), Kategori (last used in Vardag, else Övrigt, until a description exists), Datum Idag, Betalt av Du, Delning Kontots standard 50/50. The category row shows the five categories used most in Vardag plus "Alla".
3. Types `412`.
4. Taps Beskrivning, types "Systembolaget". *While he types, the category is suggested from the description (`category-suggestion.md` CS-5: the category used most often for this description in the household's history, else the keyword table, else the default) and Nöje becomes selected; no caption or marker (CS-7).*
5. (optional, one tap each) a wrong guess: taps the right category tile (or "Alla" ⟶ sheet ⟶ category); from then on the suggestion no longer changes the category (CS-2); taps Betalt av to switch to Anna; taps Delning to open the split section.
6. Taps "Spara" ⟶ *saved locally, pending* ⟶ back to `S-4`, snackbar "Utgiften sparades"; Vardag's wording/amount and the global hero update immediately (AC-10.4).
7. *2 s later, sync pushes (AC-15.3)*; the sync line returns to "Synkad nyss"; *the server pushes `expense_created` to Anna's phone*, which syncs and shows "Jacob lade till en utgift · Systembolaget · 412,00 kr · Vardag".
## F-4 Add an expense offline, then sync (US-12, FR-15)
1. **Jacob** is on a plane. `S-4` sync line: "Offline · Synkad 14 okt. 08:12".
2. F-3 steps 2–6 exactly. Snackbar "Sparad. Synkas när du är online." The row in `S-5` shows the pending marker; `S-4` sync line "Offline · 1 ändring väntar på synk".
3. He adds more and edits one; the count increases ("3 ändringar väntar på synk"). Account, category and recurring actions show "Kräver anslutning".
4. Landing: network returns ⟶ *sync on foreground or WorkManager* ⟶ sync line "Synkar…" ⟶ "Synkad nyss"; pending markers disappear.
5. **Conflict branch:** Anna edited the same expense meanwhile with a later `updated_at` ⟶ *push answers `stale`* ⟶ the row shows Anna's version; sync line "Synkad nyss · 1 ändring synkades inte" (warning style) ⟶ tap ⟶ `S-16` "Ändringar som inte synkades": "Babette · Din ändring ersattes av en nyare ändring från Anna" ⟶ tap opens the current expense (read-only `S-8`) ⟶ "Ta bort från listan".
6. **Delete branch:** Anna deleted it ⟶ reason `deleted` "Den här togs bort innan din ändring kom fram".
## F-5 Edit an expense the other person created (AC-8.2, AC-12.4)
1. **Jacob** gets a notification "Anna lade till en utgift · ICA Maxi Lindhagen · 1 147,60 kr · Vardag" ⟶ taps ⟶ `S-8` (after sync).
2. Timeline: "Anna skapade den här · 16 okt. 14:02". He sees the receipt says 1 247,60.
3. Taps "Ändra" ⟶ `S-7` "Ändra utgift" with all fields loaded, nothing focused, keypad hidden; the category is kept (no suggestion when editing, CS-1). He taps the amount; the keypad appears.
4. Changes 1 147,60 to 1 247,60 ⟶ "Spara" ⟶ back to `S-8`; timeline gains a last line "Din ändring väntar på synk".
5. *Sync* ⟶ the pending line is replaced by "Du ändrade beloppet från 1 147,60 kr till 1 247,60 kr · 16 okt. 19:40".
6. *Anna's phone* gets "Jacob ändrade en utgift · ICA Maxi Lindhagen · 1 247,60 kr"; her `S-8` shows the same timeline (her view: "Jacob ändrade beloppet …").
## F-6 Global settle-up with adjusted allocation (US-7, AC-11.3, AC-11.5, §9.4)
Start: global "Du är skyldig Anna 3 250,00 kr"; Vardag Anna owes Jacob 1 184,50; Huset Jacob owes 3 084,50; Japan 2027 Jacob owes 1 350,00.
1. **Jacob** taps "Gör upp" on `S-4` ⟶ `S-9`: "Du betalar Anna", amount 3 250,00 kr (default = global debt), proposal (oldest owing first): Huset 3 084,50, Japan 2027 165,50; "Kvar att fördela 0,00 kr"; info "Vardag påverkas inte: där är Anna skyldig dig 1 184,50 kr."
2. They agreed to clear Japan first. **Jacob** edits Japan 2027 to 1 500,00 ⟶ *row warning "Det här reglerar mer än som är skyldigt i Japan 2027"* (1 500 > 1 350); remaining "−" ⟶ "1 334,50 kr för mycket"; Bekräfta disabled.
3. Edits Huset to 1 750,00 ⟶ "Kvar att fördela 0,00 kr"; Bekräfta enabled; Japan warning remains.
4. Sets Anteckning "Swish", Datum Idag. Taps "Bekräfta" ⟶ dialog repeating the warning ⟶ "Bekräfta ändå".
5. *Saved locally* ⟶ back to `S-4`: global −3 250,00 + 3 250,00 = 0 ⟶ "Ni är kvitt"; per account Huset "Du är skyldig Anna 1 334,50 kr", Japan "Anna är skyldig dig 150,00 kr", Vardag unchanged. (This is §9.4's stated consequence; the per-account settle-ups in F-7 clear the rest.)
6. *Sync; Anna gets "Jacob registrerade en betalning · Jacob betalade dig 3 250,00 kr"* (her view).
Variant: "Lägg till konto" ⟶ picker (all non-deleted accounts, archived included) ⟶ adds a row with 0,00 to fill in. Removing a row increases "Kvar att fördela".
## F-7 Per-account settle-up (US-8, AC-11.4)
1. **Jacob** on `S-4` taps Huset ⟶ `S-5` "Du är skyldig Anna 3 084,50 kr" ⟶ "Gör upp det här kontot".
2. ⟶ `S-9` per-account variant: "Du betalar Anna", 3 084,50 kr, one fixed row Huset 3 084,50. No "Lägg till konto".
3. Optional: changes the amount to 2 000,00 ⟶ the single row follows the amount (it is the only allocation) ⟶ Bekräfta.
4. ⟶ back to `S-5`: a settlement row "Du betalade Anna 3 084,50 kr" at the top of October; header "Kvitt".
5. To settle an account where the *other* user owes (Vardag): `S-5` Vardag ⟶ "Gör upp det här kontot" ⟶ direction defaults to "Anna betalar dig" (AC-11.4); Jacob records Anna's Swish.
## F-8 Create a recurring expense (US-10, FR-9)
1. **Jacob** ⟶ Inställningar `S-13` ⟶ "Återkommande utgifter" `S-19` ⟶ "Ny".
2. Editor: amount 12 400,00; description "Hyra"; Konto Huset; Kategori Boende; Betalt av Anna (one tap); Delning Kontots standard 60/40; Dag i månaden 25; Första månad: *defaults to oktober 2026 because the 25th is still ahead (AC-9.8)*; Sista månad empty.
3. Spara (online) ⟶ list row "Hyra · 12 400,00 kr · Huset · Varje månad den 25:e · Nästa: 25 okt.".
4. *On 25 Oct at 06:00 the server creates the expense* ⟶ both phones get "Återkommande utgift tillagd · Hyra · 12 400,00 kr · Huset" (AC-9.6) ⟶ `S-8` shows "Återkommande: Hyra" and "Skapad automatiskt (återkommande) · 25 okt. 06:00".
5. Offline at step 3 ⟶ Spara disabled with "Kräver anslutning".
Pause: list row ⟶ editor ⟶ "Pausad" on ⟶ Spara ⟶ row shows "Pausad". Resume does not backfill (§9.5); the editor shows the hint "Månader som passerat under pausen skapas inte i efterhand."
## F-9 Archive an account (US-9, AC-6.4)
1. **Anna** on `S-4` ⟶ Japan 2027 ⟶ `S-5` ⟶ overflow ⟶ "Arkivera" (online).
2. *Archived* ⟶ back on `S-4`, snackbar "Japan 2027 arkiverades" with "Ångra". The account disappears from the active list and appears under "Arkiverade (2)" (collapsed) with its balance still shown; the global balance is unchanged (AC-10.5).
3. In `S-7` the account picker no longer lists it; `S-9` "Lägg till konto" still does; `S-11` still counts it.
4. Unarchive: Home ⟶ "Arkiverade" ⟶ Japan 2027 ⟶ `S-5` ("Arkiverat" badge) ⟶ "Återställ från arkiv".
5. Delete branch: overflow "Ta bort konto" is disabled with "Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter" unless the account is empty (AC-6.5).
## F-10 In-app update (US-14, FR-16)
1. *Cold start of a release build* ⟶ `GET /apk/latest` ⟶ version code higher ⟶ `S-4` banner "Version 1.2 finns" [Uppdatera] [Senare].
2. **Jacob** taps "Uppdatera". First time: dialog "Tillåt installation" explains that Android will ask once ⟶ "Fortsätt" ⟶ system settings "Installera okända appar" for Kvitt ⟶ he allows and taps back.
3. *Download* ⟶ banner "Laddar ner… 64 %" with progress; the app stays usable.
4. *Size and SHA-256 verified* ⟶ the system installer dialog ⟶ "Uppdatera" (system) ⟶ the app restarts on the new version.
5. Failure ⟶ banner "Nedladdningen misslyckades. Försök igen." ⟶ "Försök igen" (AC-16.4).
6. "Senare" hides the banner until the next cold start. The update is also reachable from `S-22` "Sök efter uppdatering".
## F-11 Remove a lost phone (US-17, FR-3) — supporting flow
1. **Jacob** (admin) ⟶ `S-13` ⟶ "Enheter" `S-20` ⟶ the lost "Galaxy S26+" row ⟶ "Ta bort" ⟶ confirmation ⟶ *revoked*.
2. The lost phone, if it ever connects, gets `device_revoked` ⟶ wipes local data ⟶ `S-18` "Den här telefonen har tagits bort" ⟶ "Para ihop igen" ⟶ `S-1`.