|
1 | | - |
2 | | -"use client"; |
3 | | - |
4 | | -import { useState, useEffect, useCallback } from "react"; |
5 | | -import dynamic from 'next/dynamic'; |
6 | | -import { useRouter, useSearchParams } from 'next/navigation'; |
7 | | -import Header from "@/components/layout/header"; |
8 | | -import Footer from "@/components/layout/footer"; |
9 | | -import HomeView from "@/components/views/home"; |
10 | | -import type { View, UserRole } from "@/app/types"; |
11 | | -import { useToast } from "@/hooks/use-toast"; |
12 | | -import { format } from "date-fns"; |
13 | | -import { Dialog, DialogContent } from "@/components/ui/dialog"; |
14 | | -import { Loader2 } from "lucide-react"; |
15 | | -import { signOut } from "firebase/auth"; |
16 | | -import { useFirebaseAuth } from "@/hooks/use-firebase-auth"; |
17 | | -import { API_BASE_URL } from "@/lib/api"; |
18 | | - |
19 | | -// A more obvious loading animation to show while dynamic components are being downloaded. |
20 | | -const ModalSkeleton = () => ( |
21 | | - <Dialog open={true}> |
22 | | - <DialogContent className="flex items-center justify-center h-64 bg-transparent border-none shadow-none"> |
23 | | - <Loader2 className="h-16 w-16 animate-spin text-primary" /> |
24 | | - </DialogContent> |
25 | | - </Dialog> |
26 | | -); |
27 | | - |
28 | | -// Dynamically import all modal views. Their code will only be loaded when they're first opened. |
29 | | -const BlogView = dynamic(() => import('@/components/views/blog'), { loading: () => <ModalSkeleton /> }); |
30 | | -const MentorsView = dynamic(() => import('@/components/views/mentors'), { loading: () => <ModalSkeleton /> }); |
31 | | -const IncubatorsView = dynamic(() => import('@/components/views/incubators'), { loading: () => <ModalSkeleton /> }); |
32 | | -const PricingView = dynamic(() => import('@/components/views/pricing'), { loading: () => <ModalSkeleton /> }); |
33 | | -const MsmesView = dynamic(() => import('@/components/views/msmes'), { loading: () => <ModalSkeleton /> }); |
34 | | -const DashboardView = dynamic(() => import('@/components/views/dashboard'), { loading: () => <ModalSkeleton /> }); |
35 | | -const MentorDashboardView = dynamic(() => import('@/components/views/mentor-dashboard'), { loading: () => <ModalSkeleton /> }); |
36 | | -const IncubatorDashboardView = dynamic(() => import('@/components/views/incubator-dashboard'), { loading: () => <ModalSkeleton /> }); |
37 | | -const MsmeDashboardView = dynamic(() => import('@/components/views/msme-dashboard'), { loading: () => <ModalSkeleton /> }); |
38 | | -const LoginModal = dynamic(() => import('@/components/auth/login-modal'), { loading: () => <ModalSkeleton /> }); |
39 | | -const SignupModal = dynamic(() => import('@/components/auth/signup-modal'), { loading: () => <ModalSkeleton /> }); |
40 | | -const EducationView = dynamic(() => import('@/components/views/education'), { loading: () => <ModalSkeleton /> }); |
41 | | -const ContactView = dynamic(() => import('@/components/views/contact'), { loading: () => <ModalSkeleton /> }); |
42 | | - |
43 | | -type User = { |
44 | | - name: string; |
45 | | - email: string; |
46 | | -} |
47 | | -type AuthProvider = 'local' | 'google' | 'linkedin'; |
| 1 | +import MainView from "@/components/views/main-view"; |
48 | 2 |
|
49 | 3 | export default function Home() { |
50 | | - const [theme, setTheme] = useState<'light' | 'dark' | null>(null); |
51 | | - const [activeView, setActiveView] = useState<View>("home"); |
52 | | - const [isLoggedIn, setLoggedIn] = useState(false); |
53 | | - const [userRole, setUserRole] = useState<UserRole | null>(null); |
54 | | - const [user, setUser] = useState<User | null>(null); |
55 | | - const [authProvider, setAuthProvider] = useState<AuthProvider | null>(null); |
56 | | - const [hasSubscription, setHasSubscription] = useState(false); |
57 | | - const [hasUsedFreeSession, setHasUsedFreeSession] = useState(false); |
58 | | - const [appliedPrograms, setAppliedPrograms] = useState<Record<string, string>>({}); |
59 | | - const { toast } = useToast(); |
60 | | - const [isLoading, setIsLoading] = useState(true); |
61 | | - const router = useRouter(); |
62 | | - const searchParams = useSearchParams(); |
63 | | - const { auth } = useFirebaseAuth(); |
64 | | - |
65 | | - // This effect runs once on mount to set initial state from localStorage |
66 | | - useEffect(() => { |
67 | | - const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null; |
68 | | - const initialTheme = savedTheme || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); |
69 | | - setTheme(initialTheme); |
70 | | - |
71 | | - const savedIsLoggedIn = localStorage.getItem('isLoggedIn'); |
72 | | - const savedUserRole = localStorage.getItem('userRole') as UserRole | null; |
73 | | - const savedUser = localStorage.getItem('user'); |
74 | | - const savedSubscription = localStorage.getItem('hasSubscription'); |
75 | | - const savedAppliedPrograms = localStorage.getItem('appliedPrograms'); |
76 | | - const savedAuthProvider = localStorage.getItem('authProvider') as AuthProvider | null; |
77 | | - |
78 | | - if (savedIsLoggedIn === 'true' && savedUserRole && savedUser) { |
79 | | - setLoggedIn(true); |
80 | | - setUserRole(savedUserRole); |
81 | | - setUser(JSON.parse(savedUser)); |
82 | | - setHasSubscription(savedSubscription === 'true'); |
83 | | - setHasUsedFreeSession(false); |
84 | | - if (savedAppliedPrograms) { |
85 | | - setAppliedPrograms(JSON.parse(savedAppliedPrograms)); |
86 | | - } |
87 | | - if (savedAuthProvider) { |
88 | | - setAuthProvider(savedAuthProvider); |
89 | | - } |
90 | | - } |
91 | | - setIsLoading(false); |
92 | | - }, []); |
93 | | - |
94 | | - // This effect handles URL-based actions like post-OAuth flow or errors |
95 | | - useEffect(() => { |
96 | | - const error = searchParams.get('error'); |
97 | | - const status = searchParams.get('status'); |
98 | | - |
99 | | - if (status === 'pending_approval') { |
100 | | - toast({ |
101 | | - title: "Registration Submitted", |
102 | | - description: "Your account is pending admin approval. You'll be notified via email once it's active.", |
103 | | - }); |
104 | | - router.replace('/'); |
105 | | - } |
106 | | - |
107 | | - if (error) { |
108 | | - let title = "Sign-in Failed"; |
109 | | - let description = "Could not sign in with provider. Please try again or use email."; |
110 | | - |
111 | | - if (error === 'account_suspended') { |
112 | | - title = "Account Suspended"; |
113 | | - description = "This account has been suspended."; |
114 | | - } else if (error === 'email_not_provided') { |
115 | | - title = "Email Missing"; |
116 | | - description = "Your provider did not share your email. Please try another method."; |
117 | | - } |
118 | | - toast({ variant: "destructive", title, description }); |
119 | | - router.replace('/'); |
120 | | - } |
121 | | - }, [searchParams, router, toast]); |
122 | | - |
123 | | - // This effect runs whenever the theme changes to update the DOM and localStorage |
124 | | - useEffect(() => { |
125 | | - if (theme) { |
126 | | - if (theme === 'dark') { |
127 | | - document.documentElement.classList.add('dark'); |
128 | | - } else { |
129 | | - document.documentElement.classList.remove('dark'); |
130 | | - } |
131 | | - localStorage.setItem('theme', theme); |
132 | | - } |
133 | | - }, [theme]); |
134 | | - |
135 | | - const handleModalOpenChange = (view: View) => (isOpen: boolean) => { |
136 | | - if (!isOpen) { |
137 | | - setActiveView("home"); |
138 | | - } else { |
139 | | - setActiveView(view); |
140 | | - } |
141 | | - }; |
142 | | - |
143 | | - const handleLoginSuccess = (data: { role: UserRole, token: string, hasSubscription: boolean, name: string, email: string, authProvider: AuthProvider }) => { |
144 | | - const { role, token, hasSubscription, name, email, authProvider } = data; |
145 | | - const userData = { name, email }; |
146 | | - setLoggedIn(true); |
147 | | - setUserRole(role); |
148 | | - setUser(userData); |
149 | | - setHasSubscription(hasSubscription); |
150 | | - setAuthProvider(authProvider); |
151 | | - |
152 | | - localStorage.setItem('isLoggedIn', 'true'); |
153 | | - localStorage.setItem('userRole', role || ''); |
154 | | - localStorage.setItem('user', JSON.stringify(userData)); |
155 | | - localStorage.setItem('hasSubscription', String(hasSubscription)); |
156 | | - localStorage.setItem('token', token); |
157 | | - localStorage.setItem('authProvider', authProvider); |
158 | | - |
159 | | - setHasUsedFreeSession(false); // Reset for prototype testing |
160 | | - setActiveView('dashboard'); |
161 | | - }; |
162 | | - |
163 | | - const handleLogout = async () => { |
164 | | - try { |
165 | | - await signOut(auth); |
166 | | - setLoggedIn(false); |
167 | | - setUserRole(null); |
168 | | - setUser(null); |
169 | | - setHasSubscription(false); |
170 | | - setAppliedPrograms({}); |
171 | | - setAuthProvider(null); |
172 | | - // Clear all auth-related items |
173 | | - localStorage.removeItem('isLoggedIn'); |
174 | | - localStorage.removeItem('userRole'); |
175 | | - localStorage.removeItem('user'); |
176 | | - localStorage.removeItem('hasSubscription'); |
177 | | - localStorage.removeItem('appliedPrograms'); |
178 | | - localStorage.removeItem('token'); |
179 | | - localStorage.removeItem('authProvider'); |
180 | | - setActiveView('home'); |
181 | | - router.push('/'); |
182 | | - toast({ title: "Logged Out", description: "You have been successfully logged out." }); |
183 | | - } catch (error) { |
184 | | - console.error("Logout failed:", error); |
185 | | - toast({ variant: "destructive", title: "Logout Failed", description: "Could not log out. Please try again." }); |
186 | | - } |
187 | | - }; |
188 | | - |
189 | | - const handleBookingSuccess = (mentorName: string, date: Date, time: string) => { |
190 | | - if (!hasUsedFreeSession) { |
191 | | - setHasUsedFreeSession(true); |
192 | | - } |
193 | | - toast({ |
194 | | - title: "Booking Confirmed!", |
195 | | - description: `Your session with ${mentorName} on ${format(date, 'PPP')} at ${time} is booked.`, |
196 | | - }); |
197 | | - }; |
198 | | - |
199 | | - const handleGetStartedOnPricing = () => { |
200 | | - if (isLoggedIn) { |
201 | | - setHasSubscription(true); |
202 | | - localStorage.setItem('hasSubscription', 'true'); |
203 | | - toast({ |
204 | | - title: "Subscription Activated!", |
205 | | - description: "You now have full access to all premium features.", |
206 | | - }); |
207 | | - setActiveView('home'); |
208 | | - } else { |
209 | | - setActiveView('signup'); |
210 | | - } |
211 | | - }; |
212 | | - |
213 | | - const handleEducationApplicationSuccess = (programTitle: string, session: { language: string, date: string, time: string }) => { |
214 | | - const newAppliedPrograms = { |
215 | | - ...appliedPrograms, |
216 | | - [programTitle]: `${session.date}, ${session.time}` |
217 | | - }; |
218 | | - setAppliedPrograms(newAppliedPrograms); |
219 | | - localStorage.setItem('appliedPrograms', JSON.stringify(newAppliedPrograms)); |
220 | | - |
221 | | - toast({ |
222 | | - title: "Application Successful!", |
223 | | - description: `You've applied for ${programTitle} on ${session.date} at ${session.time}. A calendar invite has been sent to your email.`, |
224 | | - }); |
225 | | - }; |
226 | | - |
227 | | - const renderDashboard = () => { |
228 | | - if (!isLoggedIn || activeView !== 'dashboard' || !userRole || !user || !authProvider) { |
229 | | - return null; |
230 | | - } |
231 | | - |
232 | | - switch(userRole) { |
233 | | - case 'mentor': |
234 | | - return ( |
235 | | - <MentorDashboardView |
236 | | - isOpen={true} |
237 | | - onOpenChange={handleModalOpenChange('dashboard')} |
238 | | - setActiveView={setActiveView} |
239 | | - user={user} |
240 | | - authProvider={authProvider} |
241 | | - /> |
242 | | - ); |
243 | | - case 'incubator': |
244 | | - return ( |
245 | | - <IncubatorDashboardView |
246 | | - isOpen={true} |
247 | | - onOpenChange={handleModalOpenChange('dashboard')} |
248 | | - user={user} |
249 | | - authProvider={authProvider} |
250 | | - /> |
251 | | - ); |
252 | | - case 'msme': |
253 | | - return ( |
254 | | - <MsmeDashboardView |
255 | | - isOpen={true} |
256 | | - onOpenChange={handleModalOpenChange('dashboard')} |
257 | | - user={user} |
258 | | - authProvider={authProvider} |
259 | | - /> |
260 | | - ); |
261 | | - case 'founder': |
262 | | - case 'admin': |
263 | | - return ( |
264 | | - <DashboardView |
265 | | - isOpen={true} |
266 | | - onOpenChange={handleModalOpenChange('dashboard')} |
267 | | - user={user} |
268 | | - userRole={userRole} |
269 | | - authProvider={authProvider} |
270 | | - hasSubscription={hasSubscription} |
271 | | - setActiveView={setActiveView} |
272 | | - /> |
273 | | - ); |
274 | | - default: |
275 | | - return null; |
276 | | - } |
277 | | - } |
278 | | - |
279 | | - return ( |
280 | | - <> |
281 | | - <Header |
282 | | - activeView={activeView} |
283 | | - setActiveView={setActiveView} |
284 | | - isLoggedIn={isLoggedIn} |
285 | | - onLogout={handleLogout} |
286 | | - theme={theme} |
287 | | - setTheme={setTheme} |
288 | | - isLoading={isLoading} |
289 | | - /> |
290 | | - <main className="flex-grow"> |
291 | | - <HomeView setActiveView={setActiveView} theme={theme} isLoggedIn={isLoggedIn} /> |
292 | | - </main> |
293 | | - <Footer /> |
294 | | - |
295 | | - {activeView === 'blog' && <BlogView isOpen={true} onOpenChange={handleModalOpenChange('blog')} />} |
296 | | - |
297 | | - {activeView === 'mentors' && <MentorsView |
298 | | - isOpen={true} |
299 | | - onOpenChange={handleModalOpenChange('mentors')} |
300 | | - isLoggedIn={isLoggedIn} |
301 | | - hasSubscription={hasSubscription} |
302 | | - hasUsedFreeSession={hasUsedFreeSession} |
303 | | - onBookingSuccess={handleBookingSuccess} |
304 | | - setActiveView={setActiveView} |
305 | | - />} |
306 | | - |
307 | | - {activeView === 'incubators' && <IncubatorsView |
308 | | - isOpen={true} |
309 | | - onOpenChange={handleModalOpenChange('incubators')} |
310 | | - isLoggedIn={isLoggedIn} |
311 | | - hasSubscription={hasSubscription} |
312 | | - setActiveView={setActiveView} |
313 | | - />} |
314 | | - |
315 | | - {activeView === 'pricing' && <PricingView |
316 | | - isOpen={true} |
317 | | - onOpenChange={handleModalOpenChange('pricing')} |
318 | | - onGetStartedClick={handleGetStartedOnPricing} |
319 | | - />} |
320 | | - |
321 | | - {activeView === 'msmes' && <MsmesView |
322 | | - isOpen={true} |
323 | | - onOpenChange={handleModalOpenChange('msmes')} |
324 | | - isLoggedIn={isLoggedIn} |
325 | | - hasSubscription={hasSubscription} |
326 | | - setActiveView={setActiveView} |
327 | | - />} |
328 | | - |
329 | | - {activeView === 'education' && <EducationView |
330 | | - isOpen={true} |
331 | | - onOpenChange={handleModalOpenChange('education')} |
332 | | - onApplicationSuccess={handleEducationApplicationSuccess} |
333 | | - isLoggedIn={isLoggedIn} |
334 | | - setActiveView={setActiveView} |
335 | | - appliedPrograms={appliedPrograms} |
336 | | - />} |
337 | | - |
338 | | - {renderDashboard()} |
339 | | - |
340 | | - {activeView === 'login' && <LoginModal |
341 | | - isOpen={true} |
342 | | - setIsOpen={handleModalOpenChange('login')} |
343 | | - onLoginSuccess={handleLoginSuccess} |
344 | | - />} |
345 | | - |
346 | | - {activeView === 'signup' && <SignupModal |
347 | | - isOpen={true} |
348 | | - setIsOpen={handleModalOpenChange('signup')} |
349 | | - />} |
350 | | - |
351 | | - {activeView === 'contact' && <ContactView |
352 | | - isOpen={true} |
353 | | - onOpenChange={handleModalOpenChange('contact')} |
354 | | - />} |
355 | | - </> |
356 | | - ); |
| 4 | + return <MainView />; |
357 | 5 | } |
0 commit comments