Skip to content

Commit 41f8b83

Browse files
committed
feat: Implement email verification and migration flow for V2
- Added MigrationRoute component to handle user migration checks and redirects. - Created EmailVerificationPage for users to verify their email with OTP. - Developed MigrationPage for users to complete their profile migration with additional details. - Introduced EmailVerificationService to manage email verification processes. - Implemented MigrationService to handle user migration status and data validation. - Updated githubService to remove unnecessary console logs and improve code clarity.
1 parent 9b92023 commit 41f8b83

21 files changed

+1687
-247
lines changed

src/App.jsx

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import EventsPage from './pages/EventsPage'
88
import EventDetailPage from './pages/EventDetailPage'
99
import SignInPage from './pages/SignInPage'
1010
import SignUpPage from './pages/SignUpPage'
11+
import MigrationPage from './pages/MigrationPage'
12+
import EmailVerificationPage from './pages/EmailVerificationPage'
1113
import DashboardPage from './pages/DashboardPage'
1214
import DashboardLayout from './components/dashboard/DashboardLayout'
1315
import DashboardMainPage from './pages/DashboardMainPage'
@@ -19,6 +21,7 @@ import TermsOfServicePage from './pages/TermsOfServicePage'
1921
import UpdatesPage from './pages/UpdatesPage'
2022
import { AuthProvider } from './context/AuthContext'
2123
import { ProtectedRoute } from './components/auth/ProtectedRoute'
24+
import MigrationRoute from './components/auth/MigrationRoute'
2225
import ScrollToTop from './components/common/ScrollToTop'
2326

