/** * Main App component. */ import { X } from "lucide-react"; import React, { useRef, useState } from "react"; import { Navigate, Route, Routes, useLocation } from "react-router-dom"; import { CSSTransition, SwitchTransition } from "react-transition-group"; import { Button } from "@/components/ui/button"; import { Toaster } from "@/components/ui/sonner"; import { BasicAuthPrompt } from "./components/BasicAuthPrompt"; import { OnboardingGate } from "./components/OnboardingGate"; import { useDemoInfo } from "./hooks/useDemoInfo"; import { GmailOauthCallbackPage } from "./pages/GmailOauthCallbackPage"; import { HomePage } from "./pages/HomePage"; import { InProgressBoardPage } from "./pages/InProgressBoardPage"; import { JobPage } from "./pages/JobPage"; import { OrchestratorPage } from "./pages/OrchestratorPage"; import { SettingsPage } from "./pages/SettingsPage"; import { TracerLinksPage } from "./pages/TracerLinksPage"; import { TrackingInboxPage } from "./pages/TrackingInboxPage"; import { VisaSponsorsPage } from "./pages/VisaSponsorsPage"; /** Backwards-compatibility redirects: old URL paths -> new URL paths */ const REDIRECTS: Array<{ from: string; to: string }> = [ { from: "/", to: "/jobs/ready" }, { from: "/home", to: "/overview" }, { from: "/ready", to: "/jobs/ready" }, { from: "/ready/:jobId", to: "/jobs/ready/:jobId" }, { from: "/discovered", to: "/jobs/discovered" }, { from: "/discovered/:jobId", to: "/jobs/discovered/:jobId" }, { from: "/applied", to: "/jobs/applied" }, { from: "/applied/:jobId", to: "/jobs/applied/:jobId" }, { from: "/in-progress", to: "/applications/in-progress" }, { from: "/in-progress/:jobId", to: "/applications/in-progress" }, { from: "/jobs/in_progress", to: "/applications/in-progress" }, { from: "/jobs/in_progress/:jobId", to: "/applications/in-progress" }, { from: "/all", to: "/jobs/all" }, { from: "/all/:jobId", to: "/jobs/all/:jobId" }, ]; const DEMO_WAITLIST_BANNER_DISMISSED_KEY = "jobops.demoWaitlistBannerDismissed"; export const App: React.FC = () => { const location = useLocation(); const nodeRef = useRef(null); const demoInfo = useDemoInfo(); const [demoWaitlistBannerDismissed, setDemoWaitlistBannerDismissed] = useState(() => { try { return localStorage.getItem(DEMO_WAITLIST_BANNER_DISMISSED_KEY) === "1"; } catch { return false; } }); // Determine a stable key for transitions to avoid unnecessary unmounts when switching sub-tabs const pageKey = React.useMemo(() => { const firstSegment = location.pathname.split("/")[1] || "jobs"; if (firstSegment === "jobs") { return "orchestrator"; } return firstSegment; }, [location.pathname]); return ( <> {demoInfo?.demoMode && !demoWaitlistBannerDismissed && (

This is a read-only demo. Want JobOps without the Docker setup? ☁️{" "} Cloud version coming soon — join the waitlist at{" "} try.jobops.app

)} {demoInfo?.demoMode && (
Demo mode: integrations are simulated and data resets every{" "} {demoInfo.resetCadenceHours} hours.
)}
{/* Backwards-compatibility redirects */} {REDIRECTS.map(({ from, to }) => ( } /> ))} {/* Application routes */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } />
); };