feat: ship templates, crew fields, and read/edit dashboard

- Add crew_req, hardpoints, cargo fields (DB migration + server + types)
- Add 10 ship templates from Appendix A (Caravel, Galleon, Sloop, etc.)
- Enhanced CreateShipModal with template picker that auto-fills all stats
- Dashboard defaults to compact read-only view; Edit button toggles to
  editable steppers/dropdowns
- New CrewSection showing crew required, hardpoints, and cargo
- Two-column form layout in create modal for better use of space
- Fix .gitignore data/ pattern to only match root-level data directory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bas van Rossem
2026-02-19 17:16:44 +01:00
parent cbda07d793
commit 642f1f70e8
12 changed files with 524 additions and 112 deletions

View File

@@ -0,0 +1,3 @@
ALTER TABLE ships ADD COLUMN crew_req INTEGER NOT NULL DEFAULT 0;
ALTER TABLE ships ADD COLUMN hardpoints INTEGER NOT NULL DEFAULT 0;
ALTER TABLE ships ADD COLUMN cargo INTEGER NOT NULL DEFAULT 0;

View File

@@ -15,6 +15,9 @@ export interface Ship {
maneuver_class: string | null;
size_category: string | null;
notes: string | null;
crew_req: number;
hardpoints: number;
cargo: number;
updated_at: number;
}
@@ -56,8 +59,8 @@ export function createShip(input: CreateShipInput): Ship {
const now = Date.now();
db.prepare(`
INSERT INTO ships (id, name, hull_current, hull_max, armor_current, armor_max, ac, con_save, speed, maneuver_class, size_category, notes, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO ships (id, name, hull_current, hull_max, armor_current, armor_max, ac, con_save, speed, maneuver_class, size_category, notes, crew_req, hardpoints, cargo, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
id,
input.name,
@@ -71,6 +74,9 @@ export function createShip(input: CreateShipInput): Ship {
input.maneuver_class,
input.size_category,
input.notes,
input.crew_req,
input.hardpoints,
input.cargo,
now,
);

View File

@@ -14,6 +14,9 @@ export const createShipSchema = z.object({
maneuver_class: z.enum(maneuverClasses).nullable().default(null),
size_category: z.string().max(50).nullable().default(null),
notes: z.string().nullable().default(null),
crew_req: z.number().int().min(0).default(0),
hardpoints: z.number().int().min(0).default(0),
cargo: z.number().int().min(0).default(0),
});
export const updateShipSchema = z
@@ -29,6 +32,9 @@ export const updateShipSchema = z
maneuver_class: z.enum(maneuverClasses).nullable(),
size_category: z.string().max(50).nullable(),
notes: z.string().nullable(),
crew_req: z.number().int().min(0),
hardpoints: z.number().int().min(0),
cargo: z.number().int().min(0),
})
.partial()
.refine(