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
```
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
│ ├─ Image portrait
│ ├─ 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
"+" 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
current value, a plain number sets absolutely, invalid input is reverted,
and the value renders with thousands separators. `NumputChange` is the same
@ -148,12 +156,11 @@ currently mounted on any page.
## 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
- `Character` and `Wiki` pages render placeholders
- Prepared spells list is empty; `Spell` is a name-only stub; attunements
have no UI; `Settings` is empty
- Save/Load/Clear buttons and the item "+"/"…" buttons are dead
have no UI; `Settings` has only `version`
- The item "+"/"…" buttons are dead
- `foundry.json` was loaded twice (fixed); ~3.9 MB of JSON is bundled
into the WASM and parsed on startup
- Several unused dependencies (see `MODERNIZE.md`); kept for now by

View File

@ -71,9 +71,11 @@ under consideration.
`usize`. `Numput` keeps its generics.
4. **`serde` (direct) → remove now, re-add in ROADMAP phase 1.**
No derives exist today; `serde_json::Value` works without naming `serde`
in `Cargo.toml`. Phase 1 (persistence) re-adds
No derives existed at the time; `serde_json::Value` works without naming
`serde` in `Cargo.toml`. Phase 1 (persistence) re-added
`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
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
- 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
temp HP bar, hit dice, death saves, spell slots
- 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
(search is not mounted on a page yet)
State is mock data: nothing is persisted, and several buttons and pages are
stubs. The phased plan to fix that is in [ROADMAP.md](./ROADMAP.md).
State autosaves to localStorage (debounced) and survives reloads; export/
import round-trips through a JSON file. The phased plan is in
[ROADMAP.md](./ROADMAP.md).
## Tech stack
@ -48,13 +51,14 @@ cargo check --target wasm32-unknown-unknown --no-default-features --features hyd
## Tests
End-to-end smoke tests live in `end2end/` (Playwright). With the dev server
running on port 3000:
End-to-end tests live in `end2end/` (Playwright): a smoke spec and a
persistence spec (reload keeps state; export/import round trip). With the
dev server running:
```sh
cd end2end
npm install
npx playwright test
npx playwright test --project=chromium
```
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.
- [ ] 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,
QuestBook, Quest, NoteBook, Note, ContactBook, Contact, SpellSlots, Attunements,
PreparedSpells, Spell, HitDice, DeathSaveThrows, Name, Settings; enums:
InventoryKind, ContactStatus, PreparedSpell)
- [ ] 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
- [ ] serialize Dashboard to JSON; save to localStorage via `web_sys::Storage`
- [x] verify `Store` derive and `serde` derive coexist on the same structs (prototype first)
- [x] add a `version` field to `Settings`; add a migration hook for future format changes
- [x] serialize Dashboard to JSON; save to localStorage via `web_sys::Storage`
(feature-gate to `hydrate`/`csr`; SSR renders mock/empty)
- [ ] auto-save on change — debounced effect over the `Store` — plus manual Save button
- [ ] Load button: restore from localStorage with an overwrite confirm
- [ ] Clear button: wipe localStorage with a confirm
- [ ] export: download `dashboard.json` as a blob
- [ ] import: file input → parse → validate version → replace state
- [ ] on startup, hydrate from localStorage; keep `mock()` as the fallback / "New dashboard"
- [x] auto-save on change — debounced effect over the `Store` — plus manual Save button
- [x] Load button: restore from localStorage with an overwrite confirm
- [x] Clear button: wipe localStorage with a confirm
- [x] export: download `dashboard.json` as a blob
- [x] import: file input → parse → validate version → replace state
- [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
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)
`/` currently renders the literal string "character".