From 3289a3102748f06296763aaa56e647a0a27ab16f Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Fri, 3 Jul 2026 21:37:52 +0000 Subject: [PATCH] =?UTF-8?q?Remove=20NewBook=20room=20blocking=20=E2=80=94?= =?UTF-8?q?=20app=20never=20writes=20to=20NewBook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unsellable flag is in-app visibility only; staff mark rooms out of order in NewBook through their own process. NewBook use is now read-only (room sync + occupancy filter). Co-Authored-By: Claude Fable 5 --- backend/src/db.js | 5 +-- backend/src/lib/newbook.js | 4 --- backend/src/routes/tasks.js | 39 +----------------------- frontend/src/api.ts | 6 ---- frontend/src/components/NewTaskModal.tsx | 18 +++++------ frontend/src/components/TaskModal.tsx | 31 ++----------------- frontend/src/pages/Settings.tsx | 18 ----------- frontend/src/pages/Summary.tsx | 1 - frontend/src/types.ts | 3 -- 9 files changed, 11 insertions(+), 114 deletions(-) diff --git a/backend/src/db.js b/backend/src/db.js index bd4ffa9..e8a122a 100644 --- a/backend/src/db.js +++ b/backend/src/db.js @@ -101,7 +101,6 @@ export async function initDb() { priority TEXT NOT NULL DEFAULT 'medium', -- low | medium | high | urgent status TEXT NOT NULL DEFAULT 'submitted', unusable BOOLEAN NOT NULL DEFAULT FALSE, - newbook_blocked BOOLEAN NOT NULL DEFAULT FALSE, hold_until DATE, due_date DATE, assigned_type TEXT NOT NULL DEFAULT 'staff', -- staff | contractor @@ -140,7 +139,7 @@ export async function initDb() { CREATE TABLE IF NOT EXISTS task_events ( id SERIAL PRIMARY KEY, task_id INT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, - event_type TEXT NOT NULL, -- created | status_change | reassigned | comment | photo | cost | newbook_block | newbook_unblock | reopened | edited + event_type TEXT NOT NULL, -- created | status_change | reassigned | comment | photo | cost | reopened | edited from_status TEXT, to_status TEXT, note TEXT, @@ -178,8 +177,6 @@ async function seedDefaults() { urgent_notify_email: '', notify_on_assign: true, notify_on_urgent: true, - newbook_block_status: 'Maintenance', - newbook_unblock_status: 'Dirty', } for (const [key, value] of Object.entries(defaults)) { await pool.query( diff --git a/backend/src/lib/newbook.js b/backend/src/lib/newbook.js index 1e82edf..cacd6be 100644 --- a/backend/src/lib/newbook.js +++ b/backend/src/lib/newbook.js @@ -65,7 +65,3 @@ export async function fetchBookings(fromDate, toDate) { return res?.data ?? [] } -// Update room status in NewBook. NewBook expects 'status' parameter (not 'site_status'). -export async function updateSiteStatus(siteId, status) { - return callApi('sites_update', { site_id: siteId, status }) -} diff --git a/backend/src/routes/tasks.js b/backend/src/routes/tasks.js index a1d9cd8..8bf7b7f 100644 --- a/backend/src/routes/tasks.js +++ b/backend/src/routes/tasks.js @@ -1,7 +1,7 @@ import { requireAuth, requireCap, hasCap } from '../auth.js' import { pool, getConfig } from '../db.js' import { createTask, logEvent, TRANSITIONS, PRIORITIES, STATUSES } from '../lib/task-core.js' -import { fetchBookings, updateSiteStatus } from '../lib/newbook.js' +import { fetchBookings } from '../lib/newbook.js' import { notifyAssignment } from '../lib/mailer.js' const PRIORITY_ORDER = `CASE t.priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END` @@ -266,43 +266,6 @@ export async function taskRoutes(app) { return { ok: true, added_to_template: addedToTemplate } }) - // POST /api/tasks/:id/newbook-block — set the room out of order in NewBook - app.post('/api/tasks/:id/newbook-block', { preHandler: requireCap('update') }, async (req, reply) => { - return toggleNewbookBlock(req, reply, true) - }) - - // POST /api/tasks/:id/newbook-unblock — release the room in NewBook - app.post('/api/tasks/:id/newbook-unblock', { preHandler: requireCap('update') }, async (req, reply) => { - return toggleNewbookBlock(req, reply, false) - }) - - async function toggleNewbookBlock(req, reply, block) { - const { rows } = await pool.query( - `SELECT t.*, l.newbook_site_id, l.source FROM tasks t JOIN locations l ON l.id = t.location_id WHERE t.id = $1`, - [req.params.id] - ) - if (!rows.length) return reply.status(404).send({ error: 'Task not found' }) - const task = rows[0] - if (task.source !== 'newbook' || !task.newbook_site_id) { - return reply.status(400).send({ error: 'Task location is not a NewBook room' }) - } - - const config = await getConfig() - const status = block ? (config.newbook_block_status || 'Maintenance') : (config.newbook_unblock_status || 'Dirty') - try { - await updateSiteStatus(task.newbook_site_id, status) - } catch (err) { - return reply.status(502).send({ error: `NewBook error: ${err.message}` }) - } - - await pool.query('UPDATE tasks SET newbook_blocked = $1, updated_at = NOW() WHERE id = $2', [block, task.id]) - await logEvent(task.id, block ? 'newbook_block' : 'newbook_unblock', { - note: `Room ${block ? 'blocked' : 'released'} in NewBook (status: ${status})`, - userName: req.user.name, - }) - return { ok: true, newbook_blocked: block } - } - // GET /api/occupancy — today's in-house NewBook site ids (for the unoccupied filter UI) app.get('/api/occupancy', { preHandler: requireCap('view') }, async (req, reply) => { try { diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 81df2a1..e1cc551 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -84,12 +84,6 @@ export function resolveTask(id: number, body: { export function addComment(id: number, note: string, addToTemplate = false): Promise<{ ok: boolean; added_to_template: boolean }> { return request(`/tasks/${id}/comments`, { method: 'POST', body: JSON.stringify({ note, add_to_template: addToTemplate }) }) } -export function blockRoomInNewbook(id: number): Promise<{ ok: boolean }> { - return request(`/tasks/${id}/newbook-block`, { method: 'POST' }) -} -export function unblockRoomInNewbook(id: number): Promise<{ ok: boolean }> { - return request(`/tasks/${id}/newbook-unblock`, { method: 'POST' }) -} export function fetchOccupancy(): Promise<{ date: string; occupied_site_ids: string[] }> { return request('/occupancy') } diff --git a/frontend/src/components/NewTaskModal.tsx b/frontend/src/components/NewTaskModal.tsx index 8575a95..95c05f0 100644 --- a/frontend/src/components/NewTaskModal.tsx +++ b/frontend/src/components/NewTaskModal.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react' import { X, Camera } from 'lucide-react' import type { Category, Location, Task, Priority, AppConfig, Asset } from '../types' import { PRIORITIES, PRIORITY_LABELS } from '../types' -import { createTask, fetchTasks, uploadTaskPhoto, blockRoomInNewbook, fetchAssets } from '../api' +import { createTask, fetchTasks, uploadTaskPhoto, fetchAssets } from '../api' import AssigneeSelect, { type Assignment } from './AssigneeSelect' import { PriorityBadge, StatusBadge } from './shared' @@ -33,7 +33,6 @@ export default function NewTaskModal({ categories, locations, config, onClose, o contractor_id: config?.default_contractor_id ?? null, }) - const location = useMemo(() => locations.find(l => l.id === locationId), [locations, locationId]) const locationAssets = useMemo( () => assets.filter(a => a.location_id === locationId), [assets, locationId] @@ -75,14 +74,6 @@ export default function NewTaskModal({ categories, locations, config, onClose, o await uploadTaskPhoto(task.id, file, 'report').catch(() => {}) } - // Explicit confirm — never block a room in NewBook silently - if (unusable && location?.source === 'newbook') { - const ok = window.confirm( - `Also mark ${location.name} as out of order in NewBook (status: ${config?.newbook_block_status || 'Maintenance'}) so it can't be sold?` - ) - if (ok) await blockRoomInNewbook(task.id).catch(err => window.alert(`NewBook block failed: ${err.message}`)) - } - onCreated() onClose() } catch (err) { @@ -157,10 +148,15 @@ export default function NewTaskModal({ categories, locations, config, onClose, o -