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
77
frontend/src/api.ts
Normal file
77
frontend/src/api.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import type { RoomsResponse, ActivityEntry, AppConfig, TaskData } from './types'
|
||||
|
||||
const BASE = '/room-planner/api'
|
||||
|
||||
async function request<T>(path: string, opts: RequestInit = {}): Promise<T> {
|
||||
const res = await fetch(`${BASE}${path}`, {
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json', ...opts.headers },
|
||||
...opts,
|
||||
})
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({ error: res.statusText }))
|
||||
throw new Error(err.error || `Request failed: ${res.status}`)
|
||||
}
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export function fetchRooms(date: string): Promise<RoomsResponse> {
|
||||
return request(`/rooms?date=${date}`)
|
||||
}
|
||||
|
||||
export function completeTask(taskId: string, roomId: string, serviceDate: string, bookingRef?: string) {
|
||||
return request<{ ok: boolean; site_status: string | null }>('/tasks/complete', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ task_id: taskId, room_id: roomId, service_date: serviceDate, booking_ref: bookingRef }),
|
||||
})
|
||||
}
|
||||
|
||||
export function uncompleteTask(taskId: string) {
|
||||
return request<{ ok: boolean }>('/tasks/uncomplete', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ task_id: taskId }),
|
||||
})
|
||||
}
|
||||
|
||||
export function updateStatus(roomId: string, status: string, serviceDate: string, bookingRef?: string) {
|
||||
return request<{ ok: boolean }>('/status', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ room_id: roomId, status, service_date: serviceDate, booking_ref: bookingRef }),
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchConfig(): Promise<AppConfig> {
|
||||
return request('/config')
|
||||
}
|
||||
|
||||
export function updateConfig(key: string, value: unknown): Promise<{ ok: boolean }> {
|
||||
return request(`/config/${key}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ value }),
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchTaskTypes(): Promise<Array<{ id: string; name: string }>> {
|
||||
return request('/config/task-types')
|
||||
}
|
||||
|
||||
export function fetchActivity(date: string): Promise<ActivityEntry[]> {
|
||||
return request(`/activity?date=${date}`)
|
||||
}
|
||||
|
||||
export function logActivity(entry: {
|
||||
room_id: string
|
||||
event_type: ActivityEntry['event_type']
|
||||
event_data?: Record<string, unknown>
|
||||
service_date?: string
|
||||
booking_ref?: string
|
||||
}): Promise<{ ok: boolean }> {
|
||||
return request('/activity', { method: 'POST', body: JSON.stringify(entry) })
|
||||
}
|
||||
|
||||
export function fetchEvents(since: string): Promise<{
|
||||
pings: Array<{ id: number; booking_ids: string[]; event_types: string[]; detected_at: string }>
|
||||
server_time: string
|
||||
}> {
|
||||
return request(`/events?since=${encodeURIComponent(since)}`)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue