"use client"; import { useMemo, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { CalendarDaysIcon, ChevronRightIcon, PlusIcon } from "lucide-react"; import { Item, ItemContent, ItemHeader, ItemTitle } from "@/components/ui/item"; import { Button } from "@/components/ui/button"; import { CreateSideOpDialog, type SideOpDialogOption } from "./CreateSideOpDialog"; import { parseDateKey } from "@/lib/intelligence/fridayTime"; export interface FridayCalendarMission { id: number; name: string; codeName?: string | null; status: string; startDateTime: string; operationType: string; mapName: string | null; hostName: string | null; isOwn: boolean; } export interface FridayCalendarEntry { fridayIso: string; startIso: string; missions: FridayCalendarMission[]; } interface FridayCalendarProps { entries: FridayCalendarEntry[]; remainingSlots: number; maxSlots: number; maps: SideOpDialogOption[]; campaigns: SideOpDialogOption[]; modLists: SideOpDialogOption[]; } const statusColors: Record = { Concept: "bg-muted text-muted-foreground", Planning: "bg-muted text-muted-foreground", Ready: "bg-blue-500/20 text-blue-400", Scheduled: "bg-amber-500/20 text-amber-400", Active: "bg-green-500/20 text-green-400", Completed: "bg-emerald-500/20 text-emerald-400", Cancelled: "bg-red-500/20 text-red-400", }; function formatFriday(iso: string): string { const { year, month, day } = parseDateKey(iso); return new Date(year, month - 1, day).toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric", }); } function formatTime(iso: string): string { return new Date(iso).toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", }); } export function FridayCalendar({ entries, remainingSlots, maxSlots, maps, campaigns, modLists, }: FridayCalendarProps) { const router = useRouter(); const [scheduleFor, setScheduleFor] = useState(null); const upcoming = useMemo(() => entries, [entries]); return (

Friday Operations

Schedule and manage player-run Friday side operations. Each Friday gets Discord roll-call and RSVP automatically.

0 ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-400" : "border-red-500/30 bg-red-500/10 text-red-400" }`} > {remainingSlots > 0 ? `You can schedule ${remainingSlots} more mission${remainingSlots === 1 ? "" : "s"}` : `Scheduling limit reached (${maxSlots} ahead)`}
{upcoming.map((entry) => { const fridayTaken = entry.missions.some( (m) => m.status !== "Completed" && m.status !== "Cancelled", ); const canSchedule = remainingSlots > 0 && !fridayTaken; return (
{formatFriday(entry.fridayIso)}
{entry.missions.length === 0 ? (

Nothing scheduled yet. Claim this Friday.

) : (
{entry.missions.map((m) => ( {formatTime(m.startDateTime)}
{m.name} {[ m.codeName, m.mapName, m.isOwn ? "yours" : m.hostName ? `hosted by ${m.hostName}` : null, ] .filter((part): part is string => Boolean(part)) .join(" ยท ")}
{m.status} ))}
)}
); })}

Fridays beyond this date are unavailable for scheduling via the web app.

If you want to schedule an op further out than the dates listed here, please contact Jason (Z8MB1E) directly.

{ if (!open) setScheduleFor(null); }} maps={maps} campaigns={campaigns} modLists={modLists} defaultStartDateTime={scheduleFor ?? undefined} onSaved={() => router.refresh()} />
); }