// 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