diff --git a/app/(app)/equipment/manage/equipment-manage-client.tsx b/app/(app)/equipment/manage/equipment-manage-client.tsx
index 459f863..6f404cc 100644
--- a/app/(app)/equipment/manage/equipment-manage-client.tsx
+++ b/app/(app)/equipment/manage/equipment-manage-client.tsx
@@ -10,6 +10,7 @@ import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/
import { useConfirm } from '@/components/ui/confirm'
import { createEquipment, updateEquipment, deleteEquipment } from '@/lib/actions'
import { EQUIPMENT_STATUS_LABEL } from '@/lib/equipment-logic'
+import { EquipmentReservations } from '@/components/equipment/equipment-reservations'
type Equip = {
id: string
@@ -37,6 +38,7 @@ export function EquipmentManageClient({ initial, certs }: { initial: Equip[]; ce
const [draft, setDraft] = useState(emptyDraft())
const [editId, setEditId] = useState(null)
const [editDraft, setEditDraft] = useState(emptyDraft())
+ const [resFor, setResFor] = useState(null)
const [busy, setBusy] = useState(false)
const certName = (id: string | null) => (id ? certs.find(c => c.id === id)?.name ?? null : null)
@@ -237,12 +239,16 @@ export function EquipmentManageClient({ initial, certs }: { initial: Equip[]; ce
+
)}
+ {editId !== it.id && resFor === it.id && }
))}
diff --git a/components/equipment/equipment-reservations.tsx b/components/equipment/equipment-reservations.tsx
new file mode 100644
index 0000000..bed9f59
--- /dev/null
+++ b/components/equipment/equipment-reservations.tsx
@@ -0,0 +1,90 @@
+'use client'
+
+import { useEffect, useState, useCallback } from 'react'
+import { toast } from 'sonner'
+import { listEquipmentReservations, cancelReservation } from '@/lib/actions'
+
+type Reservation = {
+ id: string
+ member_id: string
+ starts_at: string
+ ends_at: string
+ status: string
+ notes: string | null
+ space_members: { display_name: string | null; email: string | null } | null
+}
+
+function who(r: Reservation) {
+ return r.space_members?.display_name ?? r.space_members?.email ?? 'Member'
+}
+
+// Manager view of who reserved a piece of equipment, with a manager cancel.
+// listEquipmentReservations is equipment.manage-gated server-side.
+export function EquipmentReservations({ equipmentId }: { equipmentId: string }) {
+ const [loading, setLoading] = useState(false)
+ const [rows, setRows] = useState([])
+ const [busy, setBusy] = useState(null)
+
+ const load = useCallback(async () => {
+ setLoading(true)
+ const res = await listEquipmentReservations({ equipmentId })
+ setLoading(false)
+ if ('error' in res && res.error) {
+ toast.error(res.error)
+ return
+ }
+ setRows(((res as { data: Reservation[] }).data) ?? [])
+ }, [equipmentId])
+
+ useEffect(() => {
+ load()
+ }, [load])
+
+ async function onCancel(r: Reservation) {
+ setBusy(r.id)
+ const res = await cancelReservation({ reservationId: r.id })
+ setBusy(null)
+ if ('error' in res && res.error) {
+ toast.error(res.error)
+ return
+ }
+ setRows(prev => prev.map(x => (x.id === r.id ? { ...x, status: 'cancelled' } : x)))
+ toast.success('Reservation cancelled')
+ }
+
+ return (
+
+
Reservations
+ {loading ? (
+
Loading…
+ ) : rows.length === 0 ? (
+
No reservations.
+ ) : (
+
+ {rows.map(r => (
+ -
+
+
{who(r)}
+
+ {new Date(r.starts_at).toLocaleString()} – {new Date(r.ends_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
+ {' · '}
+ {r.status}
+
+ {r.notes &&
{r.notes}
}
+
+ {r.status !== 'cancelled' && (
+
+ )}
+
+ ))}
+
+ )}
+
+ )
+}