Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,15 @@ jobs:

- name: Build
run: npm run build
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
OPS_EMAIL: ${{ secrets.OPS_EMAIL }}
RESEND_FROM_EMAIL: ${{ secrets.RESEND_FROM_EMAIL }}
UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}
DISPLAY_KEY: ${{ secrets.DISPLAY_KEY }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_SIGNING_SECRET: ${{ secrets.SLACK_SIGNING_SECRET }}
17 changes: 15 additions & 2 deletions app/(dashboard)/administrator/booking-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
'use client'

import { createPortal } from 'react-dom'
import { useEffect, useState } from 'react'

interface BookingModalProps {
title: string
onClose: () => void
children: React.ReactNode
}

export default function BookingModal({ title, onClose, children }: BookingModalProps) {
return (
const [mounted, setMounted] = useState(false)
useEffect(() => { setMounted(true) }, [])
useEffect(() => {
const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose() }
document.addEventListener('keydown', handler)
return () => document.removeEventListener('keydown', handler)
}, [onClose])
if (!mounted) return null

return createPortal(
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50">
<div className="bg-[#184073] rounded-2xl shadow-2xl w-full max-w-5xl max-h-[90vh] overflow-y-auto p-8 space-y-5">
<div className="flex items-center justify-between">
Expand All @@ -16,6 +28,7 @@ export default function BookingModal({ title, onClose, children }: BookingModalP
</div>
{children}
</div>
</div>
</div>,
document.body
)
}
4 changes: 2 additions & 2 deletions app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ export default function DashboardLayout({
<span className="text-[#c8102e] font-bold text-xl tracking-tight">Chambers</span>
</div>
<p className="text-slate-500 text-xs mt-0.5">NU Student Gov. Association</p>
<p className="text-slate-600 text-xs mt-1">v1.11.11</p>
<p className="text-slate-600 text-xs mt-1">v1.12.0</p>
{userName && (
<div className="flex items-start justify-between mt-2">
<p className="text-slate-500 text-xs italic">{getGreeting()},<br />{userName}</p>
Expand Down Expand Up @@ -279,7 +279,7 @@ export default function DashboardLayout({
</div>
</nav>

<main className="flex-1 bg-gradient-to-br from-[#112244] via-[#0a1628] to-[#060e1a] p-8 overflow-y-auto">
<main className="flex-1 bg-gradient-to-br from-[#112244] via-[#0a1628] to-[#060e1a] p-8 overflow-y-auto overflow-x-hidden">
{children}
</main>
</div>
Expand Down
1 change: 1 addition & 0 deletions app/(dashboard)/sga-spaces/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ export default function SGASpacesPage() {
setModalSlot(null)
fetchCalendarData()
}}
spaces={spaces}
/>
)}

Expand Down
37 changes: 34 additions & 3 deletions app/(dashboard)/sga-spaces/space-booking-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ interface User {
email: string
}

interface Space {
id: string
name: string
capacity: number
}

interface SpaceBookingModalProps {
spaceId: string
spaceName: string
Expand All @@ -21,6 +27,7 @@ interface SpaceBookingModalProps {
initialTitle?: string
initialAttendees?: User[]
onCancelBooking?: () => Promise<void>
spaces?: Space[]
}

function isoToDateAndTime(iso: string): { date: string; time: string } {
Expand Down Expand Up @@ -52,12 +59,14 @@ export default function SpaceBookingModal({
initialTitle = '',
initialAttendees = [],
onCancelBooking,
spaces,
}: SpaceBookingModalProps) {
const isEditing = !!editBookingId

const { date: initDate, time: initStartTime } = isoToDateAndTime(initialStart)
const { time: initEndTime } = isoToDateAndTime(initialEnd)

const [selectedSpaceId, setSelectedSpaceId] = useState(spaceId)
const [title, setTitle] = useState(initialTitle)
const [date, setDate] = useState(initDate)
const [startTime, setStartTime] = useState(initStartTime)
Expand Down Expand Up @@ -87,6 +96,12 @@ export default function SpaceBookingModal({
}
}, [attendees])

useEffect(() => {
const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose() }
document.addEventListener('keydown', handler)
return () => document.removeEventListener('keydown', handler)
}, [onClose])

useEffect(() => {
if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current)
searchTimeoutRef.current = setTimeout(() => searchUsers(searchQuery), 300)
Expand Down Expand Up @@ -138,7 +153,7 @@ export default function SpaceBookingModal({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
space_id: spaceId,
space_id: selectedSpaceId,
title: title.trim(),
start_time,
end_time,
Expand All @@ -162,15 +177,15 @@ export default function SpaceBookingModal({
const labelCls = "block text-xs font-medium text-[#93b8d8] mb-1"

return (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" onClick={onClose}>
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4" onClick={onClose}>
<div
className="bg-[#0a1628] border border-[#1e5080] rounded-xl shadow-xl w-full max-w-md max-h-[90vh] overflow-y-auto"
onClick={e => e.stopPropagation()}
>
<div className="flex items-center justify-between p-5 border-b border-[#1e5080]">
<div>
<h2 className="text-lg font-semibold text-[#f0f6ff]">
{isEditing ? 'Edit Booking' : `Book ${spaceName}`}
{isEditing ? 'Edit Booking' : `Book ${spaces?.find(s => s.id === selectedSpaceId)?.name ?? spaceName}`}
</h2>
{isEditing && (
<p className="text-xs text-[#93b8d8] mt-0.5">{spaceName}</p>
Expand All @@ -184,6 +199,22 @@ export default function SpaceBookingModal({
</div>

<form onSubmit={handleSubmit} className="p-5 space-y-4">
{/* Location selector (creation mode only) */}
{!isEditing && spaces && spaces.length > 1 && (
<div>
<label className={labelCls}>Location</label>
<select
value={selectedSpaceId}
onChange={e => setSelectedSpaceId(e.target.value)}
className={inputCls}
>
{spaces.map(s => (
<option key={s.id} value={s.id}>{s.name} (cap. {s.capacity})</option>
))}
</select>
</div>
)}

{/* Title */}
<div>
<label className={labelCls}>Booking Title <span className="text-[#c8102e]">*</span></label>
Expand Down
4 changes: 2 additions & 2 deletions app/(dashboard)/sga-spaces/space-calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ export default function SpaceCalendar({
<div className="rounded-xl border border-[#1e5080] overflow-hidden bg-[#0a1628] select-none">
<div ref={scrollRef} className="overflow-y-auto" style={{ maxHeight: '648px' }}>
{/* Sticky day header */}
<div ref={headerRef} className="flex border-b border-[#1e5080] sticky top-0 z-50 bg-[#0a1628]">
<div ref={headerRef} className="flex border-b border-[#1e5080] sticky top-0 z-10 bg-[#0a1628]">
<div className="w-14 flex-shrink-0 border-r border-[#1e5080]" />
{dayLabels.map((dl, i) => (
<div
Expand Down Expand Up @@ -459,7 +459,7 @@ export default function SpaceCalendar({

{/* Drag capture overlay */}
<div
className="absolute inset-0 z-50"
className="absolute inset-0 z-20"
style={{ cursor: overlayCursor }}
onMouseMove={e => handleOverlayMouseMove(e, dayIdx)}
onMouseLeave={() => { setOverlayCursor(canBook ? 'crosshair' : 'default'); setHoveredBookingId(null) }}
Expand Down
Loading
Loading