import { VStack, HStack, Field, Input, Select, Checkbox, Button, Text, Badge, createListCollection, Spinner, Box, IconButton, } from "@chakra-ui/react"; import { useState, useEffect, useRef } from "react"; import { LuRefreshCw, LuCopy, LuCheck } from "react-icons/lu"; import { FaCheckCircle, FaExclamationCircle } from "react-icons/fa"; import { useRoomIcsSync, useRoomIcsStatus } from "../../../lib/apiHooks"; import { toaster } from "../../../components/ui/toaster"; import { roomAbsoluteUrl } from "../../../lib/routesClient"; import { assertExists, assertExistsAndNonEmptyString, NonEmptyString, parseNonEmptyString, } from "../../../lib/utils"; interface ICSSettingsProps { roomName: NonEmptyString | null; icsUrl?: string; icsEnabled?: boolean; icsFetchInterval?: number; icsLastSync?: string; icsLastEtag?: string; onChange: (settings: Partial) => void; isOwner?: boolean; isEditing?: boolean; } export interface ICSSettingsData { ics_url: string; ics_enabled: boolean; ics_fetch_interval: number; } const fetchIntervalOptions = [ { label: "1 minute", value: "1" }, { label: "5 minutes", value: "5" }, { label: "10 minutes", value: "10" }, { label: "30 minutes", value: "30" }, { label: "1 hour", value: "60" }, ]; export default function ICSSettings({ roomName, icsUrl = "", icsEnabled = false, icsFetchInterval = 5, icsLastSync, icsLastEtag, onChange, isOwner = true, isEditing = false, }: ICSSettingsProps) { const [syncStatus, setSyncStatus] = useState< "idle" | "syncing" | "success" | "error" >("idle"); const [syncMessage, setSyncMessage] = useState(""); const [syncResult, setSyncResult] = useState<{ eventsFound: number; totalEvents: number; eventsCreated: number; eventsUpdated: number; } | null>(null); const [justCopied, setJustCopied] = useState(false); const roomUrlInputRef = useRef(null); const syncMutation = useRoomIcsSync(); const fetchIntervalCollection = createListCollection({ items: fetchIntervalOptions, }); const handleCopyRoomUrl = async () => { try { await navigator.clipboard.writeText( roomAbsoluteUrl(assertExists(roomName)), ); setJustCopied(true); toaster .create({ placement: "top", duration: 3000, render: ({ dismiss }) => ( Room URL copied to clipboard! ), }) .then(() => {}); setTimeout(() => { setJustCopied(false); }, 2000); } catch (err) { console.error("Failed to copy room url:", err); } }; const handleRoomUrlClick = () => { if (roomUrlInputRef.current) { roomUrlInputRef.current.select(); handleCopyRoomUrl().then(() => {}); } }; // Clear sync results when dialog closes useEffect(() => { if (!isEditing) { setSyncStatus("idle"); setSyncResult(null); setSyncMessage(""); } }, [isEditing]); const handleForceSync = async () => { if (!roomName || !isEditing) return; // Clear previous results setSyncStatus("syncing"); setSyncResult(null); setSyncMessage(""); try { const result = await syncMutation.mutateAsync({ params: { path: { room_name: roomName }, }, }); if (result.status === "success" || result.status === "unchanged") { setSyncStatus("success"); setSyncResult({ eventsFound: result.events_found || 0, totalEvents: result.total_events || 0, eventsCreated: result.events_created || 0, eventsUpdated: result.events_updated || 0, }); } else { setSyncStatus("error"); setSyncMessage(result.error || "Sync failed"); } } catch (err: any) { setSyncStatus("error"); setSyncMessage(err.body?.detail || "Failed to force sync calendar"); } }; if (!isOwner) { return null; // ICS settings only visible to room owner } return ( onChange({ ics_enabled: !!e.checked })} > Enable ICS calendar sync {icsEnabled && ( <> Room URL To enable Reflector to recognize your calendar events as meetings, add this URL as the location in your calendar events {roomName ? ( {justCopied ? : } ) : null} ICS Calendar URL onChange({ ics_url: e.target.value })} /> Enter the ICS URL from Google Calendar, Outlook, or other calendar services Sync Interval { const value = parseInt(details.value[0]); onChange({ ics_fetch_interval: value }); }} > {fetchIntervalOptions.map((option) => ( {option.label} ))} How often to check for calendar updates {icsUrl && isEditing && roomName && ( )} {syncResult && syncStatus === "success" && ( Sync completed {syncResult.totalEvents} events downloaded,{" "} {syncResult.eventsFound} match this room {(syncResult.eventsCreated > 0 || syncResult.eventsUpdated > 0) && ( {syncResult.eventsCreated} created,{" "} {syncResult.eventsUpdated} updated )} )} {syncMessage && ( {syncMessage} )} {icsLastSync && ( Last sync: {new Date(icsLastSync).toLocaleString()} {icsLastEtag && ( ETag: {icsLastEtag.slice(0, 8)}... )} )} )} ); }