From 71e2e43ed1f0b922cf1a08c28effdc059bc87de0 Mon Sep 17 00:00:00 2001 From: tusharmagar Date: Mon, 16 Mar 2026 20:24:01 +0530 Subject: [PATCH] Enhance onboarding flow to support Google Calendar integration with Composio. Add state management for Google Calendar connection status, loading states, and connection handling. Update UI components to reflect Google Calendar connectivity in onboarding steps. --- .../onboarding/steps/completion-step.tsx | 15 ++- .../steps/connect-accounts-step.tsx | 21 ++++- .../onboarding/use-onboarding-state.ts | 93 ++++++++++++++++++- 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/apps/x/apps/renderer/src/components/onboarding/steps/completion-step.tsx b/apps/x/apps/renderer/src/components/onboarding/steps/completion-step.tsx index 54cb4e15..c01e42ea 100644 --- a/apps/x/apps/renderer/src/components/onboarding/steps/completion-step.tsx +++ b/apps/x/apps/renderer/src/components/onboarding/steps/completion-step.tsx @@ -8,8 +8,8 @@ interface CompletionStepProps { } export function CompletionStep({ state }: CompletionStepProps) { - const { connectedProviders, granolaEnabled, slackEnabled, gmailConnected, handleComplete } = state - const hasConnections = connectedProviders.length > 0 || granolaEnabled || slackEnabled || gmailConnected + const { connectedProviders, granolaEnabled, slackEnabled, gmailConnected, googleCalendarConnected, handleComplete } = state + const hasConnections = connectedProviders.length > 0 || granolaEnabled || slackEnabled || gmailConnected || googleCalendarConnected return (
@@ -76,6 +76,17 @@ export function CompletionStep({ state }: CompletionStepProps) { Gmail (Email) )} + {googleCalendarConnected && ( + + + Google Calendar + + )} {connectedProviders.includes('google') && ( ) : (
- {/* Email / Email & Calendar */} - {(useComposioForGoogle || providers.includes('google')) && ( + {/* Email & Calendar */} + {(useComposioForGoogle || useComposioForGoogleCalendar || providers.includes('google')) && (
- {useComposioForGoogle ? 'Email' : 'Email & Calendar'} + Email & Calendar {useComposioForGoogle ? ( )} + {useComposioForGoogleCalendar && ( + } + iconBg="bg-blue-500/10" + iconColor="text-blue-500" + providerState={{ isConnected: googleCalendarConnected, isLoading: googleCalendarLoading, isConnecting: googleCalendarConnecting }} + onConnect={handleConnectGoogleCalendar} + index={cardIndex++} + /> + )}
)} diff --git a/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts b/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts index b954727c..ac277626 100644 --- a/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts +++ b/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts @@ -74,6 +74,12 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { const [composioApiKeyOpen, setComposioApiKeyOpen] = useState(false) const [composioApiKeyTarget, setComposioApiKeyTarget] = useState<'slack' | 'gmail'>('gmail') + // Composio/Google Calendar state + const [useComposioForGoogleCalendar, setUseComposioForGoogleCalendar] = useState(false) + const [googleCalendarConnected, setGoogleCalendarConnected] = useState(false) + const [googleCalendarLoading, setGoogleCalendarLoading] = useState(true) + const [googleCalendarConnecting, setGoogleCalendarConnecting] = useState(false) + const updateProviderConfig = useCallback( (provider: LlmProviderFlavor, updates: Partial<{ apiKey: string; baseURL: string; model: string; knowledgeGraphModel: string }>) => { setProviderConfigs(prev => ({ @@ -125,8 +131,17 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { console.error('Failed to check composio-for-google flag:', error) } } + async function loadComposioForGoogleCalendarFlag() { + try { + const result = await window.ipc.invoke('composio:use-composio-for-google-calendar', null) + setUseComposioForGoogleCalendar(result.enabled) + } catch (error) { + console.error('Failed to check composio-for-google-calendar flag:', error) + } + } loadProviders() loadComposioForGoogleFlag() + loadComposioForGoogleCalendarFlag() }, [open]) // Load LLM models catalog on open @@ -337,6 +352,47 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { } }, [startGmailConnect]) + // Load Google Calendar connection status (Composio) + const refreshGoogleCalendarStatus = useCallback(async () => { + try { + setGoogleCalendarLoading(true) + const result = await window.ipc.invoke('composio:get-connection-status', { toolkitSlug: 'googlecalendar' }) + setGoogleCalendarConnected(result.isConnected) + } catch (error) { + console.error('Failed to load Google Calendar status:', error) + setGoogleCalendarConnected(false) + } finally { + setGoogleCalendarLoading(false) + } + }, []) + + // Connect to Google Calendar via Composio + const startGoogleCalendarConnect = useCallback(async () => { + try { + setGoogleCalendarConnecting(true) + const result = await window.ipc.invoke('composio:initiate-connection', { toolkitSlug: 'googlecalendar' }) + if (!result.success) { + toast.error(result.error || 'Failed to connect to Google Calendar') + setGoogleCalendarConnecting(false) + } + } catch (error) { + console.error('Failed to connect to Google Calendar:', error) + toast.error('Failed to connect to Google Calendar') + setGoogleCalendarConnecting(false) + } + }, []) + + // Handle Google Calendar connect button click + const handleConnectGoogleCalendar = useCallback(async () => { + const configResult = await window.ipc.invoke('composio:is-configured', null) + if (!configResult.configured) { + setComposioApiKeyTarget('gmail') + setComposioApiKeyOpen(true) + return + } + await startGoogleCalendarConnect() + }, [startGoogleCalendarConnect]) + // New step flow: // Rowboat path: 0 (welcome) → 2 (connect) → 3 (done) // BYOK path: 0 (welcome) → 1 (llm setup) → 2 (connect) → 3 (done) @@ -414,6 +470,11 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { refreshGmailStatus() } + // Refresh Google Calendar Composio status if enabled + if (useComposioForGoogleCalendar) { + refreshGoogleCalendarStatus() + } + if (providers.length === 0) return const newStates: Record = {} @@ -440,7 +501,7 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { } setProviderStates(newStates) - }, [providers, refreshGranolaConfig, refreshSlackConfig, refreshGmailStatus, useComposioForGoogle]) + }, [providers, refreshGranolaConfig, refreshSlackConfig, refreshGmailStatus, useComposioForGoogle, refreshGoogleCalendarStatus, useComposioForGoogleCalendar]) // Refresh statuses when modal opens or providers list changes useEffect(() => { @@ -480,12 +541,16 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { const cleanup = window.ipc.on('oauth:didConnect', async (event) => { if (event.provider === 'rowboat' && event.success) { - // Re-check the composio-for-google flag now that the account is connected + // Re-check composio flags now that the account is connected try { - const result = await window.ipc.invoke('composio:use-composio-for-google', null) - setUseComposioForGoogle(result.enabled) + const [googleResult, calendarResult] = await Promise.all([ + window.ipc.invoke('composio:use-composio-for-google', null), + window.ipc.invoke('composio:use-composio-for-google-calendar', null), + ]) + setUseComposioForGoogle(googleResult.enabled) + setUseComposioForGoogleCalendar(calendarResult.enabled) } catch (error) { - console.error('Failed to re-check composio-for-google flag:', error) + console.error('Failed to re-check composio flags:', error) } setCurrentStep(2) // Go to Connect Accounts } @@ -519,6 +584,17 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { toast.error(error || 'Failed to connect to Gmail') } } + + if (toolkitSlug === 'googlecalendar') { + setGoogleCalendarConnected(success) + setGoogleCalendarConnecting(false) + + if (success) { + toast.success('Connected to Google Calendar') + } else { + toast.error(error || 'Failed to connect to Google Calendar') + } + } }) return cleanup @@ -650,6 +726,13 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { handleConnectGmail, handleComposioApiKeySubmit, + // Composio/Google Calendar state + useComposioForGoogleCalendar, + googleCalendarConnected, + googleCalendarLoading, + googleCalendarConnecting, + handleConnectGoogleCalendar, + // Navigation handleNext, handleBack,