- Add vacay_holiday_calendars table (region, label, color, sort_order) - Lazy migration of existing holidays_region to first calendar row - Extract applyHolidayCalendars() helper; replace inline holiday logic - GET /vacay/plan now includes holiday_calendars array - Add POST/PUT/DELETE /vacay/plan/holiday-calendars/:id endpoints - Client VacayPlan/VacayEntry/HolidayInfo types updated - loadHolidays() loops over all calendars; per-calendar color on HolidayInfo - VacayMonthCard uses holiday.color instead of hardcoded red - VacaySettings replaced single country picker with calendar list UI - VacayPage legend renders one item per calendar - i18n: addCalendar, calendarLabel, calendarColor, noCalendars (en + de) - Fix pre-existing TS errors: VacayPlan/VacayEntry missing fields, SettingToggleProps icon/onChange types, packing.suggestions.items array type Closes #36
385 lines
14 KiB
TypeScript
385 lines
14 KiB
TypeScript
import Database from 'better-sqlite3';
|
|
|
|
function createTables(db: Database.Database): void {
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
email TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
role TEXT NOT NULL DEFAULT 'user',
|
|
maps_api_key TEXT,
|
|
unsplash_api_key TEXT,
|
|
openweather_api_key TEXT,
|
|
avatar TEXT,
|
|
oidc_sub TEXT,
|
|
oidc_issuer TEXT,
|
|
last_login DATETIME,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
key TEXT NOT NULL,
|
|
value TEXT,
|
|
UNIQUE(user_id, key)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS trips (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
description TEXT,
|
|
start_date TEXT,
|
|
end_date TEXT,
|
|
currency TEXT DEFAULT 'EUR',
|
|
cover_image TEXT,
|
|
is_archived INTEGER DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS days (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
day_number INTEGER NOT NULL,
|
|
date TEXT,
|
|
notes TEXT,
|
|
title TEXT,
|
|
UNIQUE(trip_id, day_number)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS categories (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
color TEXT DEFAULT '#6366f1',
|
|
icon TEXT DEFAULT '📍',
|
|
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tags (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
color TEXT DEFAULT '#10b981',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS places (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
lat REAL,
|
|
lng REAL,
|
|
address TEXT,
|
|
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
|
|
price REAL,
|
|
currency TEXT,
|
|
reservation_status TEXT DEFAULT 'none',
|
|
reservation_notes TEXT,
|
|
reservation_datetime TEXT,
|
|
place_time TEXT,
|
|
end_time TEXT,
|
|
duration_minutes INTEGER DEFAULT 60,
|
|
notes TEXT,
|
|
image_url TEXT,
|
|
google_place_id TEXT,
|
|
website TEXT,
|
|
phone TEXT,
|
|
transport_mode TEXT DEFAULT 'walking',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS place_tags (
|
|
place_id INTEGER NOT NULL REFERENCES places(id) ON DELETE CASCADE,
|
|
tag_id INTEGER NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (place_id, tag_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS day_assignments (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
day_id INTEGER NOT NULL REFERENCES days(id) ON DELETE CASCADE,
|
|
place_id INTEGER NOT NULL REFERENCES places(id) ON DELETE CASCADE,
|
|
order_index INTEGER DEFAULT 0,
|
|
notes TEXT,
|
|
reservation_status TEXT DEFAULT 'none',
|
|
reservation_notes TEXT,
|
|
reservation_datetime TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS packing_items (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
checked INTEGER DEFAULT 0,
|
|
category TEXT,
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS photos (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
day_id INTEGER REFERENCES days(id) ON DELETE SET NULL,
|
|
place_id INTEGER REFERENCES places(id) ON DELETE SET NULL,
|
|
filename TEXT NOT NULL,
|
|
original_name TEXT NOT NULL,
|
|
file_size INTEGER,
|
|
mime_type TEXT,
|
|
caption TEXT,
|
|
taken_at TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS trip_files (
|
|
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,
|
|
reservation_id INTEGER REFERENCES reservations(id) ON DELETE SET NULL,
|
|
filename TEXT NOT NULL,
|
|
original_name TEXT NOT NULL,
|
|
file_size INTEGER,
|
|
mime_type TEXT,
|
|
description TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS reservations (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
day_id INTEGER REFERENCES days(id) ON DELETE SET NULL,
|
|
place_id INTEGER REFERENCES places(id) ON DELETE SET NULL,
|
|
assignment_id INTEGER REFERENCES day_assignments(id) ON DELETE SET NULL,
|
|
title TEXT NOT NULL,
|
|
reservation_time TEXT,
|
|
reservation_end_time TEXT,
|
|
location TEXT,
|
|
confirmation_number TEXT,
|
|
notes TEXT,
|
|
status TEXT DEFAULT 'pending',
|
|
type TEXT DEFAULT 'other',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS trip_members (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
invited_by INTEGER REFERENCES users(id),
|
|
added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(trip_id, user_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS day_notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
day_id INTEGER NOT NULL REFERENCES days(id) ON DELETE CASCADE,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
text TEXT NOT NULL,
|
|
time TEXT,
|
|
icon TEXT DEFAULT '📝',
|
|
sort_order REAL DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS app_settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS budget_items (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
category TEXT NOT NULL DEFAULT 'Other',
|
|
name TEXT NOT NULL,
|
|
total_price REAL NOT NULL DEFAULT 0,
|
|
persons INTEGER DEFAULT NULL,
|
|
days INTEGER DEFAULT NULL,
|
|
note TEXT,
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Addon system
|
|
CREATE TABLE IF NOT EXISTS addons (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
type TEXT NOT NULL DEFAULT 'global',
|
|
icon TEXT DEFAULT 'Puzzle',
|
|
enabled INTEGER DEFAULT 0,
|
|
config TEXT DEFAULT '{}',
|
|
sort_order INTEGER DEFAULT 0
|
|
);
|
|
|
|
-- Vacay addon tables
|
|
CREATE TABLE IF NOT EXISTS vacay_plans (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
owner_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
block_weekends INTEGER DEFAULT 1,
|
|
holidays_enabled INTEGER DEFAULT 0,
|
|
holidays_region TEXT DEFAULT '',
|
|
company_holidays_enabled INTEGER DEFAULT 1,
|
|
carry_over_enabled INTEGER DEFAULT 1,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(owner_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS vacay_plan_members (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
status TEXT DEFAULT 'pending',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(plan_id, user_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS vacay_user_colors (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
|
color TEXT DEFAULT '#6366f1',
|
|
UNIQUE(user_id, plan_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS vacay_years (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
|
year INTEGER NOT NULL,
|
|
UNIQUE(plan_id, year)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS vacay_user_years (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
|
year INTEGER NOT NULL,
|
|
vacation_days INTEGER DEFAULT 30,
|
|
carried_over INTEGER DEFAULT 0,
|
|
UNIQUE(user_id, plan_id, year)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS vacay_entries (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
date TEXT NOT NULL,
|
|
note TEXT DEFAULT '',
|
|
UNIQUE(user_id, plan_id, date)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS vacay_company_holidays (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
|
date TEXT NOT NULL,
|
|
note TEXT DEFAULT '',
|
|
UNIQUE(plan_id, date)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS vacay_holiday_calendars (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
|
region TEXT NOT NULL,
|
|
label TEXT,
|
|
color TEXT NOT NULL DEFAULT '#fecaca',
|
|
sort_order INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
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,
|
|
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_out TEXT,
|
|
confirmation TEXT,
|
|
notes TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Collab addon tables
|
|
CREATE TABLE IF NOT EXISTS collab_notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
category TEXT DEFAULT 'General',
|
|
title TEXT NOT NULL,
|
|
content TEXT,
|
|
color TEXT DEFAULT '#6366f1',
|
|
pinned INTEGER DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS collab_polls (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
question TEXT NOT NULL,
|
|
options TEXT NOT NULL,
|
|
multiple INTEGER DEFAULT 0,
|
|
closed INTEGER DEFAULT 0,
|
|
deadline TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS collab_poll_votes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
poll_id INTEGER NOT NULL REFERENCES collab_polls(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
option_index INTEGER NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(poll_id, user_id, option_index)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS collab_messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trip_id INTEGER NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
text TEXT NOT NULL,
|
|
reply_to INTEGER REFERENCES collab_messages(id) ON DELETE SET NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_collab_notes_trip ON collab_notes(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_collab_polls_trip ON collab_polls(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_collab_messages_trip ON collab_messages(trip_id);
|
|
`);
|
|
|
|
db.exec(`
|
|
CREATE INDEX IF NOT EXISTS idx_places_trip_id ON places(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_places_category_id ON places(category_id);
|
|
CREATE INDEX IF NOT EXISTS idx_days_trip_id ON days(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_day_assignments_day_id ON day_assignments(day_id);
|
|
CREATE INDEX IF NOT EXISTS idx_day_assignments_place_id ON day_assignments(place_id);
|
|
CREATE INDEX IF NOT EXISTS idx_place_tags_place_id ON place_tags(place_id);
|
|
CREATE INDEX IF NOT EXISTS idx_place_tags_tag_id ON place_tags(tag_id);
|
|
CREATE INDEX IF NOT EXISTS idx_trip_members_trip_id ON trip_members(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_trip_members_user_id ON trip_members(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_packing_items_trip_id ON packing_items(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_budget_items_trip_id ON budget_items(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_reservations_trip_id ON reservations(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_trip_files_trip_id ON trip_files(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_day_notes_day_id ON day_notes(day_id);
|
|
CREATE INDEX IF NOT EXISTS idx_photos_trip_id ON photos(trip_id);
|
|
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
|
CREATE INDEX IF NOT EXISTS idx_day_accommodations_trip_id ON day_accommodations(trip_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS assignment_participants (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
assignment_id INTEGER NOT NULL REFERENCES day_assignments(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
UNIQUE(assignment_id, user_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_assignment_participants_assignment ON assignment_participants(assignment_id);
|
|
`);
|
|
}
|
|
|
|
export { createTables };
|