From 29c5f6f931f4cae38e865955eb57a5ce16e8b849 Mon Sep 17 00:00:00 2001 From: NasSharaf Date: Mon, 12 May 2025 18:38:21 -0400 Subject: [PATCH] Added onboarding modal --- apps/deploy-fe/repo_structure.txt | 48 +- .../ps/(create)/cr/(configure)/cf/page.tsx | 227 ------ .../ps/(create)/cr/(deploy)/dp/page.tsx | 270 ------- .../ps/(create)/cr/(success)/sc/[id]/page.tsx | 258 ------- .../[provider]/ps/(create)/cr/page.tsx | 203 +++++ .../(dashboard)/projects/page.tsx | 2 +- .../onboarding/OnboardingDialog.tsx | 730 +++++++++++++----- .../onboarding/OnboardingSidebar.tsx | 69 ++ .../configure-step/configure-step.tsx | 356 +++++++-- .../configure-step/disable_configure-step.tsx | 68 ++ .../onboarding/connect-step/connect-step.tsx | 198 ++++- .../connect-step/disabled_connect-step.tsx | 155 ++++ .../connect-step/repository-list.tsx | 160 +++- .../onboarding/connect-step/template-list.tsx | 156 ++-- .../onboarding/deploy-step/deploy-step.tsx | 267 ++++++- .../deploy-step/disabled_deploy-step.tsx | 42 + .../src/components/onboarding/index.ts | 3 +- .../onboarding/success-step/success-step.tsx | 97 +++ .../components/onboarding/useOnboarding.ts | 100 ++- 19 files changed, 2193 insertions(+), 1216 deletions(-) delete mode 100644 apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(configure)/cf/page.tsx delete mode 100644 apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(deploy)/dp/page.tsx delete mode 100644 apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(success)/sc/[id]/page.tsx create mode 100644 apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/page.tsx create mode 100644 apps/deploy-fe/src/components/onboarding/OnboardingSidebar.tsx create mode 100644 apps/deploy-fe/src/components/onboarding/configure-step/disable_configure-step.tsx create mode 100644 apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx create mode 100644 apps/deploy-fe/src/components/onboarding/deploy-step/disabled_deploy-step.tsx create mode 100644 apps/deploy-fe/src/components/onboarding/success-step/success-step.tsx diff --git a/apps/deploy-fe/repo_structure.txt b/apps/deploy-fe/repo_structure.txt index 7e31950..b24ba51 100644 --- a/apps/deploy-fe/repo_structure.txt +++ b/apps/deploy-fe/repo_structure.txt @@ -1,6 +1,6 @@ +./.env.example ./.env.local ./.gitignore -./.turbo/turbo-build.log ./.vscode/settings.json ./biome.jsonc ./components.json @@ -15,26 +15,28 @@ ./src/app/(web3-authenticated)/(dashboard)/home/loading.tsx ./src/app/(web3-authenticated)/(dashboard)/home/page.tsx ./src/app/(web3-authenticated)/(dashboard)/layout.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/(create)/cr/(configure)/cf/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/(create)/cr/(deploy)/dp/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/(create)/cr/(success)/sc/[id]/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/(create)/cr/(template)/tm/(configure)/cf/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/(create)/cr/(template)/tm/(deploy)/dp/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(deployments)/dep/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(integrations)/int/GitPage.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(integrations)/int/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/(collaborators)/col/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/(domains)/dom/(add)/cf/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/(domains)/dom/(add)/config/cf/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/(environment-variables)/env/EnvVarsPage.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/(environment-variables)/env/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/(git)/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/ProjectSettingsPage.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/(settings)/set/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/deployments/page.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/layout.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/loading.tsx -./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/(projects)/ps/[id]/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(configure)/cf/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(connect)/cn/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(deploy)/dp/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(success)/sc/[id]/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(template)/tm/(configure)/cf/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(template)/tm/(deploy)/dp/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(deployments)/dep/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(integrations)/int/GitPage.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(integrations)/int/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(collaborators)/col/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(domains)/dom/(add)/cf/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(domains)/dom/(add)/config/cf/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(environment-variables)/env/EnvVarsPage.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(environment-variables)/env/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(git)/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/ProjectSettingsPage.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/deployments/page.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/layout.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/loading.tsx +./src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/page.tsx ./src/app/(web3-authenticated)/(dashboard)/projects/error.tsx ./src/app/(web3-authenticated)/(dashboard)/projects/loading.tsx ./src/app/(web3-authenticated)/(dashboard)/projects/page.tsx @@ -134,6 +136,7 @@ ./src/components/iframe/auto-sign-in/index.ts ./src/components/iframe/auto-sign-in/types.ts ./src/components/iframe/check-balance-iframe/CheckBalanceIframe.tsx +./src/components/iframe/check-balance-iframe/CheckBalanceWrapper.tsx ./src/components/iframe/check-balance-iframe/useCheckBalance.tsx ./src/components/layout/index.ts ./src/components/layout/navigation/github-session-button/GitHubSessionButton.tsx @@ -203,7 +206,10 @@ ./src/context/WalletContext.tsx ./src/context/WalletContextProvider.tsx ./src/context/index.ts +./src/hooks/disabled_useRepoData.tsx +./src/hooks/useDeployment.tsx ./src/hooks/useRepoData.tsx +./src/hooks/useRepoSelection.tsx ./src/lib/utils.ts ./src/middleware.ts ./src/types/common.ts diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(configure)/cf/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(configure)/cf/page.tsx deleted file mode 100644 index a47d879..0000000 --- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(configure)/cf/page.tsx +++ /dev/null @@ -1,227 +0,0 @@ -'use client' -import { useState, useEffect } from 'react' -import { useParams, useRouter } from 'next/navigation' -import { PageWrapper } from '@/components/foundation' -import { LoadingOverlay } from '@/components/foundation/loading/loading-overlay' -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@workspace/ui/components/card' -import { Input } from '@workspace/ui/components/input' -import { Label } from '@workspace/ui/components/label' -import { Button } from '@workspace/ui/components/button' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@workspace/ui/components/select' -import { toast } from 'sonner' -import { Stepper } from '@/components/core/stepper/Stepper' -import { useRepoData } from '@/hooks/useRepoData' - -export default function ConfigureDeploymentPage() { - const router = useRouter() - const params = useParams() - const providerParam = params?.provider ? String(params.provider) : 'github' - - // Use the existing useRepoData hook to fetch all repos (empty string for ID means all repos) - const { repoData: repositories, isLoading } = useRepoData('') - - const [selectedRepo, setSelectedRepo] = useState('') - const [selectedBranch, setSelectedBranch] = useState('main') - const [projectName, setProjectName] = useState('') - const [branches, setBranches] = useState(['main']) - const [envVars, setEnvVars] = useState<{ key: string; value: string }[]>([ - { key: '', value: '' } - ]) - - // Define stepper values for the existing Stepper component - const stepperValues = [ - { step: 1, label: 'Select Repository', route: '/projects/github/ps/cr/tm/cf' }, - { step: 2, label: 'Configure', route: '/projects/github/ps/cr/cf' }, - { step: 3, label: 'Deploy', route: '/projects/github/ps/cr/dp' }, - { step: 4, label: 'Success', route: '/projects/github/ps/cr/sc' } - ] - - // When a repository is selected, update project name and branch - useEffect(() => { - if (!selectedRepo || !repositories) return - - const repo = repositories.find(r => r.full_name === selectedRepo) - if (repo) { - setProjectName(repo.name) - setSelectedBranch(repo.default_branch) - - // For simplicity, just use the default branch and some common branch names - // In a real implementation, you would fetch branches for the selected repo - setBranches([repo.default_branch, 'develop', 'feature/new-ui']) - } - }, [selectedRepo, repositories]) - - const handleRepoChange = (repo: string) => { - setSelectedRepo(repo) - } - - const handleBranchChange = (branch: string) => { - setSelectedBranch(branch) - } - - const handleProjectNameChange = (e: React.ChangeEvent) => { - setProjectName(e.target.value) - } - - const handleEnvVarChange = (index: number, field: 'key' | 'value', value: string) => { - const newEnvVars = [...envVars] - newEnvVars[index][field] = value - - // Add a new empty row if the last row has both key and value filled - if ( - index === newEnvVars.length - 1 && - newEnvVars[index].key !== '' && - newEnvVars[index].value !== '' - ) { - newEnvVars.push({ key: '', value: '' }) - } - - setEnvVars(newEnvVars) - } - - const handleSubmit = () => { - if (!selectedRepo || !selectedBranch || !projectName) { - toast.error('Please fill in all required fields') - return - } - - // Filter out empty env vars - const filteredEnvVars = envVars.filter( - envVar => envVar.key.trim() !== '' && envVar.value.trim() !== '' - ) - - // Convert env vars array to object - const environmentVariables = filteredEnvVars.reduce( - (acc, { key, value }) => ({ ...acc, [key]: value }), - {} - ) - - // Find the selected repository to get its URL - const repo = repositories?.find(r => r.full_name === selectedRepo) - - // Store the configuration in session storage to be used in the next step - sessionStorage.setItem( - 'deploymentConfig', - JSON.stringify({ - repositoryUrl: selectedRepo, - repositoryHtmlUrl: repo?.html_url || `https://github.com/${selectedRepo}`, - branch: selectedBranch, - projectName, - environmentVariables - }) - ) - - // Navigate to the deployment page - router.push(`/projects/${providerParam}/ps/cr/dp`) - } - - if (isLoading) { - return - } - - return ( - -
- {/* Using the existing Stepper component with the correct props */} - - - - - Project Configuration - - Configure your project settings for deployment - - - -
- - -
- -
- - -
- -
- - -
- -
- -
- {envVars.map((envVar, index) => ( -
- handleEnvVarChange(index, 'key', e.target.value)} - className="flex-1" - /> - handleEnvVarChange(index, 'value', e.target.value)} - className="flex-1" - /> -
- ))} -
-

