removed useOnboarding hook and used store.ts instead, modified all dependencies accordingly

This commit is contained in:
2025-09-22 23:32:25 -04:00
parent d6853b48e7
commit 6393e9fa75
13 changed files with 380 additions and 542 deletions
@@ -10,7 +10,7 @@
import { ConfigureStep } from '@/components/onboarding/configure-step'
import { ConnectStep } from '@/components/onboarding/connect-step'
import { DeployStep } from '@/components/onboarding/deploy-step'
import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { useOnboarding } from '@/components/onboarding/store'
import { ScrollArea } from '@workspace/ui/components/scroll-area'
import { SidebarNav } from './sidebar'
@@ -241,16 +241,26 @@
// export default OnboardingDialog
'use client'
import { useState } from 'react'
import { X, Github } from 'lucide-react'
import { Button } from '@workspace/ui/components/button'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@workspace/ui/components/select'
import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { ScrollableRepoList } from '@/components/onboarding/connect-step/repository-list'
import { TemplateList } from '@/components/onboarding/connect-step/template-list'
import { useOnboarding } from '@/components/onboarding/store'
import { useRepoData } from '@/hooks/useRepoData'
import { Dialog, DialogContent, DialogTitle } from '@workspace/ui/components/dialog'
import { VisuallyHidden } from '@radix-ui/react-visually-hidden'
import { Button } from '@workspace/ui/components/button'
import {
Dialog,
DialogContent,
DialogTitle
} from '@workspace/ui/components/dialog'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@workspace/ui/components/select'
import { Github, X } from 'lucide-react'
import { useState } from 'react'
interface OnboardingDialogProps {
trigger?: React.ReactNode
@@ -269,18 +279,21 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
defaultOpen = false,
onClose
}) => {
const { nextStep, setFormData, formData, currentStep, previousStep } = useOnboarding()
const [selectedRepo, setSelectedRepo] = useState<string>(formData.githubRepo || '')
const { nextStep, setFormData, formData, currentStep, previousStep } =
useOnboarding()
const [selectedRepo, setSelectedRepo] = useState<string>(
formData.githubRepo || ''
)
const [isImportMode, setIsImportMode] = useState(true)
const [isOpen, setIsOpen] = useState(defaultOpen)
const { repoData: repositories, isLoading } = useRepoData('')
// Handle repository selection
const handleRepoSelect = (repo: string) => {
setSelectedRepo(repo)
setFormData({ githubRepo: repo })
}
// Handle mode toggle between import and template
const toggleMode = (mode: 'import' | 'template') => {
setIsImportMode(mode === 'import')
@@ -293,7 +306,7 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
onClose()
}
}
return (
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
{trigger && trigger}
@@ -301,25 +314,48 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
<VisuallyHidden>
<DialogTitle>Onboarding</DialogTitle>
</VisuallyHidden>
<div className="flex h-full w-full">
{/* Left sidebar with steps */}
<div className="w-[280px] bg-zinc-950 p-8 relative overflow-hidden">
{/* Laconic logo */}
<div className="flex items-center mb-12">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="mr-2">
<path d="M12 16L4 8L12 0L20 8L12 16Z" fill="white"/>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mr-2"
>
<path d="M12 16L4 8L12 0L20 8L12 16Z" fill="white" />
</svg>
<span className="text-white text-xl font-bold">LACONIC</span>
</div>
{/* Steps */}
<div className="space-y-10">
{/* Connect step */}
<div className="flex">
<div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'connect' ? 'bg-white' : 'bg-zinc-800'} 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' ? 'text-black' : 'text-zinc-400'}>
<div
className={`w-10 h-10 rounded-lg ${currentStep === 'connect' ? 'bg-white' : 'bg-zinc-800'} 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'
? 'text-black'
: 'text-zinc-400'
}
>
<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>
</svg>
@@ -327,16 +363,40 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
<div className="w-px h-16 bg-zinc-800 mx-auto mt-2"></div>
</div>
<div>
<h3 className={`font-medium text-base ${currentStep === 'connect' ? 'text-white' : 'text-zinc-400'}`}>Connect</h3>
<p className={`text-sm ${currentStep === 'connect' ? 'text-zinc-400' : 'text-zinc-500'}`}>Connect and import a GitHub repo</p>
<h3
className={`font-medium text-base ${currentStep === 'connect' ? 'text-white' : 'text-zinc-400'}`}
>
Connect
</h3>
<p
className={`text-sm ${currentStep === 'connect' ? 'text-zinc-400' : 'text-zinc-500'}`}
>
Connect and import a GitHub repo
</p>
</div>
</div>
{/* Configure step */}
<div className="flex">
<div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'configure' ? 'bg-white' : 'bg-zinc-800'} 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' ? 'text-black' : 'text-zinc-400'}>
<div
className={`w-10 h-10 rounded-lg ${currentStep === 'configure' ? 'bg-white' : 'bg-zinc-800'} 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'
? 'text-black'
: 'text-zinc-400'
}
>
<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>
</svg>
@@ -344,59 +404,110 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
<div className="w-px h-16 bg-zinc-800 mx-auto mt-2"></div>
</div>
<div>
<h3 className={`font-medium text-base ${currentStep === 'configure' ? 'text-white' : 'text-zinc-400'}`}>Configure</h3>
<p className={`text-sm ${currentStep === 'configure' ? 'text-zinc-400' : 'text-zinc-500'}`}>Define the deployment type</p>
<h3
className={`font-medium text-base ${currentStep === 'configure' ? 'text-white' : 'text-zinc-400'}`}
>
Configure
</h3>
<p
className={`text-sm ${currentStep === 'configure' ? 'text-zinc-400' : 'text-zinc-500'}`}
>
Define the deployment type
</p>
</div>
</div>
{/* Deploy step */}
<div className="flex">
<div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'deploy' ? 'bg-white' : 'bg-zinc-800'} 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' ? 'text-black' : 'text-zinc-400'}>
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
<div
className={`w-10 h-10 rounded-lg ${currentStep === 'deploy' ? 'bg-white' : 'bg-zinc-800'} 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'
? 'text-black'
: 'text-zinc-400'
}
>
<rect
x="2"
y="2"
width="20"
height="20"
rx="5"
ry="5"
></rect>
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
</svg>
</div>
</div>
<div>
<h3 className={`font-medium text-base ${currentStep === 'deploy' ? 'text-white' : 'text-zinc-400'}`}>Deploy</h3>
<p className={`text-sm ${currentStep === 'deploy' ? 'text-zinc-400' : 'text-zinc-500'}`}>Review and confirm deployment</p>
<h3
className={`font-medium text-base ${currentStep === 'deploy' ? 'text-white' : 'text-zinc-400'}`}
>
Deploy
</h3>
<p
className={`text-sm ${currentStep === 'deploy' ? 'text-zinc-400' : 'text-zinc-500'}`}
>
Review and confirm deployment
</p>
</div>
</div>
</div>
{/* Background shape (decorative) */}
<div className="absolute -bottom-32 -left-32 w-64 h-64 rounded-full bg-zinc-900 opacity-50"></div>
</div>
{/* Main content */}
<div className="flex-1 bg-zinc-950 p-8 relative">
{/* Close button */}
<button
<button
className="absolute top-4 right-4 text-zinc-400 hover:text-white"
onClick={() => handleOpenChange(false)}
>
<X size={24} />
</button>
{currentStep === 'connect' && (
<div className="max-w-md mx-auto mt-8">
{/* Connect icon */}
<div className="flex justify-center mb-6">
<svg viewBox="0 0 24 24" width="48" height="48" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" className="text-white">
<svg
viewBox="0 0 24 24"
width="48"
height="48"
stroke="currentColor"
strokeWidth="2"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
className="text-white"
>
<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>
</svg>
</div>
{/* Connect header */}
<h2 className="text-2xl font-medium text-white text-center mb-2">Connect</h2>
<h2 className="text-2xl font-medium text-white text-center mb-2">
Connect
</h2>
<p className="text-center text-zinc-400 mb-8">
Connect and import a GitHub repo or start from a template
</p>
{/* Git account selector */}
<div className="mb-4">
<Select defaultValue="git-account">
@@ -411,25 +522,25 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
</SelectContent>
</Select>
</div>
{/* Mode buttons */}
<div className="grid grid-cols-2 gap-2 mb-4">
<Button
variant={isImportMode ? "default" : "outline"}
variant={isImportMode ? 'default' : 'outline'}
className={`py-6 ${isImportMode ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'bg-zinc-900 border-zinc-800 hover:bg-zinc-800 text-zinc-400'}`}
onClick={() => toggleMode('import')}
>
Import a repository
</Button>
<Button
variant={!isImportMode ? "default" : "outline"}
variant={!isImportMode ? 'default' : 'outline'}
className={`py-6 ${!isImportMode ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'bg-zinc-900 border-zinc-800 hover:bg-zinc-800 text-zinc-400'}`}
onClick={() => toggleMode('template')}
>
Start with a template
</Button>
</div>
{/* Repository or template list */}
{isImportMode ? (
<div className="mb-8">
@@ -445,11 +556,11 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
<TemplateList onSelect={() => {}} />
</div>
)}
{/* Navigation buttons */}
<div className="flex justify-between items-center mt-8">
<Button
variant="outline"
<Button
variant="outline"
className="text-zinc-400 bg-zinc-900 border-zinc-800 hover:bg-zinc-800"
disabled={true}
>
@@ -460,8 +571,8 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
<div className="w-12 h-1 rounded-full bg-zinc-800"></div>
<div className="w-12 h-1 rounded-full bg-zinc-800"></div>
</div>
<Button
className="bg-blue-600 hover:bg-blue-700 text-white"
<Button
className="bg-blue-600 hover:bg-blue-700 text-white"
onClick={nextStep}
disabled={!selectedRepo && isImportMode}
>
@@ -470,24 +581,26 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
</div>
</div>
)}
{currentStep === 'configure' && (
<div className="max-w-md mx-auto mt-8">
{/* Configure content will go here */}
<div className="text-center">
<h2 className="text-2xl font-medium text-white mb-2">Configure</h2>
<h2 className="text-2xl font-medium text-white mb-2">
Configure
</h2>
<p className="text-zinc-400 mb-8">
Define your deployment configuration
</p>
<div className="text-center mt-16 text-zinc-400">
Configure step content will be implemented here
</div>
{/* Navigation buttons */}
<div className="flex justify-between items-center mt-16">
<Button
variant="outline"
<Button
variant="outline"
className="text-zinc-400 bg-zinc-900 border-zinc-800 hover:bg-zinc-800"
onClick={() => previousStep()}
>
@@ -498,8 +611,8 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
<div className="w-12 h-1 rounded-full bg-blue-600"></div>
<div className="w-12 h-1 rounded-full bg-zinc-800"></div>
</div>
<Button
className="bg-blue-600 hover:bg-blue-700 text-white"
<Button
className="bg-blue-600 hover:bg-blue-700 text-white"
onClick={nextStep}
>
Next
@@ -508,24 +621,26 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
</div>
</div>
)}
{currentStep === 'deploy' && (
<div className="max-w-md mx-auto mt-8">
{/* Deploy content will go here */}
<div className="text-center">
<h2 className="text-2xl font-medium text-white mb-2">Deploy</h2>
<h2 className="text-2xl font-medium text-white mb-2">
Deploy
</h2>
<p className="text-zinc-400 mb-8">
Review and confirm your deployment
</p>
<div className="text-center mt-16 text-zinc-400">
Deploy step content will be implemented here
</div>
{/* Navigation buttons */}
<div className="flex justify-between items-center mt-16">
<Button
variant="outline"
<Button
variant="outline"
className="text-zinc-400 bg-zinc-900 border-zinc-800 hover:bg-zinc-800"
onClick={() => previousStep()}
>
@@ -536,9 +651,7 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
<div className="w-12 h-1 rounded-full bg-zinc-800"></div>
<div className="w-12 h-1 rounded-full bg-blue-600"></div>
</div>
<Button
className="bg-blue-600 hover:bg-blue-700 text-white"
>
<Button className="bg-blue-600 hover:bg-blue-700 text-white">
Deploy
</Button>
</div>
@@ -552,4 +665,4 @@ const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
)
}
export default OnboardingDialog
export default OnboardingDialog
@@ -1,7 +1,7 @@
'use client'
import { GitBranch, Settings, Box } from 'lucide-react'
import type { Step } from '@/components/onboarding/useOnboarding'
import type { Step } from '@/components/onboarding/types'
import { Box, GitBranch, Settings } from 'lucide-react'
interface OnboardingSidebarProps {
currentStep: Step
@@ -12,58 +12,101 @@ export function OnboardingSidebar({ currentStep }: OnboardingSidebarProps) {
<div className="w-[280px] bg-zinc-950 p-8 relative overflow-hidden">
{/* Laconic logo */}
<div className="flex items-center mb-12">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="mr-2">
<path d="M12 16L4 8L12 0L20 8L12 16Z" fill="white"/>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mr-2"
>
<path d="M12 16L4 8L12 0L20 8L12 16Z" fill="white" />
</svg>
<span className="text-white text-xl font-bold">LACONIC</span>
</div>
{/* Steps */}
<div className="space-y-10">
{/* Connect step */}
<div className="flex">
<div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'connect' ? 'bg-white' : 'bg-zinc-800'} flex items-center justify-center`}>
<GitBranch className={`h-5 w-5 ${currentStep === 'connect' ? 'text-black' : 'text-zinc-400'}`} />
<div
className={`w-10 h-10 rounded-lg ${currentStep === 'connect' ? 'bg-white' : 'bg-zinc-800'} flex items-center justify-center`}
>
<GitBranch
className={`h-5 w-5 ${currentStep === 'connect' ? 'text-black' : 'text-zinc-400'}`}
/>
</div>
<div className="w-px h-16 bg-zinc-800 mx-auto mt-2"></div>
</div>
<div>
<h3 className={`font-medium text-base ${currentStep === 'connect' ? 'text-white' : 'text-zinc-400'}`}>Connect</h3>
<p className={`text-sm ${currentStep === 'connect' ? 'text-zinc-400' : 'text-zinc-500'}`}>Connect and import a GitHub repo</p>
<h3
className={`font-medium text-base ${currentStep === 'connect' ? 'text-white' : 'text-zinc-400'}`}
>
Connect
</h3>
<p
className={`text-sm ${currentStep === 'connect' ? 'text-zinc-400' : 'text-zinc-500'}`}
>
Connect and import a GitHub repo
</p>
</div>
</div>
{/* Configure step */}
<div className="flex">
<div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'configure' ? 'bg-white' : 'bg-zinc-800'} flex items-center justify-center`}>
<Settings className={`h-5 w-5 ${currentStep === 'configure' ? 'text-black' : 'text-zinc-400'}`} />
<div
className={`w-10 h-10 rounded-lg ${currentStep === 'configure' ? 'bg-white' : 'bg-zinc-800'} flex items-center justify-center`}
>
<Settings
className={`h-5 w-5 ${currentStep === 'configure' ? 'text-black' : 'text-zinc-400'}`}
/>
</div>
<div className="w-px h-16 bg-zinc-800 mx-auto mt-2"></div>
</div>
<div>
<h3 className={`font-medium text-base ${currentStep === 'configure' ? 'text-white' : 'text-zinc-400'}`}>Configure</h3>
<p className={`text-sm ${currentStep === 'configure' ? 'text-zinc-400' : 'text-zinc-500'}`}>Define the deployment type</p>
<h3
className={`font-medium text-base ${currentStep === 'configure' ? 'text-white' : 'text-zinc-400'}`}
>
Configure
</h3>
<p
className={`text-sm ${currentStep === 'configure' ? 'text-zinc-400' : 'text-zinc-500'}`}
>
Define the deployment type
</p>
</div>
</div>
{/* Deploy step */}
<div className="flex">
<div className="mr-4">
<div className={`w-10 h-10 rounded-lg ${currentStep === 'deploy' || currentStep === 'success' ? 'bg-white' : 'bg-zinc-800'} flex items-center justify-center`}>
<Box className={`h-5 w-5 ${currentStep === 'deploy' || currentStep === 'success' ? 'text-black' : 'text-zinc-400'}`} />
<div
className={`w-10 h-10 rounded-lg ${currentStep === 'deploy' || currentStep === 'success' ? 'bg-white' : 'bg-zinc-800'} flex items-center justify-center`}
>
<Box
className={`h-5 w-5 ${currentStep === 'deploy' || currentStep === 'success' ? 'text-black' : 'text-zinc-400'}`}
/>
</div>
</div>
<div>
<h3 className={`font-medium text-base ${currentStep === 'deploy' || currentStep === 'success' ? 'text-white' : 'text-zinc-400'}`}>Deploy</h3>
<p className={`text-sm ${currentStep === 'deploy' || currentStep === 'success' ? 'text-zinc-400' : 'text-zinc-500'}`}>Review and confirm deployment</p>
<h3
className={`font-medium text-base ${currentStep === 'deploy' || currentStep === 'success' ? 'text-white' : 'text-zinc-400'}`}
>
Deploy
</h3>
<p
className={`text-sm ${currentStep === 'deploy' || currentStep === 'success' ? 'text-zinc-400' : 'text-zinc-500'}`}
>
Review and confirm deployment
</p>
</div>
</div>
</div>
{/* Background shape (decorative) */}
<div className="absolute -bottom-32 -left-32 w-64 h-64 rounded-full bg-zinc-900 opacity-50"></div>
</div>
)
}
}
@@ -1,68 +0,0 @@
import { FileCog } from 'lucide-react'
import { useState } from 'react'
import { useOnboarding } from '../store'
/**
* Second step in the onboarding flow
* Handles deployment configuration and environment setup
*
* Features:
* - Deployment type selection (auction/LRN)
* - Environment variable configuration
* - Account selection
*
* @component
*/
export function ConfigureStep() {
const { formData, setFormData } = useOnboarding()
const [activeTab, setActiveTab] = useState<'create-auction' | 'deployer-lrn'>(
'create-auction'
)
const [environments, setEnvironments] = useState({
production: false,
preview: false,
development: false
})
// const handleEnvironmentChange = (env: keyof typeof environments) => {
// setEnvironments((prev) => ({
// ...prev,
// [env]: !prev[env],
// }))
// setFormData({
// environmentVars: {
// ...formData.environmentVars,
// [env]: !environments[env],
// },
// })
// }
return (
<div className="w-full">
<div className="max-w-2xl mx-auto space-y-8">
<div className="flex flex-col items-center justify-center w-full max-w-[445px] mx-auto">
<div className="w-full flex flex-col items-center gap-6">
{/* Header section with icon and description */}
<div className="flex flex-col items-center gap-1">
<FileCog className="w-16 h-16 text-foreground" />
<div className="flex flex-col items-center gap-1">
<h2 className="text-2xl font-bold text-foreground">
Configure
</h2>
<p className="text-base text-muted-foreground text-center">
Set the deployer LRN for a single deployment or by creating a
deployer auction for multiple deployments
</p>
</div>
</div>
Content sections will be placed here: 1. Deployment type tabs
(auction/LRN) 2. Configuration forms 3. Environment variables 4.
Account selection ...content here/
{/* <Configure /> */}
</div>
</div>
</div>
</div>
)
}
@@ -73,12 +73,18 @@ export function ConnectStep() {
} = useAuthStatus()
// Repository data
const { repoData: repositories, isLoading: isLoadingRepos, error: repoError } = useRepoData('')
const {
repoData: repositories,
isLoading: isLoadingRepos,
error: repoError
} = useRepoData('')
// Debug repository data
useEffect(() => {
console.log('🔍 ConnectStep: Repository data changed:', {
repositories: repositories ? `Array with ${Array.isArray(repositories) ? repositories.length : 'not array'} items` : 'null',
repositories: repositories
? `Array with ${Array.isArray(repositories) ? repositories.length : 'not array'} items`
: 'null',
isLoadingRepos,
repoError,
isFullyAuthenticated
@@ -1,156 +0,0 @@
// 'use client'
// import { Button } from '@workspace/ui/components/button'
// import { useState } from 'react'
// import { useOnboarding } from '../store'
// type ConnectState = 'initial' | 'repository-select' | 'template-select'
// /**
// * First step in the onboarding flow
// * Handles GitHub connection and repository selection
// *
// * States:
// * - initial: Shows GitHub connect button
// * - repository-select: Shows list of repositories
// * - template-select: Shows available templates
// *
// * @component
// */
// export function ConnectStep() {
// const [connectState, setConnectState] = useState<ConnectState>('initial')
// const [projectName, setProjectName] = useState('')
// const { setFormData, nextStep } = useOnboarding()
// const handleConnect = () => {
// setConnectState('repository-select')
// }
// return (
// <div className="max-w-2xl w-full">
// {/* <ConnectAccountTabPanel />\ */}
// {connectState === 'initial' ? (
// <div className="flex flex-col items-center justify-center gap-6 p-8">
// <h2 className="text-2xl font-semibold text-center">
// Connect to GitHub
// </h2>
// <p className="text-center text-muted-foreground">
// Connect your GitHub account to get started
// </p>
// <Button onClick={handleConnect} />
// </div>
// ) : (
// // <ConnectAccount onAuth={() => {}} />
// <>...connect goes here</>
// )}
// </div>
// )
// }
'use client'
// src/components/onboarding/connect-step/connect-step.tsx
import { useState } from 'react'
import { useRepoData } from '@/hooks/useRepoData'
import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { ScrollableRepoList } from '@/components/onboarding/connect-step/repository-list'
import { TemplateList } from '@/components/onboarding/connect-step/template-list'
import { StepHeader } from '@/components/onboarding/common/step-header'
import { Button } from '@workspace/ui/components/button'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@workspace/ui/components/select'
import { GitBranch } from 'lucide-react'
export const ConnectStep = () => {
const { setFormData, formData, nextStep } = useOnboarding()
const [selectedRepo, setSelectedRepo] = useState<string>(formData.githubRepo || '')
const [isImportMode, setIsImportMode] = useState(true)
const { repoData: repositories, isLoading } = useRepoData('')
// Handle repository selection
const handleRepoSelect = (repo: string) => {
setSelectedRepo(repo)
setFormData({ githubRepo: repo })
}
// Handle mode toggle between import and template
const toggleMode = (mode: 'import' | 'template') => {
setIsImportMode(mode === 'import')
}
return (
<div className="flex flex-col items-center justify-center w-full px-8">
<div className="w-full max-w-md">
{/* Connect icon */}
<div className="flex justify-center mb-6">
<div className="w-16 h-16 bg-blue-500 bg-opacity-10 rounded-full flex items-center justify-center">
<GitBranch className="h-8 w-8 text-blue-500" />
</div>
</div>
{/* Connect header */}
<StepHeader
title="Connect"
description="Connect and import a GitHub repo or start from a template"
icon={<GitBranch />}
/>
{/* Git account selector */}
<Select defaultValue="git-account">
<SelectTrigger className="mb-4 bg-zinc-900 border-zinc-800">
<SelectValue placeholder="Select Git account" />
</SelectTrigger>
<SelectContent>
<SelectItem value="git-account">git-account</SelectItem>
</SelectContent>
</Select>
{/* Mode buttons */}
<div className="flex mb-4 space-x-2">
<Button
variant={isImportMode ? "default" : "outline"}
className={`flex-1 ${isImportMode ? 'bg-blue-600 hover:bg-blue-700' : 'bg-zinc-900 border-zinc-800 hover:bg-zinc-800'}`}
onClick={() => toggleMode('import')}
>
Import a repository
</Button>
<Button
variant={!isImportMode ? "default" : "outline"}
className={`flex-1 ${!isImportMode ? 'bg-blue-600 hover:bg-blue-700' : 'bg-zinc-900 border-zinc-800 hover:bg-zinc-800'}`}
onClick={() => toggleMode('template')}
>
Start with a template
</Button>
</div>
{/* Repository list or template list */}
{isImportMode ? (
<ScrollableRepoList
repositories={repositories || []}
isLoading={isLoading}
onSelect={handleRepoSelect}
selectedRepo={selectedRepo}
/>
) : (
<TemplateList onSelect={() => {}} />
)}
{/* Navigation buttons */}
<div className="flex justify-end space-x-2 mt-8">
<Button
variant="ghost"
className="text-muted-foreground bg-zinc-900 border-zinc-800 hover:bg-zinc-800"
disabled={true} // Disable previous on first step
>
Previous
</Button>
<Button
className="bg-blue-600 hover:bg-blue-700"
onClick={nextStep}
disabled={!selectedRepo && isImportMode}
>
Next
</Button>
</div>
</div>
</div>
)
}
@@ -49,16 +49,16 @@
import { Github } from 'lucide-react'
interface Repository {
id: string | number;
full_name: string;
html_url?: string;
id: string | number
full_name: string
html_url?: string
}
interface ScrollableRepoListProps {
repositories: Repository[];
isLoading: boolean;
onSelect: (repoFullName: string) => void;
selectedRepo: string;
repositories: Repository[]
isLoading: boolean
onSelect: (repoFullName: string) => void
selectedRepo: string
}
export function ScrollableRepoList({
@@ -72,14 +72,30 @@ export function ScrollableRepoList({
return (
<div className="border border-zinc-800 rounded-md bg-zinc-900 overflow-hidden">
<div className="p-6 text-center text-zinc-400">
<svg className="animate-spin h-5 w-5 text-zinc-400 mx-auto mb-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
<svg
className="animate-spin h-5 w-5 text-zinc-400 mx-auto mb-2"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Loading repositories...
</div>
</div>
);
)
}
if (!repositories || repositories.length === 0) {
@@ -89,29 +105,31 @@ export function ScrollableRepoList({
No repositories found. Make sure your GitHub account is connected.
</div>
</div>
);
)
}
// Generate mock repositories if needed for testing
const repoList = repositories.length > 0 ? repositories :
Array.from({ length: 15 }, (_, i) => ({
id: `mock-${i}`,
full_name: `git-account/repo-name-${i + 1}`
}));
const repoList =
repositories.length > 0
? repositories
: Array.from({ length: 15 }, (_, i) => ({
id: `mock-${i}`,
full_name: `git-account/repo-name-${i + 1}`
}))
return (
<div className="border border-zinc-800 rounded-md bg-zinc-900 overflow-hidden">
{/* Ensure we have a fixed height container with scrolling */}
<div
<div
className="max-h-60 overflow-y-auto"
style={{
style={{
scrollbarWidth: 'thin',
scrollbarColor: '#4b5563 #1f2937'
}}
>
{repoList.map((repo, index) => (
<div
key={repo.id || index}
<div
key={repo.id || index}
className={`flex items-center p-4 border-b border-zinc-800 last:border-b-0 cursor-pointer hover:bg-zinc-800 transition-colors duration-150 ${
selectedRepo === repo.full_name ? 'bg-zinc-800' : ''
}`}
@@ -121,12 +139,10 @@ export function ScrollableRepoList({
<Github className="inline-block h-4 w-4 mr-2 text-zinc-400" />
<span>{repo.full_name}</span>
</div>
<div className="text-sm text-zinc-400">
5 minutes ago
</div>
<div className="text-sm text-zinc-400">5 minutes ago</div>
</div>
))}
</div>
</div>
);
}
)
}
@@ -66,18 +66,21 @@
import { Box, Layout } from 'lucide-react'
interface Template {
id: string;
name: string;
description: string;
icon: React.ReactNode;
id: string
name: string
description: string
icon: React.ReactNode
}
interface TemplateListProps {
onSelect: (templateId: string) => void;
selectedTemplate?: string;
onSelect: (templateId: string) => void
selectedTemplate?: string
}
export const TemplateList = ({ onSelect, selectedTemplate }: TemplateListProps) => {
export const TemplateList = ({
onSelect,
selectedTemplate
}: TemplateListProps) => {
// Mock templates data - in a real app, this would come from an API
const templates: Template[] = [
{
@@ -98,7 +101,7 @@ export const TemplateList = ({ onSelect, selectedTemplate }: TemplateListProps)
description: 'Simple static site with HTML, CSS, and JavaScript',
icon: <Layout className="h-6 w-6" />
}
];
]
return (
<div className="border border-zinc-800 rounded-md bg-zinc-900 overflow-hidden">
@@ -116,11 +119,13 @@ export const TemplateList = ({ onSelect, selectedTemplate }: TemplateListProps)
</div>
<div className="flex-1">
<div className="font-medium text-sm">{template.name}</div>
<div className="text-xs text-muted-foreground">{template.description}</div>
<div className="text-xs text-muted-foreground">
{template.description}
</div>
</div>
</div>
))}
</div>
</div>
);
};
)
}
@@ -1,9 +1,10 @@
// src/components/onboarding/deploy-step/deploy-step.tsx
'use client'
import { useOnboarding } from '@/components/onboarding/useOnboarding'
import { useOnboarding } from '@/components/onboarding/store'
import { useWallet } from '@/context/WalletContext'
import { useDeployment } from '@/hooks/useDeployment'
import { useDirectTemplateDeployment } from '@/hooks/useDirectTemplateDeployment'
import { useTemplateDeployment } from '@/hooks/useTemplate'
import { Alert, AlertDescription } from '@workspace/ui/components/alert'
import { Badge } from '@workspace/ui/components/badge'
@@ -44,7 +45,7 @@ export function DeployStep() {
useTemplateDeployment()
// Determine deployment type and get the right deploying state
const isTemplateMode = formData.deploymentMode === 'template'
const isTemplateMode = formData.template ? true : false
const isDeploying = isTemplateMode ? isTemplateDeploying : isRepoDeploying
// Handle hydration mismatch by waiting for mount
@@ -132,11 +133,17 @@ export function DeployStep() {
throw new Error('Missing required template deployment data')
}
if (!formData.template.repoFullName) {
throw new Error('Template repository information is missing')
}
const config = {
template: {
...formData.template,
id: formData.template?.id || '',
icon: formData.template?.icon || ''
id: formData.template.id,
name: formData.template.name,
icon: formData.template.icon || '',
repoFullName: formData.template.repoFullName,
description: formData.template.description || ''
},
projectName: formData.projectName,
organizationSlug: formData.selectedOrg,
@@ -1,42 +0,0 @@
'use client'
import { useOnboarding } from '../store'
/**
* Final step in the onboarding flow
* Displays deployment summary and triggers deployment
*
* Features:
* - Configuration summary
* - Repository display
* - Deploy action
*
* @component
*/
export function DeployStep() {
useOnboarding()
return (
<div className="w-full">
<div className="max-w-2xl mx-auto space-y-8">
<div className="flex flex-col items-center justify-center w-full max-w-[445px] mx-auto">
<div className="w-full flex flex-col items-center gap-6">
{/* Header section */}
<div className="flex flex-col items-center gap-1">
<div className="flex flex-col items-center gap-1">
<h2 className="text-2xl font-bold text-foreground">Deploy</h2>
<p className="text-base text-muted-foreground text-center">
Your deployment is configured and ready to go!
</p>
</div>
</div>
Content sections will be placed here: 1. Repository info card 2.
Configuration summary 3. Deploy button
{/* ...content here */}
{/* <Deploy /> */}
</div>
</div>
</div>
</div>
)
}
@@ -7,6 +7,7 @@
'use client'
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import type { OnboardingFormData, Step } from './types'
/**
@@ -31,7 +32,7 @@ export interface OnboardingState {
}
/** Order of steps in the onboarding flow */
const STEP_ORDER: Step[] = ['connect', 'configure', 'deploy']
const STEP_ORDER: Step[] = ['connect', 'configure', 'deploy', 'success']
/** Initial form data values */
const initialFormData: OnboardingFormData = {
@@ -64,25 +65,58 @@ const initialFormData: OnboardingFormData = {
* const { currentStep, formData, nextStep } = useOnboarding()
* ```
*/
export const useOnboarding = create<OnboardingState>((set) => ({
currentStep: 'connect',
formData: initialFormData,
setCurrentStep: (step) => set({ currentStep: step }),
setFormData: (data) =>
set((state) => ({
formData: { ...state.formData, ...data }
})),
nextStep: () =>
set((state) => {
const currentIndex = STEP_ORDER.indexOf(state.currentStep)
const nextStep = STEP_ORDER[currentIndex + 1]
return nextStep ? { currentStep: nextStep } : state
export const useOnboarding = create<OnboardingState>()(
persist(
(set) => ({
currentStep: 'connect',
formData: initialFormData,
setCurrentStep: (step) => set({ currentStep: step }),
setFormData: (data) => {
set((state) => {
const newFormData = { ...state.formData, ...data }
// Keep organizationSlug and selectedOrg in sync
if (data.organizationSlug && !data.selectedOrg) {
newFormData.selectedOrg = data.organizationSlug
}
if (data.selectedOrg && !data.organizationSlug) {
newFormData.organizationSlug = data.selectedOrg
}
console.log('Store: Setting form data:', data)
console.log('Store: New state:', newFormData)
return { formData: newFormData }
})
},
nextStep: () =>
set((state) => {
const currentIndex = STEP_ORDER.indexOf(state.currentStep)
const nextStep = STEP_ORDER[currentIndex + 1]
return nextStep ? { currentStep: nextStep } : state
}),
previousStep: () =>
set((state) => {
const currentIndex = STEP_ORDER.indexOf(state.currentStep)
const previousStep = STEP_ORDER[currentIndex - 1]
return previousStep ? { currentStep: previousStep } : state
}),
resetOnboarding: () => {
set({ currentStep: 'connect', formData: initialFormData })
}
}),
previousStep: () =>
set((state) => {
const currentIndex = STEP_ORDER.indexOf(state.currentStep)
const previousStep = STEP_ORDER[currentIndex - 1]
return previousStep ? { currentStep: previousStep } : state
}),
resetOnboarding: () => set({ currentStep: 'connect', formData: initialFormData })
}))
{
name: 'laconic-onboarding',
storage: createJSONStorage(() => localStorage),
// Only persist formData, not currentStep
partialize: (state) => ({
formData: state.formData
})
}
)
)
@@ -24,8 +24,8 @@ export function SuccessStep() {
const repoName = formData.githubRepo
? formData.githubRepo.split('/').pop()
: formData.repoName
? formData.repoName.split('/').pop()
: formData.projectName || 'project'
? formData.repoName.split('/').pop()
: formData.projectName || 'project'
const deploymentUrl = `https://${repoName}.laconic.deploy` // Default deployment URL
const projectId = formData.projectId || formData.deploymentId || 'unknown-id' // Use projectId first, fallback to deploymentId
@@ -1,120 +0,0 @@
// src/components/onboarding/useOnboarding.ts
import { useState } from 'react'
import type {
OnboardingFormData} from '@/types'
// Step type for navigation
export type Step = 'connect' | 'configure' | 'deploy' | 'success'
// Initial form data
const initialFormData: OnboardingFormData = {
// Step 1: Connect
githubRepo: undefined,
template: undefined,
projectName: undefined,
deploymentMode: undefined,
// Step 2: Configure
selectedLrn: undefined,
selectedOrg: undefined, // Add this property
environmentVariables: [], // Use array type
deploymentType: undefined, // Add missing properties
deployerCount: undefined,
maxPrice: undefined,
// Step 3: Deploy
deploymentId: undefined,
repositoryUrl: undefined,
deploymentUrl: undefined, // Add missing property
projectId: undefined // Add missing property
}
export function useOnboarding() {
const [formData, setFormData] = useState<OnboardingFormData>(initialFormData)
const [currentStep, setCurrentStep] = useState<Step>('connect')
// Update form data (partial update)
const updateFormData = (updates: Partial<OnboardingFormData>) => {
setFormData(prev => ({ ...prev, ...updates }))
}
// Reset form data
const resetFormData = () => {
setFormData(initialFormData)
setCurrentStep('connect')
}
// Step navigation
const nextStep = () => {
switch (currentStep) {
case 'connect':
setCurrentStep('configure')
break
case 'configure':
setCurrentStep('deploy')
break
case 'deploy':
setCurrentStep('success')
break
}
}
const previousStep = () => {
switch (currentStep) {
case 'configure':
setCurrentStep('connect')
break
case 'deploy':
setCurrentStep('configure')
break
case 'success':
setCurrentStep('deploy')
break
}
}
// Validation helpers
const validateStep1 = () => {
if (formData.deploymentMode === 'template') {
return !!(formData.template && formData.projectName)
}
return !!formData.githubRepo
}
const validateStep2 = () => {
return !!(formData.selectedLrn && formData.selectedOrg)
}
const validateStep3 = () => {
return !!formData.deploymentId
}
return {
// Data
formData,
currentStep,
// Actions
setFormData: updateFormData,
setCurrentStep,
resetFormData,
resetOnboarding: resetFormData, // Alias for compatibility
nextStep,
previousStep,
// Validation
validateStep1,
validateStep2,
validateStep3,
// Store-like interface for compatibility
getState: () => ({
formData,
currentStep,
nextStep,
previousStep,
setFormData: updateFormData,
resetFormData
})
}
}