Files
spelljammer-ships/App plan.md
Bas van Rossem 047d4e9776 docs: add comprehensive README with testing and deployment instructions
Includes development setup, Docker deployment, API reference, WebSocket events,
troubleshooting guide, and curl-based test script. Updates App plan.md with
implementation completion status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:31:42 +01:00

315 lines
8.7 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.
Heres a concrete, **handoff-ready plan** you can give to another AI to implement a first version (v1) of a **Spelljammer Ship Tracker** with **multi-ship CRUD, constrained editing, SQLite persistence, and <1s live updates**.
---
## 0) Product goal (v1)
A small phone-friendly web app where your group can:
* Create/select/delete ships (delete requires confirm modal)
* View & edit **core ship stats** + **current weapons on the ship**
* See changes propagate to all connected clients within ~1s
* Persist “real data” server-side in **SQLite** (ship data + weapons + optional reference content)
* Show a **read-only Rules/Reference** screen for battle lookups
No authentication. Anyone can edit anything.
---
## 1) Recommended stack (simple + robust)
**Option A (recommended): Node.js + Fastify + Socket.IO + SQLite (better-sqlite3) + React (Vite)**
* Fast to implement, great websocket support, easy Docker build.
* Socket.IO simplifies reconnects & room-based updates (per ship).
* better-sqlite3 is reliable and fast for low-concurrency apps.
**Option B:** Node.js + Express + ws + SQLite
(also fine, but more manual reconnection & rooms logic)
Either way: **single Docker Compose** with one container; SQLite is a mounted file.
---
## 2) Architecture
### Backend
* REST API for CRUD and initial state fetch.
* WebSocket (Socket.IO) for real-time updates:
* Clients join a “ship room” when viewing a ship.
* When any field changes, server:
1. validates & persists to SQLite
2. broadcasts an update event to that room
### Frontend
Phone-first layout, 34 screens:
1. **Ship List** (create/select/delete)
2. **Ship Dashboard** (core stats + weapons list)
3. **Edit Weapon** (or inline editing)
4. **Rules/Reference** (read-only)
Use constrained controls:
* numeric steppers/sliders for numbers
* dropdowns for enums (size category, maneuverability class, etc.)
* toggles for booleans
* “Add weapon” form with select + numeric inputs
---
## 3) Data model (SQLite)
Implement migrations on startup.
### Tables
**ships**
* `id` TEXT (uuid, PK)
* `name` TEXT (unique-ish, not required but recommended)
* Core stats (examples; adjust to your desired fields):
* `hull_current` INTEGER NOT NULL
* `hull_max` INTEGER NOT NULL
* `armor_current` INTEGER NOT NULL
* `armor_max` INTEGER NOT NULL
* `ac` INTEGER NOT NULL
* `con_save` INTEGER NULL (or `con_score`)
* `speed` INTEGER NULL
* `maneuver_class` TEXT NULL (enum: A/B/C/D/E/F/S per your rules)
* `size_category` TEXT NULL
* `notes` TEXT NULL
* `updated_at` INTEGER (unix ms)
**weapons**
* `id` TEXT (uuid, PK)
* `ship_id` TEXT (FK -> ships.id, indexed)
* `name` TEXT NOT NULL
* `type` TEXT NULL (optional enum: siege/arcane/etc.)
* `attack_mod` INTEGER NULL
* `damage` TEXT NULL (freeform like “3d10”)
* `range` TEXT NULL
* `ammo_current` INTEGER NULL
* `ammo_max` INTEGER NULL
* `status` TEXT NULL (enum: ok/damaged/disabled)
* `notes` TEXT NULL
* `sort_order` INTEGER NOT NULL default 0
* `updated_at` INTEGER
**rules_pages** (optional; for read-only reference)
* `id` TEXT PK (e.g., “battle”, “ship-stats”)
* `title` TEXT
* `content_markdown` TEXT
* `updated_at` INTEGER
> Keep rules content as markdown stored in DB **or** ship it as static markdown files in the frontend. For v1, static markdown is simplest.
---
## 4) API design (REST)
### Ships
* `GET /api/ships` → list ships `{id, name, updated_at}`
* `POST /api/ships` → create `{name, initialStats...}` → returns full ship
* `GET /api/ships/:id` → full ship + weapons
* `PATCH /api/ships/:id` → update core stats (validated + constrained)
* `DELETE /api/ships/:id` → delete ship + weapons
### Weapons
* `POST /api/ships/:id/weapons` → add weapon
* `PATCH /api/weapons/:weaponId` → update weapon fields
* `DELETE /api/weapons/:weaponId` → delete weapon
Validation rules (server-enforced):
* current values must be `0 <= current <= max`
* max values must be `>= 0`
* enums must be in allowed set
* name length limits
---
## 5) WebSocket events (Socket.IO)
### Client → Server
* `ship:join` `{shipId}`
* `ship:leave` `{shipId}`
* `ship:update` `{shipId, patch}` // core stats patch
* `weapon:create` `{shipId, weapon}`
* `weapon:update` `{weaponId, patch}`
* `weapon:delete` `{weaponId}`
### Server → Client (broadcast to ship room)
* `ship:state` `{ship, weapons}` // on join, or after major changes
* `ship:patched` `{shipId, patch, updated_at}`
* `weapon:created` `{shipId, weapon}`
* `weapon:patched` `{shipId, weaponId, patch, updated_at}`
* `weapon:deleted` `{shipId, weaponId}`
Implementation approach:
* On any mutation, server writes to DB then emits event.
* On join, server emits current `ship:state`.
* Clients apply patches locally; on reconnect, re-join and refresh state.
Conflict model:
* **Last write wins** is fine (your requirement). Keep it simple.
---
## 6) Frontend UI details (phone-first)
### Screen 1: Ship List
* List cards: name + “last updated”
* Buttons:
* “Create ship” (modal with name + optional initial stats)
* Tap card to open
* Delete icon → confirm modal (“Delete ship X?”)
### Screen 2: Ship Dashboard
Sections:
1. **Vitals**
* Hull: current/max (stepper)
* Armor: current/max
* AC (stepper)
2. **Mobility**
* Speed (stepper)
* Maneuver class (dropdown)
* Size category (dropdown)
3. **Weapons**
* List weapons with quick fields (ammo, status)
* Tap weapon to open “Weapon details”
* “Add weapon” button
4. **Notes**
* Multi-line text
Include a top bar:
* Ship name
* “Rules” link/button
* “Back to ships”
### Screen 3: Weapon Details (or modal)
Constrained inputs for ammo/status/attack_mod.
Freeform for damage/range/notes.
### Screen 4: Rules/Reference
* Static markdown viewer with headings + quick links
* Optional “search within rules” (client-side filter)
---
## 7) Persistence + backups
* SQLite file at `/data/spelljammer.sqlite`
* Docker volume mount that path.
* Optional: periodic backup script/cron outside app (copy DB file nightly).
---
## 8) Docker Compose (single service)
* Build a single image that serves both:
* backend API + websockets
* frontend static files
* Expose one port (e.g., 3000) to Caddy reverse proxy.
Example structure (for the implementing AI):
* `/server` Fastify app
* `/web` React app build output served by server (or Caddy, but keep simple)
---
## 9) Implementation steps (what the AI should do in order)
1. Create repo scaffold with `/server` and `/web`
2. Implement SQLite schema + migrations
3. Implement REST endpoints + validation
4. Add Socket.IO with rooms:
* join/leave
* mutation events that persist + broadcast
5. Build React UI:
* routing for screens
* state store (zustand or simple context)
* socket connection + reconnection handling
6. Wire CRUD flows:
* list → select → join room → render state
* edit fields → emit update → optimistic UI
7. Add delete confirmation modal
8. Add rules markdown page (static)
9. Dockerize + compose + test behind Caddy
---
## 10) Acceptance criteria checklist
* ✅ Create ship, appears in list, persists after restart
* ✅ Select ship, see dashboard with core stats + weapons
* ✅ Delete ship requires confirm, removes weapons too
* ✅ All edits propagate to another browser within <1s
* ✅ App is usable on phone (single column layout, large touch targets)
* ✅ SQLite file persists via mounted volume
* ✅ WebSocket reconnect works (refresh state on reconnect)
* ✅ No auth, no user accounts
---
## 11) “Reference guide” integration (v1)
Since you want read-only rules “for during battle”:
Start with a **curated markdown** page that copies only the snippets you frequently need (e.g., maneuverability classes, hull/armor notes, weapon usage, combat steps). You can refine later.
(If you want, I can extract a concise “Battle Reference” page from your PDF and format it as markdown for the AI to drop into `/web/src/rules/battle.md`.)
---
## 12) v1 Implementation Status
**Status: v1 COMPLETE** (implemented 2026-02-19)
All 14 commits have been implemented:
1. ✅ Project scaffold (Fastify + Vite + TypeScript)
2. ✅ SQLite connection + migration system
3. ✅ Ship REST API with Zod validation
4. ✅ Weapon REST API with validation
5. ✅ Socket.IO with rooms + real-time events
6. ✅ React routing + layout (mobile-first dark theme)
7. ✅ Zustand stores + Socket.IO client
8. ✅ Ship List page with create modal
9. ✅ Ship Dashboard (vitals + mobility)
10. ✅ Weapons section (add/edit/detail modals + notes)
11. ✅ Delete confirmation polish
12. ✅ Rules/Reference page (collapsible battle reference)
13. ✅ Dockerfile + docker-compose
14. ✅ README with testing + deployment docs
See `README.md` for full setup, testing, and deployment instructions.