feat(classes): member calendar/signup + instructor attendance
/classes lists upcoming sessions for any member with spots-left, sign-up / join-waitlist and cancel (waitlist auto-promotes on cancel via the action). Holders of classes.instruct/manage get an inline attendees panel to mark attendance and complete the session; completion issues the class's certification through the normal grant path and the UI reports when certificates were skipped (no certifications.grant). Sidebar gets a member-visible "Classes" link.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { listUpcomingSessions } from '@/lib/actions'
|
||||
import { ClassesBrowseClient } from './classes-browse-client'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export default async function ClassesPage() {
|
||||
const supabase = await createClient()
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser()
|
||||
if (!user) redirect('/login')
|
||||
|
||||
const { data: member } = await supabase
|
||||
.from('space_members')
|
||||
.select('space_id')
|
||||
.eq('user_id', user.id)
|
||||
.in('status', ['current', 'unverified', 'late'])
|
||||
.maybeSingle()
|
||||
if (!member) redirect('/signup')
|
||||
|
||||
const [{ data: canInstruct }, { data: canManage }] = await Promise.all([
|
||||
supabase.rpc('user_has_permission', { uid: user.id, sid: member.space_id, perm: 'classes.instruct' }),
|
||||
supabase.rpc('user_has_permission', { uid: user.id, sid: member.space_id, perm: 'classes.manage' }),
|
||||
])
|
||||
|
||||
const res = await listUpcomingSessions()
|
||||
const sessions = 'data' in res ? res.data : []
|
||||
|
||||
return (
|
||||
<ClassesBrowseClient
|
||||
sessions={sessions as unknown[]}
|
||||
canRunSessions={!!canInstruct || !!canManage}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user