diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..3029be9 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "chambers-dev", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "port": 3000 + } + ] +} diff --git a/app/(dashboard)/administrator/fulfill-modal.tsx b/app/(dashboard)/administrator/fulfill-modal.tsx index c9ba68a..96115e8 100644 --- a/app/(dashboard)/administrator/fulfill-modal.tsx +++ b/app/(dashboard)/administrator/fulfill-modal.tsx @@ -2,6 +2,8 @@ import { useEffect, useState } from 'react' import BookingModal from './booking-modal' +import ScopeLabel from '@/app/_components/scope-label' +import type { BookingScope, Division } from '@/lib/booking-scope' interface Booking { id: string @@ -15,6 +17,10 @@ interface FulfillModalProps { type: string purpose: string body_id: string + bodyName: string + scope: BookingScope + division: Division | null + linkedBodies: { id: string; name: string }[] } onClose: () => void onSuccess: () => void @@ -79,6 +85,11 @@ export default function FulfillModal({ request, onClose, onSuccess }: FulfillMod

Request

{request.purpose}

{request.type}

+ {/* Booking dropdown */} diff --git a/app/(dashboard)/administrator/one-time-form.tsx b/app/(dashboard)/administrator/one-time-form.tsx index bc1a3a7..a251786 100644 --- a/app/(dashboard)/administrator/one-time-form.tsx +++ b/app/(dashboard)/administrator/one-time-form.tsx @@ -3,7 +3,8 @@ import { useState, useEffect } from 'react' import TimePicker from './time-picker' import BookingScopeSelector, { type BookingScopeValue } from '@/app/_components/booking-scope-selector' -import { DIVISIONS, type Division } from '@/lib/booking-scope' +import ScopeLabel from '@/app/_components/scope-label' +import { DIVISIONS, type Division, type BookingScope } from '@/lib/booking-scope' const STATUSES = [ 'Reserved', @@ -38,7 +39,10 @@ interface PendingRequest { status: string created_at: string body_id: string + scope: BookingScope + division: Division | null bodies: { name: string } | null + room_request_bodies: { body_id: string; bodies: { name: string } | null }[] | null room_request_details: Array<{ start_date: string | null end_date: string | null @@ -92,6 +96,7 @@ export default function OneTimeForm({ bodies, semesters, onClose, onSuccess }: O const [saving, setSaving] = useState(false) const [error, setError] = useState('') const [pendingRequests, setPendingRequests] = useState([]) + const [requestTypeFilter, setRequestTypeFilter] = useState<'all' | BookingScope>('all') useEffect(() => { fetch('/api/administrator/requests') @@ -104,9 +109,9 @@ export default function OneTimeForm({ bodies, semesters, onClose, onSuccess }: O .catch(() => {}) }, []) - const visibleRequests = scopeValue.body_id - ? pendingRequests.filter(r => r.body_id === scopeValue.body_id) - : pendingRequests + const visibleRequests = pendingRequests + .filter(r => !scopeValue.body_id || r.body_id === scopeValue.body_id) + .filter(r => requestTypeFilter === 'all' || r.scope === requestTypeFilter) const updateSession = (index: number, field: keyof OneTimeSession, value: string) => { setSessions(prev => prev.map((s, i) => i === index ? { ...s, [field]: value } : s)) @@ -306,7 +311,19 @@ export default function OneTimeForm({ bodies, semesters, onClose, onSuccess }: O
-

Open Requests

+
+

Open Requests

+ +
{visibleRequests.length === 0 ? (

No open requests

) : ( @@ -314,7 +331,11 @@ export default function OneTimeForm({ bodies, semesters, onClose, onSuccess }: O {visibleRequests.map(r => (
- {r.bodies?.name ?? '—'} + ({ id: x.body_id, name: x.bodies?.name ?? '' }))} + className="text-sm font-semibold text-[#f0f6ff]" + /> {new Date(r.created_at).toLocaleDateString()}

{r.purpose}

diff --git a/app/(dashboard)/administrator/requests-tab.tsx b/app/(dashboard)/administrator/requests-tab.tsx index 85060e0..e5fa0fd 100644 --- a/app/(dashboard)/administrator/requests-tab.tsx +++ b/app/(dashboard)/administrator/requests-tab.tsx @@ -4,6 +4,8 @@ import { useEffect, useState } from 'react' import FulfillModal from './fulfill-modal' import DenyModal from './deny-modal' import { Skeleton } from '@/app/_components/skeleton' +import ScopeLabel from '@/app/_components/scope-label' +import type { BookingScope, Division } from '@/lib/booking-scope' function RequestsTabSkeleton() { const card = (wide: boolean) => ( @@ -62,8 +64,11 @@ interface RoomRequest { status: RequestStatus notes: string | null created_at: string + scope: BookingScope + division: Division | null bodies: { name: string } | null users: { full_name: string } | null + room_request_bodies: { body_id: string; bodies: { name: string } | null }[] | null room_request_details: { room_name: string | null start_date: string @@ -113,7 +118,12 @@ export default function RequestsTab({ onCountChange }: RequestsTabProps) { type: string purpose: string body_id: string + bodyName: string + scope: BookingScope + division: Division | null + linkedBodies: { id: string; name: string }[] } | null>(null) + const [typeFilter, setTypeFilter] = useState<'all' | BookingScope>('all') const fetchRequests = async () => { const [reqRes, revRes] = await Promise.all([ @@ -135,6 +145,15 @@ export default function RequestsTab({ onCountChange }: RequestsTabProps) { if (requests.length === 0 && revisions.length === 0) return
No requests found.
+ const filteredRequests = requests.filter(r => typeFilter === 'all' || r.scope === typeFilter) + + const TYPE_FILTERS: { value: 'all' | BookingScope; label: string }[] = [ + { value: 'all', label: 'All' }, + { value: 'single', label: 'Single Body' }, + { value: 'divisional', label: 'Divisional' }, + { value: 'multi', label: 'Multi-Body' }, + ] + return (
{revisions.length > 0 && ( @@ -175,13 +194,38 @@ export default function RequestsTab({ onCountChange }: RequestsTabProps) { {requests.length > 0 && (
- {revisions.length > 0 &&

Room Requests

} - {requests.map(r => ( +
+ {revisions.length > 0 && ( +

Room Requests

+ )} +
+ {TYPE_FILTERS.map(f => ( + + ))} +
+
+ {filteredRequests.length === 0 ? ( +

No requests match this filter.

+ ) : filteredRequests.map(r => (
{/* Header */}
- {r.bodies?.name || 'Unknown Body'} + ({ id: x.body_id, name: x.bodies?.name ?? '' }))} + className="font-semibold text-[#f0f6ff]" + /> · {r.type === 'One-Time Room' ? 'One-Time/Multiple Room' : r.type}
@@ -245,7 +289,16 @@ export default function RequestsTab({ onCountChange }: RequestsTabProps) { ) : (