- Environment variables will be securely stored and available during build and runtime. -

-
-
- - - - -
-
-
- ) -} \ No newline at end of file diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(deploy)/dp/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(deploy)/dp/page.tsx deleted file mode 100644 index bd619a9..0000000 --- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(deploy)/dp/page.tsx +++ /dev/null @@ -1,270 +0,0 @@ -'use client' -import { useState, useEffect } from 'react' -import { useParams, useRouter } from 'next/navigation' -import { PageWrapper } from '@/components/foundation' -import { LoadingOverlay } from '@/components/foundation/loading/loading-overlay' -import { Stepper } from '@/components/core/stepper/Stepper' -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@workspace/ui/components/card' -import { Button } from '@workspace/ui/components/button' -import { toast } from 'sonner' -import { Progress } from '@workspace/ui/components/progress' -import { Loader2, CheckCircle, AlertCircle, GitBranch } from 'lucide-react' -import { StopWatch } from '@/components/core/stop-watch' - -interface DeploymentConfig { - repositoryUrl: string; - repositoryHtmlUrl: string; - branch: string; - projectName: string; - environmentVariables: Record; -} - -export default function DeployPage() { - const router = useRouter() - const params = useParams() - const providerParam = params?.provider ? String(params.provider) : 'github' - - const [deploymentConfig, setDeploymentConfig] = useState(null) - const [isLoading, setIsLoading] = useState(true) - const [isDeploying, setIsDeploying] = useState(false) - const [deploymentStatus, setDeploymentStatus] = useState<'idle' | 'pending' | 'building' | 'ready' | 'error'>('idle') - const [deploymentProgress, setDeploymentProgress] = useState(0) - const [, setElapsedTime] = useState(0) - const [deploymentId, setDeploymentId] = useState('') - - // Define stepper values for the existing Stepper component - const stepperValues = [ - { step: 1, label: 'Select Repository', route: '/projects/github/ps/cr/tm/cf' }, - { step: 2, label: 'Configure', route: '/projects/github/ps/cr/cf' }, - { step: 3, label: 'Deploy', route: '/projects/github/ps/cr/dp' }, - { step: 4, label: 'Success', route: '/projects/github/ps/cr/sc' } - ] - - // Load deployment config from session storage - useEffect(() => { - const storedConfig = sessionStorage.getItem('deploymentConfig') - - if (storedConfig) { - setDeploymentConfig(JSON.parse(storedConfig)) - } else { - toast.error('Deployment configuration not found') - router.push(`/projects/${providerParam}/ps/cr/cf`) - } - - setIsLoading(false) - }, [router, providerParam]) - - // Handle elapsed time updates from StopWatch component - const handleTimeUpdate = (time: number) => { - setElapsedTime(time) - } - - // Simulate deployment process (would connect to your backend in a real implementation) - const startDeployment = () => { - if (!deploymentConfig) { - toast.error('Deployment configuration not found') - return - } - - setIsDeploying(true) - setDeploymentStatus('pending') - setDeploymentProgress(10) - - // Simulate deployment steps with timeouts - setTimeout(() => { - setDeploymentStatus('building') - setDeploymentProgress(40) - - setTimeout(() => { - // 80% chance of success, 20% chance of failure (for demo purposes) - const success = Math.random() < 0.8 - - if (success) { - setDeploymentStatus('ready') - setDeploymentProgress(100) - - // Generate a random ID for the deployment - const id = Math.random().toString(36).substring(2, 10) - setDeploymentId(id) - - // Store deployment details in session storage - const deploymentDetails = { - id, - url: `https://${deploymentConfig.projectName.toLowerCase().replace(/[^a-z0-9]/g, '-')}.laconic.deploy`, - projectId: 'project_' + Math.random().toString(36).substring(2, 10), - projectName: deploymentConfig.projectName, - status: 'ready', - createdAt: new Date().toISOString(), - repository: { - name: deploymentConfig.repositoryUrl.split('/')[1], - url: deploymentConfig.repositoryHtmlUrl || `https://github.com/${deploymentConfig.repositoryUrl}`, - branch: deploymentConfig.branch - } - }; - - sessionStorage.setItem('deploymentResult', JSON.stringify(deploymentDetails)) - - // Navigate to success page after a short delay - setTimeout(() => { - router.push(`/projects/${providerParam}/ps/cr/sc/${id}`) - }, 2000) - } else { - setDeploymentStatus('error') - setDeploymentProgress(100) - } - - setIsDeploying(false) - }, 5000) // 5 seconds for building - }, 3000) // 3 seconds for pending - } - - const getStatusIcon = () => { - switch (deploymentStatus) { - case 'pending': - return - case 'building': - return - case 'ready': - return - case 'error': - return - default: - return null - } - } - - const getStatusText = () => { - switch (deploymentStatus) { - case 'pending': - return 'Preparing deployment...' - case 'building': - return 'Building your project...' - case 'ready': - return 'Deployment successful!' - case 'error': - return 'Deployment failed' - default: - return 'Ready to deploy' - } - } - - if (isLoading) { - return - } - - return ( - -
- {/* Using the existing Stepper component with the correct props */} - - - - - Deployment - - Deploy your project to Laconic's decentralized hosting - - - - {deploymentConfig && ( -
-
-

Repository

-
- -

- {deploymentConfig.repositoryUrl} ({deploymentConfig.branch}) -

-
-
-
-

Project Name

-

- {deploymentConfig.projectName} -