2427
// Admin components
@@ -53,8 +56,14 @@ const App = () => {
5356
<Route path="/terms" element={<TermsOfServicePage />} />
5457
<Route path="/updates" element={<UpdatesPage />} />
5558

56-
{/* Protected Routes */}
59+
{/* Migration and Email Verification Routes */}
5760
<Route element={<ProtectedRoute />}>
61+
<Route path="/migration" element={<MigrationPage />} />
62+
<Route path="/email-verification" element={<EmailVerificationPage />} />
63+
</Route>
64+
65+
{/* Protected Routes with Migration Check */}
66+
<Route element={<MigrationRoute />}>
5867
{/* Dashboard routes with layout */}
5968
<Route path="/dashboard" element={<DashboardLayout />}>
6069
<Route index element={<DashboardMainPage />} />
@@ -65,23 +74,25 @@ const App = () => {
6574
<Route path="/logout" element={<LogoutPage />} />
6675
</Route>
6776

68-
{/* Routes with specific role requirements */}
69-
<Route element={<ProtectedRoute requiredRoles={['ADMIN', 'GRAND_PATHFINDER', 'CHIEF_PATHFINDER']} />}>
70-
<Route path="/admin" element={<AdminLayout />}>
71-
<Route index element={<AdminDefaultRedirect />} />
72-
<Route element={<ProtectedRoute requiredRoles={['GRAND_PATHFINDER']} />}>
73-
<Route path="users" element={<AdminUsersPage />} />
74-
<Route path="specializations" element={<AdminSpecializationsPage />} />
77+
{/* Routes with specific role requirements - also wrapped with MigrationRoute */}
78+
<Route element={<MigrationRoute />}>
79+
<Route element={<ProtectedRoute requiredRoles={['ADMIN', 'GRAND_PATHFINDER', 'CHIEF_PATHFINDER']} />}>
80+
<Route path="/admin" element={<AdminLayout />}>
81+
<Route index element={<AdminDefaultRedirect />} />
82+
<Route element={<ProtectedRoute requiredRoles={['GRAND_PATHFINDER']} />}>
83+
<Route path="users" element={<AdminUsersPage />} />
84+
<Route path="specializations" element={<AdminSpecializationsPage />} />
85+
</Route>
86+
<Route path="cohorts" element={<AdminCohortsPage />} />
87+
<Route path="leagues" element={<AdminLeaguesPage />} />
88+
<Route path="weeks" element={<AdminWeeksPage />} />
89+
<Route path="sections" element={<AdminSectionsPage />} />
90+
<Route path="resources" element={<AdminResourcesPage />} />
91+
<Route path="assignments" element={<AdminAssignmentsPage />} />
7592
</Route>
76-
<Route path="cohorts" element={<AdminCohortsPage />} />
77-
<Route path="leagues" element={<AdminLeaguesPage />} />
78-
<Route path="weeks" element={<AdminWeeksPage />} />
79-
<Route path="sections" element={<AdminSectionsPage />} />
80-
<Route path="resources" element={<AdminResourcesPage />} />
81-
<Route path="assignments" element={<AdminAssignmentsPage />} />
93+
{/* Legacy redirect for old admin page */}
94+
<Route path="/admin-old" element={<AdminPage />} />
8295
</Route>
83-
{/* Legacy redirect for old admin page */}
84-
<Route path="/admin-old" element={<AdminPage />} />
8596
</Route>
8697

8798
{/* Fallback route - redirect to home */}

src/components/admin/SectionManagement.jsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,34 +99,27 @@ const SectionManagement = ({
9999
e.preventDefault();
100100
e.stopPropagation();
101101

102-
console.log('Form submitted with data:', formData);
103-
104102
const validationResult = validateForm();
105103
if (!validationResult) {
106-
console.log('Form validation failed');
107104
return;
108105
}
109106

110-
console.log('Form validation passed, submitting...');
111-
console.log('Available functions:', { onCreateSection: typeof onCreateSection, onUpdateSection: typeof onUpdateSection });
112107
setIsSubmitting(true);
113-
setSubmitError(null); try {
108+
setSubmitError(null);
109+
try {
114110
if (editingSection) {
115-
console.log('Updating section:', editingSection.id, formData);
116111
if (typeof onUpdateSection !== 'function') {
117112
throw new Error('Update function not available');
118113
}
119114
await onUpdateSection(editingSection.id, formData);
120115
showToast('Learning day updated successfully!', 'success');
121116
} else {
122-
console.log('Creating new section:', formData);
123117
if (typeof onCreateSection !== 'function') {
124118
throw new Error('Create function not available');
125119
}
126120
await onCreateSection(formData);
127121
showToast('Learning day created successfully!', 'success');
128122
}
129-
console.log('Operation completed successfully');
130123
// Only reset form and close modal on success
131124
setFormData({
132125
name: '',
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { Navigate, useLocation, Outlet } from 'react-router-dom';
3+
import { useAuth } from '../../hooks/useAuth';
4+
import LoadingScreen from '../common/LoadingScreen';
5+
6+
/**
7+
* MigrationRoute - Handles V2 migration flow
8+
* Checks if user needs migration and redirects accordingly
9+
* Supports email verification testing mode via URL parameter
10+
*/
11+
const MigrationRoute = () => {
12+
const { user, loading, isAuthenticated, getUserFlowStatus, getEmailVerificationFlowStatus } = useAuth();
13+
const [flowStatus, setFlowStatus] = useState(null);
14+
const [checkingFlow, setCheckingFlow] = useState(true);
15+
const location = useLocation();
16+
17+
useEffect(() => {
18+
const checkUserFlow = async () => {
19+
if (!isAuthenticated()) {
20+
setCheckingFlow(false);
21+
return;
22+
}
23+
24+
try {
25+
// Check if this is email verification testing mode
26+
const urlParams = new URLSearchParams(location.search);
27+
const testEmailOnly = urlParams.get('test-email') === 'true';
28+
29+
let status;
30+
if (testEmailOnly) {
31+
status = await getEmailVerificationFlowStatus();
32+
} else {
33+
status = await getUserFlowStatus();
34+
}
35+
36+
setFlowStatus(status);
37+
} catch (error) {
38+
console.error('❌ Error checking user flow:', error);
39+
// Default to allowing access if check fails
40+
setFlowStatus({
41+
requiresMigration: false,
42+
emailVerified: true,
43+
isV2User: true
44+
});
45+
} finally {
46+
setCheckingFlow(false);
47+
}
48+
};
49+
50+
if (!loading && user) {
51+
checkUserFlow();
52+
} else if (!loading) {
53+
setCheckingFlow(false);
54+
}
55+
}, [user, loading, isAuthenticated, getUserFlowStatus, getEmailVerificationFlowStatus, location.search]);
56+
57+
// Show loading while checking authentication or flow status
58+
if (loading || checkingFlow) {
59+
return <LoadingScreen message="Checking account status..." />;
60+
}
61+
62+
// If not authenticated, redirect to signin
63+
if (!isAuthenticated()) {
64+
return <Navigate to="/signin" replace />;
65+
}
66+
67+
// Skip migration checks for migration and verification pages
68+
const skipFlowPaths = ['/migration', '/email-verification'];
69+
if (skipFlowPaths.includes(location.pathname)) {
70+
return <Outlet />;
71+
}
72+
73+
// If flowStatus is still null, keep loading
74+
if (!flowStatus) {
75+
return <LoadingScreen message="Checking account status..." />;
76+
}
77+
78+
// If migration is required, redirect to migration page
79+
if (flowStatus.requiresMigration) {
80+
return <Navigate to="/migration" replace />;
81+
}
82+
83+
// If email verification is required, redirect to verification page
84+
if (!flowStatus.emailVerified) {
85+
return <Navigate to="/email-verification" replace />;
86+
}
87+
88+
// User has completed migration and verification, allow access
89+
return <Outlet />;
90+
};
91+
92+
export default MigrationRoute;

src/components/dashboard/LeagueDetailPage.jsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -592,11 +592,7 @@ const LeagueDetailPage = ({ league, onBack }) => {
592592
setTimeout(() => {
593593
setShowSuccessToast(null);
594594
}, 3000);
595-
}
596-
597-
// API call succeeded
598-
console.log(`✅ Resource ${resourceId} ${currentStatus ? 'reset' : 'completed'} successfully`);
599-
595+
}
600596
} catch (err) {
601597
console.error('❌ Error updating resource completion:', err);
602598

src/components/dashboard/LearningProgressSection.jsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,14 @@ const LearningProgressSection = ({ user }) => {
4242
setError(null);
4343

4444
// Use optimized service for progressive loading
45-
const data = await OptimizedDashboardService.loadInitialDashboardData();
46-
45+
const data = await OptimizedDashboardService.loadInitialDashboardData();
4746
// Set essential data immediately for fast UI update
4847
setDashboardData(data.dashboardData);
4948
setCohorts(data.cohorts);
5049
setLeagues(data.leagues);
5150

5251
// Set basic league statistics immediately for instant display
5352
if (data.basicLeagueStats) {
54-
console.log('Setting basic league statistics:', data.basicLeagueStats);
5553
setLeagueStatistics(data.basicLeagueStats);
5654
}
5755

@@ -81,12 +79,8 @@ const LearningProgressSection = ({ user }) => {
8179
setResourceCalculationsInProgress(new Set(leaguesNeedingCalculation.map(l => l.id)));
8280

8381
if (leaguesNeedingCalculation.length > 0) {
84-
console.log(`Starting resource calculations for ${leaguesNeedingCalculation.length} leagues:`, leaguesNeedingCalculation.map(l => l.id));
85-
8682
// Create callback to update resource counts when background calculation completes
8783
const handleResourceUpdate = (leagueId, resourceCount) => {
88-
console.log(`Resource calculation completed for league ${leagueId}: ${resourceCount} resources`);
89-
9084
setLeagueStatistics(prevStats => ({
9185
...prevStats,
9286
[leagueId]: {
@@ -98,11 +92,9 @@ const LearningProgressSection = ({ user }) => {
9892
// Track completion of each league's resource calculation
9993
setResourceCalculationsCompleted(prevCompleted => {
10094
const newCompleted = new Set([...prevCompleted, leagueId]);
101-
console.log(`Completed resource calculations: ${newCompleted.size}/${leaguesNeedingCalculation.length}`, [...newCompleted]);
10295

10396
// Check if all resource calculations are complete
10497
if (newCompleted.size === leaguesNeedingCalculation.length) {
105-
console.log('All resource calculations completed!');
10698
setShowResourcesCompleteToast(true);
10799
// Hide the toast after 4 seconds
108100
setTimeout(() => {
@@ -124,25 +116,22 @@ const LearningProgressSection = ({ user }) => {
124116
// Calculate accurate statistics in background to update basic stats
125117
OptimizedDashboardService.calculateAllLeagueStatistics(leaguesNeedingCalculation, handleResourceUpdate)
126118
.then(accurateStats => {
127-
console.log('Initial statistics calculated:', accurateStats);
128119
setLeagueStatistics(prevStats => ({
129120
...prevStats,
130121
...accurateStats
131122
}));
132123
});
133124
} else {
134-
console.log('All leagues already have complete resource calculations');
135125
// All leagues already have complete data, no calculations needed
136126
setTotalResourceCalculations(0);
137127
setResourceCalculationsInProgress(new Set());
138128
}
139129
} else {
140-
console.log('No leagues found, marking calculations as complete');
141130
setTotalResourceCalculations(0);
142131
}
143132

144133
} catch (err) {
145-
console.error('Error loading dashboard data:', err);
134+
console.error('Error loading dashboard data:', err);
146135
setError(`Failed to connect to the learning platform. Please try again later. (${err.message})`);
147136

148137
// Set fallback data to prevent crashes
@@ -283,7 +272,9 @@ const LearningProgressSection = ({ user }) => {
283272
onBack={handleBackToMain}
284273
/>
285274
);
286-
} return (
275+
}
276+
277+
return (
287278
<div className="bg-transparent">
288279
<div className="p-2 md:p-6 space-y-6">
289280

src/components/dashboard/PendingApprovalPage.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { Clock, AlertCircle } from 'lucide-react';
33

4-
const PendingApprovalPage = ({ user }) => {
4+
const PendingApprovalPage = ({ user }) => {
55
return (
66
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
77
<div className="max-w-md w-full bg-white rounded-lg shadow-lg p-8 text-center">

src/components/landingPage/Cohort.jsx

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -169,20 +169,13 @@ const Cohort = () => {
169169
const [error, setError] = useState(null);
170170
const navigate = useNavigate();
171171

172-
useEffect(() => {
173-
console.log('🔄 Cohort component mounted - starting data fetch...');
174-
172+
useEffect(() => {
175173
const loadCohortsData = async () => {
176174
try {
177175
setLoading(true);
178-
setError(null);
179-
console.log('📡 Fetching cohorts data from API...');
180-
181-
const response = await fetchCohortsStructure();
182-
console.log('✅ API Response received:', response);
183-
176+
setError(null);
177+
const response = await fetchCohortsStructure();
184178
if (response && response.data && response.data.length > 0) {
185-
console.log('📊 Setting cohorts data:', response.data[0]);
186179
setCohortsData(response);
187180
} else {
188181
console.warn('⚠️ No cohort data found in response');
@@ -193,18 +186,13 @@ const Cohort = () => {
193186
setError(err.message || 'Failed to load cohort data');
194187
} finally {
195188
setLoading(false);
196-
console.log('🏁 Loading complete');
197189
}
198190
};
199191

200192
loadCohortsData();
201193
}, []);
202-
203-
console.log('🎯 Render state - Loading:', loading, 'Error:', error, 'Data:', !!cohortsData);
204-
205194
// Loading state
206195
if (loading) {
207-
console.log('⏳ Rendering loading state...');
208196
return (
209197
<MotionSection
210198
id="cohort"
@@ -227,7 +215,6 @@ const Cohort = () => {
227215

228216
// Error state
229217
if (error) {
230-
console.log('❌ Rendering error state:', error);
231218
return (
232219
<MotionSection
233220
id="cohort"
@@ -260,10 +247,7 @@ const Cohort = () => {
260247

261248
// Get the first cohort
262249
const cohort = cohortsData?.data?.[0];
263-
console.log('🏆 Cohort data to render:', cohort);
264-
265250
if (!cohort) {
266-
console.log('📭 No cohort found - rendering empty state');
267251
return (
268252
<MotionSection
269253
id="cohort"

0 commit comments

Comments
 (0)