Room planner: controls bar redesign + semantic filters + flat list view

- Cog button in date bar toggles controls bar visibility (persisted to localStorage)
- Controls bar: [Flat List][Categories] view toggle | [States Filter][Stats Filter] filter visibility toggles | [Reset View]
- Flat list view renders all visible rooms sorted numerically by name, no category headers
- States/stats filter bars independently show/hide based on controls bar toggles
- All view preferences (controlsVisible, viewMode, statesFilterVisible, statsFilterVisible) persist across page refreshes
- Semantic flow filters: 'arriving'/'departing' each match back-to-back rooms too; 'back-to-back' as exact match
- Filter chip counts use semantic union logic to match filter behaviour
- Guest name extraction tries guests[0].firstname+lastname (NewBook field names)
- booking_locked normalised: '0'/'1' strings handled explicitly (JS truthy fix)
- Category sort order pulled from settings global_config via internal HTTP endpoint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-04 13:41:39 +00:00
parent 5f3e52d8a6
commit a4c1f9bce5
2 changed files with 137 additions and 13 deletions

View file

@ -186,7 +186,7 @@ html, body, #root {
font-weight: 600;
font-size: 14px;
}
.refresh-btn {
.refresh-btn, .cog-btn {
background: none;
border: none;
cursor: pointer;
@ -194,12 +194,53 @@ html, body, #root {
padding: 4px;
border-radius: 6px;
display: flex;
transition: color .12s;
transition: color .12s, background .12s;
}
.refresh-btn:hover { color: var(--app-primary); }
.cog-btn:hover { color: var(--app-primary); }
.cog-btn.active { color: var(--app-primary); background: color-mix(in srgb, var(--app-primary) 10%, transparent); }
.refresh-btn.spinning svg { animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* ── View controls bar ───────────────────────────────────────── */
.view-controls-bar {
background: var(--card-bg);
border-bottom: 1px solid var(--border);
padding: 6px 12px;
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
flex-wrap: wrap;
}
.control-group {
display: flex;
gap: 2px;
}
.control-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 5px 10px;
border: 1px solid var(--border);
border-radius: 6px;
background: none;
cursor: pointer;
font-size: 12px;
color: var(--text-muted);
white-space: nowrap;
transition: background .12s, color .12s;
}
.control-btn:hover { background: var(--body-bg); color: var(--text-primary); }
.control-btn.active {
background: color-mix(in srgb, var(--app-primary) 12%, transparent);
border-color: var(--app-primary);
color: var(--app-primary);
font-weight: 600;
}
.control-group .control-btn:not(:first-child) { border-left: none; border-radius: 0 6px 6px 0; }
.control-group .control-btn:first-child:not(:last-child) { border-radius: 6px 0 0 6px; }
/* ── Filter bars ─────────────────────────────────────────────── */
.filter-section {
background: var(--card-bg);