-
- {Object.keys(deploymentConfig.environmentVariables || {}).length > 0 && ( -
-

Environment Variables

-

- {Object.keys(deploymentConfig.environmentVariables).length} environment variables configured -

-
- )} -
- )} - - {deploymentStatus === 'idle' ? ( -
-

- Ready to deploy your project? Click the button below to start the deployment process. -

- -
- ) : ( -
-
-
- {getStatusIcon()} -

{getStatusText()}

-
- {/* Using your existing StopWatch component */} - -
- -

- {deploymentStatus === 'pending' && 'Setting up the deployment environment...'} - {deploymentStatus === 'building' && 'Building your application...'} - {deploymentStatus === 'ready' && 'Deployment completed successfully!'} - {deploymentStatus === 'error' && 'There was an error deploying your application. Please try again.'} -

-
- )} -
- - - {deploymentStatus === 'ready' && ( - - )} - {deploymentStatus === 'error' && ( - - )} - -
-
-
- ) -} \ No newline at end of file diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(success)/sc/[id]/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(success)/sc/[id]/page.tsx deleted file mode 100644 index 11122f7..0000000 --- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/(success)/sc/[id]/page.tsx +++ /dev/null @@ -1,258 +0,0 @@ -'use client' -import { useState, useEffect } from 'react' -import { useParams, useRouter } from 'next/navigation' -import { PageWrapper } from '@/components/foundation' -import { LoadingOverlay } from '@/components/foundation/loading/loading-overlay' -import { Stepper } from '@/components/core/stepper/Stepper' -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@workspace/ui/components/card' -import { Button } from '@workspace/ui/components/button' -import { toast } from 'sonner' -import { CheckCircle, Copy, ExternalLink, Clock } from 'lucide-react' -import Link from 'next/link' -import { relativeTimeMs } from '@/utils/time' -import { getInitials } from '@/utils/getInitials' -import { Avatar, AvatarFallback } from '@workspace/ui/components/avatar' - -interface DeploymentDetails { - id: string; - url: string; - projectId: string; - projectName: string; - status: string; - createdAt: string; - repository: { - name: string; - url: string; - branch: string; - }; -} - -export default function SuccessPage({ params }: { params: { id: string } }) { - const router = useRouter() - const paramsObj = useParams() - const providerParam = paramsObj?.provider ? String(paramsObj.provider) : 'github' - - const [isLoading, setIsLoading] = useState(true) - const [deployment, setDeployment] = useState(null) - const deploymentId = params.id - - // Define stepper values for the existing Stepper component - const stepperValues = [ - { step: 1, label: 'Select Repository', route: '/projects/github/ps/cr/tm/cf' }, - { step: 2, label: 'Configure', route: '/projects/github/ps/cr/cf' }, - { step: 3, label: 'Deploy', route: '/projects/github/ps/cr/dp' }, - { step: 4, label: 'Success', route: '/projects/github/ps/cr/sc' } - ] - - // Get deployment details from session storage - useEffect(() => { - // For now, we'll get the deployment details from session storage - // In a real app, you'd fetch this from your API - const storedDeployment = sessionStorage.getItem('deploymentResult') - - if (storedDeployment) { - setDeployment(JSON.parse(storedDeployment)) - } else { - // If not found in session storage, simulate it (for demo purposes) - // In a real app, you'd fetch from the API using the ID - simulateDeploymentDetails() - } - - setIsLoading(false) - }, [deploymentId]) - - // Simulate deployment details if needed (for demo purposes) - const simulateDeploymentDetails = () => { - const mockDeployment: DeploymentDetails = { - id: deploymentId, - url: `https://project-${deploymentId}.laconic.deploy`, - projectId: 'project_' + Math.random().toString(36).substring(2, 10), - projectName: 'Demo Project', - status: 'ready', - createdAt: new Date().toISOString(), - repository: { - name: 'demo-repo', - url: 'https://github.com/yourusername/demo-repo', - branch: 'main' - } - } - - setDeployment(mockDeployment) - } - - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text) - toast.success('Copied to clipboard') - } - - if (isLoading) { - return - } - - if (!deployment) { - return ( - -
- - -
-

- We couldn't find the deployment you're looking for. It may have been deleted or expired. -

- -
-
-
-
-
- ) - } - - // Calculate relative time for the deployment - const deploymentTime = new Date(deployment.createdAt).getTime() - const deployedBy = 'You' // In a real app, you'd get this from the deployment data - - return ( - -
- {/* Using the existing Stepper component with the correct props */} - - - - -
- - Deployment Successful -
- - Your project has been successfully deployed to Laconic's decentralized hosting - -
- -
-

Deployment URL

-
- - {deployment.url} - - - -
-
- -
-
-

Project Details

-
    -
  • - Project Name:{' '} - {deployment.projectName} -
  • -
  • - Repository:{' '} - {deployment.repository.name} -
  • -
  • - Branch:{' '} - {deployment.repository.branch} -
  • -
  • - Deployment ID:{' '} - {deployment.id} -
  • -
-
-
-

Deployment Information

-
    -
  • - Status:{' '} - - {deployment.status.toUpperCase()} - -
  • -
  • -
    - - Deployed at:{' '} - {relativeTimeMs(deploymentTime)} -
    -
  • -
  • -
    - Deployed by:{' '} - - - {getInitials(deployedBy)} - - {deployedBy} - -
    -
  • -
-
-
- -
-

What's Next?

-
    -
  • • Configure custom domains for your deployment
  • -
  • • Set up automatic deployments for new commits
  • -
  • • Add collaborators to your project
  • -
  • • Monitor deployment performance and analytics
  • -
