// js/App.jsx /** * Main Application Component * * Handles routing by directly reading window.location.hash during render. * Manages global state (user authentication, mobile menu). * Verifies the user's token on initial load. */ // React and Hooks are globally available via index.html const App = () => { // --- State --- // We remove currentRoute state. We need a way to force re-render on hash change. const [_, forceUpdate] = useReducer(x => x + 1, 0); // Simple state increment to trigger re-render const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); // --- Log current hash on every render --- console.log(`App.jsx: [Render Start] window.location.hash is: "${window.location.hash}"`); // --- Effects --- // Effect to handle hash changes - ONLY triggers a re-render now useEffect(() => { const handleHashChange = () => { console.log(`App.jsx: [Hash Listener] Hash changed to: "${window.location.hash}". Forcing re-render.`); // Force a re-render so renderPage() reads the new hash forceUpdate(); // Close mobile menu on navigation setIsMobileMenuOpen(false); }; console.log("App.jsx: [Effect] Adding hashchange listener."); window.addEventListener("hashchange", handleHashChange); // Cleanup listener on component unmount return () => { console.log("App.jsx: [Effect] Removing hashchange listener."); window.removeEventListener("hashchange", handleHashChange); }; }, []); // Empty dependency array means run only once for listener setup // Effect to verify token and fetch user data on initial load useEffect(() => { const verifyUserToken = async () => { const token = localStorage.getItem("token"); console.log("App.jsx: [Auth Effect] Checking for token:", token ? "Found" : "Not found"); if (token) { try { console.log("App.jsx: [Auth Effect] Verifying token..."); const response = await fetch("/coursera-clone/api/auth/verify_token.php", { method: "GET", headers: { Authorization: `Bearer ${token}` } }); const data = await response.json(); if (response.ok && data.success && data.user) { console.log("App.jsx: [Auth Effect] Token verified successfully:", data.user); setUser(data.user); } else { console.warn("App.jsx: [Auth Effect] Token verification failed:", data.error); localStorage.removeItem("token"); setUser(null); } } catch (error) { console.error("App.jsx: [Auth Effect] Error during token verification:", error); localStorage.removeItem("token"); setUser(null); } } else { setUser(null); } setIsLoading(false); }; verifyUserToken(); }, []); // Run only once on mount // --- Event Handlers --- const handleSignin = (userData, token) => { console.log("App.jsx: Handling signin:", userData); localStorage.setItem("token", token); setUser(userData); window.location.hash = "#/profile"; // Redirect after signin }; const handleSignout = async () => { console.log("App.jsx: Handling signout..."); const token = localStorage.getItem("token"); setUser(null); localStorage.removeItem("token"); if (token) { try { const response = await fetch("/coursera-clone/api/auth/signout.php", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" } }); const data = await response.json(); if (!response.ok || !data.success) console.warn("App.jsx: Backend signout failed:", data.error); else console.log("App.jsx: Backend signout successful."); } catch (error) { console.error("App.jsx: Error during backend signout:", error); } } window.location.hash = "#/"; // Redirect after signout }; const handleToggleMobileMenu = () => { setIsMobileMenuOpen((prev) => !prev); }; // --- Page Rendering Logic --- const renderPage = () => { // --- Read hash directly for routing --- const currentHash = window.location.hash; // e.g., "#/course/1" or "#/" or "" let route = currentHash.slice(1); // Remove the '#' -> "/course/1" or "/" or "" // Ensure route starts with '/' if it's not empty if (route && !route.startsWith('/')) { route = '/' + route; } else if (!route) { route = '/'; // Default to root if hash is empty } // --- End hash reading --- console.log(`App.jsx: [Render] renderPage() called. Routing based on calculated route: "${route}"`); if (isLoading) { return
Initializing...
; } // Route matching using the directly calculated 'route' variable switch (route) { case "/": case "/courses": if (window.CoursesPage) return ; console.error("App.jsx: [Render] CoursesPage component is not available."); return
Error: Courses Page Component Not Loaded
; case "/about": if (window.AboutPage) return ; return
About Page Component Not Loaded
; case "/contact": if (window.ContactPage) return ; return
Contact Page Component Not Loaded
; case "/signin": if (user) { window.location.hash = "#/profile"; return null; } if (window.AuthPage) return ; return
Sign In Page Component Not Loaded
; case "/profile": if (user && window.ProfilePage) return ; if (!user) { window.location.hash = "#/signin"; return
Redirecting...
; } return
Profile Page Component Not Loaded
; // --- Default Case (handles /course/:id) --- default: console.log(`App.jsx: [Render] Checking default case for route "${route}"`); // Check if route STARTS WITH '/course/' if (route && route.startsWith("/course/")) { console.log("App.jsx: [Render] Route matches /course/ pattern."); const parts = route.split("/"); if (parts.length === 3 && parts[2] && !isNaN(parseInt(parts[2], 10))) { const courseId = parseInt(parts[2], 10); console.log(`App.jsx: [Render] Extracted courseId: ${courseId}`); if (courseId > 0) { if (window.CourseDetailPage) { console.log("App.jsx: [Render] Rendering CourseDetailPage..."); return ; } else { console.error("App.jsx: [Render] CourseDetailPage component is not available."); return
Error: Course Detail Page Component Not Loaded
; } } else { console.warn(`App.jsx: [Render] Invalid course ID (not positive): ${courseId}`); return
Invalid Course ID
; } } else { console.warn(`App.jsx: [Render] Could not parse valid course ID from route: ${route}`); return
Invalid Course Route Format
; } } // Fallback 404 console.log(`App.jsx: [Render] Route "${route}" did not match any case or valid course pattern.`); return
Page Not Found (404)
; } }; // --- Main Render --- return (
{window.Header && ( )}
{renderPage()}
); }; // Make App component globally available window.App = App;