All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 3m36s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 53s
Deploy to Staging / Verify Staging (pull_request) Successful in 9s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
258 lines
9.2 KiB
TypeScript
258 lines
9.2 KiB
TypeScript
import { useState, useEffect, useCallback, Suspense } from 'react';
|
|
import { useAuth0 } from '@auth0/auth0-react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { GuideTableOfContents } from './GuideTableOfContents';
|
|
import { guideSections } from './guideTypes';
|
|
import {
|
|
GettingStartedSection,
|
|
DashboardSection,
|
|
VehiclesSection,
|
|
FuelLogsSection,
|
|
MaintenanceSection,
|
|
GasStationsSection,
|
|
DocumentsSection,
|
|
SettingsSection,
|
|
SubscriptionSection,
|
|
MobileExperienceSection,
|
|
} from './sections';
|
|
|
|
export const GuidePage = () => {
|
|
const { loginWithRedirect, isAuthenticated } = useAuth0();
|
|
const navigate = useNavigate();
|
|
const [activeSection, setActiveSection] = useState(guideSections[0].id);
|
|
const [isScrolled, setIsScrolled] = useState(false);
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
|
|
const handleAuthAction = useCallback(() => {
|
|
if (isAuthenticated) {
|
|
navigate('/garage');
|
|
return;
|
|
}
|
|
loginWithRedirect({ appState: { returnTo: '/garage' } });
|
|
}, [isAuthenticated, navigate, loginWithRedirect]);
|
|
|
|
const handleSignup = useCallback(() => {
|
|
navigate('/signup');
|
|
}, [navigate]);
|
|
|
|
// Scroll to hash target after lazy-loaded sections render
|
|
useEffect(() => {
|
|
const hash = window.location.hash.slice(1);
|
|
if (!hash) return;
|
|
|
|
// Poll for the element since Suspense sections load asynchronously
|
|
let attempts = 0;
|
|
const interval = setInterval(() => {
|
|
const target = document.getElementById(hash);
|
|
if (target) {
|
|
clearInterval(interval);
|
|
target.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
if (++attempts >= 20) clearInterval(interval);
|
|
}, 100);
|
|
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
// Track scroll position for nav background and active section
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
setIsScrolled(window.scrollY > 50);
|
|
|
|
// Determine active section from scroll position
|
|
const sectionElements = guideSections.map((s) => ({
|
|
id: s.id,
|
|
element: document.getElementById(s.id),
|
|
}));
|
|
|
|
for (let i = sectionElements.length - 1; i >= 0; i--) {
|
|
const { id, element } = sectionElements[i];
|
|
if (element) {
|
|
const rect = element.getBoundingClientRect();
|
|
if (rect.top <= 120) {
|
|
setActiveSection(id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, []);
|
|
|
|
const sectionFallback = (
|
|
<div className="py-8 text-center text-titanio/50">Loading section...</div>
|
|
);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-nero text-avus">
|
|
{/* Navigation Bar - matches HomePage style */}
|
|
<nav
|
|
className={`fixed top-0 left-0 right-0 z-50 transition-colors duration-300 ${
|
|
isScrolled ? 'bg-nero/95 backdrop-blur-sm' : 'bg-nero'
|
|
}`}
|
|
>
|
|
<div className="w-full px-4 md:px-8 lg:px-12">
|
|
<div className="flex justify-between items-center h-16">
|
|
<div className="flex-shrink-0">
|
|
<a href="/" className="flex items-center">
|
|
<img
|
|
src="/images/logos/motovaultpro-title-slogan.png"
|
|
alt="MotoVaultPro - Precision Vehicle Management"
|
|
className="h-8 md:h-10 w-auto"
|
|
/>
|
|
</a>
|
|
</div>
|
|
|
|
{/* Desktop Menu */}
|
|
<div className="hidden md:flex items-center space-x-8">
|
|
<a href="/#home" className="text-white/75 hover:text-white transition-colors">
|
|
Home
|
|
</a>
|
|
<a href="/#features" className="text-white/75 hover:text-white transition-colors">
|
|
Features
|
|
</a>
|
|
<a href="/#about" className="text-white/75 hover:text-white transition-colors">
|
|
About
|
|
</a>
|
|
<a
|
|
href="/guide"
|
|
className="text-primary-400 font-semibold transition-colors"
|
|
>
|
|
Guide
|
|
</a>
|
|
<button
|
|
onClick={handleSignup}
|
|
className="border border-primary-500/90 text-primary-500 hover:bg-primary-500/10 hover:border-primary-500 font-semibold py-2 px-6 rounded-lg transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
|
|
>
|
|
Sign Up
|
|
</button>
|
|
<button
|
|
onClick={handleAuthAction}
|
|
className="bg-primary-500 hover:bg-primary-600 text-white font-semibold py-2 px-6 rounded-lg transition-colors duration-300 shadow-lg shadow-black/30 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
|
|
>
|
|
Login
|
|
</button>
|
|
</div>
|
|
|
|
{/* Mobile Menu Button */}
|
|
<div className="md:hidden">
|
|
<button
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
className="text-white/80 hover:text-white focus:outline-none"
|
|
>
|
|
<svg
|
|
className="h-6 w-6"
|
|
fill="none"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth="2"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
{mobileMenuOpen ? (
|
|
<path d="M6 18L18 6M6 6l12 12" />
|
|
) : (
|
|
<path d="M4 6h16M4 12h16M4 18h16" />
|
|
)}
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Menu */}
|
|
{mobileMenuOpen && (
|
|
<div className="md:hidden py-4 space-y-3 bg-nero/95 backdrop-blur-sm border-t border-white/10">
|
|
<a href="/" className="block text-white/75 hover:text-white transition-colors py-2">
|
|
Home
|
|
</a>
|
|
<a href="/#features" className="block text-white/75 hover:text-white transition-colors py-2">
|
|
Features
|
|
</a>
|
|
<a href="/#about" className="block text-white/75 hover:text-white transition-colors py-2">
|
|
About
|
|
</a>
|
|
<a href="/guide" className="block text-primary-400 font-semibold py-2">
|
|
Guide
|
|
</a>
|
|
<button
|
|
onClick={handleSignup}
|
|
className="w-full border border-primary-500/90 text-primary-500 hover:bg-primary-500/10 font-semibold py-2 px-6 rounded-lg transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
|
|
>
|
|
Sign Up
|
|
</button>
|
|
<button
|
|
onClick={handleAuthAction}
|
|
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-semibold py-2 px-6 rounded-lg transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
|
|
>
|
|
Login
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Page Content */}
|
|
<div className="pt-20 px-4 md:px-8 lg:px-12 max-w-7xl mx-auto">
|
|
{/* Page Header */}
|
|
<div className="py-8 md:py-12 border-b border-white/10 mb-8">
|
|
<h1 className="text-3xl md:text-4xl font-bold text-avus mb-3">
|
|
User Guide
|
|
</h1>
|
|
<p className="text-lg text-titanio/70">
|
|
Precision Vehicle Management -- Track every mile. Own every detail.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Layout: TOC sidebar + content */}
|
|
<div className="flex flex-col md:flex-row gap-8 pb-16">
|
|
<GuideTableOfContents activeSection={activeSection} />
|
|
|
|
<main className="flex-1 min-w-0">
|
|
<Suspense fallback={sectionFallback}>
|
|
<GettingStartedSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<DashboardSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<VehiclesSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<FuelLogsSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<MaintenanceSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<GasStationsSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<DocumentsSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<SettingsSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<SubscriptionSection />
|
|
</Suspense>
|
|
<Suspense fallback={sectionFallback}>
|
|
<MobileExperienceSection />
|
|
</Suspense>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<footer className="bg-black text-white py-8 px-4 md:px-8 border-t border-white/10">
|
|
<div className="max-w-7xl mx-auto text-center">
|
|
<p className="text-white/50">
|
|
© {new Date().getFullYear()} FB Technologies LLC. All rights reserved.
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
);
|
|
};
|