-
-
- - - - -
-
-
- ) \ No newline at end of file diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/page.tsx new file mode 100644 index 0000000..a34b50c --- /dev/null +++ b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/(create)/cr/page.tsx @@ -0,0 +1,203 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useRouter } from 'next/navigation' +import { X } from 'lucide-react' +import { useTheme } from 'next-themes' +import { useOnboarding } from '@/components/onboarding/useOnboarding' +import { ConnectStep } from '@/components/onboarding/connect-step/connect-step' +import { ConfigureStep } from '@/components/onboarding/configure-step/configure-step' +import { DeployStep } from '@/components/onboarding/deploy-step/deploy-step' +import { SuccessStep } from '@/components/onboarding/success-step/success-step' +import { LaconicMark } from '@/components/assets/laconic-mark' + +/** + * Parent component for the onboarding flow + * Handles the overall layout and step transitions + */ +export default function CreateProjectFlow() { + const router = useRouter() + const { resolvedTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + const { currentStep, setCurrentStep, resetOnboarding } = useOnboarding() + + // Handle hydration mismatch by waiting for mount + useEffect(() => { + setMounted(true) + }, []) + + // Reset onboarding state when the component unmounts (optional) + useEffect(() => { + return () => { + // Optional cleanup actions + } + }, [resetOnboarding]) + + // Handle closing the modal + const handleClose = () => { + router.push('/projects') + } + + // Navigate directly to a specific step + const navigateToStep = (step: 'connect' | 'configure' | 'deploy') => { + setCurrentStep(step) + } + + // Don't render UI until after mount to prevent hydration mismatch + if (!mounted) { + return null + } + + // Determine if dark mode is active + const isDarkMode = resolvedTheme === 'dark' + + return ( +
+ {/* Fixed dimensions modal container */} +
+ {/* Left sidebar with fixed width */} +
+ {/* Laconic logo */} +
+ + LACONIC +
+ + {/* Steps - clickable */} +
+ {/* Connect step */} + + + {/* Configure step */} + + + {/* Deploy step */} + +
+ + {/* Laconic mark (larger, bottom left) */} +
+ +
+
+ + {/* Main content with fixed dimensions and scrolling */} +
+ {/* Close button */} + + + {/* Scrollable content container */} +
+ {currentStep === 'connect' && } + {currentStep === 'configure' && } + {currentStep === 'deploy' && } + {currentStep === 'success' && } +
+ + {/* Progress indicator */} +
+
+
+
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/page.tsx index efbd9e4..35ce1da 100644 --- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/page.tsx +++ b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/page.tsx @@ -72,7 +72,7 @@ export default function ProjectsPage() { void +// } + +// /** +// * OnboardingDialog component +// * +// * A dialog modal that contains the onboarding flow. +// * Can be triggered by a custom element or automatically opened. +// * Sets the initial step based on GitHub connection status. +// * Provides warnings when exiting mid-step and options to continue progress. +// */ +// const OnboardingDialog: React.FC = ({ +// trigger, +// defaultOpen = false, +// onClose +// }) => { +// const onboardingStore = useOnboarding() +// const { setCurrentStep, currentStep, formData } = onboardingStore +// // const { octokit } = useOctokit() +// const [showExitWarning, setShowExitWarning] = useState(false) +// const [showContinueAlert, setShowContinueAlert] = useState(false) +// const [isOpen, setIsOpen] = useState(defaultOpen) +// const [forceConnectStep, setForceConnectStep] = useState(false) + +// // Check for force connect flag when component mounts +// useEffect(() => { +// const shouldForceConnect = +// localStorage.getItem(ONBOARDING_FORCE_CONNECT_KEY) === 'true' +// if (shouldForceConnect) { +// setForceConnectStep(true) +// // Clear the flag so it's only used once +// localStorage.removeItem(ONBOARDING_FORCE_CONNECT_KEY) +// } +// }, []) + +// // Local implementation of reset function that handles all necessary state +// const resetOnboardingState = () => { +// // Reset step to connect +// setCurrentStep('connect') + +// // Flag to force starting from the connect step +// setForceConnectStep(true) + +// // Also reset form data to ensure substeps are cleared +// const store = onboardingStore +// store.setFormData({ +// projectName: '', +// repoName: '', +// repoDescription: '', +// framework: '', +// access: 'public', +// organizationSlug: '' +// }) +// } + +// // Close and reset onboarding dialog +// const closeOnboarding = () => { +// // Remove the "in progress" flag from localStorage +// localStorage.removeItem(ONBOARDING_PROGRESS_KEY) +// // Also remove saved state to prevent issues on next open +// localStorage.removeItem(ONBOARDING_STATE_KEY) +// // Reset component state +// resetOnboardingState() +// setShowContinueAlert(false) +// // Explicitly set isOpen to false to ensure dialog closes +// setIsOpen(false) +// } + +// // Check if there's existing progress +// useEffect(() => { +// if (isOpen) { +// const savedProgress = localStorage.getItem(ONBOARDING_PROGRESS_KEY) +// const savedState = localStorage.getItem(ONBOARDING_STATE_KEY) + +// if (savedProgress === 'true' && savedState && !forceConnectStep) { +// // Show continue or start fresh dialog +// setShowContinueAlert(true) +// } else { +// // Set initial step based on GitHub connection status +// initializeOnboarding() +// } +// } +// }, [isOpen, forceConnectStep]) + +// // Set the initial step based on GitHub connection status +// const initializeOnboarding = () => { +// // Reset previous state +// resetOnboardingState() + +// // If GitHub is connected AND we're not forcing the connect step, +// // start at the configure step. Otherwise, start at the connect step +// // if (octokit && !forceConnectStep) { +// // setCurrentStep('configure') +// // } else { +// // setCurrentStep('connect') +// // } + +// // Mark that we have onboarding in progress +// localStorage.setItem(ONBOARDING_PROGRESS_KEY, 'true') + +// // Save the initial state +// saveCurrentState() +// } + +// // Start fresh by initializing onboarding and forcing the connect step + +// // Continue from saved state and don't force the connect step + +// // Save current onboarding state +// const saveCurrentState = () => { +// try { +// const state = { +// currentStep, +// formData, +// forceConnectStep // Save this flag as part of the state +// } +// localStorage.setItem(ONBOARDING_STATE_KEY, JSON.stringify(state)) +// } catch (error) { +// console.error('Error saving onboarding state:', error) +// } +// } + +// // Load saved onboarding state + +// // Save state on step changes +// // useEffect(() => { +// // if (isOpen) { +// // saveCurrentState() +// // } +// // }, [isOpen, currentStep, formData, forceConnectStep]) + +// // Mark onboarding as completed when user reaches the deploy step +// useEffect(() => { +// if (currentStep === 'deploy') { +// localStorage.setItem(ONBOARDING_COMPLETED_KEY, 'true') +// } +// }, [currentStep]) + +// // Handle dialog close attempt +// const handleOpenChange = (open: boolean) => { +// // First update the isOpen state to ensure UI responds immediately +// setIsOpen(open) + +// if (!open) { +// // When dialog is closing, properly clean up +// closeOnboarding() + +// if (onClose) { +// onClose() +// } +// } +// } + +// // Define the missing functions to handle dialog closing + +// return ( +// <> +// +// {trigger && {trigger}} +// +// +// Onboarding +// +//
+// +//
+//
+//
+ +// {/* Exit Warning Dialog */} +// {/* +// +// Exit Onboarding? +// +// You haven't completed the onboarding process. If you exit now, your +// progress will be lost, including any organization or repository +// selections. +// +// +// Cancel +// +// Exit Anyway +// +// +// +// */} + +// {/* Continue Progress Dialog */} +// {/* +// +// Continue Onboarding? +// +// You're in the middle of setting up your project, including +// organization and repository selection. Would you like to continue +// where you left off or start fresh? +// +// +// +// Start Fresh +// +// +// Continue +// +// +// +// */} +// +// ) +// } + +// /** +// * Helper function to check if the user has completed onboarding +// * @returns {boolean} Whether onboarding has been completed +// */ +// export const hasCompletedOnboarding = (): boolean => { +// return localStorage.getItem(ONBOARDING_COMPLETED_KEY) === 'true' +// } + +// 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 { useRepoData } from '@/hooks/useRepoData' +import { Dialog, DialogContent, DialogTitle } from '@workspace/ui/components/dialog' import { VisuallyHidden } from '@radix-ui/react-visually-hidden' -import { - Dialog, - DialogContent, - DialogTitle, - DialogTrigger -} from '@workspace/ui/components/dialog' -import { useEffect, useState } from 'react' -import Onboarding from './Onboarding' -import { useOnboarding } from './store' - -// Local storage keys -const ONBOARDING_COMPLETED_KEY = 'onboarding_completed' -const ONBOARDING_STATE_KEY = 'onboarding_state' -const ONBOARDING_PROGRESS_KEY = 'onboarding_progress' -const ONBOARDING_FORCE_CONNECT_KEY = 'onboarding_force_connect' interface OnboardingDialogProps { trigger?: React.ReactNode @@ -28,214 +263,293 @@ interface OnboardingDialogProps { * * A dialog modal that contains the onboarding flow. * Can be triggered by a custom element or automatically opened. - * Sets the initial step based on GitHub connection status. - * Provides warnings when exiting mid-step and options to continue progress. */ const OnboardingDialog: React.FC = ({ trigger, defaultOpen = false, onClose }) => { - const onboardingStore = useOnboarding() - const { setCurrentStep, currentStep, formData } = onboardingStore - // const { octokit } = useOctokit() - const [showExitWarning, setShowExitWarning] = useState(false) - const [showContinueAlert, setShowContinueAlert] = useState(false) + const { nextStep, setFormData, formData, currentStep } = useOnboarding() + const [selectedRepo, setSelectedRepo] = useState(formData.githubRepo || '') + const [isImportMode, setIsImportMode] = useState(true) const [isOpen, setIsOpen] = useState(defaultOpen) - const [forceConnectStep, setForceConnectStep] = useState(false) - - // Check for force connect flag when component mounts - useEffect(() => { - const shouldForceConnect = - localStorage.getItem(ONBOARDING_FORCE_CONNECT_KEY) === 'true' - if (shouldForceConnect) { - setForceConnectStep(true) - // Clear the flag so it's only used once - localStorage.removeItem(ONBOARDING_FORCE_CONNECT_KEY) - } - }, []) - - // Local implementation of reset function that handles all necessary state - const resetOnboardingState = () => { - // Reset step to connect - setCurrentStep('connect') - - // Flag to force starting from the connect step - setForceConnectStep(true) - - // Also reset form data to ensure substeps are cleared - const store = onboardingStore - store.setFormData({ - projectName: '', - repoName: '', - repoDescription: '', - framework: '', - access: 'public', - organizationSlug: '' - }) + 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') } - // Close and reset onboarding dialog - const closeOnboarding = () => { - // Remove the "in progress" flag from localStorage - localStorage.removeItem(ONBOARDING_PROGRESS_KEY) - // Also remove saved state to prevent issues on next open - localStorage.removeItem(ONBOARDING_STATE_KEY) - // Reset component state - resetOnboardingState() - setShowContinueAlert(false) - // Explicitly set isOpen to false to ensure dialog closes - setIsOpen(false) - } - - // Check if there's existing progress - useEffect(() => { - if (isOpen) { - const savedProgress = localStorage.getItem(ONBOARDING_PROGRESS_KEY) - const savedState = localStorage.getItem(ONBOARDING_STATE_KEY) - - if (savedProgress === 'true' && savedState && !forceConnectStep) { - // Show continue or start fresh dialog - setShowContinueAlert(true) - } else { - // Set initial step based on GitHub connection status - initializeOnboarding() - } - } - }, [isOpen, forceConnectStep]) - - // Set the initial step based on GitHub connection status - const initializeOnboarding = () => { - // Reset previous state - resetOnboardingState() - - // If GitHub is connected AND we're not forcing the connect step, - // start at the configure step. Otherwise, start at the connect step - // if (octokit && !forceConnectStep) { - // setCurrentStep('configure') - // } else { - // setCurrentStep('connect') - // } - - // Mark that we have onboarding in progress - localStorage.setItem(ONBOARDING_PROGRESS_KEY, 'true') - - // Save the initial state - saveCurrentState() - } - - // Start fresh by initializing onboarding and forcing the connect step - - // Continue from saved state and don't force the connect step - - // Save current onboarding state - const saveCurrentState = () => { - try { - const state = { - currentStep, - formData, - forceConnectStep // Save this flag as part of the state - } - localStorage.setItem(ONBOARDING_STATE_KEY, JSON.stringify(state)) - } catch (error) { - console.error('Error saving onboarding state:', error) - } - } - - // Load saved onboarding state - - // Save state on step changes - // useEffect(() => { - // if (isOpen) { - // saveCurrentState() - // } - // }, [isOpen, currentStep, formData, forceConnectStep]) - - // Mark onboarding as completed when user reaches the deploy step - useEffect(() => { - if (currentStep === 'deploy') { - localStorage.setItem(ONBOARDING_COMPLETED_KEY, 'true') - } - }, [currentStep]) - - // Handle dialog close attempt + // Handle dialog open/close const handleOpenChange = (open: boolean) => { - // First update the isOpen state to ensure UI responds immediately setIsOpen(open) - - if (!open) { - // When dialog is closing, properly clean up - closeOnboarding() - - if (onClose) { - onClose() - } + if (!open && onClose) { + onClose() } } - - // Define the missing functions to handle dialog closing - + return ( - <> - - {trigger && {trigger}} - - - Onboarding - -
- + + {trigger && trigger} + + + Onboarding + + +
+ {/* Left sidebar with steps */} +
+ {/* Laconic logo */} +
+ + + + LACONIC +
+ + {/* Steps */} +
+ {/* Connect step */} +
+
+
+ + + + +
+
+
+
+

Connect

+

Connect and import a GitHub repo

+
+
+ + {/* Configure step */} +
+
+
+ + + + +
+
+
+
+

Configure

+

Define the deployment type

+
+
+ + {/* Deploy step */} +
+
+
+ + + + + +
+
+
+

Deploy

+

Review and confirm deployment

+
+
+
+ + {/* Background shape (decorative) */} +
- -
- - {/* Exit Warning Dialog */} - {/* - - Exit Onboarding? - - You haven't completed the onboarding process. If you exit now, your - progress will be lost, including any organization or repository - selections. - - - Cancel - - Exit Anyway - - - - */} - - {/* Continue Progress Dialog */} - {/* - - Continue Onboarding? - - You're in the middle of setting up your project, including - organization and repository selection. Would you like to continue - where you left off or start fresh? - - - - Start Fresh - - - Continue - - - - */} - + + {/* Main content */} +
+ {/* Close button */} + + + {currentStep === 'connect' && ( +
+ {/* Connect icon */} +
+ + + + +
+ + {/* Connect header */} +

Connect

+

+ Connect and import a GitHub repo or start from a template +

+ + {/* Git account selector */} +
+ +
+ + {/* Mode buttons */} +
+ + +
+ + {/* Repository or template list */} + {isImportMode ? ( +
+ +
+ ) : ( +
+ {}} /> +
+ )} + + {/* Navigation buttons */} +
+ +
+
+
+
+
+ +
+
+ )} + + {currentStep === 'configure' && ( +
+ {/* Configure content will go here */} +
+

Configure

+

+ Define your deployment configuration +

+ +
+ Configure step content will be implemented here +
+ + {/* Navigation buttons */} +
+ +
+
+
+
+
+ +
+
+
+ )} + + {currentStep === 'deploy' && ( +
+ {/* Deploy content will go here */} +
+

Deploy

+

+ Review and confirm your deployment +

+ +
+ Deploy step content will be implemented here +
+ + {/* Navigation buttons */} +
+ +
+
+
+
+
+ +
+
+
+ )} +
+
+
+
) } -/** - * Helper function to check if the user has completed onboarding - * @returns {boolean} Whether onboarding has been completed - */ -export const hasCompletedOnboarding = (): boolean => { - return localStorage.getItem(ONBOARDING_COMPLETED_KEY) === 'true' -} - -export default OnboardingDialog +export default OnboardingDialog \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/OnboardingSidebar.tsx b/apps/deploy-fe/src/components/onboarding/OnboardingSidebar.tsx new file mode 100644 index 0000000..837e809 --- /dev/null +++ b/apps/deploy-fe/src/components/onboarding/OnboardingSidebar.tsx @@ -0,0 +1,69 @@ +'use client' + +import { GitBranch, Settings, Box } from 'lucide-react' +import type { Step } from '@/components/onboarding/useOnboarding' + +interface OnboardingSidebarProps { + currentStep: Step +} + +export function OnboardingSidebar({ currentStep }: OnboardingSidebarProps) { + return ( +
+ {/* Laconic logo */} +
+ + + + LACONIC +
+ + {/* Steps */} +
+ {/* Connect step */} +
+
+
+ +
+
+
+
+

Connect

+

Connect and import a GitHub repo

+
+
+ + {/* Configure step */} +
+
+
+ +
+
+
+
+

Configure

+

Define the deployment type

+
+
+ + {/* Deploy step */} +
+
+
+ +
+
+
+

Deploy

+

Review and confirm deployment

+
+
+
+ + {/* Background shape (decorative) */} +
+
+ ) +} \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx b/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx index 98566b3..9ab879f 100644 --- a/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx +++ b/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx @@ -1,68 +1,320 @@ -import { FileCog } from 'lucide-react' -import { useState } from 'react' -import { useOnboarding } from '../store' +'use client' + +import { useState, useEffect } from 'react' +import { PlusCircle } from 'lucide-react' +import { useTheme } from 'next-themes' +import { useOnboarding } from '@/components/onboarding/useOnboarding' +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' -/** - * 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 { nextStep, previousStep, setFormData, formData } = useOnboarding() + const { resolvedTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + // Form state + const [deployOption, setDeployOption] = useState<'auction' | 'lrn'>( + formData.deploymentType as ('auction' | 'lrn') || 'auction' ) - const [environments, setEnvironments] = useState({ + const [numberOfDeployers, setNumberOfDeployers] = useState( + formData.deployerCount || "1" + ) + const [maxPrice, setMaxPrice] = useState( + formData.maxPrice || "1000" + ) + const [selectedLrn, setSelectedLrn] = useState( + formData.selectedLrn || "" + ) + const [selectedAccount, setSelectedAccount] = useState("") + const [environments, setEnvironments] = useState<{ + production: boolean, + preview: boolean, + development: boolean + }>(formData.environments || { 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], - // }, - // }) - // } - + const [envVars, setEnvVars] = useState<{ key: string; value: string }[]>([ + { key: '', value: '' } + ]) + + // Handle hydration mismatch by waiting for mount + useEffect(() => { + setMounted(true) + }, []) + + // Initialize environment variables from formData if available + useEffect(() => { + if (formData.environmentVariables) { + const vars: { key: string; value: string }[] = Object.entries(formData.environmentVariables).map( + ([key, value]) => ({ key, value }) + ) + setEnvVars(vars.length > 0 ? vars : [{ key: '', value: '' }]) + } + }, [formData.environmentVariables]) + + // Add an empty environment variable row + const addEnvVar = () => { + setEnvVars([...envVars, { key: '', value: '' }]) + } + + // Toggle deployment option + const toggleDeployOption = (option: 'auction' | 'lrn') => { + setDeployOption(option) + } + + // Toggle environment checkbox + const toggleEnvironment = (env: 'production' | 'preview' | 'development') => { + setEnvironments({ + ...environments, + [env]: !environments[env] + }) + } + + // Handle next step + const handleNext = () => { + // Save configuration to form data + setFormData({ + deploymentType: deployOption, + deployerCount: numberOfDeployers, + maxPrice: maxPrice, + selectedLrn: selectedLrn, + environments: environments, + environmentVariables: envVars.reduce((acc, { key, value }) => { + if (key && value) { + acc[key] = value + } + return acc + }, {} as Record) + }) + + nextStep() + } + + // Don't render UI until after mount to prevent hydration mismatch + if (!mounted) { + return null + } + + // Determine if dark mode is active + const isDarkMode = resolvedTheme === 'dark' + return ( -
-
-
-
- {/* Header section with icon and description */} -
- +
+ {/* Configure icon and header */} +
+
+ + + + +
+

