Files
PilotEdge/truckstops.html
Daniel Kovalevich 260f7c4928 first commit
2026-03-30 13:56:24 -04:00

242 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Truck Stops & Parking | PilotEdge</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
#map { height: 450px; width: 100%; border-radius: 0.75rem; }
</style>
</head>
<body class="bg-slate-50 min-h-screen flex flex-col">
<div id="main-nav"></div>
<div id="poc-banner"></div>
<!-- Page Header -->
<section class="bg-slate-900 text-white pt-24 pb-12 px-4">
<div class="max-w-7xl mx-auto">
<h1 class="text-3xl md:text-4xl font-bold mb-3">Truck Stops &amp; Parking for Oversize Loads</h1>
<p class="text-lg text-gray-400 max-w-3xl">Find oversize-friendly truck stops, rest areas, and staging locations across the US. Community-verified with driver comments and entrance dimensions.</p>
</div>
</section>
<!-- Filter Bar -->
<section class="max-w-7xl mx-auto px-4 pt-8 w-full">
<div class="bg-white rounded-2xl shadow-lg p-6">
<div class="flex flex-col md:flex-row gap-4 items-end">
<div class="flex-1">
<label class="block text-sm font-semibold text-slate-700 mb-1">Search Truck Stops</label>
<input type="text" id="ts-search" oninput="filterStops()" placeholder="e.g. Iowa 80, Amarillo, TX..." class="w-full border border-slate-300 rounded-lg px-4 py-2.5 focus:ring-2 focus:ring-amber-400 focus:border-amber-400 outline-none">
</div>
<div class="flex items-center gap-2 pb-1">
<input type="checkbox" id="ts-oversize-only" onchange="filterStops()" class="w-4 h-4 text-amber-500 border-slate-300 rounded focus:ring-amber-400">
<label for="ts-oversize-only" class="text-sm font-semibold text-slate-700 whitespace-nowrap">Oversize Friendly Only</label>
</div>
</div>
</div>
</section>
<!-- Map -->
<section class="max-w-7xl mx-auto px-4 pt-8 w-full">
<div class="bg-white rounded-2xl shadow-lg p-4">
<div id="map"></div>
</div>
</section>
<!-- Truck Stop Cards -->
<section class="max-w-7xl mx-auto px-4 pt-8 pb-8 w-full">
<div id="ts-list" class="space-y-6"></div>
</section>
<!-- Submit a Location -->
<section class="max-w-7xl mx-auto px-4 pb-12 w-full">
<div class="bg-amber-50 border-2 border-amber-200 rounded-2xl p-8 text-center">
<h2 class="text-2xl font-bold text-slate-900 mb-2">Know an Oversize-Friendly Location?</h2>
<p class="text-slate-600 mb-4">Help the community by suggesting truck stops, rest areas, or staging lots that accommodate oversize loads.</p>
<button onclick="alert('POC: This would open a submission form for new truck stop locations. Feature coming in production release.')" class="bg-amber-500 hover:bg-amber-600 text-slate-900 font-bold px-6 py-3 rounded-lg transition-colors shadow-md hover:shadow-lg">
📍 Submit a Location
</button>
</div>
</section>
<div id="main-footer"></div>
<script src="mock-data.js"></script>
<script src="mock-data-extended.js"></script>
<script src="nav.js"></script>
<script>
renderNav('truckstops');
renderBanner();
renderFooter();
// Facility emoji mapping
const facilityIcons = {
fuel: '⛽', food: '🍔', restrooms: '🚻', showers: '🚿',
mechanic: '🔧', scale: '⚖️', ev_charging: '🔌', hotel: '🏨',
trucking_museum: '🏛️', barber: '💈', chiropractor: '🦴', iron_skillet: '🍳'
};
let map, markers = [];
function initMap() {
map = L.map('map').setView([39.5, -98.5], 4);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap contributors',
maxZoom: 18
}).addTo(map);
addMarkers(MOCK_TRUCK_STOPS);
}
function addMarkers(stops) {
markers.forEach(m => map.removeLayer(m));
markers = [];
stops.forEach(stop => {
const color = stop.oversizeFriendly ? '#22c55e' : '#ef4444';
const marker = L.circleMarker([stop.location.lat, stop.location.lng], {
radius: 9, fillColor: color, color: '#fff', weight: 2, opacity: 1, fillOpacity: 0.85
}).addTo(map);
marker.bindPopup(`
<div style="min-width:200px">
<strong style="font-size:14px">${stop.name}</strong><br>
<span style="color:#64748b">${stop.location.city}, ${stop.location.state}</span><br>
<span style="display:inline-block;margin-top:4px;padding:2px 8px;border-radius:9999px;font-size:11px;font-weight:600;color:#fff;background:${stop.oversizeFriendly ? '#22c55e' : '#ef4444'}">
${stop.oversizeFriendly ? 'OVERSIZE FRIENDLY' : 'NOT RECOMMENDED'}
</span><br>
<button onclick="scrollToCard('${stop.id}')" style="margin-top:8px;background:#f59e0b;color:#0f172a;border:none;padding:6px 14px;border-radius:8px;font-weight:700;font-size:12px;cursor:pointer">View Details</button>
</div>
`);
markers.push(marker);
});
}
function scrollToCard(id) {
const el = document.getElementById('card-' + id);
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
el.classList.add('ring-2', 'ring-amber-400');
setTimeout(() => el.classList.remove('ring-2', 'ring-amber-400'), 2000);
}
}
function renderStops(stops) {
const list = document.getElementById('ts-list');
if (!stops.length) {
list.innerHTML = '<div class="text-center py-12 text-slate-500">No truck stops match your filters.</div>';
return;
}
list.innerHTML = stops.map(stop => `
<div id="card-${stop.id}" class="bg-white rounded-2xl shadow-lg p-6 transition-all duration-300">
<div class="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-4">
<div>
<h3 class="text-xl font-bold text-slate-900">${stop.name}</h3>
<p class="text-slate-500">${stop.location.city}, ${stop.location.state}</p>
</div>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold whitespace-nowrap ${stop.oversizeFriendly ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">
${stop.oversizeFriendly ? '✅ OVERSIZE FRIENDLY' : '⛔ NOT RECOMMENDED FOR OVERSIZE'}
</span>
</div>
<!-- Specs Grid -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
<div class="bg-slate-50 rounded-xl p-3 text-center">
<div class="text-xs text-slate-500 font-semibold mb-1">Entrance Width</div>
<div class="text-sm font-bold text-slate-800">${stop.entranceWidth}</div>
</div>
<div class="bg-slate-50 rounded-xl p-3 text-center">
<div class="text-xs text-slate-500 font-semibold mb-1">Entrance Height</div>
<div class="text-sm font-bold text-slate-800">${stop.entranceHeight}</div>
</div>
<div class="bg-slate-50 rounded-xl p-3 text-center">
<div class="text-xs text-slate-500 font-semibold mb-1">Lot Size</div>
<div class="text-sm font-bold text-slate-800">${stop.lotSize}</div>
</div>
<div class="bg-slate-50 rounded-xl p-3 text-center">
<div class="text-xs text-slate-500 font-semibold mb-1">Oversize Capacity</div>
<div class="text-sm font-bold text-slate-800">${stop.oversizeCapacity}</div>
</div>
</div>
<!-- Facilities -->
<div class="flex flex-wrap gap-2 mb-4">
${stop.facilities.map(f => `
<span class="bg-slate-100 rounded-full px-3 py-1 text-xs font-medium text-slate-700">
${facilityIcons[f] || '📌'} ${f.replace(/_/g, ' ')}
</span>
`).join('')}
</div>
<!-- Description -->
<p class="text-sm text-slate-600 mb-5">${stop.description}</p>
<!-- Comments Section -->
<div class="border-t border-slate-200 pt-4">
<h4 class="text-sm font-bold text-slate-900 mb-3">💬 Driver Comments (${stop.comments.length})</h4>
<div class="space-y-3 mb-4">
${stop.comments.map(c => `
<div class="bg-slate-50 rounded-xl p-3">
<div class="flex items-center gap-2 mb-1">
<span class="text-xs font-bold text-slate-700">${c.user}</span>
<span class="text-xs text-slate-400">${c.date}</span>
</div>
<p class="text-sm text-slate-600">${c.text}</p>
</div>
`).join('')}
</div>
<div id="comment-form-${stop.id}" class="hidden">
<textarea id="comment-text-${stop.id}" rows="3" placeholder="Share your experience at this location..." class="w-full border border-slate-300 rounded-lg px-4 py-2.5 text-sm focus:ring-2 focus:ring-amber-400 focus:border-amber-400 outline-none mb-2"></textarea>
<div class="flex gap-2">
<button onclick="submitComment('${stop.id}')" class="bg-amber-500 hover:bg-amber-600 text-slate-900 font-bold px-4 py-2 rounded-lg text-sm transition-colors">Submit Comment</button>
<button onclick="toggleCommentForm('${stop.id}')" class="bg-slate-200 hover:bg-slate-300 text-slate-700 font-bold px-4 py-2 rounded-lg text-sm transition-colors">Cancel</button>
</div>
</div>
<button id="comment-btn-${stop.id}" onclick="toggleCommentForm('${stop.id}')" class="text-sm font-semibold text-amber-600 hover:text-amber-700 transition-colors">
+ Add Comment
</button>
</div>
</div>
`).join('');
}
function toggleCommentForm(id) {
const form = document.getElementById('comment-form-' + id);
const btn = document.getElementById('comment-btn-' + id);
const visible = !form.classList.contains('hidden');
form.classList.toggle('hidden');
btn.classList.toggle('hidden');
}
function submitComment(id) {
const text = document.getElementById('comment-text-' + id).value.trim();
if (!text) { alert('Please enter a comment.'); return; }
alert('POC: Comment submitted! In production, this would save to the database.\n\nYour comment: "' + text + '"');
document.getElementById('comment-text-' + id).value = '';
toggleCommentForm(id);
}
function filterStops() {
const query = document.getElementById('ts-search').value.toLowerCase();
const oversizeOnly = document.getElementById('ts-oversize-only').checked;
const filtered = MOCK_TRUCK_STOPS.filter(stop => {
const matchSearch = !query ||
stop.name.toLowerCase().includes(query) ||
stop.location.city.toLowerCase().includes(query) ||
stop.location.state.toLowerCase().includes(query);
const matchOversize = !oversizeOnly || stop.oversizeFriendly;
return matchSearch && matchOversize;
});
renderStops(filtered);
addMarkers(filtered);
}
// Initialize
initMap();
renderStops(MOCK_TRUCK_STOPS);
</script>
</body>
</html>