diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 9ac50d8..65a6840 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -11,10 +11,6 @@ async function request(path: string, opts: RequestInit = {}): Promise { headers: { 'Content-Type': 'application/json', ...opts.headers }, ...opts, }) - if (res.status === 401) { - ;(window.top ?? window).location.href = '/login' - throw new Error('Unauthenticated') - } if (!res.ok) { const err = await res.json().catch(() => ({ error: res.statusText })) throw new Error(err.error || `Request failed: ${res.status}`) diff --git a/frontend/src/components/AuthGate.tsx b/frontend/src/components/AuthGate.tsx index 175e009..b25c7ea 100644 --- a/frontend/src/components/AuthGate.tsx +++ b/frontend/src/components/AuthGate.tsx @@ -12,20 +12,30 @@ export function useAuth() { export default function AuthGate({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null) + const [error, setError] = useState(null) useEffect(() => { fetch('/api/auth/verify?app=maintenance', { credentials: 'include' }) .then(r => { - if (!r.ok) { - ;(window.top ?? window).location.href = '/login' + if (r.status === 401 || r.status === 403) { + window.location.href = `/portal?redirect=${encodeURIComponent(window.location.href)}` return null } + if (!r.ok) throw new Error(`Auth check failed: ${r.status}`) return r.json() }) .then(data => { if (data) setUser(data) }) - .catch(() => { ;(window.top ?? window).location.href = '/login' }) + .catch(err => setError(err.message)) }, []) + if (error) { + return ( +
+ Authentication error: {error} +
+ ) + } + if (!user) { return (
(null) const [mineOnly, setMineOnly] = useState(false) const [roomsView, setRoomsView] = useState(false) - const [groupByLocation, setGroupByLocation] = useState(false) const [showNew, setShowNew] = useState(false) const [openTask, setOpenTask] = useState(null) @@ -84,17 +83,6 @@ export default function Summary() { return { total: tasks.length, occupied, arriving, free: tasks.length - occupied - arriving } }, [tasks, roomsView, occupiedSiteIds, arrivingSiteIds]) - const taskGroups = useMemo(() => { - if (!groupByLocation) return null - const groups: Record = {} - for (const t of tasks) { - const key = t.location_name || 'Unknown' - if (!groups[key]) groups[key] = [] - groups[key].push(t) - } - return Object.entries(groups).sort(([a], [b]) => a.localeCompare(b)) - }, [tasks, groupByLocation]) - const roomsCategoryExists = !!roomsCat function toggleRoomsView() { @@ -151,70 +139,55 @@ export default function Summary() { : 'Rooms Accessible View'} )} -
{tasks.length === 0 && !loading && (
No open tasks match these filters.
)} - {(() => { - const renderTask = (t: Task) => { - const sid = t.newbook_site_id ? String(t.newbook_site_id) : null - const isOccupied = roomsView && !!sid && occupiedSiteIds.has(sid) - const isArriving = roomsView && !!sid && !isOccupied && arrivingSiteIds.has(sid) - return ( -
setOpenTask(t.id)} - > -
-
- {t.title} - {isOccupied && Occupied} - {isArriving && Arrival today} - {t.unusable && } - {t.template_id && Recurring} -
-
- {!groupByLocation && {t.location_name}} - {t.category_name} - {t.asset_name && {t.asset_name}} - {ageLabel(t.created_at)} old - {t.due_date && ( - - due {formatDate(t.due_date)} - - )} - {t.hold_until && held until {formatDate(t.hold_until)}} - {t.photo_count > 0 && {t.photo_count}} -
+ {tasks.map(t => { + const sid = t.newbook_site_id ? String(t.newbook_site_id) : null + const isOccupied = roomsView && !!sid && occupiedSiteIds.has(sid) + const isArriving = roomsView && !!sid && !isOccupied && arrivingSiteIds.has(sid) + return ( +
setOpenTask(t.id)} + > +
+
+ {t.title} + {isOccupied && Occupied} + {isArriving && Arrival today} + {t.unusable && } + {t.template_id && Recurring}
-
- - - - {t.assigned_type === 'contractor' ? (t.contractor_name || '—') : (t.assigned_to_name || 'Unassigned')} - +
+ {t.location_name} + {t.category_name} + {t.asset_name && {t.asset_name}} + {ageLabel(t.created_at)} old + {t.due_date && ( + + due {formatDate(t.due_date)} + + )} + {t.hold_until && held until {formatDate(t.hold_until)}} + {t.photo_count > 0 && {t.photo_count}}
- ) - } - - if (taskGroups) { - return taskGroups.map(([locationName, groupTasks]) => ( -
-
{locationName} ({groupTasks.length})
- {groupTasks.map(renderTask)} +
+ + + + {t.assigned_type === 'contractor' ? (t.contractor_name || '—') : (t.assigned_to_name || 'Unassigned')} +
- )) - } - return tasks.map(renderTask) - })()} +
+ ) + })} {showNew && (