Configure

+

+ Set the deployer LRN for a single deployment or by creating a deployer auction for multiple deployments +

+
-
-

- Configure -

-

- Set the deployer LRN for a single deployment or by creating a - deployer auction for multiple deployments -

+
+ {/* Deployment options */} +
+ + +
+ + {deployOption === 'auction' ? ( + <> + {/* Auction settings */} +
+
+ + +
+
+ +
- Content sections will be placed here: 1. Deployment type tabs - (auction/LRN) 2. Configuration forms 3. Environment variables 4. - Account selection ...content here/ - {/* */} + + ) : ( + <> + {/* LRN settings */} +
+ + +
+ + )} + + {/* Environment Variables */} +
+ +
+ {envVars.map((envVar, index) => ( +
+ { + const newEnvVars = [...envVars]; + newEnvVars[index].key = e.target.value; + setEnvVars(newEnvVars); + }} + className={`bg-transparent ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`} + /> + { + const newEnvVars = [...envVars]; + newEnvVars[index].value = e.target.value; + setEnvVars(newEnvVars); + }} + className={`bg-transparent ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`} + /> +
+ ))} +
+ + {/* Environment Tags */} +
+ +
+
+ toggleEnvironment('production')} + className={isDarkMode ? 'border-zinc-600' : 'border-zinc-300'} + /> + +
+
+ toggleEnvironment('preview')} + className={isDarkMode ? 'border-zinc-600' : 'border-zinc-300'} + /> + +
+
+ toggleEnvironment('development')} + className={isDarkMode ? 'border-zinc-600' : 'border-zinc-300'} + /> + +
+
+
+ + {/* Account selection */} +
+ + +
+ + {/* Navigation buttons */} +
+ + +
) -} +} \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/configure-step/disable_configure-step.tsx b/apps/deploy-fe/src/components/onboarding/configure-step/disable_configure-step.tsx new file mode 100644 index 0000000..98566b3 --- /dev/null +++ b/apps/deploy-fe/src/components/onboarding/configure-step/disable_configure-step.tsx @@ -0,0 +1,68 @@ +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 ( +
+
+
+
+ {/* Header section with icon and description */} +
+ + +
+

+ Configure +

+

+ Set the deployer LRN for a single deployment or by creating a + deployer auction for multiple deployments +

+
+
+ Content sections will be placed here: 1. Deployment type tabs + (auction/LRN) 2. Configuration forms 3. Environment variables 4. + Account selection ...content here/ + {/* */} +
+
+
+
+ ) +} diff --git a/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx b/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx index 354b7b9..671b41e 100644 --- a/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx +++ b/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx @@ -1,48 +1,174 @@ 'use client' +import { useState, useEffect } from 'react' +import { Github } from 'lucide-react' +import { useTheme } from 'next-themes' +import { useOnboarding } from '@/components/onboarding/useOnboarding' import { Button } from '@workspace/ui/components/button' -import { useState } from 'react' -import { useOnboarding } from '../store' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@workspace/ui/components/select' +import { useRepoData } from '@/hooks/useRepoData' -type ConnectState = 'initial' | 'repository-select' | 'template-select' +interface Repository { + id: string | number + full_name: string + html_url?: string +} -/** - * 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('initial') - const [projectName, setProjectName] = useState('') - const { setFormData, nextStep } = useOnboarding() - - const handleConnect = () => { - setConnectState('repository-select') + const { nextStep, setFormData, formData } = useOnboarding() + const { resolvedTheme } = useTheme() + const [mounted, setMounted] = useState(false) + const [selectedRepo, setSelectedRepo] = useState(formData.githubRepo || '') + const [isImportMode, setIsImportMode] = useState(true) + const { repoData: repositories, isLoading } = useRepoData('') + + // Handle hydration mismatch by waiting for mount + useEffect(() => { + setMounted(true) + }, []) + + // 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') } + // Handle next step + const handleNext = () => { + if (selectedRepo || !isImportMode) { + nextStep() + } + } + + // Don't render UI until after mount to prevent hydration mismatch + if (!mounted) { + return null + } + + // Determine if dark mode is active + const isDarkMode = resolvedTheme === 'dark' + return ( -
- {/* \ */} - {connectState === 'initial' ? ( -
-

- Connect to GitHub -

-

- Connect your GitHub account to get started -

- + +
+ + {/* Repository or template list */} + {isImportMode ? ( +
+ {isLoading ? ( +
+
+ Loading repositories... +
+ ) : !repositories || repositories.length === 0 ? ( +
+ No repositories found +
+ ) : ( +
+ {repositories.map((repo: Repository) => ( +
handleRepoSelect(repo.full_name)} + > +
+ + {repo.full_name} +
+
+ 5 minutes ago +
+
+ ))} +
+ )} +
+ ) : ( +
+
+ Template selection coming soon +
+
+ )} + + {/* Navigation buttons */} +
+ + +
+
) -} +} \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx b/apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx new file mode 100644 index 0000000..8f42204 --- /dev/null +++ b/apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx @@ -0,0 +1,155 @@ +// '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('initial') +// const [projectName, setProjectName] = useState('') +// const { setFormData, nextStep } = useOnboarding() + +// const handleConnect = () => { +// setConnectState('repository-select') +// } + +// return ( +//
+// {/* \ */} +// {connectState === 'initial' ? ( +//
+//

