This commit is contained in:
Your Name 2025-08-21 16:20:52 +00:00
parent ace6f04064
commit 7cb0a0048b
9 changed files with 903 additions and 452 deletions

View File

@ -1,15 +1,15 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { LaconicMark } from '@/components/assets/laconic-mark'
import { useRouter } from 'next/navigation'
import { X } from 'lucide-react'
import { useTheme } from 'next-themes'
import { useOnboarding } from '@/components/onboarding/store'
import { ConnectStep } from '@/components/onboarding/connect-step/connect-step'
import { ConfigureStep } from '@/components/onboarding/configure-step/configure-step' import { ConfigureStep } from '@/components/onboarding/configure-step/configure-step'
import { ConnectStep } from '@/components/onboarding/connect-step/connect-step'
import { DeployStep } from '@/components/onboarding/deploy-step/deploy-step' import { DeployStep } from '@/components/onboarding/deploy-step/deploy-step'
import { SuccessStep } from '@/components/onboarding/success-step/success-step' import { SuccessStep } from '@/components/onboarding/success-step/success-step'
import { LaconicMark } from '@/components/assets/laconic-mark' import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { X } from 'lucide-react'
import { useTheme } from 'next-themes'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
/** /**
* Parent component for the onboarding flow * Parent component for the onboarding flow
@ -55,13 +55,21 @@ export default function CreateProjectFlow() {
return ( return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4"> <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
{/* Fixed dimensions modal container */} {/* Fixed dimensions modal container */}
<div className={`${isDarkMode ? 'bg-black' : 'bg-white'} rounded-xl overflow-hidden flex shadow-2xl w-[1000px] h-[620px]`}> <div
className={`${isDarkMode ? 'bg-black' : 'bg-white'} rounded-xl overflow-hidden flex shadow-2xl w-[1000px] h-[620px]`}
>
{/* Left sidebar with fixed width */} {/* Left sidebar with fixed width */}
<div className={`w-[280px] min-w-[280px] ${isDarkMode ? 'bg-zinc-900' : 'bg-zinc-50'} p-8 relative overflow-hidden border-r ${isDarkMode ? 'border-zinc-800' : 'border-zinc-200'}`}> <div
className={`w-[280px] min-w-[280px] ${isDarkMode ? 'bg-zinc-900' : 'bg-zinc-50'} p-8 relative overflow-hidden border-r ${isDarkMode ? 'border-zinc-800' : 'border-zinc-200'}`}
>
{/* Laconic logo */} {/* Laconic logo */}
<div className="flex items-center gap-2 mb-12"> <div className="flex items-center gap-2 mb-12">
<LaconicMark className="h-8 w-8" /> <LaconicMark className="h-8 w-8" />
<span className={`${isDarkMode ? 'text-white' : 'text-zinc-900'} text-xl font-bold`}>LACONIC</span> <span
className={`${isDarkMode ? 'text-white' : 'text-zinc-900'} text-xl font-bold`}
>
LACONIC
</span>
</div> </div>
{/* Steps - clickable */} {/* Steps - clickable */}
@ -72,29 +80,71 @@ export default function CreateProjectFlow() {
onClick={() => navigateToStep('connect')} onClick={() => navigateToStep('connect')}
> >
<div className="mr-4"> <div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'connect' <div
? (isDarkMode ? 'bg-white' : 'bg-black') className={`w-10 h-10 rounded-lg ${
: (isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200') currentStep === 'connect'
} flex items-center justify-center`}> ? isDarkMode
<svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" className={currentStep === 'connect' ? 'bg-white'
? (isDarkMode ? 'text-black' : 'text-white') : 'bg-black'
: (isDarkMode ? 'text-zinc-400' : 'text-zinc-600') : isDarkMode
}> ? 'bg-zinc-800'
: 'bg-zinc-200'
} flex items-center justify-center`}
>
<svg
viewBox="0 0 24 24"
width="20"
height="20"
stroke="currentColor"
strokeWidth="2"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
className={
currentStep === 'connect'
? isDarkMode
? 'text-black'
: 'text-white'
: isDarkMode
? 'text-zinc-400'
: 'text-zinc-600'
}
>
<path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path> <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
<line x1="8" y1="12" x2="16" y2="12"></line> <line x1="8" y1="12" x2="16" y2="12"></line>
</svg> </svg>
</div> </div>
<div className={`w-px h-10 ${isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200'} mx-auto mt-2`}></div> <div
className={`w-px h-10 ${isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200'} mx-auto mt-2`}
></div>
</div> </div>
<div> <div>
<h3 className={`font-medium text-base ${currentStep === 'connect' <h3
? (isDarkMode ? 'text-white' : 'text-zinc-900') className={`font-medium text-base ${
: (isDarkMode ? 'text-zinc-400' : 'text-zinc-600') currentStep === 'connect'
}`}>Connect</h3> ? isDarkMode
<p className={`text-sm ${currentStep === 'connect' ? 'text-white'
? (isDarkMode ? 'text-zinc-300' : 'text-zinc-700') : 'text-zinc-900'
: (isDarkMode ? 'text-zinc-500' : 'text-zinc-500') : isDarkMode
}`}>Connect and import a GitHub repo</p> ? 'text-zinc-400'
: 'text-zinc-600'
}`}
>
Connect
</h3>
<p
className={`text-sm ${
currentStep === 'connect'
? isDarkMode
? 'text-zinc-300'
: 'text-zinc-700'
: isDarkMode
? 'text-zinc-500'
: 'text-zinc-500'
}`}
>
Connect and import a GitHub repo
</p>
</div> </div>
</button> </button>
@ -104,29 +154,71 @@ export default function CreateProjectFlow() {
onClick={() => navigateToStep('configure')} onClick={() => navigateToStep('configure')}
> >
<div className="mr-4"> <div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'configure' <div
? (isDarkMode ? 'bg-white' : 'bg-black') className={`w-10 h-10 rounded-lg ${
: (isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200') currentStep === 'configure'
} flex items-center justify-center`}> ? isDarkMode
<svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" className={currentStep === 'configure' ? 'bg-white'
? (isDarkMode ? 'text-black' : 'text-white') : 'bg-black'
: (isDarkMode ? 'text-zinc-400' : 'text-zinc-600') : isDarkMode
}> ? 'bg-zinc-800'
: 'bg-zinc-200'
} flex items-center justify-center`}
>
<svg
viewBox="0 0 24 24"
width="20"
height="20"
stroke="currentColor"
strokeWidth="2"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
className={
currentStep === 'configure'
? isDarkMode
? 'text-black'
: 'text-white'
: isDarkMode
? 'text-zinc-400'
: 'text-zinc-600'
}
>
<path d="M12 20h9"></path> <path d="M12 20h9"></path>
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path> <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
</svg> </svg>
</div> </div>
<div className={`w-px h-10 ${isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200'} mx-auto mt-2`}></div> <div
className={`w-px h-10 ${isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200'} mx-auto mt-2`}
></div>
</div> </div>
<div> <div>
<h3 className={`font-medium text-base ${currentStep === 'configure' <h3
? (isDarkMode ? 'text-white' : 'text-zinc-900') className={`font-medium text-base ${
: (isDarkMode ? 'text-zinc-400' : 'text-zinc-600') currentStep === 'configure'
}`}>Configure</h3> ? isDarkMode
<p className={`text-sm ${currentStep === 'configure' ? 'text-white'
? (isDarkMode ? 'text-zinc-300' : 'text-zinc-700') : 'text-zinc-900'
: (isDarkMode ? 'text-zinc-500' : 'text-zinc-500') : isDarkMode
}`}>Define the deployment type</p> ? 'text-zinc-400'
: 'text-zinc-600'
}`}
>
Configure
</h3>
<p
className={`text-sm ${
currentStep === 'configure'
? isDarkMode
? 'text-zinc-300'
: 'text-zinc-700'
: isDarkMode
? 'text-zinc-500'
: 'text-zinc-500'
}`}
>
Define the deployment type
</p>
</div> </div>
</button> </button>
@ -136,14 +228,36 @@ export default function CreateProjectFlow() {
onClick={() => navigateToStep('deploy')} onClick={() => navigateToStep('deploy')}
> >
<div className="mr-4"> <div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'deploy' || currentStep === 'success' <div
? (isDarkMode ? 'bg-white' : 'bg-black') className={`w-10 h-10 rounded-lg ${
: (isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200') currentStep === 'deploy' || currentStep === 'success'
} flex items-center justify-center`}> ? isDarkMode
<svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" className={currentStep === 'deploy' || currentStep === 'success' ? 'bg-white'
? (isDarkMode ? 'text-black' : 'text-white') : 'bg-black'
: (isDarkMode ? 'text-zinc-400' : 'text-zinc-600') : isDarkMode
}> ? 'bg-zinc-800'
: 'bg-zinc-200'
} flex items-center justify-center`}
>
<svg
viewBox="0 0 24 24"
width="20"
height="20"
stroke="currentColor"
strokeWidth="2"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
className={
currentStep === 'deploy' || currentStep === 'success'
? isDarkMode
? 'text-black'
: 'text-white'
: isDarkMode
? 'text-zinc-400'
: 'text-zinc-600'
}
>
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path> <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
<polyline points="7.5 4.21 12 6.81 16.5 4.21"></polyline> <polyline points="7.5 4.21 12 6.81 16.5 4.21"></polyline>
<polyline points="7.5 19.79 7.5 14.6 3 12"></polyline> <polyline points="7.5 19.79 7.5 14.6 3 12"></polyline>
@ -154,26 +268,48 @@ export default function CreateProjectFlow() {
</div> </div>
</div> </div>
<div> <div>
<h3 className={`font-medium text-base ${currentStep === 'deploy' || currentStep === 'success' <h3
? (isDarkMode ? 'text-white' : 'text-zinc-900') className={`font-medium text-base ${
: (isDarkMode ? 'text-zinc-400' : 'text-zinc-600') currentStep === 'deploy' || currentStep === 'success'
}`}>Deploy</h3> ? isDarkMode
<p className={`text-sm ${currentStep === 'deploy' || currentStep === 'success' ? 'text-white'
? (isDarkMode ? 'text-zinc-300' : 'text-zinc-700') : 'text-zinc-900'
: (isDarkMode ? 'text-zinc-500' : 'text-zinc-500') : isDarkMode
}`}>Review and confirm deployment</p> ? 'text-zinc-400'
: 'text-zinc-600'
}`}
>
Deploy
</h3>
<p
className={`text-sm ${
currentStep === 'deploy' || currentStep === 'success'
? isDarkMode
? 'text-zinc-300'
: 'text-zinc-700'
: isDarkMode
? 'text-zinc-500'
: 'text-zinc-500'
}`}
>
Review and confirm deployment
</p>
</div> </div>
</button> </button>
</div> </div>
{/* Laconic mark (larger, bottom left) */} {/* Laconic mark (larger, bottom left) */}
<div className="absolute -bottom-2 -left-2 opacity-10"> <div className="absolute -bottom-2 -left-2 opacity-10">
<LaconicMark className={`w-40 h-40 ${isDarkMode ? 'text-zinc-300' : 'text-zinc-700'}`} /> <LaconicMark
className={`w-40 h-40 ${isDarkMode ? 'text-zinc-300' : 'text-zinc-700'}`}
/>
</div> </div>
</div> </div>
{/* Main content with fixed dimensions and scrolling */} {/* Main content with fixed dimensions and scrolling */}
<div className={`flex-1 ${isDarkMode ? 'bg-black' : 'bg-white'} relative`}> <div
className={`flex-1 ${isDarkMode ? 'bg-black' : 'bg-white'} relative`}
>
{/* Close button */} {/* Close button */}
<button <button
className={`absolute top-4 right-4 ${isDarkMode ? 'text-zinc-400 hover:text-white' : 'text-zinc-600 hover:text-zinc-900'} z-10`} className={`absolute top-4 right-4 ${isDarkMode ? 'text-zinc-400 hover:text-white' : 'text-zinc-600 hover:text-zinc-900'} z-10`}
@ -192,9 +328,15 @@ export default function CreateProjectFlow() {
{/* Progress indicator */} {/* Progress indicator */}
<div className="absolute bottom-6 left-0 right-0 flex justify-center gap-3"> <div className="absolute bottom-6 left-0 right-0 flex justify-center gap-3">
<div className={`w-12 h-1 rounded-full ${currentStep === 'connect' ? 'bg-blue-600' : (isDarkMode ? 'bg-zinc-700' : 'bg-zinc-300')}`}></div> <div
<div className={`w-12 h-1 rounded-full ${currentStep === 'configure' ? 'bg-blue-600' : (isDarkMode ? 'bg-zinc-700' : 'bg-zinc-300')}`}></div> className={`w-12 h-1 rounded-full ${currentStep === 'connect' ? 'bg-blue-600' : isDarkMode ? 'bg-zinc-700' : 'bg-zinc-300'}`}
<div className={`w-12 h-1 rounded-full ${currentStep === 'deploy' || currentStep === 'success' ? 'bg-blue-600' : (isDarkMode ? 'bg-zinc-700' : 'bg-zinc-300')}`}></div> ></div>
<div
className={`w-12 h-1 rounded-full ${currentStep === 'configure' ? 'bg-blue-600' : isDarkMode ? 'bg-zinc-700' : 'bg-zinc-300'}`}
></div>
<div
className={`w-12 h-1 rounded-full ${currentStep === 'deploy' || currentStep === 'success' ? 'bg-blue-600' : isDarkMode ? 'bg-zinc-700' : 'bg-zinc-300'}`}
></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,10 @@
import { Providers } from '@/components/providers' import { Providers } from '@/components/providers'
import { ClerkProvider } from '@clerk/nextjs' import { ClerkProvider } from '@clerk/nextjs'
import '@workspace/ui/globals.css' import '@workspace/ui/globals.css'
import { AutoSignInIFrameModal } from '@/components/iframe/auto-sign-in'
import { CheckBalanceWrapper } from '@/components/iframe/check-balance-iframe/CheckBalanceWrapper'
import type { Metadata } from 'next' import type { Metadata } from 'next'
import { Inter } from 'next/font/google' import { Inter } from 'next/font/google'
import { CheckBalanceWrapper } from '@/components/iframe/check-balance-iframe/CheckBalanceWrapper'
import { AutoSignInIFrameModal } from '@/components/iframe/auto-sign-in'
// Add root metadata with template pattern // Add root metadata with template pattern
export const metadata: Metadata = { export const metadata: Metadata = {
@ -25,7 +25,10 @@ export default function RootLayout({
children children
}: Readonly<{ children: React.ReactNode }>) { }: Readonly<{ children: React.ReactNode }>) {
return ( return (
<ClerkProvider signInFallbackRedirectUrl="/home"> <ClerkProvider
publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
signInFallbackRedirectUrl="/home"
>
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<body className={`${inter.className} `} suppressHydrationWarning> <body className={`${inter.className} `} suppressHydrationWarning>
<main> <main>

View File

@ -10,7 +10,7 @@
import { ConfigureStep } from '@/components/onboarding/configure-step' import { ConfigureStep } from '@/components/onboarding/configure-step'
import { ConnectStep } from '@/components/onboarding/connect-step' import { ConnectStep } from '@/components/onboarding/connect-step'
import { DeployStep } from '@/components/onboarding/deploy-step' import { DeployStep } from '@/components/onboarding/deploy-step'
import { useOnboarding } from '@/components/onboarding/store' import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { ScrollArea } from '@workspace/ui/components/scroll-area' import { ScrollArea } from '@workspace/ui/components/scroll-area'
import { SidebarNav } from './sidebar' import { SidebarNav } from './sidebar'

View File

@ -1,22 +1,33 @@
// src/components/onboarding/configure-step/configure-step.tsx // src/components/onboarding/configure-step/configure-step.tsx
'use client' 'use client'
import { useState, useEffect } from 'react' import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { PlusCircle, Loader2, AlertTriangle, Info } from 'lucide-react'
import { useTheme } from 'next-themes'
import { useOnboarding } from '@/components/onboarding/store'
import { useGQLClient } from '@/context' import { useGQLClient } from '@/context'
import { useWallet } from '@/context/WalletContext' import { useWallet } from '@/context/WalletContext'
import { Button } from '@workspace/ui/components/button'
import { Input } from '@workspace/ui/components/input'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@workspace/ui/components/select'
import { Checkbox } from '@workspace/ui/components/checkbox'
import { Label } from '@workspace/ui/components/label'
import { Alert, AlertDescription } from '@workspace/ui/components/alert' import { Alert, AlertDescription } from '@workspace/ui/components/alert'
import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card'
import { Badge } from '@workspace/ui/components/badge' import { Badge } from '@workspace/ui/components/badge'
import { Button } from '@workspace/ui/components/button'
import {
Card,
CardContent,
CardHeader,
CardTitle
} from '@workspace/ui/components/card'
import { Checkbox } from '@workspace/ui/components/checkbox'
import { Input } from '@workspace/ui/components/input'
import { Label } from '@workspace/ui/components/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@workspace/ui/components/select'
import { AlertTriangle, Info, Loader2, PlusCircle } from 'lucide-react'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'
import { toast } from 'sonner' import { toast } from 'sonner'
import { adaptDeployers } from '../../../utils/typeAdapters'; import { adaptDeployers } from '../../../utils/typeAdapters'
interface Deployer { interface Deployer {
deployerLrn: string deployerLrn: string
@ -44,23 +55,21 @@ export function ConfigureStep() {
// Form state // Form state
const [deployOption, setDeployOption] = useState<'auction' | 'lrn'>( const [deployOption, setDeployOption] = useState<'auction' | 'lrn'>(
formData.deploymentType as ('auction' | 'lrn') || 'lrn' // Default to LRN for simplicity (formData.deploymentType as 'auction' | 'lrn') || 'lrn' // Default to LRN for simplicity
) )
const [numberOfDeployers, setNumberOfDeployers] = useState<string>( const [numberOfDeployers, setNumberOfDeployers] = useState<string>(
formData.deployerCount || "1" formData.deployerCount || '1'
)
const [maxPrice, setMaxPrice] = useState<string>(
formData.maxPrice || "1000"
) )
const [maxPrice, setMaxPrice] = useState<string>(formData.maxPrice || '1000')
const [selectedLrn, setSelectedLrn] = useState<string>( const [selectedLrn, setSelectedLrn] = useState<string>(
formData.selectedLrn || "" formData.selectedLrn || ''
) )
const [selectedOrg, setSelectedOrg] = useState<string>( const [selectedOrg, setSelectedOrg] = useState<string>(
formData.selectedOrg || "" formData.selectedOrg || ''
) )
const [envVars, setEnvVars] = useState<{ key: string; value: string; environments: string[] }[]>([ const [envVars, setEnvVars] = useState<
{ key: '', value: '', environments: ['Production'] } { key: string; value: string; environments: string[] }[]
]) >([{ key: '', value: '', environments: ['Production'] }])
// Contexts // Contexts
const gqlClient = useGQLClient() const gqlClient = useGQLClient()
@ -81,10 +90,15 @@ export function ConfigureStep() {
// Initialize environment variables from formData if available // Initialize environment variables from formData if available
useEffect(() => { useEffect(() => {
if (formData.environmentVariables && Array.isArray(formData.environmentVariables)) { if (
setEnvVars(formData.environmentVariables.length > 0 ? formData.environmentVariables : [ formData.environmentVariables &&
{ key: '', value: '', environments: ['Production'] } Array.isArray(formData.environmentVariables)
]) ) {
setEnvVars(
formData.environmentVariables.length > 0
? formData.environmentVariables
: [{ key: '', value: '', environments: ['Production'] }]
)
} }
}, [formData.environmentVariables]) }, [formData.environmentVariables])
@ -94,10 +108,14 @@ export function ConfigureStep() {
setIsLoadingDeployers(true) setIsLoadingDeployers(true)
const deployersData = await gqlClient.getDeployers() const deployersData = await gqlClient.getDeployers()
console.log('Available deployers:', deployersData) console.log('Available deployers:', deployersData)
setDeployers(adaptDeployers(deployersData.deployers || [])); setDeployers(adaptDeployers(deployersData.deployers || []))
// Auto-select first deployer if available and none selected // Auto-select first deployer if available and none selected
if (deployersData.deployers && deployersData.deployers.length > 0 && !selectedLrn) { if (
deployersData.deployers &&
deployersData.deployers.length > 0 &&
!selectedLrn
) {
setSelectedLrn(deployersData.deployers[0]!.deployerLrn) setSelectedLrn(deployersData.deployers[0]!.deployerLrn)
} }
} catch (error) { } catch (error) {
@ -117,7 +135,11 @@ export function ConfigureStep() {
setOrganizations(orgsData.organizations || []) setOrganizations(orgsData.organizations || [])
// Auto-select first organization if available and none selected // Auto-select first organization if available and none selected
if (orgsData.organizations && orgsData.organizations.length > 0 && !selectedOrg) { if (
orgsData.organizations &&
orgsData.organizations.length > 0 &&
!selectedOrg
) {
setSelectedOrg(orgsData.organizations[0]!.slug) setSelectedOrg(orgsData.organizations[0]!.slug)
} }
} catch (error) { } catch (error) {
@ -130,7 +152,10 @@ export function ConfigureStep() {
// Add an empty environment variable row // Add an empty environment variable row
const addEnvVar = () => { const addEnvVar = () => {
setEnvVars([...envVars, { key: '', value: '', environments: ['Production'] }]) setEnvVars([
...envVars,
{ key: '', value: '', environments: ['Production'] }
])
} }
// Remove environment variable row // Remove environment variable row
@ -141,7 +166,11 @@ export function ConfigureStep() {
} }
// Update environment variable // Update environment variable
const updateEnvVar = (index: number, field: 'key' | 'value', value: string) => { const updateEnvVar = (
index: number,
field: 'key' | 'value',
value: string
) => {
const newEnvVars = [...envVars] const newEnvVars = [...envVars]
if (newEnvVars[index]) { if (newEnvVars[index]) {
newEnvVars[index][field] = value newEnvVars[index][field] = value
@ -155,9 +184,10 @@ export function ConfigureStep() {
if (newEnvVars[index]?.environments) { if (newEnvVars[index]?.environments) {
const currentEnvs = newEnvVars[index].environments const currentEnvs = newEnvVars[index].environments
if (currentEnvs.includes(environment)) { if (currentEnvs.includes(environment)) {
newEnvVars[index].environments = currentEnvs.filter(env => env !== environment) newEnvVars[index].environments = currentEnvs.filter(
(env) => env !== environment
)
} else { } else {
newEnvVars[index].environments = [...currentEnvs, environment] newEnvVars[index].environments = [...currentEnvs, environment]
} }
@ -177,7 +207,7 @@ export function ConfigureStep() {
} }
// Get selected deployer details // Get selected deployer details
const selectedDeployer = deployers.find(d => d.deployerLrn === selectedLrn) const selectedDeployer = deployers.find((d) => d.deployerLrn === selectedLrn)
// Validate form // Validate form
const canProceed = () => { const canProceed = () => {
@ -195,7 +225,9 @@ export function ConfigureStep() {
} }
// Filter out empty environment variables // Filter out empty environment variables
const validEnvVars = envVars.filter(env => env.key.trim() && env.value.trim()) const validEnvVars = envVars.filter(
(env) => env.key.trim() && env.value.trim()
)
// Save configuration to form data // Save configuration to form data
setFormData({ setFormData({
@ -221,19 +253,44 @@ export function ConfigureStep() {
// Get deployment mode info // Get deployment mode info
const isTemplateMode = formData.deploymentMode === 'template' const isTemplateMode = formData.deploymentMode === 'template'
const selectedItem = isTemplateMode ? formData.template?.name : formData.githubRepo const selectedItem = isTemplateMode
? formData.template?.name
: formData.githubRepo
return ( return (
<div className="w-full h-full flex flex-col p-8 overflow-y-auto"> <div className="w-full h-full flex flex-col p-8 overflow-y-auto">
{/* Configure icon and header */} {/* Configure icon and header */}
<div className="flex flex-col items-center justify-center mb-8"> <div className="flex flex-col items-center justify-center mb-8">
<div className="mb-4"> <div className="mb-4">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={isDarkMode ? "text-white" : "text-black"}> <svg
<path d="M12 20h9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> width="40"
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> height="40"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={isDarkMode ? 'text-white' : 'text-black'}
>
<path
d="M12 20h9"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg> </svg>
</div> </div>
<h2 className={`text-2xl font-medium text-center mb-2 ${isDarkMode ? "text-white" : "text-zinc-900"}`}>Configure</h2> <h2
className={`text-2xl font-medium text-center mb-2 ${isDarkMode ? 'text-white' : 'text-zinc-900'}`}
>
Configure
</h2>
<p className={`text-center text-zinc-500 max-w-md`}> <p className={`text-center text-zinc-500 max-w-md`}>
Define the deployment type Define the deployment type
</p> </p>
@ -252,7 +309,9 @@ export function ConfigureStep() {
<div className="space-y-2 text-sm"> <div className="space-y-2 text-sm">
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">Type:</span> <span className="text-muted-foreground">Type:</span>
<Badge variant="secondary">{isTemplateMode ? 'Template' : 'Repository'}</Badge> <Badge variant="secondary">
{isTemplateMode ? 'Template' : 'Repository'}
</Badge>
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">Source:</span> <span className="text-muted-foreground">Source:</span>
@ -268,24 +327,33 @@ export function ConfigureStep() {
{/* Organization Selection */} {/* Organization Selection */}
<div className="mb-6"> <div className="mb-6">
<Label htmlFor="organization" className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}> <Label
htmlFor="organization"
className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}
>
Organization * Organization *
</Label> </Label>
{isLoadingOrgs ? ( {isLoadingOrgs ? (
<div className="flex items-center justify-center p-3 border rounded-md"> <div className="flex items-center justify-center p-3 border rounded-md">
<Loader2 className="h-4 w-4 animate-spin mr-2" /> <Loader2 className="h-4 w-4 animate-spin mr-2" />
<span className="text-sm text-muted-foreground">Loading organizations...</span> <span className="text-sm text-muted-foreground">
Loading organizations...
</span>
</div> </div>
) : organizations.length === 0 ? ( ) : organizations.length === 0 ? (
<Alert> <Alert>
<AlertTriangle className="h-4 w-4" /> <AlertTriangle className="h-4 w-4" />
<AlertDescription> <AlertDescription>
No organizations found. You need to be part of at least one organization. No organizations found. You need to be part of at least one
organization.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
) : ( ) : (
<Select value={selectedOrg} onValueChange={setSelectedOrg}> <Select value={selectedOrg} onValueChange={setSelectedOrg}>
<SelectTrigger id="organization" className={isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}> <SelectTrigger
id="organization"
className={isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}
>
<SelectValue placeholder="Select organization" /> <SelectValue placeholder="Select organization" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -301,24 +369,38 @@ export function ConfigureStep() {
{/* Deployment options */} {/* Deployment options */}
<div className="mb-6"> <div className="mb-6">
<Label className={`text-sm mb-3 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}> <Label
className={`text-sm mb-3 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}
>
Deployment Type Deployment Type
</Label> </Label>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
<Button <Button
variant={deployOption === 'lrn' ? "default" : "outline"} variant={deployOption === 'lrn' ? 'default' : 'outline'}
className={`py-3 ${deployOption === 'lrn' className={`py-3 ${
? (isDarkMode ? 'bg-zinc-800 text-white' : 'bg-zinc-800 text-white') deployOption === 'lrn'
: (isDarkMode ? 'bg-transparent border-zinc-700 text-zinc-400' : 'bg-transparent border-zinc-300 text-zinc-600')}`} ? isDarkMode
? 'bg-zinc-800 text-white'
: 'bg-zinc-800 text-white'
: isDarkMode
? 'bg-transparent border-zinc-700 text-zinc-400'
: 'bg-transparent border-zinc-300 text-zinc-600'
}`}
onClick={() => toggleDeployOption('lrn')} onClick={() => toggleDeployOption('lrn')}
> >
Deployer LRN Deployer LRN
</Button> </Button>
<Button <Button
variant={deployOption === 'auction' ? "default" : "outline"} variant={deployOption === 'auction' ? 'default' : 'outline'}
className={`py-3 ${deployOption === 'auction' className={`py-3 ${
? (isDarkMode ? 'bg-zinc-800 text-white' : 'bg-zinc-800 text-white') deployOption === 'auction'
: (isDarkMode ? 'bg-transparent border-zinc-700 text-zinc-400' : 'bg-transparent border-zinc-300 text-zinc-600')}`} ? isDarkMode
? 'bg-zinc-800 text-white'
: 'bg-zinc-800 text-white'
: isDarkMode
? 'bg-transparent border-zinc-700 text-zinc-400'
: 'bg-transparent border-zinc-300 text-zinc-600'
}`}
onClick={() => toggleDeployOption('auction')} onClick={() => toggleDeployOption('auction')}
> >
Create Auction Create Auction
@ -329,13 +411,18 @@ export function ConfigureStep() {
{deployOption === 'lrn' ? ( {deployOption === 'lrn' ? (
/* LRN Deployment Settings */ /* LRN Deployment Settings */
<div className="mb-6"> <div className="mb-6">
<Label htmlFor="lrn" className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}> <Label
htmlFor="lrn"
className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}
>
Select Deployer LRN * Select Deployer LRN *
</Label> </Label>
{isLoadingDeployers ? ( {isLoadingDeployers ? (
<div className="flex items-center justify-center p-3 border rounded-md"> <div className="flex items-center justify-center p-3 border rounded-md">
<Loader2 className="h-4 w-4 animate-spin mr-2" /> <Loader2 className="h-4 w-4 animate-spin mr-2" />
<span className="text-sm text-muted-foreground">Loading deployers...</span> <span className="text-sm text-muted-foreground">
Loading deployers...
</span>
</div> </div>
) : deployers.length === 0 ? ( ) : deployers.length === 0 ? (
<Alert> <Alert>
@ -347,12 +434,20 @@ export function ConfigureStep() {
) : ( ) : (
<> <>
<Select value={selectedLrn} onValueChange={setSelectedLrn}> <Select value={selectedLrn} onValueChange={setSelectedLrn}>
<SelectTrigger id="lrn" className={isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}> <SelectTrigger
id="lrn"
className={
isDarkMode ? 'border-zinc-700' : 'border-zinc-300'
}
>
<SelectValue placeholder="Select a deployer" /> <SelectValue placeholder="Select a deployer" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{deployers.map((deployer) => ( {deployers.map((deployer) => (
<SelectItem key={deployer.deployerLrn} value={deployer.deployerLrn}> <SelectItem
key={deployer.deployerLrn}
value={deployer.deployerLrn}
>
<div className="flex flex-col"> <div className="flex flex-col">
<span>{deployer.deployerLrn}</span> <span>{deployer.deployerLrn}</span>
{deployer.minimumPayment && ( {deployer.minimumPayment && (
@ -370,10 +465,19 @@ export function ConfigureStep() {
{selectedDeployer && ( {selectedDeployer && (
<div className="mt-3 p-3 bg-muted rounded-md"> <div className="mt-3 p-3 bg-muted rounded-md">
<div className="text-sm space-y-1"> <div className="text-sm space-y-1">
<div><strong>API URL:</strong> {selectedDeployer.deployerApiUrl}</div> <div>
<div><strong>Base Domain:</strong> {selectedDeployer.baseDomain}</div> <strong>API URL:</strong>{' '}
{selectedDeployer.deployerApiUrl}
</div>
<div>
<strong>Base Domain:</strong>{' '}
{selectedDeployer.baseDomain}
</div>
{selectedDeployer.minimumPayment && ( {selectedDeployer.minimumPayment && (
<div><strong>Minimum Payment:</strong> {selectedDeployer.minimumPayment}</div> <div>
<strong>Minimum Payment:</strong>{' '}
{selectedDeployer.minimumPayment}
</div>
)} )}
</div> </div>
</div> </div>
@ -385,11 +489,20 @@ export function ConfigureStep() {
/* Auction Settings */ /* Auction Settings */
<div className="grid grid-cols-2 gap-4 mb-6"> <div className="grid grid-cols-2 gap-4 mb-6">
<div> <div>
<Label htmlFor="deployers" className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}> <Label
htmlFor="deployers"
className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}
>
Number of Deployers Number of Deployers
</Label> </Label>
<Select value={numberOfDeployers} onValueChange={setNumberOfDeployers}> <Select
<SelectTrigger id="deployers" className={isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}> value={numberOfDeployers}
onValueChange={setNumberOfDeployers}
>
<SelectTrigger
id="deployers"
className={isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}
>
<SelectValue placeholder="Select number" /> <SelectValue placeholder="Select number" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -402,11 +515,17 @@ export function ConfigureStep() {
</Select> </Select>
</div> </div>
<div> <div>
<Label htmlFor="maxPrice" className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}> <Label
htmlFor="maxPrice"
className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}
>
Maximum Price (aint) Maximum Price (aint)
</Label> </Label>
<Select value={maxPrice} onValueChange={setMaxPrice}> <Select value={maxPrice} onValueChange={setMaxPrice}>
<SelectTrigger id="maxPrice" className={isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}> <SelectTrigger
id="maxPrice"
className={isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}
>
<SelectValue placeholder="Select price" /> <SelectValue placeholder="Select price" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -422,10 +541,14 @@ export function ConfigureStep() {
{/* Payment Address */} {/* Payment Address */}
<div className="mb-6"> <div className="mb-6">
<Label className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}> <Label
className={`text-sm mb-2 block ${isDarkMode ? 'text-zinc-400' : 'text-zinc-700'}`}
>
Payment Address Payment Address
</Label> </Label>
<div className={`p-3 border rounded-md bg-muted ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`}> <div
className={`p-3 border rounded-md bg-muted ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`}
>
<div className="text-sm font-mono break-all"> <div className="text-sm font-mono break-all">
{wallet?.address || 'No wallet connected'} {wallet?.address || 'No wallet connected'}
</div> </div>
@ -434,12 +557,19 @@ export function ConfigureStep() {
{/* Environment Variables */} {/* Environment Variables */}
<div className="mb-6"> <div className="mb-6">
<Label className={`text-sm font-medium mb-2 block ${isDarkMode ? 'text-white' : 'text-zinc-900'}`}> <Label
className={`text-sm font-medium mb-2 block ${isDarkMode ? 'text-white' : 'text-zinc-900'}`}
>
Environment Variables Environment Variables
</Label> </Label>
<div className={`border rounded-md p-4 ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`}> <div
className={`border rounded-md p-4 ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`}
>
{envVars.map((envVar, index) => ( {envVars.map((envVar, index) => (
<div key={index} className="space-y-2 mb-4 pb-4 border-b border-muted last:border-b-0 last:mb-0 last:pb-0"> <div
key={index}
className="space-y-2 mb-4 pb-4 border-b border-muted last:border-b-0 last:mb-0 last:pb-0"
>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
<Input <Input
placeholder="KEY" placeholder="KEY"
@ -450,12 +580,16 @@ export function ConfigureStep() {
<Input <Input
placeholder="VALUE" placeholder="VALUE"
value={envVar.value} value={envVar.value}
onChange={(e) => updateEnvVar(index, 'value', e.target.value)} onChange={(e) =>
updateEnvVar(index, 'value', e.target.value)
}
className={`bg-transparent ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`} className={`bg-transparent ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`}
/> />
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<span className="text-xs text-muted-foreground">Environments:</span> <span className="text-xs text-muted-foreground">
Environments:
</span>
{['Production', 'Preview', 'Development'].map((env) => ( {['Production', 'Preview', 'Development'].map((env) => (
<div key={env} className="flex items-center gap-1"> <div key={env} className="flex items-center gap-1">
<Checkbox <Checkbox

View File

@ -1,24 +1,36 @@
// src/components/onboarding/connect-step/connect-step.tsx // src/components/onboarding/connect-step/connect-step.tsx
'use client' 'use client'
import { useState, useEffect } from 'react' import { GitHubBackendAuth } from '@/components/GitHubBackendAuth'
import { Github, Wallet, CheckCircle2, AlertTriangle, Loader2, ExternalLink, ChevronDown } from 'lucide-react' import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { useTheme } from 'next-themes' import { AVAILABLE_TEMPLATES, type TemplateDetail } from '@/constants/templates'
import { SignIn } from '@clerk/nextjs'
import { useOnboarding } from '@/components/onboarding/store'
import { useAuthStatus } from '@/hooks/useAuthStatus' import { useAuthStatus } from '@/hooks/useAuthStatus'
import { useRepoData } from '@/hooks/useRepoData' import { useRepoData } from '@/hooks/useRepoData'
import type { Template } from '@/types/onboarding'
import { adaptOptionalTemplate } from '@/utils/typeAdapters'
import { SignIn } from '@clerk/nextjs'
import { Alert, AlertDescription } from '@workspace/ui/components/alert'
import { Button } from '@workspace/ui/components/button' import { Button } from '@workspace/ui/components/button'
import { Card, CardContent } from '@workspace/ui/components/card' import { Card, CardContent } from '@workspace/ui/components/card'
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger
} from '@workspace/ui/components/collapsible'
import { Input } from '@workspace/ui/components/input' import { Input } from '@workspace/ui/components/input'
import { Label } from '@workspace/ui/components/label' import { Label } from '@workspace/ui/components/label'
import { Alert, AlertDescription } from '@workspace/ui/components/alert' import {
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@workspace/ui/components/collapsible' AlertTriangle,
CheckCircle2,
ChevronDown,
ExternalLink,
Github,
Loader2,
Wallet
} from 'lucide-react'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'
import { toast } from 'sonner' import { toast } from 'sonner'
import { GitHubBackendAuth } from '@/components/GitHubBackendAuth'
import { AVAILABLE_TEMPLATES, type TemplateDetail } from '@/constants/templates'
import { Template } from '@/types/onboarding'
import { adaptOptionalTemplate } from '@/utils/typeAdapters'
interface Repository { interface Repository {
id: string | number id: string | number
@ -33,11 +45,15 @@ export function ConnectStep() {
const [mounted, setMounted] = useState(false) const [mounted, setMounted] = useState(false)
// Repository vs Template selection // Repository vs Template selection
const [selectedRepo, setSelectedRepo] = useState<string>(formData.githubRepo || '') const [selectedRepo, setSelectedRepo] = useState<string>(
formData.githubRepo || ''
)
const [selectedTemplate, setSelectedTemplate] = useState( const [selectedTemplate, setSelectedTemplate] = useState(
adaptOptionalTemplate(formData.template) adaptOptionalTemplate(formData.template)
) )
const [projectName, setProjectName] = useState<string>(formData.projectName || '') const [projectName, setProjectName] = useState<string>(
formData.projectName || ''
)
const [isImportMode, setIsImportMode] = useState(true) const [isImportMode, setIsImportMode] = useState(true)
// Auth status and warning display // Auth status and warning display
@ -94,7 +110,9 @@ export function ConnectStep() {
template: template, template: template,
githubRepo: '', githubRepo: '',
deploymentMode: 'template', deploymentMode: 'template',
projectName: projectName || `my-${template.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}` projectName:
projectName ||
`my-${template.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`
}) })
} }
@ -163,7 +181,8 @@ export function ConnectStep() {
} }
// For repository import, project name is optional but we'll use repo name as fallback // For repository import, project name is optional but we'll use repo name as fallback
const finalProjectName = projectName.trim() || (isImportMode ? selectedRepo.split('/')[1] : '') const finalProjectName =
projectName.trim() || (isImportMode ? selectedRepo.split('/')[1] : '')
// Set final form data and proceed // Set final form data and proceed
setFormData({ setFormData({
@ -182,7 +201,9 @@ export function ConnectStep() {
<div className="w-full h-full flex items-center justify-center"> <div className="w-full h-full flex items-center justify-center">
<div className="text-center"> <div className="text-center">
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4" /> <Loader2 className="h-8 w-8 animate-spin mx-auto mb-4" />
<p className="text-sm text-zinc-500">Loading authentication status...</p> <p className="text-sm text-zinc-500">
Loading authentication status...
</p>
</div> </div>
</div> </div>
) )
@ -196,7 +217,9 @@ export function ConnectStep() {
<div className="max-w-2xl w-full mx-auto"> <div className="max-w-2xl w-full mx-auto">
{/* Header */} {/* Header */}
<div className="text-center mb-8"> <div className="text-center mb-8">
<h2 className={`text-2xl font-medium ${isDarkMode ? "text-white" : "text-zinc-900"} mb-2`}> <h2
className={`text-2xl font-medium ${isDarkMode ? 'text-white' : 'text-zinc-900'} mb-2`}
>
Connect Connect
</h2> </h2>
<p className="text-zinc-500 mb-6"> <p className="text-zinc-500 mb-6">
@ -204,12 +227,15 @@ export function ConnectStep() {
</p> </p>
{/* GitHub Account Selector - Only show if multiple accounts */} {/* GitHub Account Selector - Only show if multiple accounts */}
{clerk.user?.externalAccounts && clerk.user.externalAccounts.length > 1 && ( {clerk.user?.externalAccounts &&
clerk.user.externalAccounts.length > 1 && (
<div className="flex items-center justify-center mb-6"> <div className="flex items-center justify-center mb-6">
<div className="flex items-center gap-2 px-4 py-2 bg-zinc-100 dark:bg-zinc-800 rounded-md cursor-pointer hover:bg-zinc-200 dark:hover:bg-zinc-700"> <div className="flex items-center gap-2 px-4 py-2 bg-zinc-100 dark:bg-zinc-800 rounded-md cursor-pointer hover:bg-zinc-200 dark:hover:bg-zinc-700">
<Github className="h-4 w-4" /> <Github className="h-4 w-4" />
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{clerk.user?.externalAccounts?.find((acc: any) => acc.provider === 'github')?.username || 'git-account'} {clerk.user?.externalAccounts?.find(
(acc: any) => acc.provider === 'github'
)?.username || 'git-account'}
</span> </span>
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
</div> </div>
@ -224,7 +250,10 @@ export function ConnectStep() {
<Alert className="mb-6 cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/20"> <Alert className="mb-6 cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/20">
<AlertTriangle className="h-4 w-4" /> <AlertTriangle className="h-4 w-4" />
<AlertDescription className="flex items-center justify-between w-full"> <AlertDescription className="flex items-center justify-between w-full">
<span>Authentication required to continue ({progress.completed}/{progress.total} complete)</span> <span>
Authentication required to continue ({progress.completed}/
{progress.total} complete)
</span>
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@ -236,7 +265,9 @@ export function ConnectStep() {
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Github className="h-4 w-4" /> <Github className="h-4 w-4" />
<span className="text-sm font-medium">Sign in with Clerk</span> <span className="text-sm font-medium">
Sign in with Clerk
</span>
</div> </div>
<div className="scale-90 origin-top-left"> <div className="scale-90 origin-top-left">
<SignIn routing="hash" /> <SignIn routing="hash" />
@ -250,9 +281,15 @@ export function ConnectStep() {
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Github className="h-4 w-4" /> <Github className="h-4 w-4" />
<span className="text-sm font-medium">Connect GitHub Account</span> <span className="text-sm font-medium">
Connect GitHub Account
</span>
</div> </div>
<Button size="sm" variant="outline" onClick={() => window.open('/user-profile', '_blank')}> <Button
size="sm"
variant="outline"
onClick={() => window.open('/user-profile', '_blank')}
>
<ExternalLink className="h-3 w-3 mr-2" /> <ExternalLink className="h-3 w-3 mr-2" />
Connect GitHub Connect GitHub
</Button> </Button>
@ -265,7 +302,9 @@ export function ConnectStep() {
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Wallet className="h-4 w-4" /> <Wallet className="h-4 w-4" />
<span className="text-sm font-medium">Connect Wallet</span> <span className="text-sm font-medium">
Connect Wallet
</span>
</div> </div>
<Button size="sm" onClick={handleConnectWallet}> <Button size="sm" onClick={handleConnectWallet}>
Connect Wallet Connect Wallet
@ -274,14 +313,20 @@ export function ConnectStep() {
</Card> </Card>
)} )}
{missing.githubBackendSync && !missing.walletConnection && !missing.clerkGithub && ( {missing.githubBackendSync &&
!missing.walletConnection &&
!missing.clerkGithub && (
<Card className="border-amber-200 bg-amber-50/50 dark:bg-amber-950/20"> <Card className="border-amber-200 bg-amber-50/50 dark:bg-amber-950/20">
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Github className="h-4 w-4" /> <Github className="h-4 w-4" />
<span className="text-sm font-medium">Sync GitHub Access</span> <span className="text-sm font-medium">
Sync GitHub Access
</span>
</div> </div>
<GitHubBackendAuth onAuthStatusChange={handleGithubAuthChange} /> <GitHubBackendAuth
onAuthStatusChange={handleGithubAuthChange}
/>
</CardContent> </CardContent>
</Card> </Card>
)} )}
@ -292,8 +337,9 @@ export function ConnectStep() {
{/* Mode Selection Tabs */} {/* Mode Selection Tabs */}
<div className="grid grid-cols-2 gap-1 p-1 bg-zinc-100 dark:bg-zinc-800 rounded-lg mb-6"> <div className="grid grid-cols-2 gap-1 p-1 bg-zinc-100 dark:bg-zinc-800 rounded-lg mb-6">
<Button <Button
variant={isImportMode ? "default" : "ghost"} variant={isImportMode ? 'default' : 'ghost'}
className={`${isImportMode className={`${
isImportMode
? 'bg-white dark:bg-zinc-700 shadow-sm' ? 'bg-white dark:bg-zinc-700 shadow-sm'
: 'bg-transparent hover:bg-white/50 dark:hover:bg-zinc-700/50' : 'bg-transparent hover:bg-white/50 dark:hover:bg-zinc-700/50'
}`} }`}
@ -302,8 +348,9 @@ export function ConnectStep() {
Import a repository Import a repository
</Button> </Button>
<Button <Button
variant={!isImportMode ? "default" : "ghost"} variant={!isImportMode ? 'default' : 'ghost'}
className={`${!isImportMode className={`${
!isImportMode
? 'bg-white dark:bg-zinc-700 shadow-sm' ? 'bg-white dark:bg-zinc-700 shadow-sm'
: 'bg-transparent hover:bg-white/50 dark:hover:bg-zinc-700/50' : 'bg-transparent hover:bg-white/50 dark:hover:bg-zinc-700/50'
}`} }`}
@ -327,7 +374,8 @@ export function ConnectStep() {
<Alert> <Alert>
<AlertTriangle className="h-4 w-4" /> <AlertTriangle className="h-4 w-4" />
<AlertDescription> <AlertDescription>
No repositories found. Make sure your GitHub account has repositories. No repositories found. Make sure your GitHub account has
repositories.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
</div> </div>
@ -346,9 +394,13 @@ export function ConnectStep() {
> >
<Github className="h-5 w-5 mr-3 text-zinc-500 flex-shrink-0" /> <Github className="h-5 w-5 mr-3 text-zinc-500 flex-shrink-0" />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="font-medium text-sm">{repo.full_name}</div> <div className="font-medium text-sm">
{repo.full_name}
</div>
{repo.description && ( {repo.description && (
<div className="text-xs text-zinc-500 truncate">{repo.description}</div> <div className="text-xs text-zinc-500 truncate">
{repo.description}
</div>
)} )}
</div> </div>
{selectedRepo === repo.full_name && ( {selectedRepo === repo.full_name && (
@ -361,7 +413,10 @@ export function ConnectStep() {
{/* Project Name Input for Repository Import */} {/* Project Name Input for Repository Import */}
{selectedRepo && ( {selectedRepo && (
<div className="mt-6 space-y-2"> <div className="mt-6 space-y-2">
<Label htmlFor="projectName" className="text-sm font-medium"> <Label
htmlFor="projectName"
className="text-sm font-medium"
>
Project Name Project Name
</Label> </Label>
<Input <Input
@ -382,7 +437,8 @@ export function ConnectStep() {
) : ( ) : (
/* Template Selection */ /* Template Selection */
<div className="space-y-4"> <div className="space-y-4">
{AVAILABLE_TEMPLATES.filter(t => !t.isComingSoon).map((template) => ( {AVAILABLE_TEMPLATES.filter((t) => !t.isComingSoon).map(
(template) => (
<div <div
key={template.id} key={template.id}
className={`flex items-center p-4 rounded-lg border cursor-pointer transition-all ${ className={`flex items-center p-4 rounded-lg border cursor-pointer transition-all ${
@ -395,13 +451,19 @@ export function ConnectStep() {
{/* Template Icon */} {/* Template Icon */}
<div className="flex items-center justify-center w-10 h-10 rounded-lg bg-zinc-100 dark:bg-zinc-800 mr-4"> <div className="flex items-center justify-center w-10 h-10 rounded-lg bg-zinc-100 dark:bg-zinc-800 mr-4">
<div className="w-6 h-6 bg-zinc-600 dark:bg-zinc-400 rounded flex items-center justify-center text-xs font-bold text-white"> <div className="w-6 h-6 bg-zinc-600 dark:bg-zinc-400 rounded flex items-center justify-center text-xs font-bold text-white">
{template.icon === 'web' ? 'PWA' : template.icon === 'nextjs' ? 'N' : 'IMG'} {template.icon === 'web'
? 'PWA'
: template.icon === 'nextjs'
? 'N'
: 'IMG'}
</div> </div>
</div> </div>
{/* Template Info */} {/* Template Info */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="font-medium text-sm mb-1">{template.name}</div> <div className="font-medium text-sm mb-1">
{template.name}
</div>
<div className="flex items-center text-xs text-zinc-500"> <div className="flex items-center text-xs text-zinc-500">
<Github className="h-3 w-3 mr-1" /> <Github className="h-3 w-3 mr-1" />
{template.repoFullName} {template.repoFullName}
@ -413,7 +475,8 @@ export function ConnectStep() {
<CheckCircle2 className="h-5 w-5 text-blue-500 flex-shrink-0" /> <CheckCircle2 className="h-5 w-5 text-blue-500 flex-shrink-0" />
)} )}
</div> </div>
))} )
)}
{/* Project Name Input for Templates */} {/* Project Name Input for Templates */}
{selectedTemplate && ( {selectedTemplate && (
@ -443,7 +506,12 @@ export function ConnectStep() {
</Button> </Button>
<Button <Button
onClick={handleNext} onClick={handleNext}
disabled={!isFullyAuthenticated || (isImportMode ? !selectedRepo : (!selectedTemplate || !projectName.trim()))} disabled={
!isFullyAuthenticated ||
(isImportMode
? !selectedRepo
: !selectedTemplate || !projectName.trim())
}
> >
Next Next
</Button> </Button>

View File

@ -1,19 +1,30 @@
// src/components/onboarding/deploy-step/deploy-step.tsx // src/components/onboarding/deploy-step/deploy-step.tsx
'use client' 'use client'
import { useState, useEffect } from 'react' import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { useTheme } from 'next-themes'
import { Github, Loader2, AlertTriangle, CheckCircle2 } from 'lucide-react'
import { useOnboarding } from '@/components/onboarding/store'
import { useWallet } from '@/context/WalletContext' import { useWallet } from '@/context/WalletContext'
import { useDeployment } from '@/hooks/useDeployment' import { useDeployment } from '@/hooks/useDeployment'
import { useTemplateDeployment } from '@/hooks/useTemplate' import { useTemplateDeployment } from '@/hooks/useTemplate'
import { Button } from '@workspace/ui/components/button'
import { Progress } from '@workspace/ui/components/progress'
import { Dialog, DialogContent, DialogTitle, DialogDescription, DialogFooter } from '@workspace/ui/components/dialog'
import { Alert, AlertDescription } from '@workspace/ui/components/alert' import { Alert, AlertDescription } from '@workspace/ui/components/alert'
import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card'
import { Badge } from '@workspace/ui/components/badge' import { Badge } from '@workspace/ui/components/badge'
import { Button } from '@workspace/ui/components/button'
import {
Card,
CardContent,
CardHeader,
CardTitle
} from '@workspace/ui/components/card'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogTitle
} from '@workspace/ui/components/dialog'
import { Progress } from '@workspace/ui/components/progress'
import { AlertTriangle, CheckCircle2, Github, Loader2 } from 'lucide-react'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'
import { toast } from 'sonner' import { toast } from 'sonner'
export function DeployStep() { export function DeployStep() {
@ -29,7 +40,8 @@ export function DeployStep() {
// Contexts and hooks // Contexts and hooks
const { wallet } = useWallet() const { wallet } = useWallet()
const { deployRepository, isDeploying: isRepoDeploying } = useDeployment() const { deployRepository, isDeploying: isRepoDeploying } = useDeployment()
const { deployTemplate, isDeploying: isTemplateDeploying } = useTemplateDeployment() const { deployTemplate, isDeploying: isTemplateDeploying } =
useTemplateDeployment()
// Determine deployment type and get the right deploying state // Determine deployment type and get the right deploying state
const isTemplateMode = formData.deploymentMode === 'template' const isTemplateMode = formData.deploymentMode === 'template'
@ -53,7 +65,10 @@ export function DeployStep() {
return { return {
name: formData.githubRepo?.split('/').pop() || 'Repository', name: formData.githubRepo?.split('/').pop() || 'Repository',
source: formData.githubRepo || 'Unknown Repository', source: formData.githubRepo || 'Unknown Repository',
projectName: formData.projectName || formData.githubRepo?.split('/').pop() || 'New Project', projectName:
formData.projectName ||
formData.githubRepo?.split('/').pop() ||
'New Project',
type: 'Repository' type: 'Repository'
} }
} }
@ -105,7 +120,9 @@ export function DeployStep() {
} }
} catch (error) { } catch (error) {
console.error('Deployment failed:', error) console.error('Deployment failed:', error)
setDeploymentError(error instanceof Error ? error.message : 'Deployment failed') setDeploymentError(
error instanceof Error ? error.message : 'Deployment failed'
)
} }
} }
@ -125,8 +142,8 @@ export function DeployStep() {
organizationSlug: formData.selectedOrg, organizationSlug: formData.selectedOrg,
environmentVariables: formData.environmentVariables || [], environmentVariables: formData.environmentVariables || [],
deployerLrn: formData.selectedLrn deployerLrn: formData.selectedLrn
}; }
const result = await deployTemplate(config); const result = await deployTemplate(config)
// Save deployment results // Save deployment results
setFormData({ setFormData({
@ -156,7 +173,10 @@ export function DeployStep() {
organizationSlug: formData.selectedOrg, organizationSlug: formData.selectedOrg,
repository: formData.githubRepo, repository: formData.githubRepo,
branch: 'main', // Default branch branch: 'main', // Default branch
name: formData.projectName || formData.githubRepo.split('/').pop() || 'New Project', name:
formData.projectName ||
formData.githubRepo.split('/').pop() ||
'New Project',
environmentVariables: formData.environmentVariables || [] environmentVariables: formData.environmentVariables || []
} }
@ -194,18 +214,66 @@ export function DeployStep() {
<div className="max-w-md w-full mx-auto"> <div className="max-w-md w-full mx-auto">
{/* Deploy icon */} {/* Deploy icon */}
<div className="mx-auto mb-6 flex justify-center"> <div className="mx-auto mb-6 flex justify-center">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={isDarkMode ? "text-white" : "text-black"}> <svg
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> width="48"
<polyline points="7.5 4.21 12 6.81 16.5 4.21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> height="48"
<polyline points="7.5 19.79 7.5 14.6 3 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> viewBox="0 0 24 24"
<polyline points="21 12 16.5 14.6 16.5 19.79" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> fill="none"
<polyline points="3.27 6.96 12 12.01 20.73 6.96" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> xmlns="http://www.w3.org/2000/svg"
<line x1="12" y1="22.08" x2="12" y2="12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> className={isDarkMode ? 'text-white' : 'text-black'}
>
<path
d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<polyline
points="7.5 4.21 12 6.81 16.5 4.21"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<polyline
points="7.5 19.79 7.5 14.6 3 12"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<polyline
points="21 12 16.5 14.6 16.5 19.79"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<polyline
points="3.27 6.96 12 12.01 20.73 6.96"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="12"
y1="22.08"
x2="12"
y2="12"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg> </svg>
</div> </div>
{/* Deploy header */} {/* Deploy header */}
<h2 className={`text-2xl font-medium ${isDarkMode ? "text-white" : "text-zinc-900"} text-center mb-2`}> <h2
className={`text-2xl font-medium ${isDarkMode ? 'text-white' : 'text-zinc-900'} text-center mb-2`}
>
{isDeploying ? 'Deploying...' : 'Deploy'} {isDeploying ? 'Deploying...' : 'Deploy'}
</h2> </h2>
<p className="text-center text-zinc-500 mb-8"> <p className="text-center text-zinc-500 mb-8">
@ -226,7 +294,9 @@ export function DeployStep() {
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Github className="h-4 w-4 text-muted-foreground" /> <Github className="h-4 w-4 text-muted-foreground" />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="font-medium text-sm">{deploymentInfo.projectName}</div> <div className="font-medium text-sm">
{deploymentInfo.projectName}
</div>
<div className="text-xs text-muted-foreground font-mono"> <div className="text-xs text-muted-foreground font-mono">
{deploymentInfo.source} {deploymentInfo.source}
</div> </div>
@ -240,15 +310,22 @@ export function DeployStep() {
</div> </div>
<div> <div>
<div className="text-muted-foreground">Deployer</div> <div className="text-muted-foreground">Deployer</div>
<div className="font-medium">{formData.selectedLrn ? 'LRN' : 'Auction'}</div> <div className="font-medium">
{formData.selectedLrn ? 'LRN' : 'Auction'}
</div>
</div> </div>
</div> </div>
{formData.environmentVariables && formData.environmentVariables.length > 0 && ( {formData.environmentVariables &&
formData.environmentVariables.length > 0 && (
<div className="pt-2"> <div className="pt-2">
<div className="text-xs text-muted-foreground mb-1">Environment Variables</div> <div className="text-xs text-muted-foreground mb-1">
Environment Variables
</div>
<div className="text-xs"> <div className="text-xs">
{formData.environmentVariables.length} variable{formData.environmentVariables.length !== 1 ? 's' : ''} configured {formData.environmentVariables.length} variable
{formData.environmentVariables.length !== 1 ? 's' : ''}{' '}
configured
</div> </div>
</div> </div>
)} )}
@ -273,7 +350,8 @@ export function DeployStep() {
<AlertDescription> <AlertDescription>
<div className="font-medium">Deployment Successful!</div> <div className="font-medium">Deployment Successful!</div>
<div className="text-sm mt-1"> <div className="text-sm mt-1">
Your project has been deployed successfully. You'll be redirected to the project dashboard. Your project has been deployed successfully. You'll be
redirected to the project dashboard.
</div> </div>
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@ -283,13 +361,21 @@ export function DeployStep() {
{isDeploying && ( {isDeploying && (
<div className="mb-8"> <div className="mb-8">
<div className="flex justify-between items-center mb-2"> <div className="flex justify-between items-center mb-2">
<div className={`${isDarkMode ? "text-white" : "text-zinc-900"} text-sm`}> <div
{isTemplateMode ? 'Creating repository from template...' : 'Deploying repository...'} className={`${isDarkMode ? 'text-white' : 'text-zinc-900'} text-sm`}
>
{isTemplateMode
? 'Creating repository from template...'
: 'Deploying repository...'}
</div> </div>
</div> </div>
<Progress value={undefined} className={`h-2 ${isDarkMode ? "bg-zinc-800" : "bg-zinc-200"}`} /> <Progress
value={undefined}
className={`h-2 ${isDarkMode ? 'bg-zinc-800' : 'bg-zinc-200'}`}
/>
<div className="text-xs text-muted-foreground mt-2"> <div className="text-xs text-muted-foreground mt-2">
This process may take several minutes. Please do not close this window. This process may take several minutes. Please do not close this
window.
</div> </div>
</div> </div>
)} )}
@ -298,7 +384,7 @@ export function DeployStep() {
<div className="flex justify-between items-center mt-4"> <div className="flex justify-between items-center mt-4">
<Button <Button
variant="outline" variant="outline"
className={`${isDarkMode ? "text-zinc-400 border-zinc-700" : "text-zinc-600 border-zinc-300"} bg-transparent`} className={`${isDarkMode ? 'text-zinc-400 border-zinc-700' : 'text-zinc-600 border-zinc-300'} bg-transparent`}
onClick={previousStep} onClick={previousStep}
disabled={isDeploying || deploymentSuccess} disabled={isDeploying || deploymentSuccess}
> >
@ -315,7 +401,7 @@ export function DeployStep() {
</Button> </Button>
) : isDeploying ? ( ) : isDeploying ? (
<Button <Button
className={`${isDarkMode ? "bg-zinc-700 text-zinc-300" : "bg-zinc-300 text-zinc-600"}`} className={`${isDarkMode ? 'bg-zinc-700 text-zinc-300' : 'bg-zinc-300 text-zinc-600'}`}
disabled disabled
> >
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Loader2 className="mr-2 h-4 w-4 animate-spin" />
@ -327,9 +413,22 @@ export function DeployStep() {
onClick={handlePayAndDeploy} onClick={handlePayAndDeploy}
disabled={deploymentError !== null} disabled={deploymentError !== null}
> >
{formData.deploymentType === 'auction' ? 'Pay and Deploy' : 'Deploy'} {formData.deploymentType === 'auction'
<svg className="ml-2 h-4 w-4" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> ? 'Pay and Deploy'
<path d="M5 12H19M19 12L13 6M19 12L13 18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> : 'Deploy'}
<svg
className="ml-2 h-4 w-4"
fill="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 12H19M19 12L13 6M19 12L13 18"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg> </svg>
</Button> </Button>
)} )}
@ -360,7 +459,9 @@ export function DeployStep() {
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">Source:</span> <span className="text-muted-foreground">Source:</span>
<span className="font-mono text-xs">{deploymentInfo.source}</span> <span className="font-mono text-xs">
{deploymentInfo.source}
</span>
</div> </div>
</div> </div>
</div> </div>
@ -378,7 +479,9 @@ export function DeployStep() {
<div className="space-y-2"> <div className="space-y-2">
<h3 className="text-sm font-medium">Deployer</h3> <h3 className="text-sm font-medium">Deployer</h3>
<div className="text-sm"> <div className="text-sm">
<div className="font-mono text-xs">{formData.selectedLrn}</div> <div className="font-mono text-xs">
{formData.selectedLrn}
</div>
</div> </div>
</div> </div>
)} )}
@ -402,17 +505,10 @@ export function DeployStep() {
</div> </div>
<DialogFooter className="flex justify-end space-x-2"> <DialogFooter className="flex justify-end space-x-2">
<Button <Button variant="outline" onClick={handleCancelConfirm}>
variant="outline"
onClick={handleCancelConfirm}
>
Cancel Cancel
</Button> </Button>
<Button <Button onClick={handleConfirmDeploy}>Confirm Deployment</Button>
onClick={handleConfirmDeploy}
>
Confirm Deployment
</Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>

View File

@ -10,9 +10,7 @@
// Main component // Main component
export { default as Onboarding } from './Onboarding' export { default as Onboarding } from './Onboarding'
export { export { default as OnboardingDialog } from './OnboardingDialog'
default as OnboardingDialog,
} from './OnboardingDialog'
// Step components // Step components
export { ConfigureStep } from './configure-step' export { ConfigureStep } from './configure-step'
@ -27,7 +25,7 @@ export * from './common'
export * from './sidebar' export * from './sidebar'
// Store and hooks // Store and hooks
export { useOnboarding } from './store' export { useOnboarding } from './useOnboarding'
// Types // Types
export * from './types' export * from './types'

View File

@ -7,7 +7,7 @@
/** /**
* Available steps in the onboarding flow * Available steps in the onboarding flow
*/ */
export type Step = 'connect' | 'configure' | 'deploy' export type Step = 'connect' | 'configure' | 'deploy' | 'success'
/** /**
* Form data collected during the onboarding process * Form data collected during the onboarding process

50
pnpm-lock.yaml generated
View File

@ -127,8 +127,8 @@ importers:
specifier: ^10.9.2 specifier: ^10.9.2
version: 10.9.2(@types/node@20.17.23)(typescript@5.8.2) version: 10.9.2(@types/node@20.17.23)(typescript@5.8.2)
typeorm: typeorm:
specifier: ^0.3.19 specifier: ^0.3.26
version: 0.3.21(better-sqlite3@9.6.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@20.17.23)(typescript@5.8.2)) version: 0.3.26(better-sqlite3@12.2.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@20.17.23)(typescript@5.8.2))
typescript: typescript:
specifier: ^5.3.3 specifier: ^5.3.3
version: 5.8.2 version: 5.8.2
@ -152,8 +152,8 @@ importers:
specifier: ^11.0.4 specifier: ^11.0.4
version: 11.0.4 version: 11.0.4
better-sqlite3: better-sqlite3:
specifier: ^9.2.2 specifier: ^12.2.0
version: 9.6.0 version: 12.2.0
copyfiles: copyfiles:
specifier: ^2.4.1 specifier: ^2.4.1
version: 2.4.1 version: 2.4.1
@ -2892,8 +2892,9 @@ packages:
before-after-hook@3.0.2: before-after-hook@3.0.2:
resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==}
better-sqlite3@9.6.0: better-sqlite3@12.2.0:
resolution: {integrity: sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==} resolution: {integrity: sha512-eGbYq2CT+tos1fBwLQ/tkBt9J5M3JEHjku4hbvQUePCckkvVf14xWj+1m7dGoK81M/fOjFT7yM9UMeKT/+vFLQ==}
engines: {node: 20.x || 22.x || 23.x || 24.x}
big-integer@1.6.36: big-integer@1.6.36:
resolution: {integrity: sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==} resolution: {integrity: sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==}
@ -3323,6 +3324,14 @@ packages:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
dedent@1.6.0:
resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==}
peerDependencies:
babel-plugin-macros: ^3.1.0
peerDependenciesMeta:
babel-plugin-macros:
optional: true
deep-extend@0.6.0: deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
@ -5374,29 +5383,28 @@ packages:
typeforce@1.18.0: typeforce@1.18.0:
resolution: {integrity: sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==} resolution: {integrity: sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==}
typeorm@0.3.21: typeorm@0.3.26:
resolution: {integrity: sha512-lh4rUWl1liZGjyPTWpwcK8RNI5x4ekln+/JJOox1wCd7xbucYDOXWD+1cSzTN3L0wbTGxxOtloM5JlxbOxEufA==} resolution: {integrity: sha512-o2RrBNn3lczx1qv4j+JliVMmtkPSqEGpG0UuZkt9tCfWkoXKu8MZnjvp2GjWPll1SehwemQw6xrbVRhmOglj8Q==}
engines: {node: '>=16.13.0'} engines: {node: '>=16.13.0'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
'@google-cloud/spanner': ^5.18.0 '@google-cloud/spanner': ^5.18.0 || ^6.0.0 || ^7.0.0
'@sap/hana-client': ^2.12.25 '@sap/hana-client': ^2.14.22
better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0
hdb-pool: ^0.1.6
ioredis: ^5.0.4 ioredis: ^5.0.4
mongodb: ^5.8.0 mongodb: ^5.8.0 || ^6.0.0
mssql: ^9.1.1 || ^10.0.1 || ^11.0.1 mssql: ^9.1.1 || ^10.0.1 || ^11.0.1
mysql2: ^2.2.5 || ^3.0.1 mysql2: ^2.2.5 || ^3.0.1
oracledb: ^6.3.0 oracledb: ^6.3.0
pg: ^8.5.1 pg: ^8.5.1
pg-native: ^3.0.0 pg-native: ^3.0.0
pg-query-stream: ^4.0.0 pg-query-stream: ^4.0.0
redis: ^3.1.1 || ^4.0.0 redis: ^3.1.1 || ^4.0.0 || ^5.0.14
reflect-metadata: ^0.1.14 || ^0.2.0 reflect-metadata: ^0.1.14 || ^0.2.0
sql.js: ^1.4.0 sql.js: ^1.4.0
sqlite3: ^5.0.3 sqlite3: ^5.0.3
ts-node: ^10.7.0 ts-node: ^10.7.0
typeorm-aurora-data-api-driver: ^2.0.0 typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0
peerDependenciesMeta: peerDependenciesMeta:
'@google-cloud/spanner': '@google-cloud/spanner':
optional: true optional: true
@ -5404,8 +5412,6 @@ packages:
optional: true optional: true
better-sqlite3: better-sqlite3:
optional: true optional: true
hdb-pool:
optional: true
ioredis: ioredis:
optional: true optional: true
mongodb: mongodb:
@ -8454,7 +8460,7 @@ snapshots:
before-after-hook@3.0.2: {} before-after-hook@3.0.2: {}
better-sqlite3@9.6.0: better-sqlite3@12.2.0:
dependencies: dependencies:
bindings: 1.5.0 bindings: 1.5.0
prebuild-install: 7.1.3 prebuild-install: 7.1.3
@ -8925,6 +8931,8 @@ snapshots:
dependencies: dependencies:
mimic-response: 3.1.0 mimic-response: 3.1.0
dedent@1.6.0: {}
deep-extend@0.6.0: {} deep-extend@0.6.0: {}
defaults@1.0.4: defaults@1.0.4:
@ -11206,7 +11214,7 @@ snapshots:
typeforce@1.18.0: {} typeforce@1.18.0: {}
typeorm@0.3.21(better-sqlite3@9.6.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@20.17.23)(typescript@5.8.2)): typeorm@0.3.26(better-sqlite3@12.2.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@20.17.23)(typescript@5.8.2)):
dependencies: dependencies:
'@sqltools/formatter': 1.2.5 '@sqltools/formatter': 1.2.5
ansis: 3.17.0 ansis: 3.17.0
@ -11214,6 +11222,7 @@ snapshots:
buffer: 6.0.3 buffer: 6.0.3
dayjs: 1.11.13 dayjs: 1.11.13
debug: 4.4.0 debug: 4.4.0
dedent: 1.6.0
dotenv: 16.4.7 dotenv: 16.4.7
glob: 10.4.5 glob: 10.4.5
reflect-metadata: 0.2.2 reflect-metadata: 0.2.2
@ -11223,9 +11232,10 @@ snapshots:
uuid: 11.1.0 uuid: 11.1.0
yargs: 17.7.2 yargs: 17.7.2
optionalDependencies: optionalDependencies:
better-sqlite3: 9.6.0 better-sqlite3: 12.2.0
ts-node: 10.9.2(@types/node@20.17.23)(typescript@5.8.2) ts-node: 10.9.2(@types/node@20.17.23)(typescript@5.8.2)
transitivePeerDependencies: transitivePeerDependencies:
- babel-plugin-macros
- supports-color - supports-color
typescript@5.8.2: {} typescript@5.8.2: {}