Integrate LiveKit SFU and harden booking workflow
This commit is contained in:
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
||||
import { getCurrentCustomer } from "@/lib/auth/customer";
|
||||
import { getCustomerBookings } from "@/lib/booking/queries";
|
||||
import { PersonalCalendar } from "@/components/booking/PersonalCalendar";
|
||||
import { getMeetingSettingsGlobal } from "@/lib/payload/globals";
|
||||
|
||||
export const metadata: Metadata = { title: "Mein Kalender" };
|
||||
|
||||
@@ -10,12 +11,13 @@ export default async function KontoKalenderPage() {
|
||||
if (!customer) return null;
|
||||
|
||||
const bookings = await getCustomerBookings(customer.id);
|
||||
const meetingSettings = await getMeetingSettingsGlobal();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="font-serif text-xl font-medium text-anouma-plum">Mein Kalender</h2>
|
||||
<div className="mt-6">
|
||||
<PersonalCalendar bookings={bookings} />
|
||||
<PersonalCalendar bookings={bookings} meetingSettings={meetingSettings} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,8 @@ import { getCurrentCustomer } from "@/lib/auth/customer";
|
||||
import { getCustomerBookings } from "@/lib/booking/queries";
|
||||
import { BookingCard } from "@/components/booking/BookingCard";
|
||||
import { ArrowLink } from "@/components/ArrowLink";
|
||||
import { getMeetingSettingsGlobal } from "@/lib/payload/globals";
|
||||
import { getBookingMeetingStatus } from "@/lib/booking/meetingAccess";
|
||||
|
||||
export const metadata: Metadata = { title: "Mein Konto" };
|
||||
|
||||
@@ -11,6 +13,7 @@ export default async function KontoOverviewPage() {
|
||||
if (!customer) return null; // Layout already redirects; keeps TS happy.
|
||||
|
||||
const bookings = await getCustomerBookings(customer.id);
|
||||
const meetingSettings = await getMeetingSettingsGlobal();
|
||||
const pending = bookings.filter((b) => b.status === "pending");
|
||||
const confirmed = bookings.filter((b) => b.status === "confirmed" && new Date(b.date) >= new Date());
|
||||
const past = bookings.filter((b) => new Date(b.date) < new Date() || b.status === "rejected" || b.status === "cancelled");
|
||||
@@ -53,7 +56,7 @@ export default async function KontoOverviewPage() {
|
||||
) : (
|
||||
<div className="mt-4 grid gap-4 sm:grid-cols-2">
|
||||
{confirmed.map((b) => (
|
||||
<BookingCard key={b.id} booking={b} />
|
||||
<BookingCard key={b.id} booking={b} meetingStatus={getBookingMeetingStatus(b, meetingSettings)} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { Metadata } from "next";
|
||||
import { getCurrentCustomer } from "@/lib/auth/customer";
|
||||
import { getCustomerBookings } from "@/lib/booking/queries";
|
||||
import { BookingCard } from "@/components/booking/BookingCard";
|
||||
import { getMeetingSettingsGlobal } from "@/lib/payload/globals";
|
||||
import { getBookingMeetingStatus } from "@/lib/booking/meetingAccess";
|
||||
|
||||
export const metadata: Metadata = { title: "Meine Termine" };
|
||||
|
||||
@@ -10,6 +12,7 @@ export default async function KontoTerminePage() {
|
||||
if (!customer) return null;
|
||||
|
||||
const bookings = await getCustomerBookings(customer.id);
|
||||
const meetingSettings = await getMeetingSettingsGlobal();
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -21,7 +24,7 @@ export default async function KontoTerminePage() {
|
||||
) : (
|
||||
<div className="mt-4 grid gap-4 sm:grid-cols-2">
|
||||
{bookings.map((b) => (
|
||||
<BookingCard key={b.id} booking={b} />
|
||||
<BookingCard key={b.id} booking={b} meetingStatus={getBookingMeetingStatus(b, meetingSettings)} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { JoinForm } from "@/components/meeting/JoinForm";
|
||||
import { OrganicBlob } from "@/components/OrganicBlob";
|
||||
import { getEventForJoin } from "@/lib/payload/content";
|
||||
import { combineDateAndTime } from "@/lib/meeting/status";
|
||||
import { formatBerlinDate, formatBerlinTime } from "@/lib/calendar/timezone";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -21,8 +22,8 @@ export default async function BeitretenPage({ params }: Args) {
|
||||
if (!event || !event.isOnline) notFound();
|
||||
|
||||
const start = combineDateAndTime(event.date, event.startTime);
|
||||
const dateLabel = start.toLocaleDateString("de-DE", { day: "2-digit", month: "long" });
|
||||
const timeLabel = start.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" });
|
||||
const dateLabel = formatBerlinDate(start, { day: "2-digit", month: "long" });
|
||||
const timeLabel = formatBerlinTime(start, { hour: "2-digit", minute: "2-digit" });
|
||||
|
||||
return (
|
||||
<section className="relative flex min-h-[80vh] items-center overflow-hidden py-20">
|
||||
|
||||
@@ -76,7 +76,7 @@ export default async function EventDetailPage({ params }: Args) {
|
||||
const { user } = await payload.auth({ headers: await headers() });
|
||||
// Must check the collection, not just presence — a logged-in customer
|
||||
// (collection "customers") must never be treated as host.
|
||||
const isHost = user?.collection === "users";
|
||||
const isHost = user?.collection === "users" && user.role === "admin";
|
||||
const settings = await payload.findGlobal({ slug: "meeting-settings" });
|
||||
const start = combineDateAndTime(event.date, event.startTime);
|
||||
const end = event.endTime
|
||||
|
||||
@@ -21,7 +21,9 @@ export default async function TerminePage() {
|
||||
const events = await getAllEvents();
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const upcoming = events.filter((e) => new Date(e.date) >= today).reverse();
|
||||
const upcoming = events
|
||||
.filter((e) => new Date(e.date) >= today)
|
||||
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
||||
const past = events.filter((e) => new Date(e.date) < today);
|
||||
const siteUrl = await getSiteUrl();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user