+// Connect to GitHub +//

+//

+// Connect your GitHub account to get started +//

+//
+// ) : ( +// // {}} /> +// <>...connect goes here +// )} +//
+// ) +// } + +'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(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 ( +
+
+ {/* Connect icon */} +
+
+ +
+
+ + {/* Connect header */} + + + {/* Git account selector */} + + + {/* Mode buttons */} +
+ + +
+ + {/* Repository list or template list */} + {isImportMode ? ( + + ) : ( + {}} /> + )} + + {/* Navigation buttons */} +
+ + +
+
+
+ ) +} diff --git a/apps/deploy-fe/src/components/onboarding/connect-step/repository-list.tsx b/apps/deploy-fe/src/components/onboarding/connect-step/repository-list.tsx index 67076cf..f4add3b 100644 --- a/apps/deploy-fe/src/components/onboarding/connect-step/repository-list.tsx +++ b/apps/deploy-fe/src/components/onboarding/connect-step/repository-list.tsx @@ -1,46 +1,132 @@ -import { Button } from '@workspace/ui/components/button' -import { GithubIcon } from 'lucide-react' +// import { Button } from '@workspace/ui/components/button' +// import { GithubIcon } from 'lucide-react' + +// interface Repository { +// name: string +// updatedAt: string +// } + +// interface RepositoryListProps { +// repositories: Repository[] +// onSelect: (repo: Repository) => void +// } + +// interface RepoCardProps { +// repo: Repository +// onClick: () => void +// } + +// function RepoCard({ repo, onClick }: RepoCardProps) { +// return ( +// +// ) +// } + +// export function RepositoryList({ +// repositories, +// onSelect +// }: RepositoryListProps) { +// return ( +//
+// {repositories.map((repo) => ( +// onSelect(repo)} /> +// ))} +//
+// ) +// } + +'use client' +import { Github } from 'lucide-react' interface Repository { - name: string - updatedAt: string + id: string | number; + full_name: string; + html_url?: string; } -interface RepositoryListProps { - repositories: Repository[] - onSelect: (repo: Repository) => void +interface ScrollableRepoListProps { + repositories: Repository[]; + isLoading: boolean; + onSelect: (repoFullName: string) => void; + selectedRepo: string; } -interface RepoCardProps { - repo: Repository - onClick: () => void -} - -function RepoCard({ repo, onClick }: RepoCardProps) { - return ( - - ) -} - -export function RepositoryList({ +export function ScrollableRepoList({ repositories, - onSelect -}: RepositoryListProps) { + isLoading, + onSelect, + selectedRepo +}: ScrollableRepoListProps) { + // If there are no repositories or we're loading, show appropriate message + if (isLoading) { + return ( +
+
+ + + + + Loading repositories... +
+
+ ); + } + + if (!repositories || repositories.length === 0) { + return ( +
+
+ No repositories found. Make sure your GitHub account is connected. +
+
+ ); + } + + // 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}` + })); + return ( -
- {repositories.map((repo) => ( - onSelect(repo)} /> - ))} +
+ {/* Ensure we have a fixed height container with scrolling */} +
+ {repoList.map((repo, index) => ( +
onSelect(repo.full_name)} + > +
+ + {repo.full_name} +
+
+ 5 minutes ago +
+
+ ))} +
- ) -} + ); +} \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/connect-step/template-list.tsx b/apps/deploy-fe/src/components/onboarding/connect-step/template-list.tsx index 60a688d..0c8eb4b 100644 --- a/apps/deploy-fe/src/components/onboarding/connect-step/template-list.tsx +++ b/apps/deploy-fe/src/components/onboarding/connect-step/template-list.tsx @@ -1,62 +1,126 @@ -import { Button } from '@workspace/ui/components/button' -import { Input } from '@workspace/ui/components/input' -import type React from 'react' +// import { Button } from '@workspace/ui/components/button' +// import { Input } from '@workspace/ui/components/input' +// import type React from 'react' +// interface Template { +// id: string +// name: string +// description: string +// icon: React.ReactNode +// } + +// interface TemplateListProps { +// templates: Template[] +// onSelect: (template: Template) => void +// projectName: string +// onProjectNameChange: (name: string) => void +// } + +// export function TemplateList({ +// templates, +// onSelect, +// projectName, +// onProjectNameChange +// }: TemplateListProps) { +// return ( +//
+//
+// +// onProjectNameChange(e.target.value)} +// placeholder="new-repository-name" +// className="bg-background" +// /> +//
+//
+// {templates.map((template) => ( +// +// ))} +//
+//
+// ) +// } + +'use client' +// src/components/onboarding/connect-step/template-list.tsx +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 { - templates: Template[] - onSelect: (template: Template) => void - projectName: string - onProjectNameChange: (name: string) => void + onSelect: (templateId: string) => void; + selectedTemplate?: string; } -export function TemplateList({ - templates, - onSelect, - projectName, - onProjectNameChange -}: TemplateListProps) { +export const TemplateList = ({ onSelect, selectedTemplate }: TemplateListProps) => { + // Mock templates data - in a real app, this would come from an API + const templates: Template[] = [ + { + id: 'next-js', + name: 'Next.js', + description: 'React framework with hybrid static & server rendering', + icon: + }, + { + id: 'react-app', + name: 'React App', + description: 'Modern React application with Vite', + icon: + }, + { + id: 'static-site', + name: 'Static Site', + description: 'Simple static site with HTML, CSS, and JavaScript', + icon: + } + ]; + return ( -
-
- - onProjectNameChange(e.target.value)} - placeholder="new-repository-name" - className="bg-background" - /> -
-
+
+
{templates.map((template) => ( - +
))}
- ) -} + ); +}; \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx b/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx index 9fa7161..104d695 100644 --- a/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx +++ b/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx @@ -1,42 +1,247 @@ 'use client' -import { useOnboarding } from '../store' +import { useState, useEffect } from 'react' +import { useTheme } from 'next-themes' +import { Github, Loader2 } from 'lucide-react' +import { useOnboarding } from '@/components/onboarding/useOnboarding' +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' -/** - * Final step in the onboarding flow - * Displays deployment summary and triggers deployment - * - * Features: - * - Configuration summary - * - Repository display - * - Deploy action - * - * @component - */ export function DeployStep() { - useOnboarding() - + const { previousStep, nextStep, formData, setFormData } = useOnboarding() + const { resolvedTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + // State + const [isDeploying, setIsDeploying] = useState(false) + const [deploymentProgress, setDeploymentProgress] = useState(0) + const [showConfirmDialog, setShowConfirmDialog] = useState(false) + + // Handle hydration mismatch by waiting for mount + useEffect(() => { + setMounted(true) + }, []) + + // Repository information from previous steps + const repoFullName = formData.githubRepo || 'git-account/repo-name' + const branch = 'main' + + // Open the confirmation modal + const handlePayAndDeploy = () => { + setShowConfirmDialog(true) + } + + // Close the confirmation modal + const handleCancelConfirm = () => { + setShowConfirmDialog(false) + } + + // Handle confirmed deployment + const handleConfirmDeploy = () => { + setShowConfirmDialog(false) + startDeployment() + } + + // Start the deployment process + const startDeployment = () => { + setIsDeploying(true) + + // Simulate deployment process with progress updates + let progress = 0 + const interval = setInterval(() => { + progress += 10 + setDeploymentProgress(progress) + + if (progress >= 100) { + clearInterval(interval) + + // Generate deployment ID and create URL + const deploymentId = `deploy-${Math.random().toString(36).substring(2, 9)}` + const repoName = repoFullName.split('/').pop() || 'app' + const projectId = `proj-${Math.random().toString(36).substring(2, 9)}` + + // Save deployment info + setFormData({ + deploymentId, + deploymentUrl: `https://${repoName}.laconic.deploy`, + projectId + }) + + // Move to success step after short delay + setTimeout(() => { + nextStep() + }, 500) + } + }, 500) + } + + // Don't render UI until after mount to prevent hydration mismatch + if (!mounted) { + return null + } + + // Determine if dark mode is active + const isDarkMode = resolvedTheme === 'dark' + return ( -
-
-
-
- {/* Header section */} -
-
-

