diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index fbbd914..ac28906 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -47,6 +47,9 @@ const AdminLogsMobileScreen = lazy(() => import('./features/admin/mobile/AdminLo
const AdminCommunityStationsPage = lazy(() => import('./features/admin/pages/AdminCommunityStationsPage').then(m => ({ default: m.AdminCommunityStationsPage })));
const AdminCommunityStationsMobileScreen = lazy(() => import('./features/admin/mobile/AdminCommunityStationsMobileScreen').then(m => ({ default: m.AdminCommunityStationsMobileScreen })));
+// Public pages (lazy-loaded)
+const GuidePage = lazy(() => import('./pages/GuidePage/GuidePage').then(m => ({ default: m.GuidePage })));
+
// Auth pages (lazy-loaded)
const SignupPage = lazy(() => import('./features/auth/pages/SignupPage').then(m => ({ default: m.SignupPage })));
const VerifyEmailPage = lazy(() => import('./features/auth/pages/VerifyEmailPage').then(m => ({ default: m.VerifyEmailPage })));
@@ -368,7 +371,7 @@ function App() {
// Skip on auth routes -- their query params must survive until Auth0 SDK processes them
useEffect(() => {
const path = window.location.pathname;
- if (path === '/callback' || path === '/signup' || path === '/verify-email') return;
+ if (path === '/callback' || path === '/signup' || path === '/verify-email' || path === '/guide') return;
const screen = routeToScreen[path];
if (screen && screen !== activeScreen) {
navigateToScreen(screen, { source: 'url-sync' });
@@ -380,7 +383,7 @@ function App() {
// Auth0 SDK needs for handleRedirectCallback (child effects fire before parent effects)
useEffect(() => {
const path = window.location.pathname;
- if (path === '/callback' || path === '/signup' || path === '/verify-email') return;
+ if (path === '/callback' || path === '/signup' || path === '/verify-email' || path === '/guide') return;
const targetPath = screenToRoute[activeScreen];
if (targetPath && path !== targetPath) {
window.history.replaceState(null, '', targetPath);
@@ -499,8 +502,9 @@ function App() {
const isSignupRoute = location.pathname === '/signup';
const isVerifyEmailRoute = location.pathname === '/verify-email';
const isOnboardingRoute = location.pathname === '/onboarding';
+ const isGuideRoute = location.pathname === '/guide';
const isAuthRoute = isSignupRoute || isVerifyEmailRoute || isOnboardingRoute;
- const shouldShowHomePage = !isGarageRoute && !isCallbackRoute && !isAuthRoute;
+ const shouldShowHomePage = !isGarageRoute && !isCallbackRoute && !isAuthRoute && !isGuideRoute;
const [callbackTimedOut, setCallbackTimedOut] = useState(false);
useEffect(() => {
@@ -635,6 +639,21 @@ function App() {
);
}
+ if (isGuideRoute) {
+ return (
+
+
+ Loading guide...
+
+ }>
+
+
+
+
+ );
+ }
+
// Signup route is public - no authentication required
if (isSignupRoute) {
return (
diff --git a/frontend/src/pages/GuidePage/GuidePage.tsx b/frontend/src/pages/GuidePage/GuidePage.tsx
new file mode 100644
index 0000000..2ddb6fa
--- /dev/null
+++ b/frontend/src/pages/GuidePage/GuidePage.tsx
@@ -0,0 +1,238 @@
+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]);
+
+ // 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 = (
+
Loading section...
+ );
+
+ return (
+
+ {/* Navigation Bar - matches HomePage style */}
+
+
+ {/* Page Content */}
+
+ {/* Page Header */}
+
+
+ User Guide
+
+
+ Precision Vehicle Management -- Track every mile. Own every detail.
+
+
+
+ {/* Layout: TOC sidebar + content */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Footer */}
+
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/GuideTableOfContents.tsx b/frontend/src/pages/GuidePage/GuideTableOfContents.tsx
new file mode 100644
index 0000000..a2b522c
--- /dev/null
+++ b/frontend/src/pages/GuidePage/GuideTableOfContents.tsx
@@ -0,0 +1,92 @@
+import { useState, useEffect } from 'react';
+import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { guideSections } from './guideTypes';
+
+interface GuideTableOfContentsProps {
+ activeSection: string;
+}
+
+export const GuideTableOfContents = ({ activeSection }: GuideTableOfContentsProps) => {
+ const [isMobile, setIsMobile] = useState(false);
+ const [tocExpanded, setTocExpanded] = useState(false);
+
+ useEffect(() => {
+ const check = () => setIsMobile(window.innerWidth < 768);
+ check();
+ window.addEventListener('resize', check);
+ return () => window.removeEventListener('resize', check);
+ }, []);
+
+ const handleClick = (sectionId: string) => {
+ const element = document.getElementById(sectionId);
+ if (element) {
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ if (isMobile) {
+ setTocExpanded(false);
+ }
+ };
+
+ const tocContent = (
+
+ );
+
+ if (isMobile) {
+ return (
+
+
setTocExpanded(expanded)}
+ sx={{
+ backgroundColor: 'rgba(255,255,255,0.03)',
+ border: '1px solid rgba(255,255,255,0.08)',
+ borderRadius: '8px !important',
+ '&:before': { display: 'none' },
+ }}
+ >
+ }
+ sx={{
+ color: 'rgba(242,243,246,0.9)',
+ fontWeight: 600,
+ minHeight: 48,
+ '& .MuiAccordionSummary-content': { margin: '12px 0' },
+ }}
+ >
+ Table of Contents
+
+
+ {tocContent}
+
+
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/components/GuideScreenshot.tsx b/frontend/src/pages/GuidePage/components/GuideScreenshot.tsx
new file mode 100644
index 0000000..aa7c5cf
--- /dev/null
+++ b/frontend/src/pages/GuidePage/components/GuideScreenshot.tsx
@@ -0,0 +1,26 @@
+interface GuideScreenshotProps {
+ src: string;
+ alt: string;
+ caption?: string;
+ mobile?: boolean;
+}
+
+export const GuideScreenshot = ({ src, alt, caption, mobile }: GuideScreenshotProps) => {
+ return (
+
+
+

+
+ {caption && (
+
+ {caption}
+
+ )}
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/components/GuideTable.tsx b/frontend/src/pages/GuidePage/components/GuideTable.tsx
new file mode 100644
index 0000000..9c5efbc
--- /dev/null
+++ b/frontend/src/pages/GuidePage/components/GuideTable.tsx
@@ -0,0 +1,68 @@
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Paper,
+} from '@mui/material';
+
+interface GuideTableProps {
+ headers: string[];
+ rows: string[][];
+}
+
+export const GuideTable = ({ headers, rows }: GuideTableProps) => {
+ return (
+
+
+
+
+ {headers.map((header, idx) => (
+
+ {header}
+
+ ))}
+
+
+
+ {rows.map((row, rowIdx) => (
+
+ {row.map((cell, cellIdx) => (
+
+ {cell}
+
+ ))}
+
+ ))}
+
+
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/guideTypes.ts b/frontend/src/pages/GuidePage/guideTypes.ts
new file mode 100644
index 0000000..272d420
--- /dev/null
+++ b/frontend/src/pages/GuidePage/guideTypes.ts
@@ -0,0 +1,114 @@
+export interface GuideSubSection {
+ id: string;
+ title: string;
+}
+
+export interface GuideSection {
+ id: string;
+ title: string;
+ subSections: GuideSubSection[];
+}
+
+export const guideSections: GuideSection[] = [
+ {
+ id: 'getting-started',
+ title: '1. Getting Started',
+ subSections: [
+ { id: 'creating-an-account', title: 'Creating an Account' },
+ { id: 'logging-in', title: 'Logging In' },
+ { id: 'onboarding', title: 'Onboarding' },
+ { id: 'trouble-logging-in', title: 'Trouble Logging In' },
+ ],
+ },
+ {
+ id: 'dashboard',
+ title: '2. Dashboard',
+ subSections: [
+ { id: 'your-fleet-overview', title: 'Your Fleet Overview' },
+ { id: 'quick-actions', title: 'Quick Actions' },
+ ],
+ },
+ {
+ id: 'vehicles',
+ title: '3. Vehicles',
+ subSections: [
+ { id: 'viewing-your-vehicles', title: 'Viewing Your Vehicles' },
+ { id: 'adding-a-vehicle', title: 'Adding a Vehicle' },
+ { id: 'vin-decode', title: 'VIN Decode' },
+ { id: 'vehicle-detail-page', title: 'Vehicle Detail Page' },
+ { id: 'editing-a-vehicle', title: 'Editing a Vehicle' },
+ { id: 'deleting-a-vehicle', title: 'Deleting a Vehicle' },
+ ],
+ },
+ {
+ id: 'fuel-logs',
+ title: '4. Fuel Logs',
+ subSections: [
+ { id: 'fuel-logs-overview', title: 'Fuel Logs Overview' },
+ { id: 'logging-fuel', title: 'Logging Fuel' },
+ { id: 'receipt-scanning', title: 'Receipt Scanning' },
+ { id: 'editing-and-deleting-fuel-logs', title: 'Editing and Deleting Fuel Logs' },
+ ],
+ },
+ {
+ id: 'maintenance',
+ title: '5. Maintenance',
+ subSections: [
+ { id: 'maintenance-records', title: 'Maintenance Records' },
+ { id: 'adding-a-maintenance-record', title: 'Adding a Maintenance Record' },
+ { id: 'maintenance-schedules', title: 'Maintenance Schedules' },
+ { id: 'creating-a-schedule', title: 'Creating a Schedule' },
+ ],
+ },
+ {
+ id: 'gas-stations',
+ title: '6. Gas Stations',
+ subSections: [
+ { id: 'finding-stations', title: 'Finding Stations' },
+ { id: 'saved-stations', title: 'Saved Stations' },
+ { id: 'premium-93-stations', title: 'Premium 93 Stations' },
+ ],
+ },
+ {
+ id: 'documents',
+ title: '7. Documents',
+ subSections: [
+ { id: 'documents-overview', title: 'Documents Overview' },
+ { id: 'adding-a-document', title: 'Adding a Document' },
+ { id: 'document-types', title: 'Document Types' },
+ ],
+ },
+ {
+ id: 'settings',
+ title: '8. Settings',
+ subSections: [
+ { id: 'profile', title: 'Profile' },
+ { id: 'security-and-privacy', title: 'Security and Privacy' },
+ { id: 'subscription', title: 'Subscription' },
+ { id: 'notifications', title: 'Notifications' },
+ { id: 'appearance-and-units', title: 'Appearance and Units' },
+ { id: 'data-import-and-export', title: 'Data Import and Export' },
+ { id: 'account-actions', title: 'Account Actions' },
+ ],
+ },
+ {
+ id: 'subscription-tiers',
+ title: '9. Subscription Tiers and Pro Features',
+ subSections: [
+ { id: 'tier-comparison', title: 'Tier Comparison' },
+ { id: 'vin-camera-scanning', title: 'VIN Camera Scanning (Pro)' },
+ { id: 'fuel-receipt-scanning', title: 'Fuel Receipt Scanning (Pro)' },
+ { id: 'maintenance-receipt-scanning', title: 'Maintenance Receipt Scanning (Pro)' },
+ { id: 'maintenance-manual-pdf', title: 'Maintenance Manual PDF (Pro)' },
+ { id: 'email-ingestion', title: 'Email Ingestion (Pro)' },
+ { id: 'shared-vehicle-documents', title: 'Shared Vehicle Documents (Pro)' },
+ { id: 'community-station-submissions', title: 'Community Station Submissions (Pro)' },
+ { id: 'managing-your-subscription', title: 'Managing Your Subscription' },
+ ],
+ },
+ {
+ id: 'mobile-experience',
+ title: '10. Mobile Experience',
+ subSections: [],
+ },
+];
diff --git a/frontend/src/pages/GuidePage/sections/DashboardSection.tsx b/frontend/src/pages/GuidePage/sections/DashboardSection.tsx
new file mode 100644
index 0000000..271b7f0
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/DashboardSection.tsx
@@ -0,0 +1,8 @@
+export const DashboardSection = () => {
+ return (
+
+ 2. Dashboard
+ Content loading in Milestone 2...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/DocumentsSection.tsx b/frontend/src/pages/GuidePage/sections/DocumentsSection.tsx
new file mode 100644
index 0000000..c098c8a
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/DocumentsSection.tsx
@@ -0,0 +1,8 @@
+export const DocumentsSection = () => {
+ return (
+
+ 7. Documents
+ Content loading in Milestone 3...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/FuelLogsSection.tsx b/frontend/src/pages/GuidePage/sections/FuelLogsSection.tsx
new file mode 100644
index 0000000..c1e45a2
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/FuelLogsSection.tsx
@@ -0,0 +1,8 @@
+export const FuelLogsSection = () => {
+ return (
+
+ 4. Fuel Logs
+ Content loading in Milestone 2...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/GasStationsSection.tsx b/frontend/src/pages/GuidePage/sections/GasStationsSection.tsx
new file mode 100644
index 0000000..0c46913
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/GasStationsSection.tsx
@@ -0,0 +1,8 @@
+export const GasStationsSection = () => {
+ return (
+
+ 6. Gas Stations
+ Content loading in Milestone 3...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/GettingStartedSection.tsx b/frontend/src/pages/GuidePage/sections/GettingStartedSection.tsx
new file mode 100644
index 0000000..c20832a
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/GettingStartedSection.tsx
@@ -0,0 +1,8 @@
+export const GettingStartedSection = () => {
+ return (
+
+ 1. Getting Started
+ Content loading in Milestone 2...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/MaintenanceSection.tsx b/frontend/src/pages/GuidePage/sections/MaintenanceSection.tsx
new file mode 100644
index 0000000..f58fbe2
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/MaintenanceSection.tsx
@@ -0,0 +1,8 @@
+export const MaintenanceSection = () => {
+ return (
+
+ 5. Maintenance
+ Content loading in Milestone 2...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/MobileExperienceSection.tsx b/frontend/src/pages/GuidePage/sections/MobileExperienceSection.tsx
new file mode 100644
index 0000000..7f9f764
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/MobileExperienceSection.tsx
@@ -0,0 +1,8 @@
+export const MobileExperienceSection = () => {
+ return (
+
+ 10. Mobile Experience
+ Content loading in Milestone 3...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/SettingsSection.tsx b/frontend/src/pages/GuidePage/sections/SettingsSection.tsx
new file mode 100644
index 0000000..a8fcb52
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/SettingsSection.tsx
@@ -0,0 +1,8 @@
+export const SettingsSection = () => {
+ return (
+
+ 8. Settings
+ Content loading in Milestone 3...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/SubscriptionSection.tsx b/frontend/src/pages/GuidePage/sections/SubscriptionSection.tsx
new file mode 100644
index 0000000..9a0061f
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/SubscriptionSection.tsx
@@ -0,0 +1,8 @@
+export const SubscriptionSection = () => {
+ return (
+
+ 9. Subscription Tiers and Pro Features
+ Content loading in Milestone 3...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/VehiclesSection.tsx b/frontend/src/pages/GuidePage/sections/VehiclesSection.tsx
new file mode 100644
index 0000000..a4e7fc0
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/VehiclesSection.tsx
@@ -0,0 +1,8 @@
+export const VehiclesSection = () => {
+ return (
+
+ 3. Vehicles
+ Content loading in Milestone 2...
+
+ );
+};
diff --git a/frontend/src/pages/GuidePage/sections/index.ts b/frontend/src/pages/GuidePage/sections/index.ts
new file mode 100644
index 0000000..5a6ce7b
--- /dev/null
+++ b/frontend/src/pages/GuidePage/sections/index.ts
@@ -0,0 +1,12 @@
+import { lazy } from 'react';
+
+export const GettingStartedSection = lazy(() => import('./GettingStartedSection').then(m => ({ default: m.GettingStartedSection })));
+export const DashboardSection = lazy(() => import('./DashboardSection').then(m => ({ default: m.DashboardSection })));
+export const VehiclesSection = lazy(() => import('./VehiclesSection').then(m => ({ default: m.VehiclesSection })));
+export const FuelLogsSection = lazy(() => import('./FuelLogsSection').then(m => ({ default: m.FuelLogsSection })));
+export const MaintenanceSection = lazy(() => import('./MaintenanceSection').then(m => ({ default: m.MaintenanceSection })));
+export const GasStationsSection = lazy(() => import('./GasStationsSection').then(m => ({ default: m.GasStationsSection })));
+export const DocumentsSection = lazy(() => import('./DocumentsSection').then(m => ({ default: m.DocumentsSection })));
+export const SettingsSection = lazy(() => import('./SettingsSection').then(m => ({ default: m.SettingsSection })));
+export const SubscriptionSection = lazy(() => import('./SubscriptionSection').then(m => ({ default: m.SubscriptionSection })));
+export const MobileExperienceSection = lazy(() => import('./MobileExperienceSection').then(m => ({ default: m.MobileExperienceSection })));