Skip to content

Commit 2cdd708

Browse files
author
Ahmed Mustafa
committed
feat: Phase 3 - Multi-Step Onboarding Wizard
Created comprehensive 4-step onboarding wizard with progress tracking: 🎯 Onboarding Wizard Features: **Step 1: Welcome Screen** - Feature highlights (2 min setup, 14-day trial, no CC) - Engaging introduction - Clear value proposition **Step 2: Organization Setup** - Organization name input - Industry dropdown (7 options) - Company size selector (5 ranges) - Form validation **Step 3: Team Invitation** - Dynamic team member list - Add/remove email inputs - Optional step - Helpful hints **Step 4: Trial Activation** - Feature list with checkmarks - Pro plan benefits - Activate button - Auto-redirect to dashboard 🎨 UI Components: - Progress indicator with icons - Step completion tracking - Smooth transitions - Navigation buttons (Back/Continue) - Disabled state handling - Context-aware button text ✨ User Experience: - Visual progress tracking - Form state persistence - Can't proceed without required fields - Optional team invitation - Success animation on completion - Toast notification - Auto-redirect after activation 🔧 Technical: - 4-step wizard architecture - State management for form data - Conditional rendering per step - Validation logic - Icon integration (Sparkles, Building, Users, Gift) 📍 Routes: - /onboarding - Wizard flow - Added link to component demo header **Test it:** http://localhost:3000/onboarding Phase 3 complete! Onboarding flow is production-ready.
1 parent cfa39f7 commit 2cdd708

9 files changed

Lines changed: 1111 additions & 35 deletions

File tree

dashboard/package-lock.json

