Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 28 additions & 26 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<BrowserRouter>
{/* Toast provider for global error notifications */}
<OnboardingTour run={runTour} />
<Toaster position="bottom-right" />

{/* Fires a $pageview event to PostHog on every SPA route change */}
<PostHogPageView />
<InstallPrompt />
<BackToTopButton />
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<LandingPage />} />
Expand All @@ -48,7 +50,7 @@ useEffect(() => {
<Route path="/map" element={<MarketMapPage />} />
<Route path="/leaderboard" element={<Leaderboard />} />
<Route path="/results" element={<ResultsPage />} />

{/* Public shareable report — MUST be before the * catchall */}
<Route path="/report/:id" element={<PublicReport />} />

Expand All @@ -58,4 +60,4 @@ useEffect(() => {
</Routes>
</BrowserRouter>
);
}
}
42 changes: 42 additions & 0 deletions src/components/BackToTopButton.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<button
type="button"
onClick={scrollToTop}
aria-label="Back to top"
disabled={!isVisible}
tabIndex={isVisible ? 0 : -1}
aria-hidden={!isVisible}
className={`fixed bottom-6 right-6 z-50 flex h-12 w-12 items-center justify-center rounded-full border border-primary/40 bg-surface/90 text-primary shadow-lg shadow-primary/20 backdrop-blur transition-all duration-300 hover:-translate-y-1 hover:bg-primary hover:text-on-primary hover:shadow-primary/40 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${
isVisible
? "translate-y-0 opacity-100"
: "pointer-events-none translate-y-4 opacity-0"
}`}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
>
<ArrowUp className="h-5 w-5" />
</button>
);
}
Loading