"use client"; import { Box, VStack, HStack, Text, Button, Spinner, Badge, Icon, Flex, } from "@chakra-ui/react"; import React from "react"; import { FaUsers, FaClock, FaCalendarAlt, FaPlus } from "react-icons/fa"; import { LuX } from "react-icons/lu"; import type { components } from "../reflector-api"; import { useRoomActiveMeetings, useRoomJoinMeeting, useMeetingDeactivate, useRoomGetByName, } from "../lib/apiHooks"; import { useRouter } from "next/navigation"; import Link from "next/link"; import { formatDateTime, formatCountdown, formatStartedAgo, } from "../lib/timeUtils"; import MinimalHeader from "../components/MinimalHeader"; // Meeting join settings const EARLY_JOIN_MINUTES = 5; // Allow joining 5 minutes before meeting starts type Meeting = components["schemas"]["Meeting"]; interface MeetingSelectionProps { roomName: string; isOwner: boolean; isSharedRoom: boolean; authLoading: boolean; onMeetingSelect: (meeting: Meeting) => void; onCreateUnscheduled: () => void; } export default function MeetingSelection({ roomName, isOwner, isSharedRoom, authLoading, onMeetingSelect, onCreateUnscheduled, }: MeetingSelectionProps) { const router = useRouter(); // Use React Query hooks for data fetching const roomQuery = useRoomGetByName(roomName); const activeMeetingsQuery = useRoomActiveMeetings(roomName); const joinMeetingMutation = useRoomJoinMeeting(); const deactivateMeetingMutation = useMeetingDeactivate(); const room = roomQuery.data; const allMeetings = activeMeetingsQuery.data || []; // Separate current ongoing meetings from upcoming meetings (created by worker, within 5 minutes) const now = new Date(); const currentMeetings = allMeetings.filter((meeting) => { const startTime = new Date(meeting.start_date); // Meeting is ongoing if it started and participants have joined or it's been running for a while return ( meeting.num_clients > 0 || now.getTime() - startTime.getTime() > 60000 ); // 1 minute threshold }); const upcomingMeetings = allMeetings.filter((meeting) => { const startTime = new Date(meeting.start_date); const minutesUntilStart = Math.floor( (startTime.getTime() - now.getTime()) / (1000 * 60), ); // Show meetings that start within 5 minutes and haven't started yet return ( minutesUntilStart <= EARLY_JOIN_MINUTES && minutesUntilStart > 0 && meeting.num_clients === 0 ); }); const loading = roomQuery.isLoading || activeMeetingsQuery.isLoading; const error = roomQuery.error || activeMeetingsQuery.error; const handleJoinMeeting = async (meetingId: string) => { try { const meeting = await joinMeetingMutation.mutateAsync({ params: { path: { room_name: roomName, meeting_id: meetingId, }, }, }); onMeetingSelect(meeting); } catch (err) { console.error("Failed to join meeting:", err); // Handle error appropriately since we don't have setError anymore } }; const handleJoinUpcoming = async (meeting: Meeting) => { // Join the upcoming meeting and navigate to local meeting page try { const joinedMeeting = await joinMeetingMutation.mutateAsync({ params: { path: { room_name: roomName, meeting_id: meeting.id, }, }, }); onMeetingSelect(joinedMeeting); } catch (err) { console.error("Failed to join upcoming meeting:", err); } }; const handleJoinDirect = (meeting: Meeting) => { // Navigate to local meeting page instead of external URL onMeetingSelect(meeting); }; const handleEndMeeting = async (meetingId: string) => { try { await deactivateMeetingMutation.mutateAsync({ params: { path: { meeting_id: meetingId, }, }, }); } catch (err) { console.error("Failed to end meeting:", err); } }; if (loading) { return ( Loading meetings... ); } if (error) { return ( Error {"Failed to load meetings"} ); } // Generate display name for room const displayName = room?.name || roomName; const roomTitle = displayName.endsWith("'s") || displayName.endsWith("s") ? `${displayName} Room` : `${displayName}'s Room`; const handleLeaveMeeting = () => { router.push("/"); }; return ( {/* Current Ongoing Meetings - BIG DISPLAY */} {currentMeetings.length > 0 && ( Live Meeting{currentMeetings.length > 1 ? "s" : ""} {currentMeetings.map((meeting) => ( {(meeting.calendar_metadata as any)?.title || "Live Meeting"} LIVE {isOwner && (meeting.calendar_metadata as any)?.description && ( {(meeting.calendar_metadata as any).description} )} {meeting.num_clients} participants Started {formatStartedAgo(meeting.start_date)} {isOwner && (meeting.calendar_metadata as any)?.attendees && ( {(meeting.calendar_metadata as any).attendees .slice(0, 4) .map((attendee: any, idx: number) => ( {attendee.name || attendee.email} ))} {(meeting.calendar_metadata as any).attendees.length > 4 && ( + {(meeting.calendar_metadata as any).attendees .length - 4}{" "} more )} )} {isOwner && ( )} ))} )} {/* Upcoming Meetings - SMALLER ASIDE DISPLAY */} {upcomingMeetings.length > 0 && ( Starting Soon {upcomingMeetings.map((meeting) => { const now = new Date(); const startTime = new Date(meeting.start_date); const minutesUntilStart = Math.floor( (startTime.getTime() - now.getTime()) / (1000 * 60), ); return ( {(meeting.calendar_metadata as any)?.title || "Upcoming Meeting"} in {minutesUntilStart} minute {minutesUntilStart !== 1 ? "s" : ""} Starts: {formatDateTime(meeting.start_date)} ); })} )} {/* Create Unscheduled Meeting - Only for room owners or shared rooms */} {(isOwner || isSharedRoom) && ( Start a Quick Meeting Jump into a meeting room right away )} {/* Message for non-owners of private rooms - only show when auth is not loading */} {!authLoading && !isOwner && !isSharedRoom && ( Only the room owner can create unscheduled meetings in this private room. )} ); }