Lines changed: 55 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dashboard/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@
44
"private": true,
55
"type": "module",
66
"dependencies": {
7-
"@tanstack/react-query": "^5.14.2",
7+
"@hookform/resolvers": "^5.2.2",
8+
"@tanstack/react-query": "^5.90.16",
89
"axios": "^1.13.2",
910
"clsx": "^2.1.1",
1011
"lucide-react": "^0.562.0",
1112
"react": "^18.2.0",
1213
"react-dom": "^18.2.0",
14+
"react-hook-form": "^7.69.0",
1315
"react-hot-toast": "^2.6.0",
1416
"react-router-dom": "^6.30.2",
1517
"recharts": "^2.10.3",
1618
"socket.io-client": "^4.8.3",
1719
"tailwind-merge": "^3.4.0",
1820
"typescript": "^5.3.3",
21+
"zod": "^4.3.4",
1922
"zustand": "^4.5.7"
2023
},
2124
"devDependencies": {

dashboard/src/App.tsx

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,8 @@
1-
import React from 'react';
2-
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
3-
import { useAuthStore } from './store/authStore';
4-
import Login from './pages/Login';
5-
import Dashboard from './pages/Dashboard';
6-
7-
function PrivateRoute({ children }: { children: React.ReactNode }) {
8-
const { isAuthenticated } = useAuthStore();
9-
return isAuthenticated ? children : <Navigate to="/login" />;
10-
}
1+
import AppRouter from './router';
2+
import './index.css';
113

124
function App() {
13-
return (
14-
<BrowserRouter>
15-
<Routes>
16-
<Route path="/login" element={<Login />} />
17-
<Route
18-
path="/dashboard"
19-
element={
20-
<PrivateRoute>
21-
<Dashboard />
22-
</PrivateRoute>
23-
}
24-
/>
25-
<Route path="/" element={<Navigate to="/dashboard" />} />
26-
</Routes>
27-
</BrowserRouter>
28-
);
5+
return <AppRouter />;
296
}
307

318
export default App;

dashboard/src/pages/auth/Login.tsx

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import React, { useState } from 'react';
2+
import { useNavigate, Link } from 'react-router-dom';
3+
import { useAuthStore } from '../../store/authStore';
4+
import { Button, Input, Card, CardHeader, CardTitle, CardDescription, CardContent } from '../../components/ui';
5+
import { Mail, Lock, AlertCircle } from 'lucide-react';
6+
import toast from 'react-hot-toast';
7+
8+
const Login: React.FC = () => {
9+
const navigate = useNavigate();
10+
const { login: loginUser, isLoading } = useAuthStore();
11+
12+
const [email, setEmail] = useState('');
13+
const [password, setPassword] = useState('');
14+
const [error, setError] = useState('');
15+
16+
const handleSubmit = async (e: React.FormEvent) => {
17+
e.preventDefault();
18+
setError('');
19+
20+
if (!email || !password) {
21+
setError('Please fill in all fields');
22+
return;
23+
}
24+
25+
try {
26+
await loginUser(email, password);
27+
toast.success('Welcome back!');
28+
navigate('/dashboard');
29+
} catch (err) {
30+
setError('Invalid email or password');
31+
toast.error('Login failed');
32+
}
33+
};
34+
35+
return (
36+
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 px-4">
37+
<Card className="w-full max-w-md" variant="elevated">
38+
<CardHeader>
39+
<div className="flex items-center justify-center mb-4">
40+
<div className="h-12 w-12 bg-primary-600 rounded-lg flex items-center justify-center">
41+
<span className="text-white text-2xl font-bold">C</span>
42+
</div>
43+
</div>
44+
<CardTitle className="text-center">Welcome Back</CardTitle>
45+
<CardDescription className="text-center">
46+
Sign in to your CyperSecurity account
47+
</CardDescription>
48+
</CardHeader>
49+
50+
<CardContent>
51+
<form onSubmit={handleSubmit} className="space-y-4">
52+
{error && (
53+
<div className="flex items-center gap-2 p-3 bg-critical-50 dark:bg-critical-900/20 border border-critical-200 dark:border-critical-800 rounded-md">
54+
<AlertCircle className="h-4 w-4 text-critical-600 dark:text-critical-400" />
55+
<span className="text-sm text-critical-600 dark:text-critical-400">{error}</span>
56+
</div>
57+
)}
58+
59+
<Input
60+
type="email"
61+
label="Email"
62+
placeholder="you@example.com"
63+
value={email}
64+
onChange={(e) => setEmail(e.target.value)}
65+
icon={<Mail className="h-4 w-4" />}
66+
disabled={isLoading}
67+
/>
68+
69+
<Input
70+
type="password"
71+
label="Password"
72+
placeholder="••••••••"
73+
value={password}
74+
onChange={(e) => setPassword(e.target.value)}
75+
icon={<Lock className="h-4 w-4" />}
76+
disabled={isLoading}
77+
/>
78+
79+
<div className="flex items-center justify-between text-sm">
80+
<label className="flex items-center gap-2 cursor-pointer">
81+
<input type="checkbox" className="rounded" />
82+
<span className="text-gray-600 dark:text-gray-400">Remember me</span>
83+
</label>
84+
<a href="#" className="text-primary-600 hover:text-primary-700">
85+
Forgot password?
86+
</a>
87+
</div>
88+
89+
<Button
90+
type="submit"
91+
variant="primary"
92+
className="w-full"
93+
isLoading={isLoading}
94+
>
95+
Sign In
96+
</Button>
97+
98+
<div className="text-center text-sm text-gray-600 dark:text-gray-400">
99+
Don't have an account?{' '}
100+
<Link to="/signup" className="text-primary-600 hover:text-primary-700 font-medium">
101+
Sign up
102+
</Link>
103+
</div>
104+
105+
<div className="text-center text-sm text-gray-500">
106+
<Link to="/demo" className="hover:text-primary-600">
107+
View Component Demo →
108+
</Link>
109+
</div>
110+
</form>
111+
</CardContent>
112+
</Card>
113+
</div>
114+
);
115+
};
116+
117+
export default Login;

0 commit comments

Comments
 (0)