fix: AuthGate stuck on Loading — verify returns flat user object

The auth /verify endpoint returns { user_id, name, email, is_admin, caps }
directly, not wrapped in a user key, so data.user was undefined and the
gate never rendered. Also: caps come back as bare slugs when ?app= is
passed, so the prefixed room-planner:cap checks always failed — replaced
with the standard can(user, cap) helper honouring is_admin.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-03 16:25:20 +00:00
parent 2993dfa216
commit 5a2dab637c
5 changed files with 14 additions and 7 deletions

View file

@ -24,7 +24,7 @@ export default function AuthGate({ children }: { children: React.ReactNode }) {
if (!r.ok) throw new Error(`Auth check failed: ${r.status}`)
return r.json()
})
.then(data => { if (data) setUser(data.user) })
.then(data => { if (data) setUser(data) })
.catch(err => setError(err.message))
}, [])

View file

@ -1,12 +1,13 @@
import { NavLink } from 'react-router-dom'
import { BedDouble, Settings, LogOut } from 'lucide-react'
import { useAuth } from './AuthGate'
import { can } from '../types'
const ICON_PROPS = { size: 16, strokeWidth: 1.75 }
export default function Layout({ children }: { children: React.ReactNode }) {
const { user } = useAuth()
const hasCap = (cap: string) => user.caps.includes(`room-planner:${cap}`)
const hasCap = (cap: string) => can(user, cap)
return (
<div className="app-shell">

View file

@ -1,5 +1,6 @@
import { useState, useCallback } from 'react'
import { X, CheckSquare, Square, Package, ClipboardList } from 'lucide-react'
import { can } from '../types'
import type { RoomData, TaskData, AppConfig, User } from '../types'
import { bookingStatusColor, isDirty } from '../lib/booking-flow'
import { detectTwin } from '../lib/twin-detect'
@ -36,7 +37,7 @@ export default function RoomModal({ room, viewDate, config, user, onClose, onTas
const [statusLoading, setStatusLoading] = useState(false)
const [notesTab, setNotesTab] = useState<'booking' | 'departing'>('booking')
const hasCap = (cap: string) => user.caps.includes(`room-planner:${cap}`)
const hasCap = (cap: string) => can(user, cap)
const todayTasks = room.tasks.filter(t => {
const d = t.task_when_date || t.task_period_from