NewBook-connected daily housekeeping planner. Replaces the hotelhubmodule-housekeeping-dailylist WordPress plugin. LXC 120 · 10.10.10.120:3080 · slug: room-planner. - 3-day booking window (yesterday/today/tomorrow) fetched live from NewBook - Task completion ticks back to NewBook; room status patches NewBook directly - 23px border sliver CSS system for adjacent-day booking status - 3-state filter cycling (off→inclusive→exclusive) for categories and flow types - Stat filters for outstanding tasks and clean/dirty status - Rolling 48h activity log with checkout/checkin/status/tasks events - newbook_pings event bus for future NewBook poller integration - Room modal with permission-gated guest/rate/notes, task checkboxes, status buttons - Placeholder sections for future linen-count and routine-tasks modules - Settings page: task type colours, twin/extra-bed detection, category exclusions - Mobile-first layout (sidebar desktop, compact top bar mobile) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
70 lines
2.2 KiB
JavaScript
70 lines
2.2 KiB
JavaScript
import pg from 'pg'
|
|
|
|
const { Pool } = pg
|
|
export const pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
|
|
|
export async function initDb() {
|
|
await pool.query(`
|
|
-- Instance-level config: task display, twin detection, note visibility, etc.
|
|
CREATE TABLE IF NOT EXISTS config (
|
|
key TEXT PRIMARY KEY,
|
|
value JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Rolling activity log. Pruned to 48h on each write.
|
|
CREATE TABLE IF NOT EXISTS activity_log (
|
|
id SERIAL PRIMARY KEY,
|
|
room_id TEXT NOT NULL,
|
|
event_type TEXT NOT NULL,
|
|
event_data JSONB,
|
|
user_name TEXT,
|
|
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
service_date DATE NOT NULL,
|
|
booking_ref TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS activity_log_date_idx ON activity_log (service_date, occurred_at DESC);
|
|
|
|
-- Lightweight NewBook change signals from the poller. No booking data stored.
|
|
CREATE TABLE IF NOT EXISTS newbook_pings (
|
|
id SERIAL PRIMARY KEY,
|
|
booking_ids TEXT[] NOT NULL,
|
|
event_types TEXT[] NOT NULL,
|
|
detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + INTERVAL '5 minutes'
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS newbook_pings_expires_idx ON newbook_pings (expires_at);
|
|
`)
|
|
|
|
await seedDefaultConfig()
|
|
}
|
|
|
|
async function seedDefaultConfig() {
|
|
const defaults = {
|
|
task_display: {},
|
|
twin_detection: {
|
|
enabled: true,
|
|
custom_field_ids: [],
|
|
keywords: ['twin', 'two single', '2 single', 'single beds', 'sofabed', 'sofa bed'],
|
|
exclude_keywords: ['twin room', 'twin suite'],
|
|
},
|
|
extra_bed_detection: {
|
|
enabled: true,
|
|
keywords: ['extra bed', 'extra cot', 'rollaway', 'roll away', 'fold out'],
|
|
},
|
|
excluded_categories: [],
|
|
hide_excluded_categories: false,
|
|
visible_note_types: [],
|
|
checkout_notification_timeout: 30,
|
|
default_checkout_time: process.env.DEFAULT_CHECKOUT_TIME || '11:00',
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(defaults)) {
|
|
await pool.query(
|
|
`INSERT INTO config (key, value) VALUES ($1, $2) ON CONFLICT (key) DO NOTHING`,
|
|
[key, JSON.stringify(value)]
|
|
)
|
|
}
|
|
}
|