Deploy

-

- Your deployment is configured and ready to go! -

+ <> +
+
+ {/* Deploy icon */} +
+ + + + + + + + +
+ + {/* Deploy header */} +

Deploy

+

+ Your deployment is configured and ready to go! +

+ + {/* Repository info */} +
+
+
+ +
+
+
{repoFullName}
+
+ + + + {branch} +
- Content sections will be placed here: 1. Repository info card 2. - Configuration summary 3. Deploy button - {/* ...content here */} - {/* */} +
+ + {/* Deployment progress */} + {isDeploying && deploymentProgress > 0 && ( +
+
+
+ {deploymentProgress < 30 && "Preparing deployment..."} + {deploymentProgress >= 30 && deploymentProgress < 90 && "Deploying your project..."} + {deploymentProgress >= 90 && "Finalizing deployment..."} +
+
{deploymentProgress}%
+
+ +
+ )} + + {/* Navigation buttons */} +
+ + + {isDeploying ? ( + + ) : ( + + )}
-
+ + {/* Transaction Confirmation Dialog */} + + + Confirm Transaction + + This is a dialog description. + + +
+ {/* From */} +
+

From

+
+
Address
+
laconic1sdfjwel4jfkasfjgjal45ioasjj5jjlajfjj355
+
+
+
Public Key
+
laconic1sdfjwel4jfkasfjgjal45ioasjj5jjlajfjj355
+
+
+
HD Path
+
m/44/118/0/0/0
+
+
+ + {/* Balance */} +
+
Balance
+
129600
+
+ + {/* To */} +
+
To
+
laconic1sdfjwel4jfkasfjgjal45ioasjj5jjlajfjj355
+
+ + {/* Amount */} +
+
Amount
+
+
Balance (aint)
+
129600
+
+
+
Amount (aint)
+
3000
+
+
+
+ + + + + +
+
+ ) -} +} \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/deploy-step/disabled_deploy-step.tsx b/apps/deploy-fe/src/components/onboarding/deploy-step/disabled_deploy-step.tsx new file mode 100644 index 0000000..9fa7161 --- /dev/null +++ b/apps/deploy-fe/src/components/onboarding/deploy-step/disabled_deploy-step.tsx @@ -0,0 +1,42 @@ +'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 ( +
+
+
+
+ {/* Header section */} +
+
+

