Files
la-huasca-ts/src/lib/schema.ts
T
muken 81c12ace7b feat: image optimization with multiple sizes and lazy loading
- Generate thumbnail (150px), medium (800px), full size on upload
- OptimizedImage component with Intersection Observer lazy loading
- Blur placeholder while loading
- Fetches appropriate size based on props (thumbnail/medium/full)
- Async image loading reduces initial page load time
- Added medium and thumbnail columns to images table
2026-06-30 23:34:23 -05:00

502 lines
20 KiB
TypeScript

import { db } from './db';
import bcrypt from 'bcryptjs';
export async function initSchema() {
await db.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'user')),
comments_disabled BOOLEAN DEFAULT FALSE,
first_name VARCHAR(100),
last_name VARCHAR(100),
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS images (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
data TEXT NOT NULL,
thumbnail TEXT,
medium TEXT,
category VARCHAR(100) DEFAULT 'other',
room_id INTEGER REFERENCES rooms(id) ON DELETE SET NULL,
location_target VARCHAR(100) DEFAULT 'gallery',
uploaded_at TIMESTAMP DEFAULT NOW()
);
`);
// Add columns if they don't exist (for existing databases)
await db.query(`ALTER TABLE images ADD COLUMN IF NOT EXISTS medium TEXT`);
await db.query(`ALTER TABLE images ADD COLUMN IF NOT EXISTS thumbnail TEXT`);
await db.query(`
CREATE TABLE IF NOT EXISTS reservations (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
date DATE NOT NULL,
time TIME NOT NULL,
guests INTEGER NOT NULL,
table_name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS coupons (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
code VARCHAR(50) NOT NULL,
discount VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS offers (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS benefits (
id SERIAL PRIMARY KEY,
text VARCHAR(500) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS config (
key VARCHAR(100) PRIMARY KEY,
value TEXT NOT NULL
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS rooms (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL,
photos TEXT[] DEFAULT '{}',
featured_photo VARCHAR(500),
active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS menu_items (
id SERIAL PRIMARY KEY,
category VARCHAR(100) NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
price NUMERIC(10,2) NOT NULL,
is_daily_special BOOLEAN DEFAULT FALSE,
active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
photos TEXT[] DEFAULT '{}',
featured_photo VARCHAR(500),
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS places (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
photos TEXT[] DEFAULT '{}',
featured_photo VARCHAR(500),
distance_km NUMERIC(8,2),
visit_time VARCHAR(100),
suggestion TEXT,
active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS reviews (
id SERIAL PRIMARY KEY,
author_name VARCHAR(255) NOT NULL,
rating INTEGER NOT NULL CHECK (rating BETWEEN 1 AND 5),
text TEXT NOT NULL,
approved BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS slides (
id SERIAL PRIMARY KEY,
title VARCHAR(255),
subtitle TEXT,
image_id INTEGER REFERENCES images(id) ON DELETE SET NULL,
image_url VARCHAR(500),
active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS room_comments (
id SERIAL PRIMARY KEY,
room_id INTEGER REFERENCES rooms(id) ON DELETE CASCADE,
photo_url VARCHAR(500),
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
first_name VARCHAR(100) NOT NULL,
last_initial VARCHAR(10),
text TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
deleted_by_admin BOOLEAN DEFAULT FALSE
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS social_events (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
price VARCHAR(50),
date DATE,
photos TEXT[] DEFAULT '{}',
featured_photo VARCHAR(500),
active BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS social_event_reservations (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
social_event_id INTEGER REFERENCES social_events(id) ON DELETE CASCADE,
guests INTEGER NOT NULL,
notes TEXT,
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'cancelled')),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(user_id, social_event_id)
);
`);
await db.query(`CREATE TABLE IF NOT EXISTS calendar_events (
id SERIAL PRIMARY KEY,
date DATE NOT NULL UNIQUE,
title VARCHAR(255) NOT NULL,
type VARCHAR(100) NOT NULL,
description TEXT,
color VARCHAR(20) NOT NULL,
active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
);`);
await db.query(`
CREATE TABLE IF NOT EXISTS user_sessions (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
ALTER TABLE rooms ADD COLUMN IF NOT EXISTS featured_photo VARCHAR(500);
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS comments_disabled BOOLEAN DEFAULT FALSE;
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS first_name VARCHAR(100);
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS last_name VARCHAR(100);
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS preferred_language VARCHAR(10) DEFAULT 'es';
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS terms_accepted_at TIMESTAMP;
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS email VARCHAR(255);
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS phone VARCHAR(50);
`);
await db.query(`
ALTER TABLE users ADD COLUMN IF NOT EXISTS country VARCHAR(100);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS terms_content (
id SERIAL PRIMARY KEY,
language VARCHAR(10) NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
version VARCHAR(20) NOT NULL DEFAULT '1.0',
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(language, version)
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS availability (
id SERIAL PRIMARY KEY,
room_id INTEGER REFERENCES rooms(id) ON DELETE CASCADE,
date DATE NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'available' CHECK (status IN ('available', 'booked', 'maintenance')),
price NUMERIC(10,2),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(room_id, date)
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS trips (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
room_id INTEGER REFERENCES rooms(id) ON DELETE SET NULL,
check_in DATE NOT NULL,
check_out DATE NOT NULL,
notes TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'confirmed' CHECK (status IN ('pending', 'confirmed', 'checked_in', 'checked_out', 'cancelled')),
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
sender_role VARCHAR(20) NOT NULL CHECK (sender_role IN ('user', 'admin')),
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS room_amenities (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
icon_svg TEXT NOT NULL,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS room_amenity_links (
room_id INTEGER REFERENCES rooms(id) ON DELETE CASCADE,
amenity_id INTEGER REFERENCES room_amenities(id) ON DELETE CASCADE,
PRIMARY KEY (room_id, amenity_id)
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS reservations (
id SERIAL PRIMARY KEY,
room_id INTEGER NOT NULL REFERENCES rooms(id),
user_id INTEGER NOT NULL REFERENCES users(id),
check_in DATE NOT NULL,
check_out DATE NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
total_price DECIMAL(10,2),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS room_availability (
id SERIAL PRIMARY KEY,
room_id INTEGER NOT NULL REFERENCES rooms(id),
date DATE NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'available',
reason TEXT,
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(room_id, date)
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY,
room_id INTEGER REFERENCES rooms(id),
user_id INTEGER NOT NULL REFERENCES users(id),
message TEXT NOT NULL,
read_by_admins BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`ALTER TABLE room_comments ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'approved'`);
await db.query(`CREATE INDEX IF NOT EXISTS idx_reservations_room_dates ON reservations(room_id, check_in, check_out)`);
await db.query(`CREATE INDEX IF NOT EXISTS idx_room_availability_room_date ON room_availability(room_id, date)`);
await db.query(`CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at DESC)`);
}
export async function migrateFromLegacyPlaces() {
await db.query(`
UPDATE places SET photos = ARRAY[photo] WHERE photo IS NOT NULL AND (photos IS NULL OR photos = '{}');
`);
await db.query(`
ALTER TABLE places DROP COLUMN IF EXISTS photo;
`);
await db.query(`
ALTER TABLE places ADD COLUMN IF NOT EXISTS featured_photo VARCHAR(500);
`);
await db.query(`
ALTER TABLE rooms ADD COLUMN IF NOT EXISTS featured_photo VARCHAR(500);
`);
}
export async function seed() {
if (process.env.NODE_ENV === 'production') {
throw new Error('Seeding is disabled in production');
}
const adminPassword = process.env.BOOTSTRAP_ADMIN_PASSWORD;
const userPassword = process.env.BOOTSTRAP_USER_PASSWORD;
const wifiPassword = process.env.BOOTSTRAP_WIFI_PASSWORD;
const tagline = process.env.BOOTSTRAP_TAGLINE || 'Hotel · Restaurant · Andean Highlands';
const { rows: adminRows } = await db.query(`SELECT id FROM users WHERE username = 'admin'`);
if (adminRows.length === 0 && adminPassword) {
const adminHash = bcrypt.hashSync(adminPassword, 10);
const seedRows: Array<[string, string, string]> = [['admin', adminHash, 'admin']];
if (userPassword) {
seedRows.push(['user', bcrypt.hashSync(userPassword, 10), 'user']);
}
await db.query(
`INSERT INTO users (username, password_hash, role) VALUES ${seedRows
.map((_, index) => `($${index * 3 + 1}, $${index * 3 + 2}, $${index * 3 + 3})`)
.join(', ')}`,
seedRows.flat()
);
}
const { rows: offerRows } = await db.query(`SELECT id FROM offers`);
if (offerRows.length === 0) {
await db.query(
`INSERT INTO offers (title, description) VALUES ($1, $2), ($3, $4)`,
[
'Summer Wine Weekend',
'Complimentary wine tasting with every two-night stay.',
'Sunset Dinner',
'Three-course menu on the terrace every Friday.',
]
);
}
const { rows: benefitRows } = await db.query(`SELECT id FROM benefits`);
if (benefitRows.length === 0) {
await db.query(
`INSERT INTO benefits (text) VALUES ($1), ($2), ($3), ($4)`,
[
'Priority booking for peak weekends',
'Late checkout on availability',
'Free welcome cocktail',
'Member-only events and tastings',
]
);
}
const { rows: configWifiRows } = await db.query(`SELECT key FROM config WHERE key = 'wifi_password'`);
if (configWifiRows.length === 0 && wifiPassword) {
await db.query(`INSERT INTO config (key, value) VALUES ($1, $2)`, ['wifi_password', wifiPassword]);
}
const { rows: configTaglineRows } = await db.query(`SELECT key FROM config WHERE key = 'tagline'`);
if (configTaglineRows.length === 0) {
await db.query(`INSERT INTO config (key, value) VALUES ($1, $2)`, ['tagline', tagline]);
}
const { rows: calendarRows } = await db.query(`SELECT id FROM calendar_events`);
if (calendarRows.length === 0) {
await db.query(
`INSERT INTO calendar_events (date, title, type, description, color, active) VALUES ($1, $2, $3, $4, $5, $6)`,
['2026-06-30', 'Welcome Dinner', 'event', 'Andean tasting menu', '#b45309', true]
);
}
const { rows: socialRows } = await db.query(`SELECT id FROM social_events`);
if (socialRows.length === 0) {
await db.query(
`INSERT INTO social_events (name, description, price, date, photos, featured_photo, active, sort_order) VALUES ($1, $2, $3, $4, '{}', null, $5, $6)`,
['Sunset Tasting', 'Local wines on the terrace.', 'USD 35', '2026-07-04', true, 1]
);
}
const { rows: socialReservationRows } = await db.query(`SELECT id FROM social_event_reservations`);
if (socialReservationRows.length === 0) {
const { rows: adminIdRows } = await db.query(`SELECT id FROM users WHERE username = 'admin'`);
if (adminIdRows.length > 0) {
await db.query(
`INSERT INTO social_event_reservations (user_id, social_event_id, guests, notes, status) SELECT $1, id, $2, $3, $4 FROM social_events LIMIT 1`,
[adminIdRows[0].id, 2, 'Anniversary', 'confirmed']
);
}
}
const { rows: amenityRows } = await db.query(`SELECT id FROM room_amenities`);
if (amenityRows.length === 0) {
const defaultAmenities = [
['Wi-Fi', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/></svg>', 1],
['TV', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="15" rx="2" ry="2"/><polyline points="17 2 12 7 7 2"/></svg>', 2],
['Shower', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4v5a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5V4"/><path d="M4 9h4"/><path d="M6 9v12"/><path d="M10 4h10a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H10"/><circle cx="6" cy="14" r="1"/><circle cx="6" cy="18" r="1"/><circle cx="10" cy="14" r="1"/><circle cx="10" cy="18" r="1"/></svg>', 3],
['Bathtub', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16a1 1 0 0 1 1 1v3a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4v-3a1 1 0 0 1 1-1z"/><path d="M6 12V5a2 2 0 0 1 2-2h3v2.25"/><circle cx="12" cy="5" r=".5"/></svg>', 4],
['Fireplace', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.14-4.052 1.5-5 .5 2 2 4 2 6a2.5 2.5 0 0 0 5 0c0-1.38-.5-2-1-3-.5-1-1-2-1-3.5 1.5 1 3 2.5 3 5a4 4 0 1 1-8 0"/><path d="M4 19h16a2 2 0 0 0 2-2v-2H2v2a2 2 0 0 0 2 2z"/></svg>', 5],
['Minibar', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3h18v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3z"/><path d="M5 9v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V9"/><line x1="10" y1="13" x2="14" y2="13"/></svg>', 6],
['Coffee Maker', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 8h1a4 4 0 1 1 0 8h-1"/><path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"/><line x1="6" y1="2" x2="6" y2="4"/><line x1="10" y1="2" x2="10" y2="4"/><line x1="14" y1="2" x2="14" y2="4"/></svg>', 7],
['Safe', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/><circle cx="12" cy="16" r="1"/></svg>', 8],
['Heating', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z"/></svg>', 9],
['Balcony', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="10" rx="2"/><line x1="3" y1="17" x2="21" y2="17"/><line x1="7" y1="11" x2="7" y2="21"/><line x1="12" y1="11" x2="12" y2="21"/><line x1="17" y1="11" x2="17" y2="21"/></svg>', 10],
['King Bed', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 18v-4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4"/><path d="M2 14v-2a2 2 0 0 1 2-2h1"/><path d="M22 14v-2a2 2 0 0 0-2-2h-1"/><path d="M5 4h14a2 2 0 0 1 2 2v2H3V6a2 2 0 0 1 2-2z"/><rect x="3" y="10" width="18" height="4" rx="1"/></svg>', 11],
['Queen Bed', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 18v-3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3"/><path d="M2 13v-2a2 2 0 0 1 2-2h1"/><path d="M18 13v-2a2 2 0 0 0-2-2h-1"/><path d="M4 5h10a2 2 0 0 1 2 2v1H2V7a2 2 0 0 1 2-2z"/><rect x="2" y="9" width="14" height="3" rx="1"/></svg>', 13],
['2 Queen Beds', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 15v-2a1.5 1.5 0 0 1 1.5-1.5H10a1.5 1.5 0 0 1 1.5 1.5v2"/><path d="M12.5 15v-2a1.5 1.5 0 0 1 1.5-1.5H21.5a1.5 1.5 0 0 1 1.5 1.5v2"/><path d="M1 11V9.5A1.5 1.5 0 0 1 2.5 8h8A1.5 1.5 0 0 1 12 9.5V11"/><path d="M12 11V9.5A1.5 1.5 0 0 1 13.5 8h8A1.5 1.5 0 0 1 23 9.5V11"/><rect x="1" y="8" width="11" height="2.5" rx="0.75"/><rect x="12" y="8" width="11" height="2.5" rx="0.75"/></svg>', 14],
['Mountain View', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m8 3 4 8 5-5 5 15H2L8 3z"/></svg>', 12],
];
for (const [name, icon_svg, sort_order] of defaultAmenities) {
await db.query(
'INSERT INTO room_amenities (name, icon_svg, sort_order) VALUES ($1, $2, $3)',
[name, icon_svg, sort_order]
);
}
}
}
export async function resetDatabase() {
await db.query('DROP TABLE IF EXISTS room_comments, reviews, places, menu_items, rooms, config, benefits, offers, coupons, reservations, images, users, slides, calendar_events, social_events, social_event_reservations CASCADE');
await initSchema();
await migrateFromLegacyPlaces();
await seed();
}