feat: add room-planner app — 3-day HK room view with NewBook integration
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>
This commit is contained in:
commit
1e658b6a48
39 changed files with 3765 additions and 0 deletions
70
backend/src/db.js
Normal file
70
backend/src/db.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
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)]
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue