118 lines
4.0 KiB
TypeScript
118 lines
4.0 KiB
TypeScript
export const dynamic = 'force-dynamic';
|
|
import React from 'react';
|
|
import Link from 'next/link';
|
|
import { notFound } from 'next/navigation';
|
|
import { prisma } from '@/lib/prisma';
|
|
import { generateJitsiToken, JITSI_DOMAIN } from '@/lib/jitsi';
|
|
import JitsiClassroom from '@/components/classroom/JitsiClassroom';
|
|
|
|
interface PageProps {
|
|
params: Promise<{ tenant: string; classId: string }>;
|
|
searchParams: Promise<{ role?: string; userId?: string }>;
|
|
}
|
|
|
|
export default async function ClassroomLivePage({
|
|
params, searchParams }: PageProps) {
|
|
const { tenant: tenantSlug, classId } = await params;
|
|
const { role: queryRole, userId: queryUserId } = await searchParams;
|
|
|
|
// 1. Verify tenant & class session
|
|
const session = await prisma.classSession.findUnique({
|
|
where: { id: classId },
|
|
include: {
|
|
tenant: true,
|
|
bookings: {
|
|
include: { user: true },
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!session || session.tenant.slug !== tenantSlug) {
|
|
notFound();
|
|
}
|
|
|
|
// 2. Resolve user & permissions
|
|
const isHostRequested = queryRole === 'INSTRUCTOR' || queryRole === 'SUPER_ADMIN';
|
|
|
|
let user = null;
|
|
if (queryUserId) {
|
|
user = await prisma.user.findUnique({
|
|
where: { id: queryUserId },
|
|
});
|
|
}
|
|
|
|
if (!user) {
|
|
// Select first appropriate user in the tenant matching requested role
|
|
user = await prisma.user.findFirst({
|
|
where: {
|
|
tenantId: session.tenantId,
|
|
role: isHostRequested ? { in: ['INSTRUCTOR', 'SUPER_ADMIN'] } : 'CLIENT',
|
|
},
|
|
});
|
|
}
|
|
|
|
// Fallback demo user if tenant has no seeded users yet
|
|
const effectiveUser = user || {
|
|
id: `guest-${Math.random().toString(36).slice(2, 8)}`,
|
|
name: isHostRequested ? 'Head Coach' : 'Fitness Member',
|
|
email: `${isHostRequested ? 'coach' : 'member'}@${tenantSlug}.local`,
|
|
role: (isHostRequested ? 'INSTRUCTOR' : 'CLIENT') as 'INSTRUCTOR' | 'CLIENT',
|
|
};
|
|
|
|
const isHost = effectiveUser.role === 'INSTRUCTOR' || effectiveUser.role === 'SUPER_ADMIN';
|
|
|
|
// 3. Generate signed Jitsi JWT token for this session
|
|
const jwtToken = generateJitsiToken({
|
|
roomName: session.jitsiRoomId,
|
|
tenantSlug: session.tenant.slug,
|
|
user: {
|
|
id: effectiveUser.id,
|
|
name: effectiveUser.name || 'Participant',
|
|
email: effectiveUser.email,
|
|
role: effectiveUser.role,
|
|
},
|
|
expiresInSeconds: 7200,
|
|
});
|
|
|
|
return (
|
|
<div className="h-screen h-dvh w-full min-h-0 min-w-0 overflow-hidden bg-neutral-950 text-neutral-100 font-sans p-2 sm:p-4 md:p-6 flex flex-col">
|
|
{/* Top Banner Navigation */}
|
|
<div className="max-w-7xl w-full min-w-0 mx-auto mb-2 sm:mb-4 shrink-0 flex flex-col sm:flex-row sm:items-center justify-between gap-2 sm:gap-3">
|
|
<div className="flex min-w-0 items-center gap-2 sm:gap-3">
|
|
<Link
|
|
href={`/${tenantSlug}/classes`}
|
|
className="shrink-0 text-xs text-neutral-400 hover:text-white transition px-2.5 py-1 rounded bg-neutral-900 border border-neutral-800"
|
|
>
|
|
← Exit to Dashboard
|
|
</Link>
|
|
<span className="min-w-0 text-sm font-semibold text-white truncate" title={session.title}>
|
|
{session.title}
|
|
</span>
|
|
<span className="hidden md:inline min-w-0 truncate text-xs font-mono text-neutral-500" title={session.tenant.name}>
|
|
• {session.tenant.name}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex min-w-0 items-center gap-2">
|
|
<span className="truncate text-xs text-neutral-400">
|
|
Signed in as: <strong className="text-white">{effectiveUser.name}</strong> ({effectiveUser.role})
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Jitsi WebRTC Classroom Container */}
|
|
<div className="max-w-7xl w-full min-h-0 min-w-0 mx-auto flex-1 flex flex-col">
|
|
<JitsiClassroom
|
|
roomName={session.jitsiRoomId}
|
|
tenantSlug={tenantSlug}
|
|
jwtToken={jwtToken}
|
|
domain={JITSI_DOMAIN}
|
|
displayName={effectiveUser.name || 'Participant'}
|
|
email={effectiveUser.email}
|
|
isHost={isHost}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|