Build onboarding from docs comments
This commit is contained in:
parent
52512beaa2
commit
4d7ccae0e5
@ -5,6 +5,8 @@ import { DashboardLayout } from './layouts/DashboardLayout';
|
||||
import Index from './pages';
|
||||
import AuthPage from './pages/AuthPage';
|
||||
import BuyPrepaidService from './pages/BuyPrepaidService';
|
||||
import OnboardingDemoPage from './pages/OnboardingDemoPage';
|
||||
import OnboardingPage from './pages/OnboardingPage';
|
||||
import { projectsRoutesWithoutSearch } from './pages/org-slug/projects/project-routes';
|
||||
import Settings from './pages/org-slug/Settings';
|
||||
import { BASE_URL } from './utils/constants';
|
||||
@ -49,6 +51,14 @@ const router = createBrowserRouter([
|
||||
path: '/buy-prepaid-service',
|
||||
element: <BuyPrepaidService />,
|
||||
},
|
||||
{
|
||||
path: '/onboarding',
|
||||
element: <OnboardingPage />,
|
||||
},
|
||||
{
|
||||
path: '/onboarding-demo',
|
||||
element: <OnboardingDemoPage />,
|
||||
},
|
||||
]);
|
||||
|
||||
/**
|
||||
@ -64,9 +74,11 @@ function App() {
|
||||
credentials: 'include',
|
||||
}).then((res) => {
|
||||
const path = window.location.pathname;
|
||||
const publicPaths = ['/login', '/onboarding', '/onboarding-demo'];
|
||||
|
||||
if (res.status !== 200) {
|
||||
localStorage.clear();
|
||||
if (path !== '/login') {
|
||||
if (!publicPaths.includes(path)) {
|
||||
window.location.pathname = '/login';
|
||||
}
|
||||
} else {
|
||||
|
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* NavigationSidebar.tsx
|
||||
*
|
||||
* This component displays a sidebar with navigation steps for the onboarding flow.
|
||||
* It highlights the current step and allows navigation to completed steps.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Display the logo at the top
|
||||
* 2. Show a list of navigation items for each step
|
||||
* 3. Highlight the current step
|
||||
* 4. Make completed steps clickable for navigation
|
||||
* 5. Style according to the shadcn/ui design system
|
||||
*/
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
Code,
|
||||
GitBranch,
|
||||
Github,
|
||||
Rocket,
|
||||
Server,
|
||||
Settings
|
||||
} from 'lucide-react';
|
||||
import React from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from './store/navigationStore';
|
||||
|
||||
// Map steps to icons and labels
|
||||
const stepConfig = {
|
||||
[OnboardingStep.CONNECT]: {
|
||||
icon: Github,
|
||||
label: 'Connect'
|
||||
},
|
||||
[OnboardingStep.REPOSITORY]: {
|
||||
icon: GitBranch,
|
||||
label: 'Repository'
|
||||
},
|
||||
[OnboardingStep.TEMPLATE]: {
|
||||
icon: Code,
|
||||
label: 'Template'
|
||||
},
|
||||
[OnboardingStep.CONFIGURE]: {
|
||||
icon: Settings,
|
||||
label: 'Configure'
|
||||
},
|
||||
[OnboardingStep.DEPLOYMENT_OPTIONS]: {
|
||||
icon: Server,
|
||||
label: 'Options'
|
||||
},
|
||||
[OnboardingStep.DEPLOY]: {
|
||||
icon: Rocket,
|
||||
label: 'Deploy'
|
||||
},
|
||||
};
|
||||
|
||||
const NavigationSidebar: React.FC = () => {
|
||||
const { currentStep, completedSteps, setCurrentStep, canGoToStep } = useNavigationStore();
|
||||
|
||||
// Order of steps to display
|
||||
const steps = [
|
||||
OnboardingStep.CONNECT,
|
||||
OnboardingStep.REPOSITORY,
|
||||
OnboardingStep.TEMPLATE,
|
||||
OnboardingStep.CONFIGURE,
|
||||
OnboardingStep.DEPLOYMENT_OPTIONS,
|
||||
OnboardingStep.DEPLOY,
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-64 h-full bg-background border-r p-4 flex flex-col">
|
||||
{/* Logo */}
|
||||
<div className="mb-8 p-2">
|
||||
<h1 className="text-xl font-bold">Snowball</h1>
|
||||
</div>
|
||||
|
||||
{/* Navigation Items */}
|
||||
<nav className="space-y-2">
|
||||
{steps.map((step) => {
|
||||
const { icon: Icon, label } = stepConfig[step];
|
||||
const isActive = currentStep === step;
|
||||
const isCompleted = completedSteps[step];
|
||||
const isClickable = canGoToStep(step);
|
||||
|
||||
return (
|
||||
<button
|
||||
key={step}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-colors",
|
||||
isActive && "bg-primary text-primary-foreground",
|
||||
!isActive && isCompleted && "text-foreground hover:bg-muted/80",
|
||||
!isActive && !isCompleted && "text-muted-foreground",
|
||||
!isClickable && "opacity-50 cursor-not-allowed",
|
||||
isClickable && !isActive && "hover:bg-muted cursor-pointer"
|
||||
)}
|
||||
onClick={() => isClickable && setCurrentStep(step)}
|
||||
disabled={!isClickable}
|
||||
>
|
||||
<Icon size={18} />
|
||||
<span>{label}</span>
|
||||
{isCompleted && !isActive && (
|
||||
<span className="ml-auto w-2 h-2 bg-primary rounded-full" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationSidebar;
|
@ -0,0 +1,35 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent, DialogTrigger
|
||||
} from '@/components/ui/dialog';
|
||||
import React from 'react';
|
||||
import OnboardingLayout from './OnboardingLayout';
|
||||
|
||||
interface OnboardingDialogProps {
|
||||
trigger?: React.ReactNode;
|
||||
defaultOpen?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* OnboardingDialog component
|
||||
*
|
||||
* A dialog modal that contains the onboarding flow.
|
||||
* Can be triggered by a custom element or automatically opened.
|
||||
*/
|
||||
const OnboardingDialog: React.FC<OnboardingDialogProps> = ({
|
||||
trigger,
|
||||
defaultOpen = false
|
||||
}) => {
|
||||
return (
|
||||
<Dialog defaultOpen={defaultOpen}>
|
||||
{trigger && <DialogTrigger asChild>{trigger}</DialogTrigger>}
|
||||
<DialogContent className="max-w-[95vw] max-h-[95vh] w-[1200px] h-[800px] overflow-hidden p-0">
|
||||
<div className="h-full overflow-hidden">
|
||||
<OnboardingLayout isDialog={true} />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingDialog;
|
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* OnboardingLayout.tsx
|
||||
*
|
||||
* This component serves as the main wrapper for the entire onboarding flow.
|
||||
* It handles the layout structure with a sidebar and main content area.
|
||||
* Modified to work well in both full page and dialog contexts.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Use the existing layout components where possible
|
||||
* 2. Render the NavigationSidebar on the left
|
||||
* 3. Render the current step component in the main content area
|
||||
* 4. The current step should be determined by the navigation store
|
||||
* 5. Add proper spacing and responsive behavior
|
||||
*
|
||||
* Dependencies:
|
||||
* - NavigationSidebar
|
||||
* - ConnectView
|
||||
* - RepositoryListView
|
||||
* - TemplateSelectionView
|
||||
* - ConfigureView
|
||||
* - DeploymentOptionsView
|
||||
* - DeployView
|
||||
* - useNavigationStore
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import NavigationSidebar from './NavigationSidebar';
|
||||
import ProgressIndicator from './ProgressIndicator';
|
||||
import useNavigationStore, { OnboardingStep } from './store/navigationStore';
|
||||
|
||||
// Import view components
|
||||
import ConfigureView from './views/ConfigureView';
|
||||
import ConnectView from './views/ConnectView';
|
||||
import DeploymentOptionsView from './views/DeploymentOptionsView';
|
||||
import DeployView from './views/DeployView';
|
||||
import RepositoryListView from './views/RepositoryListView';
|
||||
import TemplateSelectionView from './views/TemplateSelectionView';
|
||||
|
||||
interface OnboardingLayoutProps {
|
||||
isDialog?: boolean;
|
||||
}
|
||||
|
||||
const OnboardingLayout: React.FC<OnboardingLayoutProps> = ({
|
||||
isDialog = false
|
||||
}) => {
|
||||
const { currentStep } = useNavigationStore();
|
||||
|
||||
// Render the appropriate view component based on the current step
|
||||
const renderCurrentView = () => {
|
||||
switch (currentStep) {
|
||||
case OnboardingStep.CONNECT:
|
||||
return <ConnectView />;
|
||||
case OnboardingStep.REPOSITORY:
|
||||
return <RepositoryListView />;
|
||||
case OnboardingStep.TEMPLATE:
|
||||
return <TemplateSelectionView />;
|
||||
case OnboardingStep.CONFIGURE:
|
||||
return <ConfigureView />;
|
||||
case OnboardingStep.DEPLOYMENT_OPTIONS:
|
||||
return <DeploymentOptionsView />;
|
||||
case OnboardingStep.DEPLOY:
|
||||
return <DeployView />;
|
||||
default:
|
||||
return <ConnectView />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`flex ${isDialog ? 'h-full' : 'h-screen'} w-full bg-background`}>
|
||||
{/* Navigation Sidebar */}
|
||||
<NavigationSidebar />
|
||||
|
||||
{/* Main Content Area */}
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
<main className="flex-1 overflow-y-auto p-6">
|
||||
{renderCurrentView()}
|
||||
</main>
|
||||
|
||||
{/* Progress Indicator Footer */}
|
||||
<footer className="border-t p-4">
|
||||
<ProgressIndicator />
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingLayout;
|
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* ProgressIndicator.tsx
|
||||
*
|
||||
* This component displays a horizontal indicator showing the current progress
|
||||
* through the onboarding flow steps.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Use dots to represent each step
|
||||
* 2. Highlight the current step
|
||||
* 3. Show completed steps differently
|
||||
* 4. Style according to the shadcn/ui design system
|
||||
*/
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import React from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from './store/navigationStore';
|
||||
|
||||
const ProgressIndicator: React.FC = () => {
|
||||
const { currentStep, completedSteps } = useNavigationStore();
|
||||
|
||||
// Define the steps to show in the progress indicator
|
||||
const steps = [
|
||||
OnboardingStep.CONNECT,
|
||||
OnboardingStep.REPOSITORY,
|
||||
OnboardingStep.TEMPLATE,
|
||||
OnboardingStep.CONFIGURE,
|
||||
OnboardingStep.DEPLOYMENT_OPTIONS,
|
||||
OnboardingStep.DEPLOY,
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center space-x-2 py-4">
|
||||
{steps.map((step, index) => {
|
||||
const isActive = currentStep === step;
|
||||
const isCompleted = completedSteps[step];
|
||||
|
||||
return (
|
||||
<div
|
||||
key={step}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"w-3 h-3 rounded-full transition-all duration-200",
|
||||
isActive && "w-4 h-4 bg-primary",
|
||||
isCompleted && !isActive && "bg-primary opacity-70",
|
||||
!isActive && !isCompleted && "bg-muted"
|
||||
)}
|
||||
/>
|
||||
{index < steps.length - 1 && (
|
||||
<div className="w-8 h-0.5 bg-muted mt-1.5" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressIndicator;
|
31
packages/frontend/src/components/onboarding/README.md
Normal file
31
packages/frontend/src/components/onboarding/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Onboarding Flow
|
||||
|
||||
This directory contains the implementation of the onboarding flow for the application.
|
||||
|
||||
## Documentation
|
||||
|
||||
Please refer to the following documentation for details about the onboarding flow:
|
||||
|
||||
- [Component Breakdown](./docs/Component-Breakdown.md) - Overview of all components used in the onboarding flow
|
||||
- [State Management](./docs/State-Management.md) - Details about Zustand store implementations for state management
|
||||
- [Form Integration Plan](./docs/Form-Integration-Plan.md) - Guidelines for integrating existing form components
|
||||
|
||||
## Implementation Approach
|
||||
|
||||
The onboarding flow uses a navigation-only state management approach with Zustand to coordinate between steps, while preserving the functionality of existing form components.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
- `/docs` - Documentation files
|
||||
- `/store` - Zustand store implementation
|
||||
- `/views` - Main view components for each step of the flow
|
||||
|
||||
## Getting Started
|
||||
|
||||
To implement the onboarding flow, follow these steps:
|
||||
|
||||
1. Review the documentation in the `/docs` directory
|
||||
2. Implement the navigation store as described in the State Management document
|
||||
3. Create the core layout components
|
||||
4. Implement each view component according to the Form Integration Plan
|
||||
5. Test the flow with existing form components
|
@ -0,0 +1,88 @@
|
||||
# Onboarding Flow Component Architecture
|
||||
|
||||
## Core Layout Components
|
||||
|
||||
1. **AppLayout**
|
||||
- Uses shadcn/ui `Sheet` for responsive sidebar
|
||||
- Main content area with proper padding
|
||||
|
||||
2. **Sidebar**
|
||||
- Uses shadcn/ui `Sidebar` component
|
||||
- Contains logo and navigation steps
|
||||
|
||||
3. **NavigationStep**
|
||||
- Modified shadcn/ui `NavigationMenuItem`
|
||||
- Displays icon, step label, and active state
|
||||
|
||||
4. **ProgressIndicator**
|
||||
- Custom component using shadcn/ui styling conventions
|
||||
- Horizontal dots showing current flow progress
|
||||
|
||||
## Page Components
|
||||
|
||||
1. **ConnectView**
|
||||
- Main component for initial GitHub connection screen
|
||||
- Contains options to import or start from template
|
||||
|
||||
2. **RepositoryListView**
|
||||
- Displays recent repositories with timestamps
|
||||
- Uses shadcn/ui `ScrollArea` for scrollable list
|
||||
|
||||
3. **ConfigureView**
|
||||
- Settings configuration screen with form elements
|
||||
- Multiple sections for different configuration options
|
||||
|
||||
4. **DeployView**
|
||||
- Final deployment screen with status information
|
||||
- Confirmation elements and action buttons
|
||||
|
||||
## UI Components
|
||||
|
||||
1. **TemplateCard**
|
||||
- Uses shadcn/ui `Card`, `CardHeader`, `CardContent`, `CardFooter`
|
||||
- Displays template options with icons and descriptions
|
||||
|
||||
2. **ActionButton**
|
||||
- Extends shadcn/ui `Button` with variants for primary/secondary actions
|
||||
- Consistent styling across all screens
|
||||
|
||||
3. **RepositoryItem**
|
||||
- Custom list item with GitHub repo information
|
||||
- Uses Lucide icons and formatting for timestamps
|
||||
|
||||
4. **FormComponents**
|
||||
- Uses shadcn/ui `Form`, `FormField`, `FormItem`, `FormLabel`, `FormControl`
|
||||
- Includes `Input`, `Select`, `Checkbox`, etc.
|
||||
|
||||
5. **EnvironmentVariablesSection**
|
||||
- Form section for environment variables
|
||||
- Uses shadcn/ui `Input` and add button functionality
|
||||
|
||||
6. **DeploymentStatusIndicator**
|
||||
- Shows deployment status with appropriate icon
|
||||
- Uses shadcn/ui styling conventions
|
||||
|
||||
7. **NavigationControls**
|
||||
- Contains Previous/Next buttons
|
||||
- Uses shadcn/ui `Button` with appropriate variants
|
||||
|
||||
## Icons
|
||||
|
||||
- All icons from Lucide React library:
|
||||
- `Git`, `Github`, `Settings`, `Box`, `Terminal`
|
||||
- `Plus`, `Check`, `ChevronRight`, `ChevronLeft`
|
||||
- `ArrowRight`, `ExternalLink`
|
||||
|
||||
## Form Elements
|
||||
|
||||
1. **ProjectForm**
|
||||
- Uses shadcn/ui `Form` component
|
||||
- Fields for project name, deployment type, etc.
|
||||
|
||||
2. **DropdownSelects**
|
||||
- Uses shadcn/ui `Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`
|
||||
- For container URLs, deployment numbers, etc.
|
||||
|
||||
3. **CheckboxGroup**
|
||||
- Group of shadcn/ui `Checkbox` components
|
||||
- For environment type selection (Production, Preview, Development)
|
@ -0,0 +1,133 @@
|
||||
# Form Integration Plan for Onboarding Flow
|
||||
|
||||
## Overview
|
||||
This document outlines how to integrate existing form components into the onboarding flow without modifying their functionality. The navigation state will be managed by Zustand as defined previously, while the form sections will maintain their current implementation.
|
||||
|
||||
## Key Form Sections to Integrate
|
||||
|
||||
### Connect Step Forms
|
||||
1. **GitHub Connection Form**
|
||||
- Location: Appears in the first screen
|
||||
- Purpose: Authenticates with GitHub
|
||||
- Integration point: `ConnectView.tsx`
|
||||
- Implementation note: Import existing form as-is
|
||||
|
||||
2. **Repository Selection List**
|
||||
- Location: Appears after GitHub connection
|
||||
- Purpose: Displays repositories with timestamps for selection
|
||||
- Integration point: `ConnectView.tsx` (conditional render after connection)
|
||||
- Implementation note: Preserve existing event handlers
|
||||
|
||||
3. **Template Selection Grid**
|
||||
- Location: Alternative to repository selection
|
||||
- Purpose: Displays template options
|
||||
- Integration point: `ConnectView.tsx` (conditional render)
|
||||
- Implementation note: Maintain existing selection logic
|
||||
|
||||
### Configure Step Forms
|
||||
1. **Deployment URL Configuration**
|
||||
- Location: Top of configure screen
|
||||
- Purpose: Sets deployment URL
|
||||
- Integration point: `ConfigureView.tsx`
|
||||
- Implementation note: Import without modifying validation
|
||||
|
||||
2. **Environment Variables Form**
|
||||
- Location: Middle of configure screen
|
||||
- Purpose: Configure environment settings
|
||||
- Integration point: `ConfigureView.tsx`
|
||||
- Implementation note: Preserve existing add/remove functionality
|
||||
|
||||
3. **Deployment Options Form**
|
||||
- Location: Bottom of configure screen
|
||||
- Purpose: Sets deployment constraints (max price, instances)
|
||||
- Integration point: `ConfigureView.tsx`
|
||||
- Implementation note: Keep all validation rules intact
|
||||
|
||||
4. **Environment Type Selection**
|
||||
- Location: Configure screen
|
||||
- Purpose: Select environments (Production/Preview/Development)
|
||||
- Integration point: `ConfigureView.tsx`
|
||||
- Implementation note: Maintain checkbox group behavior
|
||||
|
||||
### Deploy Step Forms
|
||||
1. **Deployment Status Display**
|
||||
- Location: Deploy screen
|
||||
- Purpose: Shows deployment progress
|
||||
- Integration point: `DeployView.tsx`
|
||||
- Implementation note: Keep status monitoring logic
|
||||
|
||||
2. **Payment Details Form**
|
||||
- Location: Final deploy screen
|
||||
- Purpose: Collect payment information
|
||||
- Integration point: `DeployView.tsx` (conditional render)
|
||||
- Implementation note: Preserve all payment processing logic
|
||||
|
||||
## Integration Approach
|
||||
|
||||
1. **Wrapper Component Strategy**
|
||||
```tsx
|
||||
// Example approach for integrating existing forms
|
||||
const ConfigureView: React.FC = () => {
|
||||
const { markStepCompleted, goToNextStep, goToPreviousStep } = useNavigationStore()
|
||||
|
||||
// Completion handler that works with the existing form
|
||||
const handleStepComplete = (formData: any) => {
|
||||
// Form handled its own submission already
|
||||
// Just update navigation state
|
||||
markStepCompleted(OnboardingStep.CONFIGURE)
|
||||
goToNextStep()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h1 className="text-2xl font-bold">Configure</h1>
|
||||
|
||||
{/* Import existing form component without modifying it */}
|
||||
<ExistingDeploymentUrlForm />
|
||||
<ExistingEnvironmentVariablesForm />
|
||||
<ExistingDeploymentOptionsForm />
|
||||
|
||||
{/* Navigation controls added around existing forms */}
|
||||
<div className="flex justify-between">
|
||||
<Button variant="outline" onClick={goToPreviousStep}>
|
||||
Previous
|
||||
</Button>
|
||||
<Button onClick={handleStepComplete}>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
2. **Event Coordination**
|
||||
- Listen for existing form submission events
|
||||
- Update navigation state after form events complete
|
||||
- Don't modify form validation or submission logic
|
||||
|
||||
3. **Form Data Access**
|
||||
- Access form data through existing mechanisms (refs, context, etc.)
|
||||
- Don't introduce new state management for form data
|
||||
|
||||
## Implementation Guidelines
|
||||
|
||||
1. **DO NOT:**
|
||||
- Change form validation logic
|
||||
- Modify form submission handlers
|
||||
- Alter form state management
|
||||
- Change form UI components
|
||||
|
||||
2. **DO:**
|
||||
- Wrap existing forms in the appropriate step components
|
||||
- Add navigation controls outside the forms
|
||||
- Listen for form completion events
|
||||
- Update navigation state based on form events
|
||||
|
||||
## Data Flow
|
||||
1. User interacts with existing form components
|
||||
2. Forms validate and process data using their existing logic
|
||||
3. After form processing completes, navigation state is updated
|
||||
4. Navigation controls the flow between steps
|
||||
|
||||
This approach ensures that the existing form functionality remains unchanged while integrating with the new navigation flow.
|
@ -0,0 +1,124 @@
|
||||
# Onboarding Flow State Management
|
||||
|
||||
This document describes the state management approach for the onboarding flow using Zustand.
|
||||
|
||||
## Complete State Management
|
||||
|
||||
The full state management approach includes all aspects of the onboarding flow:
|
||||
|
||||
```typescript
|
||||
// Define the steps of our onboarding flow
|
||||
export enum OnboardingStep {
|
||||
CONNECT = 'connect',
|
||||
CONFIGURE = 'configure',
|
||||
DEPLOY = 'deploy'
|
||||
}
|
||||
|
||||
// Define the repository type
|
||||
interface Repository {
|
||||
id: string
|
||||
name: string
|
||||
lastUpdated: Date
|
||||
}
|
||||
|
||||
// Define the template type
|
||||
interface Template {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
type: 'web' | 'api' | 'other'
|
||||
}
|
||||
|
||||
// Define the deployment configuration type
|
||||
interface DeploymentConfig {
|
||||
deploymentUrl?: string
|
||||
maxDeploys?: number
|
||||
environmentTypes: {
|
||||
production: boolean
|
||||
preview: boolean
|
||||
development: boolean
|
||||
}
|
||||
selectedContainerUrl?: string
|
||||
}
|
||||
|
||||
// Define our store state
|
||||
interface OnboardingState {
|
||||
// Current step in the flow
|
||||
currentStep: OnboardingStep
|
||||
|
||||
// Step completion status
|
||||
completedSteps: {
|
||||
[key in OnboardingStep]: boolean
|
||||
}
|
||||
|
||||
// GitHub connection status
|
||||
isConnected: boolean
|
||||
|
||||
// Selected method (import or template)
|
||||
selectedMethod?: 'import' | 'template'
|
||||
|
||||
// Selected repository (if import method is chosen)
|
||||
selectedRepository?: Repository
|
||||
|
||||
// Recent repositories (shown in the import list)
|
||||
recentRepositories: Repository[]
|
||||
|
||||
// Selected template (if template method is chosen)
|
||||
selectedTemplate?: Template
|
||||
|
||||
// Deployment configuration
|
||||
deploymentConfig: DeploymentConfig
|
||||
|
||||
// Deployment status
|
||||
deploymentStatus?: 'configuring' | 'ready' | 'deployed' | 'failed'
|
||||
|
||||
// Actions
|
||||
setCurrentStep: (step: OnboardingStep) => void
|
||||
markStepCompleted: (step: OnboardingStep) => void
|
||||
connect: () => void
|
||||
disconnect: () => void
|
||||
selectMethod: (method: 'import' | 'template') => void
|
||||
selectRepository: (repository: Repository) => void
|
||||
selectTemplate: (template: Template) => void
|
||||
updateDeploymentConfig: (config: Partial<DeploymentConfig>) => void
|
||||
setDeploymentStatus: (status: 'configuring' | 'ready' | 'deployed' | 'failed') => void
|
||||
resetOnboarding: () => void
|
||||
goToNextStep: () => void
|
||||
goToPreviousStep: () => void
|
||||
}
|
||||
```
|
||||
|
||||
## Navigation-Only State Management
|
||||
|
||||
For integration with existing form components, a navigation-only state management approach is recommended:
|
||||
|
||||
```typescript
|
||||
// Define the steps of our onboarding flow
|
||||
export enum OnboardingStep {
|
||||
CONNECT = 'connect',
|
||||
CONFIGURE = 'configure',
|
||||
DEPLOY = 'deploy'
|
||||
}
|
||||
|
||||
// Define our navigation state
|
||||
interface NavigationState {
|
||||
// Current step in the flow
|
||||
currentStep: OnboardingStep
|
||||
|
||||
// Step completion status
|
||||
completedSteps: {
|
||||
[key in OnboardingStep]: boolean
|
||||
}
|
||||
|
||||
// Navigation actions
|
||||
setCurrentStep: (step: OnboardingStep) => void
|
||||
markStepCompleted: (step: OnboardingStep) => void
|
||||
markStepIncomplete: (step: OnboardingStep) => void
|
||||
goToNextStep: () => void
|
||||
goToPreviousStep: () => void
|
||||
resetNavigation: () => void
|
||||
canGoToStep: (step: OnboardingStep) => boolean
|
||||
}
|
||||
```
|
||||
|
||||
The navigation-only approach allows for easier integration with existing form components and state management.
|
24
packages/frontend/src/components/onboarding/index.ts
Normal file
24
packages/frontend/src/components/onboarding/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* index.ts
|
||||
*
|
||||
* This file exports all components from the onboarding module for easy importing.
|
||||
*/
|
||||
|
||||
// Export the main layout
|
||||
export { default as OnboardingLayout } from './OnboardingLayout';
|
||||
|
||||
// Export the navigation store
|
||||
export { default as useNavigationStore } from './store/navigationStore';
|
||||
export { OnboardingStep } from './store/navigationStore';
|
||||
|
||||
// Export view components
|
||||
export { default as ConnectView } from './views/ConnectView';
|
||||
export { default as RepositoryListView } from './views/RepositoryListView';
|
||||
export { default as TemplateSelectionView } from './views/TemplateSelectionView';
|
||||
export { default as ConfigureView } from './views/ConfigureView';
|
||||
export { default as DeploymentOptionsView } from './views/DeploymentOptionsView';
|
||||
export { default as DeployView } from './views/DeployView';
|
||||
|
||||
// Export other components
|
||||
export { default as NavigationSidebar } from './NavigationSidebar';
|
||||
export { default as ProgressIndicator } from './ProgressIndicator';
|
@ -0,0 +1,175 @@
|
||||
/**
|
||||
* navigationStore.ts
|
||||
*
|
||||
* This file implements the Zustand store for navigation state management.
|
||||
* It focuses on navigation-only state to coordinate between steps.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Define the OnboardingStep enum for all steps
|
||||
* 2. Create a NavigationState interface with:
|
||||
* - currentStep tracking
|
||||
* - completedSteps status object
|
||||
* - navigation actions (setCurrentStep, markStepCompleted, etc.)
|
||||
* 3. Implement the store with Zustand
|
||||
* 4. Add persistence with localStorage
|
||||
* 5. Implement navigation logic (can only go to completed steps or next incomplete)
|
||||
* 6. Add actions for resetting navigation
|
||||
*
|
||||
* See the State-Management.md documentation for detailed guidance.
|
||||
*/
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
// Define the steps of the onboarding flow
|
||||
export enum OnboardingStep {
|
||||
CONNECT = 'connect',
|
||||
REPOSITORY = 'repository',
|
||||
TEMPLATE = 'template',
|
||||
CONFIGURE = 'configure',
|
||||
DEPLOYMENT_OPTIONS = 'deployment_options',
|
||||
DEPLOY = 'deploy'
|
||||
}
|
||||
|
||||
// Define the navigation state interface
|
||||
interface NavigationState {
|
||||
// Current step in the flow
|
||||
currentStep: OnboardingStep;
|
||||
|
||||
// Step completion status
|
||||
completedSteps: {
|
||||
[key in OnboardingStep]: boolean;
|
||||
};
|
||||
|
||||
// Navigation actions
|
||||
setCurrentStep: (step: OnboardingStep) => void;
|
||||
markStepCompleted: (step: OnboardingStep) => void;
|
||||
markStepIncomplete: (step: OnboardingStep) => void;
|
||||
goToNextStep: () => void;
|
||||
goToPreviousStep: () => void;
|
||||
resetNavigation: () => void;
|
||||
canGoToStep: (step: OnboardingStep) => boolean;
|
||||
}
|
||||
|
||||
// Order of steps for navigation
|
||||
const stepOrder: OnboardingStep[] = [
|
||||
OnboardingStep.CONNECT,
|
||||
OnboardingStep.REPOSITORY,
|
||||
OnboardingStep.TEMPLATE,
|
||||
OnboardingStep.CONFIGURE,
|
||||
OnboardingStep.DEPLOYMENT_OPTIONS,
|
||||
OnboardingStep.DEPLOY,
|
||||
];
|
||||
|
||||
// Create and export the store
|
||||
const useNavigationStore = create<NavigationState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
// Initial state
|
||||
currentStep: OnboardingStep.CONNECT,
|
||||
completedSteps: {
|
||||
[OnboardingStep.CONNECT]: false,
|
||||
[OnboardingStep.REPOSITORY]: false,
|
||||
[OnboardingStep.TEMPLATE]: false,
|
||||
[OnboardingStep.CONFIGURE]: false,
|
||||
[OnboardingStep.DEPLOYMENT_OPTIONS]: false,
|
||||
[OnboardingStep.DEPLOY]: false,
|
||||
},
|
||||
|
||||
// Set current step if allowed
|
||||
setCurrentStep: (step: OnboardingStep) => {
|
||||
const { canGoToStep } = get();
|
||||
if (canGoToStep(step)) {
|
||||
set({ currentStep: step });
|
||||
}
|
||||
},
|
||||
|
||||
// Mark a step as completed
|
||||
markStepCompleted: (step: OnboardingStep) => {
|
||||
set((state) => ({
|
||||
completedSteps: {
|
||||
...state.completedSteps,
|
||||
[step]: true,
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
// Mark a step as incomplete
|
||||
markStepIncomplete: (step: OnboardingStep) => {
|
||||
set((state) => ({
|
||||
completedSteps: {
|
||||
...state.completedSteps,
|
||||
[step]: false,
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
// Go to the next step in the flow
|
||||
goToNextStep: () => {
|
||||
const { currentStep } = get();
|
||||
const currentIndex = stepOrder.indexOf(currentStep);
|
||||
|
||||
if (currentIndex < stepOrder.length - 1) {
|
||||
const nextStep = stepOrder[currentIndex + 1];
|
||||
set({ currentStep: nextStep });
|
||||
}
|
||||
},
|
||||
|
||||
// Go to the previous step in the flow
|
||||
goToPreviousStep: () => {
|
||||
const { currentStep } = get();
|
||||
const currentIndex = stepOrder.indexOf(currentStep);
|
||||
|
||||
if (currentIndex > 0) {
|
||||
const prevStep = stepOrder[currentIndex - 1];
|
||||
set({ currentStep: prevStep });
|
||||
}
|
||||
},
|
||||
|
||||
// Reset navigation state
|
||||
resetNavigation: () => {
|
||||
set({
|
||||
currentStep: OnboardingStep.CONNECT,
|
||||
completedSteps: {
|
||||
[OnboardingStep.CONNECT]: false,
|
||||
[OnboardingStep.REPOSITORY]: false,
|
||||
[OnboardingStep.TEMPLATE]: false,
|
||||
[OnboardingStep.CONFIGURE]: false,
|
||||
[OnboardingStep.DEPLOYMENT_OPTIONS]: false,
|
||||
[OnboardingStep.DEPLOY]: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// Check if navigation to a step is allowed
|
||||
canGoToStep: (step: OnboardingStep) => {
|
||||
const { completedSteps } = get();
|
||||
const stepIndex = stepOrder.indexOf(step);
|
||||
const currentStepIndex = stepOrder.indexOf(get().currentStep);
|
||||
|
||||
// Can always go to current step or previous completed steps
|
||||
if (step === get().currentStep || (stepIndex < currentStepIndex && completedSteps[step])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Can go to the next step if all previous steps are completed
|
||||
if (stepIndex === currentStepIndex + 1) {
|
||||
// Check if all previous steps are completed
|
||||
for (let i = 0; i < stepIndex; i++) {
|
||||
if (!completedSteps[stepOrder[i]]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'onboarding-navigation-storage',
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export default useNavigationStore;
|
@ -0,0 +1,163 @@
|
||||
/**
|
||||
* ConfigureView.tsx
|
||||
*
|
||||
* This component displays configuration options for deployment.
|
||||
* It includes settings for deployment URL, environment variables, and other options.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Display form sections for various configuration options
|
||||
* 2. Include input fields for deployment URL
|
||||
* 3. Add environment variables section
|
||||
* 4. Include deployment options (number of instances, etc.)
|
||||
* 5. Add navigation controls
|
||||
*/
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { ArrowLeft, ArrowRight, Plus, Settings } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from '../store/navigationStore';
|
||||
|
||||
const ConfigureView: React.FC = () => {
|
||||
const [deploymentUrl, setDeploymentUrl] = useState('');
|
||||
const [maxDeployments, setMaxDeployments] = useState('');
|
||||
const [environmentTypes, setEnvironmentTypes] = useState({
|
||||
production: true,
|
||||
preview: true,
|
||||
development: false,
|
||||
});
|
||||
|
||||
const { markStepCompleted, goToNextStep, goToPreviousStep } = useNavigationStore();
|
||||
|
||||
const handleNext = () => {
|
||||
// In a real app, would validate inputs
|
||||
markStepCompleted(OnboardingStep.CONFIGURE);
|
||||
goToNextStep();
|
||||
};
|
||||
|
||||
const toggleEnvironmentType = (type: 'production' | 'preview' | 'development') => {
|
||||
setEnvironmentTypes({
|
||||
...environmentTypes,
|
||||
[type]: !environmentTypes[type],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col max-w-4xl mx-auto space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2">Configure</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Set the deployment URL for a single deployment or by creating a separate section for multiple deployments
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-16 h-16 flex items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||
<Settings size={32} className="text-primary" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold mb-4">Configure</h2>
|
||||
</div>
|
||||
|
||||
{/* Configuration Form */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Deployment Settings</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Deployment URL */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="deployment-url">Deployment URL</Label>
|
||||
<Input
|
||||
id="deployment-url"
|
||||
placeholder="my-app.example.com"
|
||||
value={deploymentUrl}
|
||||
onChange={(e) => setDeploymentUrl(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Number of Deployments */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="max-deployments">Number of Deployments</Label>
|
||||
<Select value={maxDeployments} onValueChange={setMaxDeployments}>
|
||||
<SelectTrigger id="max-deployments">
|
||||
<SelectValue placeholder="Select" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1">1</SelectItem>
|
||||
<SelectItem value="2">2</SelectItem>
|
||||
<SelectItem value="3">3</SelectItem>
|
||||
<SelectItem value="4">4</SelectItem>
|
||||
<SelectItem value="5">5</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Environment Variables */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<Label>Environment Variables</Label>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
Add Variable
|
||||
</Button>
|
||||
</div>
|
||||
{/* Environment variables would be implemented here */}
|
||||
</div>
|
||||
|
||||
{/* Environment Types */}
|
||||
<div className="space-y-2">
|
||||
<Label>Environment Types</Label>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="production"
|
||||
checked={environmentTypes.production}
|
||||
onCheckedChange={() => toggleEnvironmentType('production')}
|
||||
/>
|
||||
<Label htmlFor="production" className="cursor-pointer">Production</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="preview"
|
||||
checked={environmentTypes.preview}
|
||||
onCheckedChange={() => toggleEnvironmentType('preview')}
|
||||
/>
|
||||
<Label htmlFor="preview" className="cursor-pointer">Preview</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="development"
|
||||
checked={environmentTypes.development}
|
||||
onCheckedChange={() => toggleEnvironmentType('development')}
|
||||
/>
|
||||
<Label htmlFor="development" className="cursor-pointer">Development</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-between">
|
||||
<Button variant="outline" onClick={goToPreviousStep}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<Button onClick={handleNext}>
|
||||
Next
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigureView;
|
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* ConnectView.tsx
|
||||
*
|
||||
* This component displays the initial connection screen for GitHub authentication.
|
||||
* It shows a button to connect with GitHub and options to select import or template after connection.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Use existing Card components from shared/ui
|
||||
* 2. Add a GitHub connect button with icon
|
||||
* 3. Show two card options after connection: Import Repository or Start with Template
|
||||
* 4. Add selection behavior for the two options
|
||||
* 5. Show a "Next" button that's enabled when an option is selected
|
||||
* 6. Use navigation store to mark step as completed and go to next step
|
||||
* 7. Match the dark theme UI shown in the screenshots
|
||||
*
|
||||
* Dependencies:
|
||||
* - Card, CardHeader, CardContent, CardFooter from shared components
|
||||
* - Button from shared components
|
||||
* - Github icon from Lucide React
|
||||
* - useNavigationStore
|
||||
*/
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { ArrowRight, Code, GitBranch, Github } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from '../store/navigationStore';
|
||||
|
||||
const ConnectView: React.FC = () => {
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState<'import' | 'template' | null>(null);
|
||||
const { markStepCompleted, goToNextStep, setCurrentStep } = useNavigationStore();
|
||||
|
||||
// Function to simulate GitHub connection
|
||||
const handleConnect = () => {
|
||||
// In a real app, this would trigger GitHub OAuth
|
||||
setIsConnected(true);
|
||||
// Mark the connect step as completed
|
||||
markStepCompleted(OnboardingStep.CONNECT);
|
||||
};
|
||||
|
||||
// Function to handle option selection
|
||||
const handleOptionSelect = (option: 'import' | 'template') => {
|
||||
setSelectedOption(option);
|
||||
};
|
||||
|
||||
// Function to handle navigation to next step
|
||||
const handleNext = () => {
|
||||
if (selectedOption === 'import') {
|
||||
setCurrentStep(OnboardingStep.REPOSITORY);
|
||||
} else if (selectedOption === 'template') {
|
||||
setCurrentStep(OnboardingStep.TEMPLATE);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center max-w-4xl mx-auto space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2">Connect</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Connect and import a GitHub repo or start from a template
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{!isConnected ? (
|
||||
// Not connected state - Show GitHub connect
|
||||
<div className="flex flex-col items-center space-y-6 w-full max-w-md">
|
||||
<div className="flex justify-center p-6">
|
||||
<div className="w-24 h-24 flex items-center justify-center rounded-full bg-primary/10">
|
||||
<Github size={48} className="text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold text-center">Deploy your first app</h2>
|
||||
<p className="text-center text-muted-foreground">
|
||||
Once connected, you can import a repository from your account or start with one of our templates.
|
||||
</p>
|
||||
|
||||
<Button onClick={handleConnect} size="lg" className="mt-4">
|
||||
<Github className="mr-2 h-5 w-5" />
|
||||
Connect to GitHub
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
// Connected state - Show import/template options
|
||||
<div className="w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
{/* Import Repository Option */}
|
||||
<Card
|
||||
className={`cursor-pointer border-2 transition-colors ${
|
||||
selectedOption === 'import' ? 'border-primary' : 'border-border'
|
||||
}`}
|
||||
onClick={() => handleOptionSelect('import')}
|
||||
>
|
||||
<CardContent className="p-6 flex flex-col items-center">
|
||||
<div className="w-16 h-16 flex items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||
<GitBranch size={32} className="text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">Import a repository</h3>
|
||||
<p className="text-center text-muted-foreground">
|
||||
Select from your existing GitHub repositories
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Start with Template Option */}
|
||||
<Card
|
||||
className={`cursor-pointer border-2 transition-colors ${
|
||||
selectedOption === 'template' ? 'border-primary' : 'border-border'
|
||||
}`}
|
||||
onClick={() => handleOptionSelect('template')}
|
||||
>
|
||||
<CardContent className="p-6 flex flex-col items-center">
|
||||
<div className="w-16 h-16 flex items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||
<Code size={32} className="text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">Start with a template</h3>
|
||||
<p className="text-center text-muted-foreground">
|
||||
Choose from our pre-configured templates
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={handleNext}
|
||||
disabled={!selectedOption}
|
||||
className="px-6"
|
||||
>
|
||||
Next
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectView;
|
126
packages/frontend/src/components/onboarding/views/DeployView.tsx
Normal file
126
packages/frontend/src/components/onboarding/views/DeployView.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* DeployView.tsx
|
||||
*
|
||||
* This component displays the final deployment screen with status information
|
||||
* and payment options.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Display deployment status with confirmations
|
||||
* 2. Show project details
|
||||
* 3. Include payment or deployment button
|
||||
* 4. Add navigation controls
|
||||
* 5. Match the dark theme UI shown in the screenshots
|
||||
*/
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { ArrowLeft, ArrowRight, Check, Rocket } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from '../store/navigationStore';
|
||||
|
||||
const DeployView: React.FC = () => {
|
||||
const [isDeployed, setIsDeployed] = useState(false);
|
||||
const { markStepCompleted, goToPreviousStep } = useNavigationStore();
|
||||
|
||||
const handleDeploy = () => {
|
||||
// In a real app, this would actually deploy the project
|
||||
setIsDeployed(true);
|
||||
markStepCompleted(OnboardingStep.DEPLOY);
|
||||
};
|
||||
|
||||
// Mock project data
|
||||
const project = {
|
||||
name: 'Progressive Web App (PWA)',
|
||||
url: 'git.account/repo-name',
|
||||
status: 'configured',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col max-w-4xl mx-auto space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2">Deploy</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Your deployment is configured and ready to go!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-16 h-16 flex items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||
<Rocket size={32} className="text-primary" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold mb-4">Deploy</h2>
|
||||
</div>
|
||||
|
||||
{/* Deployment Status Card */}
|
||||
<Card className="border border-border">
|
||||
<CardContent className="p-6">
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="text-xl font-medium mb-2">
|
||||
{isDeployed ? 'Deployment Complete!' : 'Your deployment is configured and ready to go!'}
|
||||
</h3>
|
||||
<p className="text-muted-foreground">
|
||||
{isDeployed
|
||||
? 'Your app has been successfully deployed and is now live.'
|
||||
: 'Review your settings and click "Pay and Deploy" to launch your application.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Project Details */}
|
||||
<div className="space-y-4 mb-6">
|
||||
<div className="flex justify-between items-center p-3 bg-muted/50 rounded-md">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-2 w-2 rounded-full bg-green-500"></div>
|
||||
<span className="font-medium">Progressive Web App (PWA)</span>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">$7 per month</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground p-2">
|
||||
<Check size={16} className="text-green-500" />
|
||||
<span>git.account/repo-name</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Deploy Button */}
|
||||
{!isDeployed ? (
|
||||
<Button
|
||||
className="w-full"
|
||||
size="lg"
|
||||
onClick={handleDeploy}
|
||||
>
|
||||
Pay and Deploy
|
||||
</Button>
|
||||
) : (
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-green-500">
|
||||
<Check size={20} />
|
||||
<span className="font-medium">Successfully Deployed</span>
|
||||
</div>
|
||||
<Button variant="outline" className="w-full">
|
||||
View Dashboard
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-between">
|
||||
<Button variant="outline" onClick={goToPreviousStep}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
{isDeployed && (
|
||||
<Button variant="default">
|
||||
Finish
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployView;
|
@ -0,0 +1,151 @@
|
||||
/**
|
||||
* DeploymentOptionsView.tsx
|
||||
*
|
||||
* This component displays advanced deployment options like container selection,
|
||||
* account selection, and other deployment-specific settings.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Display forms for deployment options
|
||||
* 2. Include dropdown for container URL selection
|
||||
* 3. Include account selection
|
||||
* 4. Add navigation controls
|
||||
*/
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { ArrowLeft, ArrowRight, Server } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from '../store/navigationStore';
|
||||
|
||||
const DeploymentOptionsView: React.FC = () => {
|
||||
const [selectedContainer, setSelectedContainer] = useState('');
|
||||
const [selectedAccount, setSelectedAccount] = useState('');
|
||||
const [environmentTypes, setEnvironmentTypes] = useState({
|
||||
production: true,
|
||||
preview: true,
|
||||
development: false,
|
||||
});
|
||||
|
||||
const { markStepCompleted, goToNextStep, goToPreviousStep } = useNavigationStore();
|
||||
|
||||
const handleNext = () => {
|
||||
// In a real app, would validate inputs
|
||||
markStepCompleted(OnboardingStep.DEPLOYMENT_OPTIONS);
|
||||
goToNextStep();
|
||||
};
|
||||
|
||||
const toggleEnvironmentType = (type: 'production' | 'preview' | 'development') => {
|
||||
setEnvironmentTypes({
|
||||
...environmentTypes,
|
||||
[type]: !environmentTypes[type],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col max-w-4xl mx-auto space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2">Configure</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Set the deployment URL for a single deployment or by creating a separate section for multiple deployments
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-16 h-16 flex items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||
<Server size={32} className="text-primary" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold mb-4">Deployment Options</h2>
|
||||
</div>
|
||||
|
||||
{/* Deployment Options Form */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Container Settings</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Container URL */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="container-url">Select Container URL</Label>
|
||||
<Select value={selectedContainer} onValueChange={setSelectedContainer}>
|
||||
<SelectTrigger id="container-url">
|
||||
<SelectValue placeholder="Select" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="container1">container-123.example.com</SelectItem>
|
||||
<SelectItem value="container2">container-456.example.com</SelectItem>
|
||||
<SelectItem value="container3">container-789.example.com</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Environment Types */}
|
||||
<div className="space-y-2">
|
||||
<Label>Environment Variables</Label>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="production-option"
|
||||
checked={environmentTypes.production}
|
||||
onCheckedChange={() => toggleEnvironmentType('production')}
|
||||
/>
|
||||
<Label htmlFor="production-option" className="cursor-pointer">Production</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="preview-option"
|
||||
checked={environmentTypes.preview}
|
||||
onCheckedChange={() => toggleEnvironmentType('preview')}
|
||||
/>
|
||||
<Label htmlFor="preview-option" className="cursor-pointer">Preview</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="development-option"
|
||||
checked={environmentTypes.development}
|
||||
onCheckedChange={() => toggleEnvironmentType('development')}
|
||||
/>
|
||||
<Label htmlFor="development-option" className="cursor-pointer">Development</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Account Selection */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="select-account">Select Account</Label>
|
||||
<Select value={selectedAccount} onValueChange={setSelectedAccount}>
|
||||
<SelectTrigger id="select-account">
|
||||
<SelectValue placeholder="Select" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="account1">Personal Account</SelectItem>
|
||||
<SelectItem value="account2">Team Account</SelectItem>
|
||||
<SelectItem value="account3">Organization Account</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-between">
|
||||
<Button variant="outline" onClick={goToPreviousStep}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<Button onClick={handleNext}>
|
||||
Next
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeploymentOptionsView;
|
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* RepositoryListView.tsx
|
||||
*
|
||||
* This component displays a list of GitHub repositories for the user to select from.
|
||||
* It shows repository names with timestamps and allows selection.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Use a scrollable list component
|
||||
* 2. Show repository names with timestamps
|
||||
* 3. Allow selection with radio buttons
|
||||
* 4. Add navigation controls (back/next)
|
||||
* 5. Add proper spacing and responsive behavior
|
||||
*/
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { ArrowLeft, ArrowRight, GitBranch } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from '../store/navigationStore';
|
||||
|
||||
// Mock data for repositories
|
||||
const mockRepositories = [
|
||||
{ id: '1', name: 'username/repo-name', updatedAt: '4 minutes ago' },
|
||||
{ id: '2', name: 'username/another-repo', updatedAt: '6 minutes ago' },
|
||||
{ id: '3', name: 'username/test-project', updatedAt: '2 hours ago' },
|
||||
{ id: '4', name: 'username/awesome-app', updatedAt: '1 day ago' },
|
||||
{ id: '5', name: 'username/frontend-demo', updatedAt: '3 days ago' },
|
||||
];
|
||||
|
||||
const RepositoryListView: React.FC = () => {
|
||||
const [selectedRepo, setSelectedRepo] = useState<string | null>(null);
|
||||
const { markStepCompleted, goToNextStep, goToPreviousStep } = useNavigationStore();
|
||||
|
||||
const handleRepoSelect = (repoId: string) => {
|
||||
setSelectedRepo(repoId);
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (selectedRepo) {
|
||||
markStepCompleted(OnboardingStep.REPOSITORY);
|
||||
goToNextStep();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col max-w-4xl mx-auto space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2">Connect</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Connect and import a GitHub repo or start from a template
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-16 h-16 flex items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||
<GitBranch size={32} className="text-primary" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold mb-4">Import a repository</h2>
|
||||
</div>
|
||||
|
||||
{/* Repository List */}
|
||||
<div className="border rounded-lg w-full">
|
||||
<ScrollArea className="h-64">
|
||||
<RadioGroup value={selectedRepo || ''} onValueChange={handleRepoSelect} className="p-1">
|
||||
{mockRepositories.map((repo) => (
|
||||
<div
|
||||
key={repo.id}
|
||||
className="flex items-center space-x-3 border-b last:border-0 p-4 hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
<RadioGroupItem value={repo.id} id={`repo-${repo.id}`} className="data-[state=checked]:border-primary data-[state=checked]:bg-primary" />
|
||||
<Label htmlFor={`repo-${repo.id}`} className="flex flex-1 justify-between cursor-pointer">
|
||||
<div className="flex items-center space-x-2">
|
||||
<GitBranch size={18} className="text-muted-foreground" />
|
||||
<span>{repo.name}</span>
|
||||
</div>
|
||||
<span className="text-muted-foreground text-sm">{repo.updatedAt}</span>
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-between">
|
||||
<Button variant="outline" onClick={goToPreviousStep}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleNext}
|
||||
disabled={!selectedRepo}
|
||||
>
|
||||
Next
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RepositoryListView;
|
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* TemplateSelectionView.tsx
|
||||
*
|
||||
* This component displays a grid of templates for the user to select from.
|
||||
* It shows template cards with icons, names, and descriptions.
|
||||
*
|
||||
* Implementation:
|
||||
* 1. Display a grid of template cards
|
||||
* 2. Show template details including icon, name, and description
|
||||
* 3. Allow selection of a template
|
||||
* 4. Add navigation controls
|
||||
* 5. Add proper spacing and responsive behavior
|
||||
*/
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { ArrowLeft, ArrowRight, Code } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
import useNavigationStore, { OnboardingStep } from '../store/navigationStore';
|
||||
|
||||
// Mock data for templates
|
||||
const mockTemplates = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Progressive Web App (PWA)',
|
||||
description: 'A responsive web app with offline capabilities',
|
||||
icon: Code,
|
||||
category: 'web'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Design Upload Pack',
|
||||
description: 'Simple file upload and processing pipeline',
|
||||
icon: Code,
|
||||
category: 'utility'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'React + Redux + TailwindCSS',
|
||||
description: 'Modern frontend stack with state management',
|
||||
icon: Code,
|
||||
category: 'web'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Node.js API Starter',
|
||||
description: 'Backend API with Express and MongoDB',
|
||||
icon: Code,
|
||||
category: 'api'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: 'E-commerce Platform',
|
||||
description: 'Full-stack shop with payment processing',
|
||||
icon: Code,
|
||||
category: 'fullstack'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
name: 'Static Blog Starter',
|
||||
description: 'JAMstack blog with markdown support',
|
||||
icon: Code,
|
||||
category: 'content'
|
||||
},
|
||||
];
|
||||
|
||||
const TemplateSelectionView: React.FC = () => {
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<string | null>(null);
|
||||
const { markStepCompleted, goToNextStep, goToPreviousStep } = useNavigationStore();
|
||||
|
||||
const handleTemplateSelect = (templateId: string) => {
|
||||
setSelectedTemplate(templateId);
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (selectedTemplate) {
|
||||
markStepCompleted(OnboardingStep.TEMPLATE);
|
||||
goToNextStep();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col max-w-4xl mx-auto space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2">Connect</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Connect and import a GitHub repo or start from a template
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-16 h-16 flex items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||
<Code size={32} className="text-primary" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold mb-4">Start with a template</h2>
|
||||
</div>
|
||||
|
||||
{/* Template Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{mockTemplates.map((template) => {
|
||||
const Icon = template.icon;
|
||||
const isSelected = selectedTemplate === template.id;
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={template.id}
|
||||
className={`cursor-pointer border-2 transition-colors ${
|
||||
isSelected ? 'border-primary' : 'border-border'
|
||||
}`}
|
||||
onClick={() => handleTemplateSelect(template.id)}
|
||||
>
|
||||
<CardContent className="p-4 flex items-start space-x-4">
|
||||
<div className="w-10 h-10 flex items-center justify-center rounded-full bg-primary/10 mt-1">
|
||||
<Icon size={20} className="text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium">{template.name}</h3>
|
||||
<p className="text-sm text-muted-foreground">{template.description}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-between">
|
||||
<Button variant="outline" onClick={goToPreviousStep}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleNext}
|
||||
disabled={!selectedTemplate}
|
||||
>
|
||||
Next
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TemplateSelectionView;
|
@ -1,4 +1,6 @@
|
||||
import OnboardingDialog from '@/components/onboarding/OnboardingDialog';
|
||||
import AutoSignInIFrameModal from '@/components/shared/auth/AutoSignInIFrameModal';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
/**
|
||||
* AuthPage component that renders the authentication page with an auto sign-in iframe modal.
|
||||
@ -17,8 +19,18 @@ const AuthPage = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-center relative z-10 flex-1 pb-12">
|
||||
<div className="flex-center relative z-10 flex-1 pb-12 flex-col">
|
||||
<AutoSignInIFrameModal />
|
||||
<div className="mt-8 text-center">
|
||||
<p className="text-muted-foreground mb-2">Want to see our new onboarding flow?</p>
|
||||
<OnboardingDialog
|
||||
trigger={
|
||||
<Button variant="outline">
|
||||
Try Onboarding Demo
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
57
packages/frontend/src/pages/OnboardingDemoPage.tsx
Normal file
57
packages/frontend/src/pages/OnboardingDemoPage.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import OnboardingDialog from '@/components/onboarding/OnboardingDialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
/**
|
||||
* OnboardingDemoPage component
|
||||
*
|
||||
* This page serves as a landing page with a prominent button to open the onboarding dialog.
|
||||
* It provides information about the onboarding feature and launches it in a modal dialog.
|
||||
*/
|
||||
const OnboardingDemoPage: React.FC = () => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-background p-4">
|
||||
<Card className="w-full max-w-2xl">
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="text-3xl font-bold">Onboarding Demo</CardTitle>
|
||||
<CardDescription className="text-lg mt-2">
|
||||
Experience the new user onboarding flow for Snowball deployment
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="text-center">
|
||||
<p className="mb-4">
|
||||
The onboarding flow guides users through the following steps:
|
||||
</p>
|
||||
<ul className="text-left list-disc list-inside space-y-2 mb-6">
|
||||
<li>Connecting with GitHub</li>
|
||||
<li>Selecting a repository or template</li>
|
||||
<li>Configuring deployment settings</li>
|
||||
<li>Setting deployment options</li>
|
||||
<li>Deploying your application</li>
|
||||
</ul>
|
||||
<p className="text-muted-foreground mb-8">
|
||||
All data in this demo is simulated and no actual deployments will be made.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<OnboardingDialog
|
||||
trigger={
|
||||
<Button size="lg" className="px-8">
|
||||
Open Onboarding Dialog
|
||||
</Button>
|
||||
}
|
||||
defaultOpen={dialogOpen}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingDemoPage;
|
15
packages/frontend/src/pages/OnboardingPage.tsx
Normal file
15
packages/frontend/src/pages/OnboardingPage.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import OnboardingLayout from '../components/onboarding/OnboardingLayout';
|
||||
|
||||
/**
|
||||
* OnboardingPage component
|
||||
*
|
||||
* This page serves as a demo wrapper for the onboarding feature.
|
||||
* It simply renders the OnboardingLayout component which handles
|
||||
* the entire onboarding flow.
|
||||
*/
|
||||
const OnboardingPage: React.FC = () => {
|
||||
return <OnboardingLayout />;
|
||||
};
|
||||
|
||||
export default OnboardingPage;
|
Loading…
Reference in New Issue
Block a user