fix(bookings): preserve accommodation dates when place is unlinked or missing

- Remove NOT NULL constraint on day_accommodations.place_id (migration)
  and change ON DELETE CASCADE → SET NULL so deleting a place no longer
  cascades to the accommodation row
- Switch listAccommodations / getAccommodationWithPlace to LEFT JOIN so
  accommodations without a linked place are visible to the modal
- Relax create/update guards in reservationService to only require
  start_day_id + end_day_id, not place_id; place_id remains optional
- Client save guard now sends create_accommodation whenever FROM/TO days
  are set, regardless of whether a hotel place was selected
- Add re-hydration useEffect in ReservationModal to back-fill hotel
  fields from the accommodations prop when it arrives after modal opens
  (race between isOpen and the tripAccommodations fetch)
- Fix demo-seed TDZ crash: move db Proxy declaration before DEMO_MODE
  block so circular require in demo-reset resolves correctly
- Sidebar accommodation badge falls back to reservation title when
  place_name is null; click/cursor disabled for placeless accommodations
- listAccommodations now joins reservations to expose reservation_title
This commit is contained in:
jubnl
2026-04-20 23:08:42 +02:00
parent 5984adb2ea
commit 16b81a8356
7 changed files with 73 additions and 24 deletions
+9 -9
View File
@@ -35,15 +35,6 @@ function initDb(): void {
initDb();
if (process.env.DEMO_MODE === 'true') {
try {
const { seedDemoData } = require('../demo/demo-seed');
seedDemoData(_db);
} catch (err: unknown) {
console.error('[Demo] Seed error:', err instanceof Error ? err.message : err);
}
}
const db = new Proxy({} as Database.Database, {
get(_, prop: string | symbol) {
if (!_db) throw new Error('Database connection is not available (restore in progress?)');
@@ -56,6 +47,15 @@ const db = new Proxy({} as Database.Database, {
},
});
if (process.env.DEMO_MODE === 'true') {
try {
const { seedDemoData } = require('../demo/demo-seed');
seedDemoData(_db);
} catch (err: unknown) {
console.error('[Demo] Seed error:', err instanceof Error ? err.message : err);
}
}
function closeDb(): void {
if (_db) {
try { _db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch (e) {}
+30
View File
@@ -1876,6 +1876,36 @@ function runMigrations(db: Database.Database): void {
if (!hasCol) return;
db.prepare('DELETE FROM oauth_tokens WHERE audience IS NULL').run();
},
// Remove NOT NULL constraint on day_accommodations.place_id so hotel
// reservations created from the Bookings tab without a linked place can
// still persist their date range. Change ON DELETE CASCADE → SET NULL so
// deleting a place orphans the accommodation row instead of cascading.
() => {
db.exec(`
CREATE TABLE day_accommodations_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
place_id INTEGER REFERENCES places(id) ON DELETE SET NULL,
start_day_id INTEGER NOT NULL REFERENCES days(id) ON DELETE CASCADE,
end_day_id INTEGER NOT NULL REFERENCES days(id) ON DELETE CASCADE,
check_in TEXT,
check_in_end TEXT,
check_out TEXT,
confirmation TEXT,
notes TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO day_accommodations_new
SELECT id, trip_id, place_id, start_day_id, end_day_id,
check_in, check_in_end, check_out, confirmation, notes, created_at
FROM day_accommodations;
DROP TABLE day_accommodations;
ALTER TABLE day_accommodations_new RENAME TO day_accommodations;
CREATE INDEX IF NOT EXISTS idx_day_accommodations_trip_id ON day_accommodations(trip_id);
CREATE INDEX IF NOT EXISTS idx_day_accommodations_start_day_id ON day_accommodations(start_day_id);
CREATE INDEX IF NOT EXISTS idx_day_accommodations_end_day_id ON day_accommodations(end_day_id);
`);
},
];
if (currentVersion < migrations.length) {
+1 -1
View File
@@ -344,7 +344,7 @@ function createTables(db: Database.Database): void {
CREATE TABLE IF NOT EXISTS day_accommodations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
place_id INTEGER NOT NULL REFERENCES places(id) ON DELETE CASCADE,
place_id INTEGER REFERENCES places(id) ON DELETE SET NULL,
start_day_id INTEGER NOT NULL REFERENCES days(id) ON DELETE CASCADE,
end_day_id INTEGER NOT NULL REFERENCES days(id) ON DELETE CASCADE,
check_in TEXT,