"use client" import { useEffect, useState } from "react" import Link from "next/link" import FullCalendar from "@fullcalendar/react" import dayGridPlugin from "@fullcalendar/daygrid" import timeGridPlugin from "@fullcalendar/timegrid" import listPlugin from "@fullcalendar/list" import interactionPlugin from "@fullcalendar/interaction" import type { EventClickArg, EventDropArg } from "@fullcalendar/core" import { toast } from "sonner" import { CreditCard, FileText, ClipboardList, X, CalendarDays, Copy, Check, ArrowRight } from "lucide-react" import "./calendar.css" type EventType = "rent" | "lease" | "inspection" export interface CalEvent { id: string title: string start: string allDay: boolean backgroundColor: string borderColor: string editable: boolean extendedProps: { type: EventType entityId: string status?: string subtitle: string href: string } } const TYPE_META: Record = { rent: { label: "Rent", color: "#6366f1", Icon: CreditCard }, lease: { label: "Leases", color: "#f59e0b", Icon: FileText }, inspection: { label: "Inspections", color: "#14b8a6", Icon: ClipboardList }, } export function CalendarClient({ events, canWrite, subscribeUrl, }: { events: CalEvent[] canWrite: boolean subscribeUrl: string }) { const [mounted, setMounted] = useState(false) const [active, setActive] = useState>({ rent: true, lease: true, inspection: true }) const [selected, setSelected] = useState<(CalEvent["extendedProps"] & { title: string; start: string }) | null>(null) const [showSubscribe, setShowSubscribe] = useState(false) useEffect(() => setMounted(true), []) const shown = events.filter((e) => active[e.extendedProps.type]) function toggle(t: EventType) { setActive((a) => ({ ...a, [t]: !a[t] })) } function onEventClick(info: EventClickArg) { info.jsEvent.preventDefault() const p = info.event.extendedProps as CalEvent["extendedProps"] setSelected({ ...p, title: info.event.title, start: info.event.startStr }) } async function onEventDrop(info: EventDropArg) { const { type, entityId } = info.event.extendedProps as CalEvent["extendedProps"] if (!canWrite || type === "lease") { info.revert() toast.error(type === "lease" ? "Lease end dates can't be moved here" : "You don't have permission to reschedule") return } const newDate = info.event.startStr.slice(0, 10) const endpoint = type === "rent" ? `/api/rent/${entityId}` : `/api/inspections/${entityId}` const body = type === "rent" ? { due_date: newDate } : { date: newDate } try { const res = await fetch(endpoint, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }) if (res.ok) toast.success(`Rescheduled to ${newDate}`) else { info.revert() toast.error("Couldn't reschedule") } } catch { info.revert() toast.error("Network error") } } return (
{/* Toolbar: filters + subscribe */}
{(Object.keys(TYPE_META) as EventType[]).map((t) => { const m = TYPE_META[t] const on = active[t] return ( ) })}
{/* Calendar */}
{mounted ? ( ) : (
Loading calendar…
)}
{selected && setSelected(null)} />} {showSubscribe && setShowSubscribe(false)} />}
) } function EventDetail({ ev, onClose }: { ev: CalEvent["extendedProps"] & { title: string; start: string }; onClose: () => void }) { const m = TYPE_META[ev.type] const dateLabel = new Date(ev.start + "T12:00:00").toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" }) return (
e.stopPropagation()}>

{ev.title}

{m.label}

Date
{dateLabel}
{ev.subtitle &&
Details
{ev.subtitle}
} {ev.status &&
Status
{ev.status}
}
Open {m.label.toLowerCase()}
) } function SubscribeModal({ url, onClose }: { url: string; onClose: () => void }) { const [copied, setCopied] = useState(false) const webcal = url.replace(/^https?:\/\//, "webcal://") return (
e.stopPropagation()}>

Subscribe to your calendar

One-way sync — new rent, lease, and inspection dates appear automatically in your calendar app.

{url ? ( <>
e.currentTarget.select()} />

Google Calendar: Settings → Add calendar → From URL → paste the link.

Apple Calendar: File → New Calendar Subscription → paste, or open this webcal link.

Outlook: Add calendar → Subscribe from web → paste the link.

Keep this link private — anyone with it can view your dates.

Download .ics
) : (

Your feed link isn't available yet. Refresh the page and try again.

)}
) }