docs: mark Phase 1 done; sync architecture, modernize, readme

- ROADMAP: all Phase 1 boxes checked, done-notes added
- MODERNIZE: serde item marked APPLIED (persistence uses the derives)
- ARCHITECTURE: persist module + modal in the component tree, gaps list
  updated (persistence no longer a gap)
- README: header buttons + autosave in 'What works today', tests section
  describes both specs, playwright command pins the chromium project
This commit is contained in:
yaroslav k 2026-08-16 11:50:41 +03:00
parent cc11700621
commit 9c4d95220d
4 changed files with 45 additions and 24 deletions

View File

@ -99,9 +99,10 @@ JSON as-is.
## Component tree ## Component tree
```
App App
├─ Header name, image, date steppers, session steppers, Save/Load/Clear (dead) ├─ Header name, image, date steppers, session steppers,
│ Save (download)/Load (file import)/Clear (wipe)
├─ Modal overlay dialog: confirm (action) or error (close)
├─ Sidebar ├─ Sidebar
│ ├─ Image portrait │ ├─ Image portrait
│ ├─ Names first / alias «» / last │ ├─ Names first / alias «» / last
@ -125,6 +126,13 @@ closures by index; "move" transfers a row between the personal and common
inventories, "remove" deletes it. The "…" (details) button and the search inventories, "remove" deletes it. The "…" (details) button and the search
"+" button are unwired. "+" button are unwired.
Persistence lives in `state::persist`: on hydration a deferred timer loads
the saved dashboard from localStorage (SSR markup stays authoritative),
then a debounced (500 ms) effect writes on any store change via
`store.track()`. `parse_dashboard` version-checks the JSON against
`Settings::VERSION` (the migration hook). `ModalState` holds title/message/
action as signals; the confirm button reads the action at click time.
`Numput` is the shared numeric input: `+n`/`-n` adjust relative to the `Numput` is the shared numeric input: `+n`/`-n` adjust relative to the
current value, a plain number sets absolutely, invalid input is reverted, current value, a plain number sets absolutely, invalid input is reverted,
and the value renders with thousands separators. `NumputChange` is the same and the value renders with thousands separators. `NumputChange` is the same
@ -148,12 +156,11 @@ currently mounted on any page.
## Current gaps ## Current gaps
- No persistence; state is recreated from `mock()` each load
- `Character` and `Wiki` pages render placeholders
- Quests, notes, contacts: models and mock data only - Quests, notes, contacts: models and mock data only
- `Character` and `Wiki` pages render placeholders
- Prepared spells list is empty; `Spell` is a name-only stub; attunements - Prepared spells list is empty; `Spell` is a name-only stub; attunements
have no UI; `Settings` is empty have no UI; `Settings` has only `version`
- Save/Load/Clear buttons and the item "+"/"…" buttons are dead - The item "+"/"…" buttons are dead
- `foundry.json` was loaded twice (fixed); ~3.9 MB of JSON is bundled - `foundry.json` was loaded twice (fixed); ~3.9 MB of JSON is bundled
into the WASM and parsed on startup into the WASM and parsed on startup
- Several unused dependencies (see `MODERNIZE.md`); kept for now by - Several unused dependencies (see `MODERNIZE.md`); kept for now by

View File

@ -71,9 +71,11 @@ under consideration.
`usize`. `Numput` keeps its generics. `usize`. `Numput` keeps its generics.
4. **`serde` (direct) → remove now, re-add in ROADMAP phase 1.** 4. **`serde` (direct) → remove now, re-add in ROADMAP phase 1.**
No derives exist today; `serde_json::Value` works without naming `serde` No derives existed at the time; `serde_json::Value` works without naming
in `Cargo.toml`. Phase 1 (persistence) re-adds `serde` in `Cargo.toml`. Phase 1 (persistence) re-added
`serde = { version = "1", features = ["derive"] }`. `serde = { version = "1", features = ["derive"] }`.
**APPLIED 2026-08-16:** persistence is in; `serde` is used by every entity
derive — keep it.
Result: dropping the under-consideration items takes `[dependencies]` from Result: dropping the under-consideration items takes `[dependencies]` from
27 entries to about 18. If the five kept deps are dropped later too, it goes 27 entries to about 18. If the five kept deps are dropped later too, it goes

View File

@ -5,7 +5,9 @@ game. The UI is in Russian.
## What works today ## What works today
- Campaign header: name, in-game date steppers, session counter - Campaign header: name, in-game date steppers, session counter, and
Save / Load / Clear: download `dashboard.json`, import a file back,
wipe to a blank campaign
- Character sidebar: portrait, names, class, level, XP, hit points with - Character sidebar: portrait, names, class, level, XP, hit points with
temp HP bar, hit dice, death saves, spell slots temp HP bar, hit dice, death saves, spell slots
- Inventories page (`/inv`): personal and party-common inventories, - Inventories page (`/inv`): personal and party-common inventories,
@ -14,8 +16,9 @@ game. The UI is in Russian.
- Bundled 5e.tools item data (~5k items) with fuzzy search components - Bundled 5e.tools item data (~5k items) with fuzzy search components
(search is not mounted on a page yet) (search is not mounted on a page yet)
State is mock data: nothing is persisted, and several buttons and pages are State autosaves to localStorage (debounced) and survives reloads; export/
stubs. The phased plan to fix that is in [ROADMAP.md](./ROADMAP.md). import round-trips through a JSON file. The phased plan is in
[ROADMAP.md](./ROADMAP.md).
## Tech stack ## Tech stack
@ -48,13 +51,14 @@ cargo check --target wasm32-unknown-unknown --no-default-features --features hyd
## Tests ## Tests
End-to-end smoke tests live in `end2end/` (Playwright). With the dev server End-to-end tests live in `end2end/` (Playwright): a smoke spec and a
running on port 3000: persistence spec (reload keeps state; export/import round trip). With the
dev server running:
```sh ```sh
cd end2end cd end2end
npm install npm install
npx playwright test npx playwright test --project=chromium
``` ```
or let cargo-leptos manage the server: `cargo leptos end2end`. or let cargo-leptos manage the server: `cargo leptos end2end`.

View File

@ -72,24 +72,32 @@ Cheap, unblocks the rest. Can happen any time; do it first.
The foundation. Every later phase depends on it. The foundation. Every later phase depends on it.
- [ ] derive `serde::Serialize` / `Deserialize` on all entities in `src/entities/mod.rs` - [x] derive `serde::Serialize` / `Deserialize` on all entities in `src/entities/mod.rs`
(Dashboard, PlayerData, Balance, Inventory, InventoryEntry, InventoryItem, (Dashboard, PlayerData, Balance, Inventory, InventoryEntry, InventoryItem,
QuestBook, Quest, NoteBook, Note, ContactBook, Contact, SpellSlots, Attunements, QuestBook, Quest, NoteBook, Note, ContactBook, Contact, SpellSlots, Attunements,
PreparedSpells, Spell, HitDice, DeathSaveThrows, Name, Settings; enums: PreparedSpells, Spell, HitDice, DeathSaveThrows, Name, Settings; enums:
InventoryKind, ContactStatus, PreparedSpell) InventoryKind, ContactStatus, PreparedSpell)
- [ ] verify `Store` derive and `serde` derive coexist on the same structs (prototype first) - [x] verify `Store` derive and `serde` derive coexist on the same structs (prototype first)
- [ ] add a `version` field to `Settings`; add a migration hook for future format changes - [x] add a `version` field to `Settings`; add a migration hook for future format changes
- [ ] serialize Dashboard to JSON; save to localStorage via `web_sys::Storage` - [x] serialize Dashboard to JSON; save to localStorage via `web_sys::Storage`
(feature-gate to `hydrate`/`csr`; SSR renders mock/empty) (feature-gate to `hydrate`/`csr`; SSR renders mock/empty)
- [ ] auto-save on change — debounced effect over the `Store` — plus manual Save button - [x] auto-save on change — debounced effect over the `Store` — plus manual Save button
- [ ] Load button: restore from localStorage with an overwrite confirm - [x] Load button: restore from localStorage with an overwrite confirm
- [ ] Clear button: wipe localStorage with a confirm - [x] Clear button: wipe localStorage with a confirm
- [ ] export: download `dashboard.json` as a blob - [x] export: download `dashboard.json` as a blob
- [ ] import: file input → parse → validate version → replace state - [x] import: file input → parse → validate version → replace state
- [ ] on startup, hydrate from localStorage; keep `mock()` as the fallback / "New dashboard" - [x] on startup, hydrate from localStorage; keep `mock()` as the fallback / "New dashboard"
- **Acceptance:** close the tab, reopen — state is intact. Export → import into a - **Acceptance:** close the tab, reopen — state is intact. Export → import into a
fresh browser — identical state. fresh browser — identical state.
**Done (2026-08-16):** implementation in `src/state/persist.rs` + header buttons +
`components/modal.rs`; e2e-verified in chromium (reload persistence and
export/import round trip). Notes:
- Save = download; Load = file import; Clear = wipe (merged per user decision).
- Clear writes the blank dashboard back to storage; mock stays the first-visit fallback.
- Version check is the migration hook: `parse_dashboard` returns
`ImportError::{Parse, NewerVersion, OldVersion}` (RU messages).
## Phase 2 — Character page (M, ~1-2 days) ## Phase 2 — Character page (M, ~1-2 days)
`/` currently renders the literal string "character". `/` currently renders the literal string "character".