// src/components/onboarding/connect-step/connect-step.tsx 'use client' import { useState, useEffect } from 'react' import { Github, Wallet, CheckCircle2, AlertTriangle, Loader2, ExternalLink, ChevronDown } from 'lucide-react' import { useTheme } from 'next-themes' import { SignIn } from '@clerk/nextjs' import { useOnboarding } from '@/components/onboarding/useOnboarding' import { useAuthStatus } from '@/hooks/useAuthStatus' import { useRepoData } from '@/hooks/useRepoData' import { Button } from '@workspace/ui/components/button' import { Card, CardContent } from '@workspace/ui/components/card' import { Input } from '@workspace/ui/components/input' import { Label } from '@workspace/ui/components/label' import { Alert, AlertDescription } from '@workspace/ui/components/alert' import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@workspace/ui/components/collapsible' import { toast } from 'sonner' import { GitHubBackendAuth } from '@/components/GitHubBackendAuth' import { AVAILABLE_TEMPLATES, type TemplateDetail } from '@/constants/templates' interface Repository { id: string | number full_name: string html_url?: string description?: string } export function ConnectStep() { const { nextStep, setFormData, formData } = useOnboarding() const { resolvedTheme } = useTheme() const [mounted, setMounted] = useState(false) // Repository vs Template selection const [selectedRepo, setSelectedRepo] = useState(formData.githubRepo || '') const [selectedTemplate, setSelectedTemplate] = useState( formData.template || undefined ) const [projectName, setProjectName] = useState(formData.projectName || '') const [isImportMode, setIsImportMode] = useState(true) // Auth status and warning display const [showAuthWarning, setShowAuthWarning] = useState(false) // Auth status hook const { clerk, wallet, backend, isFullyAuthenticated, isReady, missing, progress, connectWallet, checkGithubBackendAuth } = useAuthStatus() // Repository data const { repoData: repositories, isLoading: isLoadingRepos } = useRepoData('') // Handle hydration mismatch by waiting for mount useEffect(() => { setMounted(true) }, []) // Auto-hide auth warning when fully authenticated useEffect(() => { if (isFullyAuthenticated) { setShowAuthWarning(false) } }, [isFullyAuthenticated]) // Handle repository selection const handleRepoSelect = (repo: string) => { setSelectedRepo(repo) setSelectedTemplate(undefined) setFormData({ githubRepo: repo, template: undefined, deploymentMode: 'repository', projectName }) } // Handle template selection const handleTemplateSelect = (template: TemplateDetail) => { setSelectedTemplate(template) setSelectedRepo('') // Auto-fill project name if empty if (!projectName) { const suggestedName = `my-${template.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}` setProjectName(suggestedName) } setFormData({ template: template, githubRepo: '', deploymentMode: 'template', projectName: projectName || `my-${template.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}` }) } // Handle mode toggle between import and template const toggleMode = (mode: 'import' | 'template') => { setIsImportMode(mode === 'import') // Clear selections when switching modes if (mode === 'import') { setSelectedTemplate(undefined) setFormData({ template: undefined, deploymentMode: 'repository', projectName }) } else { setSelectedRepo('') setFormData({ githubRepo: '', deploymentMode: 'template', projectName }) } } // Handle project name change const handleProjectNameChange = (value: string) => { setProjectName(value) setFormData({ projectName: value }) } // Handle wallet connection const handleConnectWallet = async () => { try { await connectWallet() toast.success('Wallet connected successfully') } catch (error) { console.error('Wallet connection failed:', error) toast.error('Failed to connect wallet') } } // Handle GitHub backend auth status change const handleGithubAuthChange = async (isAuthenticated: boolean) => { await checkGithubBackendAuth() if (isAuthenticated) { toast.success('GitHub backend authentication completed!') } } // Handle next step const handleNext = () => { if (!isFullyAuthenticated) { toast.error('Please complete all authentication steps first') setShowAuthWarning(true) return } if (isImportMode && !selectedRepo) { toast.error('Please select a repository to continue') return } if (!isImportMode && (!selectedTemplate || !projectName.trim())) { toast.error('Please select a template and enter a project name') return } // For repository import, project name is optional but we'll use repo name as fallback const finalProjectName = projectName.trim() || (isImportMode ? selectedRepo.split('/')[1] : '') // Set final form data and proceed setFormData({ deploymentMode: isImportMode ? 'repository' : 'template', githubRepo: isImportMode ? selectedRepo : '', template: !isImportMode ? selectedTemplate : undefined, projectName: finalProjectName }) nextStep() } // Don't render UI until after mount to prevent hydration mismatch if (!mounted || !isReady) { return (

Loading authentication status...

) } // Determine if dark mode is active const isDarkMode = resolvedTheme === 'dark' return (
{/* Header */}

Connect

Connect and import a GitHub repo or start from a template

{/* GitHub Account Selector - Only show if multiple accounts */} {clerk.user?.externalAccounts && clerk.user.externalAccounts.length > 1 && (
{clerk.user?.externalAccounts?.find(acc => acc.provider === 'github')?.username || 'git-account'}
)}
{/* Authentication Warning - Only show if not fully authenticated */} {!isFullyAuthenticated && ( Authentication required to continue ({progress.completed}/{progress.total} complete) {/* Authentication steps - same as before but in collapsible */} {missing.clerkSignIn && (
Sign in with Clerk
)} {missing.clerkGithub && !missing.clerkSignIn && (
Connect GitHub Account
)} {missing.walletConnection && (
Connect Wallet
)} {missing.githubBackendSync && !missing.walletConnection && !missing.clerkGithub && (
Sync GitHub Access
)}
)} {/* Mode Selection Tabs */}
{/* Content Area */} {isImportMode ? ( /* Repository Selection */
{isLoadingRepos ? (
Loading repositories...
) : !repositories || repositories.length === 0 ? (
No repositories found. Make sure your GitHub account has repositories.
) : ( <>
{repositories.map((repo: Repository) => (
handleRepoSelect(repo.full_name)} >
{repo.full_name}
{repo.description && (
{repo.description}
)}
{selectedRepo === repo.full_name && ( )}
))}
{/* Project Name Input for Repository Import */} {selectedRepo && (
handleProjectNameChange(e.target.value)} placeholder="my-project-name" className="w-full" />

This will be the name of your deployment project

)} )}
) : ( /* Template Selection */
{AVAILABLE_TEMPLATES.filter(t => !t.isComingSoon).map((template) => (
handleTemplateSelect(template)} > {/* Template Icon */}
{template.icon === 'web' ? 'PWA' : template.icon === 'nextjs' ? 'N' : 'IMG'}
{/* Template Info */}
{template.name}
{template.repoFullName}
{/* Selection Indicator */} {selectedTemplate?.id === template.id && ( )}
))} {/* Project Name Input for Templates */} {selectedTemplate && (
handleProjectNameChange(e.target.value)} placeholder="new-repository-name" className="w-full" />

This will be the name of your new GitHub repository

)}
)} {/* Navigation */}
) }