feat(packing): item quantity, bag rename, multi-user bags, save as template

- Add quantity field to packing items (persisted, visible per item)
- Bags are now renamable (click to edit in sidebar)
- Bags support multiple user assignments with avatar display
- New packing_bag_members table for multi-user bag ownership
- Save current packing list as reusable template
- Add bag members API endpoint (PUT /bags/:bagId/members)
- Migration 74: quantity on packing_items, user_id on packing_bags, packing_bag_members table
This commit is contained in:
Maurice
2026-04-05 19:28:33 +02:00
parent f3679739d8
commit 48bf149d01
7 changed files with 337 additions and 53 deletions
+17
View File
@@ -826,6 +826,23 @@ function runMigrations(db: Database.Database): void {
() => {
try { db.exec('ALTER TABLE budget_items ADD COLUMN reservation_id INTEGER REFERENCES reservations(id) ON DELETE SET NULL DEFAULT NULL'); } catch (err: any) { if (!err.message?.includes('duplicate column name')) throw err; }
},
// Migration 74: Add quantity to packing_items + user_id to packing_bags + bag_members table
() => {
try { db.exec('ALTER TABLE packing_items ADD COLUMN quantity INTEGER NOT NULL DEFAULT 1'); } catch (err: any) { if (!err.message?.includes('duplicate column name')) throw err; }
try { db.exec('ALTER TABLE packing_bags ADD COLUMN user_id INTEGER REFERENCES users(id) ON DELETE SET NULL DEFAULT NULL'); } catch (err: any) { if (!err.message?.includes('duplicate column name')) throw err; }
db.exec(`
CREATE TABLE IF NOT EXISTS packing_bag_members (
bag_id INTEGER NOT NULL REFERENCES packing_bags(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY (bag_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_packing_bag_members_bag ON packing_bag_members(bag_id);
`);
// Migrate existing single user_id to bag_members
const bagsWithUser = db.prepare('SELECT id, user_id FROM packing_bags WHERE user_id IS NOT NULL').all() as { id: number; user_id: number }[];
const ins = db.prepare('INSERT OR IGNORE INTO packing_bag_members (bag_id, user_id) VALUES (?, ?)');
for (const b of bagsWithUser) ins.run(b.id, b.user_id);
},
];
if (currentVersion < migrations.length) {