Adds the management/CRUD layer for meetings: a signed JWT join-link
token service (jose), instant/scheduled meeting creation endpoints,
and a protected /dashboard page listing the host's active, upcoming,
and past meetings with a "Nueva reunión" action area (instant button +
scheduling dialog). Also fixes a Base UI console warning (missing
nativeButton={false}) on Button instances rendered as Link.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
85 lines
2.7 KiB
TypeScript
85 lines
2.7 KiB
TypeScript
import { redirect } from "next/navigation"
|
|
|
|
import { auth } from "@/auth"
|
|
import { prisma } from "@/lib/prisma"
|
|
import { createJoinLinkToken } from "@/lib/join-link"
|
|
import { DashboardContent } from "@/components/dashboard/dashboard-content"
|
|
import type { MeetingDTO } from "@/components/dashboard/types"
|
|
|
|
// This route reads the session (cookies) and queries the DB for the
|
|
// current user's meetings on every request, so it can't be part of the
|
|
// static shell under Cache Components. Opting out of instant-navigation
|
|
// validation here is the documented migration path (see
|
|
// node_modules/next/dist/docs/01-app/02-guides/authentication-with-cache-components.md)
|
|
// for a route that should always render fresh data rather than a cached
|
|
// shell — dashboard data (active/upcoming meetings) must never be stale.
|
|
export const instant = false
|
|
|
|
const PAST_MEETINGS_LIMIT = 10
|
|
|
|
export default async function DashboardPage() {
|
|
const session = await auth()
|
|
|
|
if (!session?.user?.id) {
|
|
redirect("/login?callbackUrl=/dashboard")
|
|
}
|
|
|
|
const hostUserId = Number(session.user.id)
|
|
|
|
const [activeMeetings, upcomingMeetings, pastMeetings] = await Promise.all([
|
|
prisma.meeting.findMany({
|
|
where: { hostUserId, status: "active" },
|
|
orderBy: { startedAt: "desc" },
|
|
}),
|
|
prisma.meeting.findMany({
|
|
where: {
|
|
hostUserId,
|
|
status: "scheduled",
|
|
scheduledAt: { gte: new Date() },
|
|
},
|
|
orderBy: { scheduledAt: "asc" },
|
|
}),
|
|
prisma.meeting.findMany({
|
|
where: { hostUserId, status: "ended" },
|
|
orderBy: { endedAt: "desc" },
|
|
take: PAST_MEETINGS_LIMIT,
|
|
}),
|
|
])
|
|
|
|
async function toDTO(
|
|
meeting: (typeof activeMeetings)[number],
|
|
withJoinLink: boolean
|
|
): Promise<MeetingDTO> {
|
|
return {
|
|
id: meeting.id,
|
|
uuid: meeting.uuid,
|
|
title: meeting.title,
|
|
type: meeting.type,
|
|
status: meeting.status,
|
|
scheduledAt: meeting.scheduledAt?.toISOString() ?? null,
|
|
startedAt: meeting.startedAt?.toISOString() ?? null,
|
|
endedAt: meeting.endedAt?.toISOString() ?? null,
|
|
joinUrl: withJoinLink
|
|
? `/join/${meeting.uuid}?token=${await createJoinLinkToken(meeting.uuid, {
|
|
scheduledAt: meeting.scheduledAt,
|
|
})}`
|
|
: null,
|
|
}
|
|
}
|
|
|
|
const [active, upcoming, past] = await Promise.all([
|
|
Promise.all(activeMeetings.map((meeting) => toDTO(meeting, true))),
|
|
Promise.all(upcomingMeetings.map((meeting) => toDTO(meeting, true))),
|
|
Promise.all(pastMeetings.map((meeting) => toDTO(meeting, false))),
|
|
])
|
|
|
|
return (
|
|
<DashboardContent
|
|
displayName={session.user.displayName}
|
|
active={active}
|
|
upcoming={upcoming}
|
|
past={past}
|
|
/>
|
|
)
|
|
}
|