diff --git a/src/App.tsx b/src/App.tsx index cf051cb..28a58d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,43 +1,45 @@ import { useState, useEffect } from "react"; import OnboardingTour from "./components/OnboardingTour"; -import { BrowserRouter, Routes, Route } from 'react-router-dom'; -import { Toaster } from 'react-hot-toast'; -import Layout from './components/Layout'; -import LandingPage from './pages/LandingPage'; -import AuthPage from './pages/AuthPage'; -import ModeSelectPage from './pages/ModeSelectPage'; -import ScannerPage from './pages/ScannerPage'; -import AnalysisDashboard from './pages/AnalysisDashboard'; -import MarketMapPage from './pages/MarketMapPage'; -import ResultsPage from './pages/ResultsPage'; -import Leaderboard from './pages/Leaderboard'; -import PostHogPageView from './components/PostHogPageView'; -import NotFound from './pages/NotFound'; -import InstallPrompt from './components/InstallPrompt'; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { Toaster } from "react-hot-toast"; +import Layout from "./components/Layout"; +import LandingPage from "./pages/LandingPage"; +import AuthPage from "./pages/AuthPage"; +import ModeSelectPage from "./pages/ModeSelectPage"; +import ScannerPage from "./pages/ScannerPage"; +import AnalysisDashboard from "./pages/AnalysisDashboard"; +import MarketMapPage from "./pages/MarketMapPage"; +import ResultsPage from "./pages/ResultsPage"; +import Leaderboard from "./pages/Leaderboard"; +import PostHogPageView from "./components/PostHogPageView"; +import NotFound from "./pages/NotFound"; +import InstallPrompt from "./components/InstallPrompt"; import PublicReport from "./pages/PublicReport"; +import BackToTopButton from "./components/BackToTopButton"; export default function App() { const [runTour, setRunTour] = useState(false); -useEffect(() => { - const completed = localStorage.getItem("tour-completed"); + useEffect(() => { + const completed = localStorage.getItem("tour-completed"); - if (!completed) { - setTimeout(() => { - setRunTour(true); - localStorage.setItem("tour-completed", "true"); - }, 0); - } -}, []); + if (!completed) { + setTimeout(() => { + setRunTour(true); + localStorage.setItem("tour-completed", "true"); + }, 0); + } + }, []); return ( {/* Toast provider for global error notifications */} - + {/* Fires a $pageview event to PostHog on every SPA route change */} + }> } /> @@ -48,7 +50,7 @@ useEffect(() => { } /> } /> } /> - + {/* Public shareable report — MUST be before the * catchall */} } /> @@ -58,4 +60,4 @@ useEffect(() => { ); -} \ No newline at end of file +} diff --git a/src/components/BackToTopButton.tsx b/src/components/BackToTopButton.tsx new file mode 100644 index 0000000..1ae4a23 --- /dev/null +++ b/src/components/BackToTopButton.tsx @@ -0,0 +1,42 @@ +import { useEffect, useState } from "react"; +import { ArrowUp } from "lucide-react"; + +export default function BackToTopButton() { + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const toggleVisibility = () => { + setIsVisible(window.scrollY > 300); + }; + + window.addEventListener("scroll", toggleVisibility); + toggleVisibility(); + + return () => window.removeEventListener("scroll", toggleVisibility); + }, []); + + const scrollToTop = () => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }; + + return ( + + ); +}