"use client"; import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { ClockIcon, CornerDownLeftIcon, GlobeIcon, MapPinIcon, Pen, UsersIcon } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; import type { ExtraField } from "@/atoms/chat/hitl-edit-panel.atom"; import { openHitlEditPanelAtom } from "@/atoms/chat/hitl-edit-panel.atom"; import { PlateEditor } from "@/components/editor/plate-editor"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GoogleCalendarAccount { id: number; name: string; auth_expired?: boolean; } interface CalendarEntry { id: string; summary: string; primary?: boolean; } interface InterruptResult { __interrupt__: true; __decided__?: "approve" | "reject" | "edit"; __completed__?: boolean; action_requests: Array<{ name: string; args: Record; }>; review_configs: Array<{ action_name: string; allowed_decisions: Array<"approve" | "edit" | "reject">; }>; context?: { accounts?: GoogleCalendarAccount[]; calendars?: CalendarEntry[]; timezone?: string; error?: string; }; } interface SuccessResult { status: "success"; event_id: string; html_link?: string; message?: string; } interface ErrorResult { status: "error"; message: string; } interface AuthErrorResult { status: "auth_error"; message: string; connector_type?: string; } interface InsufficientPermissionsResult { status: "insufficient_permissions"; connector_id: number; message: string; } type CreateCalendarEventResult = | InterruptResult | SuccessResult | ErrorResult | InsufficientPermissionsResult | AuthErrorResult; function isInterruptResult(result: unknown): result is InterruptResult { return ( typeof result === "object" && result !== null && "__interrupt__" in result && (result as InterruptResult).__interrupt__ === true ); } function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && result !== null && "status" in result && (result as ErrorResult).status === "error" ); } function isAuthErrorResult(result: unknown): result is AuthErrorResult { return ( typeof result === "object" && result !== null && "status" in result && (result as AuthErrorResult).status === "auth_error" ); } function isInsufficientPermissionsResult(result: unknown): result is InsufficientPermissionsResult { return ( typeof result === "object" && result !== null && "status" in result && (result as InsufficientPermissionsResult).status === "insufficient_permissions" ); } function formatDateTime(iso: string): string { try { return new Date(iso).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short", }); } catch { return iso; } } function ApprovalCard({ args, interruptData, onDecision, }: { args: { summary: string; start_datetime: string; end_datetime: string; description?: string; location?: string; attendees?: string[]; }; interruptData: InterruptResult; onDecision: (decision: { type: "approve" | "reject" | "edit"; message?: string; edited_action?: { name: string; args: Record }; }) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); const [isPanelOpen, setIsPanelOpen] = useState(false); const [wasEdited, setWasEdited] = useState(false); const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom); const [pendingEdits, setPendingEdits] = useState<{ summary: string; description: string; start_datetime: string; end_datetime: string; location: string; attendees: string; } | null>(null); const accounts = interruptData.context?.accounts ?? []; const validAccounts = accounts.filter((a) => !a.auth_expired); const expiredAccounts = accounts.filter((a) => a.auth_expired); const calendars = interruptData.context?.calendars ?? []; const timezone = interruptData.context?.timezone ?? ""; const defaultAccountId = useMemo(() => { if (validAccounts.length === 1) return String(validAccounts[0].id); return ""; }, [validAccounts]); const defaultCalendarId = useMemo(() => { const primary = calendars.find((c) => c.primary); if (primary) return primary.id; if (calendars.length === 1) return calendars[0].id; return ""; }, [calendars]); const [selectedAccountId, setSelectedAccountId] = useState(defaultAccountId); const [selectedCalendarId, setSelectedCalendarId] = useState(defaultCalendarId); useEffect(() => { if (defaultAccountId && !selectedAccountId) setSelectedAccountId(defaultAccountId); }, [defaultAccountId, selectedAccountId]); useEffect(() => { if (defaultCalendarId && !selectedCalendarId) setSelectedCalendarId(defaultCalendarId); }, [defaultCalendarId, selectedCalendarId]); const reviewConfig = interruptData.review_configs[0]; const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"]; const canEdit = allowedDecisions.includes("edit"); const canApprove = !!selectedAccountId && !!selectedCalendarId && !!(pendingEdits?.summary ?? args.summary)?.trim(); const handleApprove = useCallback(() => { if (phase !== "pending" || isPanelOpen || !canApprove) return; if (!allowedDecisions.includes("approve")) return; const isEdited = pendingEdits !== null; setWasEdited(isEdited); setProcessing(); const finalArgs: Record = { ...args, connector_id: selectedAccountId ? Number(selectedAccountId) : null, calendar_id: selectedCalendarId || null, }; if (pendingEdits) { finalArgs.summary = pendingEdits.summary; finalArgs.description = pendingEdits.description; if (pendingEdits.start_datetime) finalArgs.start_datetime = pendingEdits.start_datetime; if (pendingEdits.end_datetime) finalArgs.end_datetime = pendingEdits.end_datetime; if (pendingEdits.location !== undefined) finalArgs.location = pendingEdits.location; if (pendingEdits.attendees !== undefined) { finalArgs.attendees = pendingEdits.attendees .split(",") .map((e) => e.trim()) .filter(Boolean); } } onDecision({ type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: finalArgs, }, }); }, [ phase, isPanelOpen, canApprove, allowedDecisions, setProcessing, onDecision, interruptData, args, selectedAccountId, selectedCalendarId, pendingEdits, ]); useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey) { handleApprove(); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [handleApprove]); const attendeesList = (args.attendees as string[]) ?? []; const displayAttendees = pendingEdits?.attendees ? pendingEdits.attendees .split(",") .map((e) => e.trim()) .filter(Boolean) : attendeesList; return (
{/* Header */}

{phase === "rejected" ? "Calendar Event Rejected" : phase === "processing" || phase === "complete" ? "Calendar Event Approved" : "Create Calendar Event"}

{phase === "processing" ? ( ) : phase === "complete" ? (

{wasEdited ? "Event created with your changes" : "Event created"}

) : phase === "rejected" ? (

Event creation was cancelled

) : (

Requires your approval to proceed

)}
{phase === "pending" && canEdit && ( )}
{/* Context section - pending with real dropdowns */} {phase === "pending" && interruptData.context && ( <>
{interruptData.context.error ? (

{interruptData.context.error}

) : ( <> {accounts.length > 0 && (

Google Calendar Account *

)} {calendars.length > 0 && (

Calendar *

)} {timezone && (

Timezone

{timezone}
)} )}
)} {/* Content preview - visible in ALL phases */}
{(pendingEdits?.summary ?? args.summary) && (

{pendingEdits?.summary ?? args.summary}

)} {((pendingEdits?.start_datetime ?? args.start_datetime) || (pendingEdits?.end_datetime ?? args.end_datetime)) && (
{(pendingEdits?.start_datetime ?? args.start_datetime) ? formatDateTime(pendingEdits?.start_datetime ?? args.start_datetime) : ""} {(pendingEdits?.start_datetime ?? args.start_datetime) && (pendingEdits?.end_datetime ?? args.end_datetime) ? " — " : ""} {(pendingEdits?.end_datetime ?? args.end_datetime) ? formatDateTime(pendingEdits?.end_datetime ?? args.end_datetime) : ""}
)} {(pendingEdits?.location ?? args.location) && (
{pendingEdits?.location ?? args.location}
)} {displayAttendees.length > 0 && (
{displayAttendees.join(", ")}
)} {(pendingEdits?.description ?? args.description) && (
)}
{/* Action buttons - pending only */} {phase === "pending" && ( <>
{allowedDecisions.includes("approve") && ( )} {allowedDecisions.includes("reject") && ( )}
)}
); } function ErrorCard({ result }: { result: ErrorResult }) { return (

Failed to create calendar event

{result.message}

); } function AuthErrorCard({ result }: { result: AuthErrorResult }) { return (

Google Calendar authentication expired

{result.message}

); } function InsufficientPermissionsCard({ result }: { result: InsufficientPermissionsResult }) { return (

Additional Google Calendar permissions required

{result.message}

); } function SuccessCard({ result }: { result: SuccessResult }) { return (

{result.message || "Calendar event created successfully"}

{result.html_link && ( )}
); } export const CreateCalendarEventToolUI = ({ args, result, }: ToolCallMessagePartProps< { summary: string; start_datetime: string; end_datetime: string; description?: string; location?: string; attendees?: string[]; }, CreateCalendarEventResult >) => { if (!result) return null; if (isInterruptResult(result)) { return ( { window.dispatchEvent( new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) ); }} /> ); } if ( typeof result === "object" && result !== null && "status" in result && (result as { status: string }).status === "rejected" ) { return null; } if (isAuthErrorResult(result)) return ; if (isInsufficientPermissionsResult(result)) return ; if (isErrorResult(result)) return ; return ; };