Deploy

+

+ Your deployment is configured and ready to go! +

+
+
+ Content sections will be placed here: 1. Repository info card 2. + Configuration summary 3. Deploy button + {/* ...content here */} + {/* */} +
+
+
+
+ ) +} diff --git a/apps/deploy-fe/src/components/onboarding/index.ts b/apps/deploy-fe/src/components/onboarding/index.ts index 67f0538..bb850c4 100644 --- a/apps/deploy-fe/src/components/onboarding/index.ts +++ b/apps/deploy-fe/src/components/onboarding/index.ts @@ -19,7 +19,8 @@ export { export { ConfigureStep } from './configure-step' export { ConnectStep } from './connect-step' export { DeployStep } from './deploy-step' - +export { OnboardingSidebar } from './OnboardingSidebar' +export { SuccessStep } from './success-step/success-step' // Common components export * from './common' diff --git a/apps/deploy-fe/src/components/onboarding/success-step/success-step.tsx b/apps/deploy-fe/src/components/onboarding/success-step/success-step.tsx new file mode 100644 index 0000000..b31ab9a --- /dev/null +++ b/apps/deploy-fe/src/components/onboarding/success-step/success-step.tsx @@ -0,0 +1,97 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useRouter, useParams } from 'next/navigation' +import { useTheme } from 'next-themes' +import { CheckCircle } from 'lucide-react' +import { Button } from '@workspace/ui/components/button' +import { useOnboarding } from '@/components/onboarding/useOnboarding' +import { toast } from 'sonner' + +export function SuccessStep() { + const router = useRouter() + const params = useParams() + const { resolvedTheme } = useTheme() + const [mounted, setMounted] = useState(false) + const providerParam = params?.provider ? String(params.provider) : 'github' + const { formData, resetOnboarding } = useOnboarding() + + // Handle hydration mismatch by waiting for mount + useEffect(() => { + setMounted(true) + }, []) + + // Get deployment info from form data + const repoName = formData.githubRepo ? formData.githubRepo.split('/').pop() : 'blogapp' + const deploymentUrl = formData.deploymentUrl || `https://${repoName}.laconic.deploy` + const projectId = formData.projectId || 'unknown-id' + + // Function to copy URL to clipboard + + // Handle "Visit Site" button + + // Handle "View Project" button - navigates to project page + const handleViewProject = () => { + resetOnboarding() // Reset state for next time + router.push(`/projects/${providerParam}/ps/${projectId}`) + } + + // Don't render UI until after mount to prevent hydration mismatch + if (!mounted) { + return null + } + + // Determine if dark mode is active + const isDarkMode = resolvedTheme === 'dark' + + return ( +
+
+ {/* Success icon */} +
+ +
+ + {/* Success header */} +

+ Successfully +

+

+ Your auction was successfully created +

+ + {/* Next steps section */} +
+

Next steps

+ +
+
+
+
Setup Domain
+
Add a custom domain to your project.
+
+ +
+
+
+ + {/* Action buttons */} +
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/apps/deploy-fe/src/components/onboarding/useOnboarding.ts b/apps/deploy-fe/src/components/onboarding/useOnboarding.ts index 599ce95..93f7060 100644 --- a/apps/deploy-fe/src/components/onboarding/useOnboarding.ts +++ b/apps/deploy-fe/src/components/onboarding/useOnboarding.ts @@ -1,42 +1,86 @@ 'use client' import { create } from 'zustand' +import { persist } from 'zustand/middleware' -export type Step = 'connect' | 'configure' | 'deploy' +export type Step = 'connect' | 'configure' | 'deploy' | 'success' + +export interface EnvironmentVariables { + key: string + value: string +} + +export interface OnboardingFormData { + // Connect step + githubRepo?: string + + // Configure step + deploymentType?: 'auction' | 'lrn' + deployerCount?: string + maxPrice?: string + selectedLrn?: string + environments?: { + production: boolean + preview: boolean + development: boolean + } + environmentVariables?: Record + + // Deploy step + deploymentId?: string + deploymentUrl?: string + + // Success step + projectId?: string +} interface OnboardingState { currentStep: Step - formData: { - githubRepo?: string - deploymentType?: string - environmentVars?: Record - } + formData: OnboardingFormData setCurrentStep: (step: Step) => void - setFormData: (data: Partial) => void + setFormData: (data: Partial) => void nextStep: () => void previousStep: () => void + resetOnboarding: () => void } -const STEP_ORDER: Step[] = ['connect', 'configure', 'deploy'] +const STEP_ORDER: Step[] = ['connect', 'configure', 'deploy', 'success'] -export const useOnboarding = create((set) => ({ - currentStep: 'connect', - formData: {}, - 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()( + persist( + (set) => ({ + currentStep: 'connect', + formData: {}, + + 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 + }), + + 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: {} + }) }), - previousStep: () => - set((state) => { - const currentIndex = STEP_ORDER.indexOf(state.currentStep) - const previousStep = STEP_ORDER[currentIndex - 1] - return previousStep ? { currentStep: previousStep } : state - }) -})) + { + name: 'laconic-onboarding-storage' + } + ) +) \ No newline at end of file