Reorganize frontend into public/ with pages/ and js/ subdirectories

- public/index.html — landing page at root
- public/pages/ — all feature pages (regulations, loadboard, etc.)
- public/js/ — api.js, nav.js, mock data files
- All links updated to absolute paths (/pages/, /js/)
- Express static path updated to serve from public/
- Seed script path updated for new mock data location
- README updated with new project structure and setup guide
- Added .env.example template

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Daniel Kovalevich
2026-03-30 15:52:56 -04:00
parent f917fb8014
commit 93efb907ff
20 changed files with 281 additions and 109 deletions

193
public/js/nav.js Normal file
View File

@@ -0,0 +1,193 @@
// =============================================
// Shared Navigation, Banner, and Footer
// Include on every page via <script src="/js/nav.js">
// =============================================
function renderNav(activePage) {
const nav = document.getElementById('main-nav');
if (!nav) return;
const isActive = (id) => id === activePage;
const linkClass = (id) => isActive(id) ? 'text-amber-400 font-semibold' : 'text-gray-300 hover:text-white';
const dropLinkClass = (id) => isActive(id) ? 'bg-amber-50 text-amber-700 font-semibold' : 'text-slate-700 hover:bg-slate-50';
const regulationsActive = ['regulations','equipment','contacts','calendar'].includes(activePage);
const roadIntelActive = ['truckstops','bridges','weighstations','alerts'].includes(activePage);
nav.innerHTML = `
<nav class="bg-slate-900 text-white fixed top-0 w-full z-50 shadow-lg">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<a href="/" class="flex items-center space-x-2 flex-shrink-0">
<span class="text-2xl">🚛</span>
<span class="text-xl font-bold text-amber-400 tracking-tight">PilotEdge</span>
</a>
<!-- Desktop Nav -->
<div class="hidden lg:flex items-center space-x-1">
<a href="/" class="${linkClass('home')} px-3 py-2 rounded-md text-sm transition-colors">Home</a>
<!-- Regulations Dropdown -->
<div class="relative group">
<button class="${regulationsActive ? 'text-amber-400 font-semibold' : 'text-gray-300 hover:text-white'} px-3 py-2 rounded-md text-sm transition-colors flex items-center gap-1">
Regulations
<svg class="w-3.5 h-3.5 opacity-60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div class="absolute left-0 top-full pt-1 hidden group-hover:block" style="min-width:220px;">
<div class="bg-white rounded-xl shadow-xl border border-slate-200 py-2">
<a href="/pages/regulations.html" class="${dropLinkClass('regulations')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">State Regulations Map</div>
<div class="text-xs text-slate-400 mt-0.5">Permits & escort thresholds</div>
</a>
<a href="/pages/regulations.html#equipment" class="${dropLinkClass('equipment')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">Equipment Requirements</div>
<div class="text-xs text-slate-400 mt-0.5">Escort & carrier gear by state</div>
</a>
<a href="/pages/contacts.html" class="${dropLinkClass('contacts')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">DOT Contact Directory</div>
<div class="text-xs text-slate-400 mt-0.5">Permit office phone & email</div>
</a>
<a href="/pages/calendar.html" class="${dropLinkClass('calendar')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">Seasonal Calendar</div>
<div class="text-xs text-slate-400 mt-0.5">Restrictions & closures</div>
</a>
</div>
</div>
</div>
<!-- Road Intel Dropdown -->
<div class="relative group">
<button class="${roadIntelActive ? 'text-amber-400 font-semibold' : 'text-gray-300 hover:text-white'} px-3 py-2 rounded-md text-sm transition-colors flex items-center gap-1">
Road Intel
<svg class="w-3.5 h-3.5 opacity-60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div class="absolute left-0 top-full pt-1 hidden group-hover:block" style="min-width:220px;">
<div class="bg-white rounded-xl shadow-xl border border-slate-200 py-2">
<a href="/pages/truckstops.html" class="${dropLinkClass('truckstops')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">Truck Stops & Parking</div>
<div class="text-xs text-slate-400 mt-0.5">Oversize-friendly locations</div>
</a>
<a href="/pages/bridges.html" class="${dropLinkClass('bridges')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">Bridge Clearances</div>
<div class="text-xs text-slate-400 mt-0.5">Height & width restrictions</div>
</a>
<a href="/pages/weighstations.html" class="${dropLinkClass('weighstations')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">Weigh Stations</div>
<div class="text-xs text-slate-400 mt-0.5">Live open/closed status</div>
</a>
<a href="/pages/alerts.html" class="${dropLinkClass('alerts')} block px-4 py-2.5 text-sm transition-colors">
<div class="font-medium">Route & Weather Alerts</div>
<div class="text-xs text-slate-400 mt-0.5">Closures, construction, wind</div>
</a>
</div>
</div>
</div>
<a href="/pages/loadboard.html" class="${linkClass('loadboard')} px-3 py-2 rounded-md text-sm transition-colors">Load Board</a>
<a href="/pages/locator.html" class="${linkClass('locator')} px-3 py-2 rounded-md text-sm transition-colors">Find Escorts</a>
<a href="/pages/documents.html" class="${linkClass('documents')} px-3 py-2 rounded-md text-sm transition-colors">Documents</a>
<a href="/pages/order.html" class="ml-2 bg-amber-500 hover:bg-amber-600 text-slate-900 font-bold px-4 py-2 rounded-lg transition-colors shadow-md hover:shadow-lg text-sm">
Request Service
</a>
</div>
<!-- Mobile menu button -->
<button onclick="toggleMobileMenu()" class="lg:hidden text-gray-300 hover:text-white p-2" aria-label="Toggle menu">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
</div>
</div>
<!-- Mobile Menu -->
<div id="mobile-menu" class="lg:hidden hidden border-t border-slate-700 px-4 pb-4 max-h-[80vh] overflow-y-auto">
<a href="/" class="block py-2 px-3 mt-2 rounded ${isActive('home') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Home</a>
<div class="mt-2 mb-1 px-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Regulations</div>
<a href="/pages/regulations.html" class="block py-2 px-3 rounded ${isActive('regulations') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">State Regulations Map</a>
<a href="/pages/regulations.html#equipment" class="block py-2 px-3 rounded ${isActive('equipment') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Equipment Requirements</a>
<a href="/pages/contacts.html" class="block py-2 px-3 rounded ${isActive('contacts') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">DOT Contacts</a>
<a href="/pages/calendar.html" class="block py-2 px-3 rounded ${isActive('calendar') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Seasonal Calendar</a>
<div class="mt-2 mb-1 px-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Road Intel</div>
<a href="/pages/truckstops.html" class="block py-2 px-3 rounded ${isActive('truckstops') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Truck Stops & Parking</a>
<a href="/pages/bridges.html" class="block py-2 px-3 rounded ${isActive('bridges') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Bridge Clearances</a>
<a href="/pages/weighstations.html" class="block py-2 px-3 rounded ${isActive('weighstations') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Weigh Stations</a>
<a href="/pages/alerts.html" class="block py-2 px-3 rounded ${isActive('alerts') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Route & Weather Alerts</a>
<div class="mt-2 mb-1 px-3 text-xs font-semibold text-slate-500 uppercase tracking-wider">Services</div>
<a href="/pages/loadboard.html" class="block py-2 px-3 rounded ${isActive('loadboard') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Load Board</a>
<a href="/pages/locator.html" class="block py-2 px-3 rounded ${isActive('locator') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Find Escorts</a>
<a href="/pages/documents.html" class="block py-2 px-3 rounded ${isActive('documents') ? 'text-amber-400 bg-slate-800' : 'text-gray-300'}">Document Vault</a>
<a href="/pages/order.html" class="block py-2 px-3 mt-3 bg-amber-500 text-slate-900 font-bold rounded-lg text-center">Request Service</a>
</div>
</nav>
`;
}
function toggleMobileMenu() {
const menu = document.getElementById('mobile-menu');
if (menu) menu.classList.toggle('hidden');
}
function renderBanner() {
const banner = document.getElementById('poc-banner');
if (!banner) return;
banner.innerHTML = `
<div class="bg-amber-100 border-b border-amber-300 text-amber-900 text-center text-sm py-2 mt-16 px-4">
⚠️ <strong>POC / Demo</strong> — All regulation data is simulated for demonstration purposes. Verify with official state DOT sources before real-world use.
</div>
`;
}
function renderFooter() {
const footer = document.getElementById('main-footer');
if (!footer) return;
footer.innerHTML = `
<footer class="bg-slate-900 text-gray-400 mt-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div class="grid md:grid-cols-4 gap-8">
<div>
<div class="flex items-center space-x-2 mb-4">
<span class="text-2xl">🚛</span>
<span class="text-lg font-bold text-amber-400">PilotEdge</span>
</div>
<p class="text-sm">Your complete resource for oversize and overdimensional load hauling. Built by industry professionals, for industry professionals.</p>
</div>
<div>
<h4 class="text-white font-semibold mb-3">Regulations</h4>
<ul class="space-y-2 text-sm">
<li><a href="/pages/regulations.html" class="hover:text-white transition-colors">State Regulations Map</a></li>
<li><a href="/pages/contacts.html" class="hover:text-white transition-colors">DOT Contact Directory</a></li>
<li><a href="/pages/calendar.html" class="hover:text-white transition-colors">Seasonal Calendar</a></li>
</ul>
</div>
<div>
<h4 class="text-white font-semibold mb-3">Road Intel</h4>
<ul class="space-y-2 text-sm">
<li><a href="/pages/truckstops.html" class="hover:text-white transition-colors">Truck Stops & Parking</a></li>
<li><a href="/pages/bridges.html" class="hover:text-white transition-colors">Bridge Clearances</a></li>
<li><a href="/pages/weighstations.html" class="hover:text-white transition-colors">Weigh Stations</a></li>
<li><a href="/pages/alerts.html" class="hover:text-white transition-colors">Route & Weather Alerts</a></li>
</ul>
</div>
<div>
<h4 class="text-white font-semibold mb-3">Services</h4>
<ul class="space-y-2 text-sm">
<li><a href="/pages/loadboard.html" class="hover:text-white transition-colors">Load Board</a></li>
<li><a href="/pages/locator.html" class="hover:text-white transition-colors">Find Escort Vehicles</a></li>
<li><a href="/pages/documents.html" class="hover:text-white transition-colors">Document Vault</a></li>
<li><a href="/pages/order.html" class="hover:text-white transition-colors">Request Escort Service</a></li>
</ul>
</div>
</div>
<div class="border-t border-slate-700 mt-8 pt-6 text-center text-sm">
&copy; 2026 PilotEdge. All rights reserved. | <span class="text-amber-400">V1 Proof of Concept</span>
</div>
</div>
</footer>
`;
}