Compare commits

..

No commits in common. "83ac37e4a1de676032dacf957a7b04f24e0a0bc0" and "25d6323aa437b007355faca95881f54a9d598083" have entirely different histories.

3 changed files with 53 additions and 74 deletions

View file

@ -11,10 +11,6 @@ async function request<T>(path: string, opts: RequestInit = {}): Promise<T> {
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}`)

View file

@ -12,20 +12,30 @@ export function useAuth() {
export default function AuthGate({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [error, setError] = useState<string | null>(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 (
<div style={{ padding: 32, color: 'var(--danger)', fontFamily: 'var(--font)' }}>
Authentication error: {error}
</div>
)
}
if (!user) {
return (
<div style={{

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { Plus, RefreshCw, Camera, BedDouble, Layers } from 'lucide-react'
import { Plus, RefreshCw, Camera, BedDouble } from 'lucide-react'
import type { Task, Category, Location, AppConfig, TaskStatus } from '../types'
import { STATUS_LABELS, can } from '../types'
import { fetchTasks, fetchLocations, fetchConfig, fetchOccupancy } from '../api'
@ -25,7 +25,6 @@ export default function Summary() {
const [categoryFilter, setCategoryFilter] = useState<number | null>(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<number | null>(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<string, Task[]> = {}
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,17 +139,13 @@ export default function Summary() {
: 'Rooms Accessible View'}
</button>
)}
<button className={`chip ${groupByLocation ? 'active' : ''}`} onClick={() => setGroupByLocation(v => !v)}>
<Layers size={13} strokeWidth={1.75} /> Group by location
</button>
</div>
{tasks.length === 0 && !loading && (
<div className="empty-state">No open tasks match these filters.</div>
)}
{(() => {
const renderTask = (t: Task) => {
{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)
@ -181,7 +165,7 @@ export default function Summary() {
{t.template_id && <span className="badge badge-outline">Recurring</span>}
</div>
<div className="task-card-meta">
{!groupByLocation && <span>{t.location_name}</span>}
<span>{t.location_name}</span>
<span>{t.category_name}</span>
{t.asset_name && <span>{t.asset_name}</span>}
<span>{ageLabel(t.created_at)} old</span>
@ -203,18 +187,7 @@ export default function Summary() {
</div>
</div>
)
}
if (taskGroups) {
return taskGroups.map(([locationName, groupTasks]) => (
<div key={locationName}>
<div className="section-title">{locationName} <span style={{ fontWeight: 400, textTransform: 'none', letterSpacing: 0 }}>({groupTasks.length})</span></div>
{groupTasks.map(renderTask)}
</div>
))
}
return tasks.map(renderTask)
})()}
})}
{showNew && (
<NewTaskModal