426 lines
32 KiB
HTML
426 lines
32 KiB
HTML
|
<!doctype html>
|
|||
|
<html lang="en" class="scroll-smooth">
|
|||
|
<head>
|
|||
|
<meta charset="utf-8" />
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|||
|
<title>Support & Downloads · Leitan GPS</title>
|
|||
|
<meta name="description" content="Search by model or serial to access manuals, firmware/software, knowledge base articles, and videos for construction machinery GPS products." />
|
|||
|
<meta name="theme-color" content="#0ea5e9" />
|
|||
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='46' fill='%230ea5e9'/%3E%3Cpath d='M50 20a30 30 0 1030 30A30 30 0 0050 20zm0 10a20 20 0 11-20 20A20 20 0 0150 30z' fill='white'/%3E%3C/svg%3E" />
|
|||
|
|
|||
|
<!-- Tailwind v4 Play CDN -->
|
|||
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|||
|
<script type="tailwind-config">
|
|||
|
export default {
|
|||
|
darkMode: 'class',
|
|||
|
theme: {
|
|||
|
extend: {
|
|||
|
colors: {
|
|||
|
brand: { 50:'#eff6ff',100:'#dbeafe',200:'#bfdbfe',300:'#93c5fd',400:'#60a5fa',500:'#3b82f6',600:'#2563eb',700:'#1d4ed8',800:'#1e40af',900:'#1e3a8a' }
|
|||
|
},
|
|||
|
boxShadow: { soft: '0 10px 30px rgba(0,0,0,0.06)' }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</script>
|
|||
|
<style>
|
|||
|
html { scroll-behavior: smooth; }
|
|||
|
::selection { background: #bfdbfe; }
|
|||
|
</style>
|
|||
|
</head>
|
|||
|
<body class="bg-white text-gray-800 antialiased dark:bg-gray-950 dark:text-gray-100">
|
|||
|
<!-- Decorative BG -->
|
|||
|
<div aria-hidden="true" class="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
|
|||
|
<div class="absolute -top-32 left-1/2 h-80 w-[1100px] -translate-x-1/2 rounded-full bg-gradient-to-r from-brand-400/30 via-sky-400/20 to-cyan-400/30 blur-3xl dark:from-brand-600/30 dark:via-sky-600/20 dark:to-cyan-600/30"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- Header -->
|
|||
|
<header class="sticky top-0 z-50 backdrop-blur supports-[backdrop-filter]:bg-white/60 bg-white/80 border-b border-gray-200/70 dark:bg-gray-950/70 dark:border-gray-800">
|
|||
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|||
|
<div class="flex h-16 items-center justify-between">
|
|||
|
<a href="/index.html" class="flex items-center gap-2">
|
|||
|
<span class="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-brand-600 text-white shadow-soft">L</span>
|
|||
|
<span class="font-semibold">Leitan GPS</span>
|
|||
|
</a>
|
|||
|
<nav class="hidden md:flex items-center gap-8 text-sm">
|
|||
|
<a href="/support/index.html" aria-current="page" class="text-brand-700 dark:text-brand-300 font-medium">Support</a>
|
|||
|
<a href="/products/index.html" class="hover:text-brand-600 dark:hover:text-brand-400">Catalog</a>
|
|||
|
<span class="h-5 w-px bg-gray-300/60 dark:bg-gray-700"></span>
|
|||
|
<a href="/employee/index.html" class="hover:text-brand-600 dark:hover:text-brand-400">Employee</a>
|
|||
|
<span class="h-5 w-px bg-gray-300/60 dark:bg-gray-700"></span>
|
|||
|
<div class="flex items-center gap-3" role="group" aria-label="Language">
|
|||
|
<button type="button" class="text-xs px-2 py-1 rounded border border-gray-300/80 hover:border-brand-500 hover:text-brand-600 dark:border-gray-700 dark:hover:border-brand-500" title="Italiano (not implemented)">IT</button>
|
|||
|
<button type="button" class="text-xs px-2 py-1 rounded border border-brand-500 text-brand-700 dark:text-brand-300" aria-pressed="true" title="English">EN</button>
|
|||
|
</div>
|
|||
|
</nav>
|
|||
|
<div class="flex items-center gap-3">
|
|||
|
<span id="empBadge" class="hidden sm:inline-flex items-center gap-1 rounded-full border border-gray-300 px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:text-gray-300"></span>
|
|||
|
<button id="themeToggle" aria-label="Toggle theme" class="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-gray-300 bg-white shadow-sm hover:border-brand-500 dark:bg-gray-900 dark:border-gray-700">
|
|||
|
<svg id="iconSun" class="h-5 w-5 hidden" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2m0 14v2m9-9h-2M5 12H3m15.364-6.364l-1.414 1.414M7.05 16.95l-1.414 1.414m0-11.314l1.414 1.414M16.95 16.95l1.414 1.414"/><circle cx="12" cy="12" r="4"/></svg>
|
|||
|
<svg id="iconMoon" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg>
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</header>
|
|||
|
|
|||
|
<!-- Hero Search -->
|
|||
|
<section class="relative">
|
|||
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|||
|
<div class="grid lg:grid-cols-2 gap-10 py-12 lg:py-16 items-center">
|
|||
|
<div>
|
|||
|
<p class="inline-flex items-center gap-2 rounded-full border border-brand-200 bg-white/80 px-3 py-1 text-xs text-brand-700 shadow-sm dark:bg-gray-900/70 dark:border-brand-800 dark:text-brand-300">
|
|||
|
<span class="h-2 w-2 rounded-full bg-brand-500"></span> Support & Downloads
|
|||
|
</p>
|
|||
|
<h1 class="mt-4 text-3xl sm:text-4xl font-bold tracking-tight">Find manuals, firmware, KB and videos</h1>
|
|||
|
<p class="mt-3 text-base text-gray-600 dark:text-gray-300 max-w-2xl">Search by <strong>model</strong> or <strong>serial number</strong>. Results are linked to the selected product. Internal items require employee access.</p>
|
|||
|
|
|||
|
<form id="searchForm" class="mt-6">
|
|||
|
<label class="relative block">
|
|||
|
<input id="q" type="text" placeholder="e.g. A1 or SN-A1-2025-…" class="w-full rounded-xl border border-gray-300 bg-white px-4 py-4 pr-28 text-base shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 dark:bg-gray-950 dark:border-gray-800" />
|
|||
|
<button class="absolute right-2 top-1/2 -translate-y-1/2 inline-flex items-center justify-center rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm hover:border-brand-500 dark:bg-gray-900 dark:border-gray-700" type="submit" aria-label="Search">
|
|||
|
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M11 18a7 7 0 100-14 7 7 0 000 14z"/></svg>
|
|||
|
</button>
|
|||
|
</label>
|
|||
|
<p class="mt-2 text-xs text-gray-500">Tip: Paste a serial number to auto‑resolve its model.</p>
|
|||
|
</form>
|
|||
|
|
|||
|
<div class="mt-6 grid grid-cols-2 gap-3 text-sm">
|
|||
|
<button data-example="A1" class="rounded-xl border border-gray-200 p-3 hover:border-brand-500 dark:border-gray-800">Try: A1</button>
|
|||
|
<button data-example="SN-A1-2025-0001" class="rounded-xl border border-gray-200 p-3 hover:border-brand-500 dark:border-gray-800">Try: SN-A1-2025-0001</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="relative">
|
|||
|
<div class="relative rounded-2xl border border-gray-200 bg-white p-3 shadow-soft dark:bg-gray-900 dark:border-gray-800">
|
|||
|
<div class="aspect-video w-full overflow-hidden rounded-xl bg-gray-100 dark:bg-gray-800">
|
|||
|
<img src="https://images.pexels.com/photos/280140/pexels-photo-280140.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=1600" alt="Construction cranes — Pexels" class="h-full w-full object-cover"/>
|
|||
|
</div>
|
|||
|
<div class="mt-3 flex items-center justify-between">
|
|||
|
<div class="text-sm text-gray-600 dark:text-gray-300">2‑min overview: wiring & installation</div>
|
|||
|
<a href="https://www.pexels.com/video/time-lapse-video-of-an-excavator-5342014/" target="_blank" rel="noopener" class="inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm border border-gray-300 hover:border-brand-500 dark:border-gray-700">
|
|||
|
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.26a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/></svg>
|
|||
|
Watch on Pexels
|
|||
|
</a>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
|
|||
|
<!-- Sticky Filters -->
|
|||
|
<div id="filters" class="sticky top-[64px] z-40 border-y border-gray-200/70 bg-white/80 backdrop-blur supports-[backdrop-filter]:bg-white/60 dark:bg-gray-950/70 dark:border-gray-800">
|
|||
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-3">
|
|||
|
<div class="flex flex-wrap items-center gap-2 text-sm">
|
|||
|
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="doc" checked> Documents</label>
|
|||
|
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="sw" checked> Software</label>
|
|||
|
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="kb" checked> Knowledge</label>
|
|||
|
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="vid" checked> Videos</label>
|
|||
|
<span class="h-5 w-px bg-gray-300/60 dark:bg-gray-700"></span>
|
|||
|
<select id="langFilter" class="rounded-lg border border-gray-300 bg-white px-2.5 py-1.5 dark:bg-gray-950 dark:border-gray-800"><option value="">All languages</option><option value="en">EN</option><option value="zh">ZH</option><option value="it">IT</option></select>
|
|||
|
<select id="platFilter" class="rounded-lg border border-gray-300 bg-white px-2.5 py-1.5 dark:bg-gray-950 dark:border-gray-800"><option value="">All platforms</option><option value="win">Windows</option><option value="mac">macOS</option><option value="linux">Linux</option><option value="all">All</option></select>
|
|||
|
<span class="ml-auto text-xs text-gray-500">Employee visibility: <span id="empVis">Public only</span></span>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- Product card and Results -->
|
|||
|
<section class="py-10">
|
|||
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|||
|
<!-- Selected product card -->
|
|||
|
<div id="productCard" class="hidden rounded-2xl border border-gray-200 bg-white p-6 shadow-soft dark:bg-gray-900 dark:border-gray-800"></div>
|
|||
|
|
|||
|
<!-- Empty state / Latest updates -->
|
|||
|
<div id="emptyState" class="mt-6">
|
|||
|
<h2 class="text-xl sm:text-2xl font-semibold">Latest updates</h2>
|
|||
|
<div id="latestGrid" class="mt-4 grid gap-6 lg:grid-cols-3"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- Results lists -->
|
|||
|
<div id="results" class="mt-8 space-y-10 hidden">
|
|||
|
<div id="docSection">
|
|||
|
<div class="flex items-end justify-between gap-4">
|
|||
|
<h3 class="text-lg font-semibold">Manuals & documents</h3>
|
|||
|
<div class="text-xs text-gray-500">Language filter applies</div>
|
|||
|
</div>
|
|||
|
<div id="docList" class="mt-4 grid gap-4"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div id="swSection" class="border-t border-gray-200/70 pt-8 dark:border-gray-800">
|
|||
|
<div class="flex items-end justify-between gap-4">
|
|||
|
<h3 class="text-lg font-semibold">Software & firmware</h3>
|
|||
|
<div class="text-xs text-gray-500">Platform filter applies</div>
|
|||
|
</div>
|
|||
|
<div id="swList" class="mt-4 grid gap-4"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div id="kbSection" class="border-t border-gray-200/70 pt-8 dark:border-gray-800">
|
|||
|
<h3 class="text-lg font-semibold">Knowledge base</h3>
|
|||
|
<div id="kbList" class="mt-4 grid gap-4 lg:grid-cols-2"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div id="vidSection" class="border-t border-gray-200/70 pt-8 dark:border-gray-800">
|
|||
|
<div class="flex items-end justify-between gap-4">
|
|||
|
<h3 class="text-lg font-semibold">Videos</h3>
|
|||
|
<div class="text-xs text-gray-500">Demo player — replace with HLS in production</div>
|
|||
|
</div>
|
|||
|
<div id="videoList" class="mt-4 grid gap-6 lg:grid-cols-2"></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
|
|||
|
<!-- Version Drawer -->
|
|||
|
<div id="drawer" class="fixed inset-0 z-[60] hidden">
|
|||
|
<div class="absolute inset-0 bg-black/40" onclick="toggleDrawer(false)"></div>
|
|||
|
<aside class="absolute right-0 top-0 h-full w-full max-w-md bg-white dark:bg-gray-950 shadow-xl p-6 overflow-y-auto">
|
|||
|
<div class="flex items-center justify-between">
|
|||
|
<h4 class="font-semibold">Version history</h4>
|
|||
|
<button class="rounded-lg border border-gray-300 px-2 py-1 text-sm dark:border-gray-700" onclick="toggleDrawer(false)">Close</button>
|
|||
|
</div>
|
|||
|
<div id="drawerBody" class="mt-4 space-y-4 text-sm"></div>
|
|||
|
</aside>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- Firmware Risk Modal -->
|
|||
|
<div id="risk" class="fixed inset-0 z-[70] hidden">
|
|||
|
<div class="absolute inset-0 bg-black/40" onclick="toggleRisk(false)"></div>
|
|||
|
<div class="absolute left-1/2 top-1/2 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-gray-200 bg-white p-6 shadow-2xl dark:bg-gray-900 dark:border-gray-800">
|
|||
|
<h4 class="font-semibold">Firmware download confirmation</h4>
|
|||
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">Confirm you have checked compatibility and rollback instructions. Incorrect firmware may brick the device.</p>
|
|||
|
<label class="mt-4 inline-flex items-center gap-2 text-sm"><input type="checkbox" id="riskAck" class="accent-brand-600"> I understand the risk</label>
|
|||
|
<div class="mt-4 flex justify-end gap-2">
|
|||
|
<button class="rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700" onclick="toggleRisk(false)">Cancel</button>
|
|||
|
<button id="riskGo" class="rounded-lg bg-brand-600 px-3 py-1.5 text-sm text-white disabled:opacity-50" disabled>Download</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- Footer -->
|
|||
|
<footer class="mt-12 border-t border-gray-200/70 dark:border-gray-800">
|
|||
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-10">
|
|||
|
<div class="grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
|||
|
<div>
|
|||
|
<div class="flex items-center gap-2">
|
|||
|
<span class="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-brand-600 text-white shadow-soft">L</span>
|
|||
|
<span class="font-semibold">Leitan GPS</span>
|
|||
|
</div>
|
|||
|
<p class="mt-3 text-sm text-gray-600 dark:text-gray-300">Support & Downloads for construction machinery GPS automation.</p>
|
|||
|
</div>
|
|||
|
<div>
|
|||
|
<div class="font-semibold">Product</div>
|
|||
|
<ul class="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
|
|||
|
<li><a href="/products/index.html" class="hover:underline">Catalog</a></li>
|
|||
|
<li><a href="/employee/index.html" class="hover:underline">Employee portal</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<div>
|
|||
|
<div class="font-semibold">Resources</div>
|
|||
|
<ul class="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
|
|||
|
<li><a href="#" class="hover:underline">Privacy policy</a></li>
|
|||
|
<li><a href="#" class="hover:underline">Cookie preferences</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<div>
|
|||
|
<div class="font-semibold">Contact</div>
|
|||
|
<ul class="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
|
|||
|
<li>support@example.com</li>
|
|||
|
<li>Mon–Fri, 09:00–18:00</li>
|
|||
|
<li>Italy / Singapore</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="mt-8 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-gray-500">
|
|||
|
<div>© 2025 Leitan Tech. All rights reserved.</div>
|
|||
|
<a href="#top" class="inline-flex items-center gap-1 hover:text-brand-600 dark:hover:text-brand-400">Back to top <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></a>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</footer>
|
|||
|
|
|||
|
<!-- Scripts -->
|
|||
|
<script>
|
|||
|
// Theme init
|
|||
|
(function(){ const s=localStorage.getItem('theme'); const d=window.matchMedia('(prefers-color-scheme: dark)').matches; if (s==='dark' || (!s && d)) document.documentElement.classList.add('dark'); })();
|
|||
|
const themeBtn=document.getElementById('themeToggle'); const sun=document.getElementById('iconSun'); const moon=document.getElementById('iconMoon');
|
|||
|
function syncIcons(){ const d=document.documentElement.classList.contains('dark'); sun?.classList.toggle('hidden',!d); moon?.classList.toggle('hidden',d); }
|
|||
|
themeBtn?.addEventListener('click',()=>{ document.documentElement.classList.toggle('dark'); localStorage.setItem('theme', document.documentElement.classList.contains('dark')?'dark':'light'); syncIcons(); }); syncIcons();
|
|||
|
|
|||
|
// Employee mode
|
|||
|
const isEmp = !!localStorage.getItem('employee');
|
|||
|
const empBadge = document.getElementById('empBadge');
|
|||
|
empBadge.textContent = isEmp ? 'Employee mode' : 'Visitor mode';
|
|||
|
empBadge.classList.remove('hidden');
|
|||
|
document.getElementById('empVis').textContent = isEmp ? 'Public + Internal' : 'Public only';
|
|||
|
|
|||
|
// Demo datasets (normally load /data/*.json)
|
|||
|
const PRODUCTS=[
|
|||
|
{id:'A1', model:'A1', category:'Positioning unit', snPrefixes:['SN-A1-','A1-2025-'], specs:{Accuracy:'RTK cm-level',Interfaces:'CAN/RS485',Ingress:'IP67'}},
|
|||
|
{id:'IMU-PRO', model:'IMU-PRO', category:'Sensor (IMU)', snPrefixes:['SN-IMU-','IMU-2025-'], specs:{Axes:'6-axis',Bias:'Low drift',Ingress:'IP65'}},
|
|||
|
{id:'AX-5', model:'AX-5', category:'Antenna', snPrefixes:['SN-AX5-'], specs:{Type:'GNSS L1/L2',Mount:'Magnetic',Ingress:'IP66'}}
|
|||
|
];
|
|||
|
const DOCS=[
|
|||
|
{id:'doc-001', productId:'A1', title:'A1 Wiring & Installation', type:'manual', version:'v1.8', lang:'en', size:'2.3MB', updatedAt:'2025-07-08', checksum:'SHA256-…d9c', url:'#', visibility:'public'},
|
|||
|
{id:'doc-002', productId:'A1', title:'A1 Service Manual', type:'service', version:'v1.7', lang:'en', size:'4.7MB', updatedAt:'2025-07-22', checksum:'SHA256-…9ab', url:'#', visibility:'internal'},
|
|||
|
{id:'doc-010', productId:'IMU-PRO', title:'IMU‑PRO Datasheet', type:'datasheet', version:'v3.0', lang:'en', size:'1.1MB', updatedAt:'2025-06-30', checksum:'SHA256-…ab2', url:'#', visibility:'public'}
|
|||
|
];
|
|||
|
const SW=[
|
|||
|
{id:'sw-101', productId:'A1', title:'A1 Config Tool', kind:'software', version:'v2.2.0', platform:'win', size:'18.4MB', updatedAt:'2025-06-20', checksum:'SHA256-…6a5', notesUrl:'#', compat:'A1 hw v2.x+', url:'#', visibility:'public', risk:false},
|
|||
|
{id:'sw-102', productId:'A1', title:'A1 Firmware', kind:'firmware', version:'v2.3.1', platform:'all', size:'21.0MB', updatedAt:'2025-08-02', checksum:'MD5-…1c3', notesUrl:'#', compat:'A1 hw v2.x+', url:'#', visibility:'internal', risk:true},
|
|||
|
{id:'sw-210', productId:'IMU-PRO', title:'IMU‑PRO Tool', kind:'software', version:'v1.4.2', platform:'win', size:'11.2MB', updatedAt:'2025-07-10', checksum:'SHA256-…9ee', notesUrl:'#', compat:'IMU‑PRO all', url:'#', visibility:'public', risk:false}
|
|||
|
];
|
|||
|
const KB=[
|
|||
|
{id:'kb-301', productId:'A1', title:'Position drift under vibration — checklist', summary:'Mounting, grounding, EMI, power ripple…', tags:['Troubleshooting','Installation'], url:'#', visibility:'internal'},
|
|||
|
{id:'kb-302', productId:'A1', title:'Quick start — first fix in 60s', summary:'A minimal path to first RTK fix.', tags:['Quickstart'], url:'#', visibility:'public'}
|
|||
|
];
|
|||
|
const VIDS=[
|
|||
|
{id:'vid-501', productId:'A1', title:'A1 quick install (2:13)', duration:'02:13', poster:'https://images.pexels.com/photos/14452156/pexels-photo-14452156.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=1600', sources:[{src:'/media/a1-install-720.mp4',label:'720p'}], visibility:'public'},
|
|||
|
{id:'vid-502', productId:'A1', title:'Internal: wiring pitfalls (3:40)', duration:'03:40', poster:'https://images.pexels.com/photos/159306/pexels-photo-159306.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=1600', sources:[{src:'/media/a1-internal-720.mp4',label:'720p'}], visibility:'internal'}
|
|||
|
];
|
|||
|
|
|||
|
// Resolve query to product
|
|||
|
const params=new URLSearchParams(location.search); const q=params.get('q')||''; if(q) document.getElementById('q').value=q;
|
|||
|
function resolveProduct(input){ if(!input) return null; const s=input.trim().toUpperCase();
|
|||
|
// serial match
|
|||
|
for(const p of PRODUCTS){ if(p.snPrefixes?.some(pre=>s.startsWith(pre.toUpperCase()))) return p; }
|
|||
|
// model exact or prefix
|
|||
|
return PRODUCTS.find(p=>p.model.toUpperCase()===s || s.startsWith(p.model.toUpperCase()))||null;
|
|||
|
}
|
|||
|
|
|||
|
// UI helpers
|
|||
|
function badgeInternal(v){ return v==='internal'?'<span class="inline-flex items-center gap-1 rounded bg-gray-100 px-2 py-1 text-[11px] text-gray-600 dark:bg-gray-800 dark:text-gray-300">\uD83D\uDD12 Internal</span>':''; }
|
|||
|
function lockCls(v){ return v==='internal' && !isEmp ? 'opacity-40 pointer-events-none' : ''; }
|
|||
|
|
|||
|
// Render product card
|
|||
|
let CURRENT=null;
|
|||
|
function renderProductCard(p){ if(!p){ document.getElementById('productCard').classList.add('hidden'); return; }
|
|||
|
const specs=Object.entries(p.specs||{}).map(([k,v])=>`<div class='rounded-xl border border-gray-200 p-3 dark:border-gray-800'><dt class='text-gray-500'>${k}</dt><dd class='font-medium'>${v}</dd></div>`).join('');
|
|||
|
document.getElementById('productCard').innerHTML=`
|
|||
|
<div class='flex flex-col md:flex-row items-start md:items-center justify-between gap-4'>
|
|||
|
<div>
|
|||
|
<div class='text-sm text-gray-500'>${p.category}</div>
|
|||
|
<h2 class='mt-1 text-2xl font-semibold'>${p.model}</h2>
|
|||
|
<div class='mt-1 text-xs text-gray-500'>SN prefixes: ${(p.snPrefixes||[]).join(', ')||'—'}</div>
|
|||
|
</div>
|
|||
|
<div class='grid grid-cols-2 sm:grid-cols-3 gap-3 text-sm'>${specs}</div>
|
|||
|
<div class='flex gap-2'>
|
|||
|
<a class='rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700' href='/p/${encodeURIComponent(p.model.toLowerCase())}.html?model=${encodeURIComponent(p.model)}'>Open product page</a>
|
|||
|
</div>
|
|||
|
</div>`;
|
|||
|
document.getElementById('productCard').classList.remove('hidden');
|
|||
|
}
|
|||
|
|
|||
|
// Render latest updates (when no selection)
|
|||
|
function renderLatest(){ const all=[...DOCS,...SW,...KB,...VIDS]; all.sort((a,b)=>new Date(b.updatedAt||'1970-01-01')-new Date(a.updatedAt||'1970-01-01')); const top=all.slice(0,3);
|
|||
|
document.getElementById('latestGrid').innerHTML=top.map(x=>`
|
|||
|
<article class='rounded-2xl border border-gray-200 bg-white p-6 shadow-soft dark:bg-gray-900 dark:border-gray-800'>
|
|||
|
<div class='text-xs text-gray-500'>${x.kind||x.type||'Resource'} · ${x.updatedAt||'—'}</div>
|
|||
|
<h3 class='mt-1 font-semibold'>${x.title||x.model}</h3>
|
|||
|
<p class='mt-1 text-sm text-gray-600 dark:text-gray-300'>${x.size?x.size+' · ':''}${x.version?x.version+' · ':''}${x.platform?x.platform.toUpperCase()+' · ':''}${x.checksum||''}</p>
|
|||
|
<div class='mt-3 flex gap-2'>
|
|||
|
<span>${badgeInternal(x.visibility||'public')}</span>
|
|||
|
<button class='rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700' onclick='document.getElementById("q").value="A1"; startSearch();'>View</button>
|
|||
|
</div>
|
|||
|
</article>`).join('');
|
|||
|
}
|
|||
|
|
|||
|
// Render results for selected product
|
|||
|
function renderResults(){ const p=CURRENT; if(!p) return; const lang=document.getElementById('langFilter').value; const plat=document.getElementById('platFilter').value;
|
|||
|
// filter toggles
|
|||
|
const chosenTypes=[...document.querySelectorAll('#filters input[type=checkbox][data-type]:checked')].map(el=>el.getAttribute('data-type'));
|
|||
|
// docs
|
|||
|
const docs=DOCS.filter(d=>d.productId===p.id && (!lang||d.lang===lang));
|
|||
|
document.getElementById('docSection').classList.toggle('hidden',!chosenTypes.includes('doc'));
|
|||
|
document.getElementById('docList').innerHTML=docs.map(d=>`
|
|||
|
<div class='flex flex-col sm:flex-row items-start sm:items-center gap-4 rounded-2xl border border-gray-200 bg-white p-4 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(d.visibility)}'>
|
|||
|
<div class='flex min-w-0 flex-1 items-center gap-4'>
|
|||
|
<div class='flex h-12 w-12 items-center justify-center rounded-xl bg-brand-50 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300'>
|
|||
|
<svg class='h-6 w-6' fill='none' stroke='currentColor' stroke-width='1.5' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' d='M12 6v6m0 0l-3-3m3 3l3-3M4 18h16'/></svg>
|
|||
|
</div>
|
|||
|
<div class='min-w-0'>
|
|||
|
<div class='font-medium truncate'>${d.title} (${(d.version||'').toUpperCase()} · ${(d.lang||'').toUpperCase()})</div>
|
|||
|
<div class='text-xs text-gray-500'>${d.type} · ${d.size||'—'} · Updated: ${d.updatedAt||'—'} · ${d.checksum||''}</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class='flex shrink-0 items-center gap-3'>
|
|||
|
${badgeInternal(d.visibility)}
|
|||
|
<a href='${d.url||'#'}' class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700'>Download</a>
|
|||
|
<button class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700' onclick='openDrawer("${d.id}")'>Version history</button>
|
|||
|
</div>
|
|||
|
</div>`).join('');
|
|||
|
// sw
|
|||
|
const sw=SW.filter(s=>s.productId===p.id && (!plat||s.platform===plat||s.platform==='all'));
|
|||
|
document.getElementById('swSection').classList.toggle('hidden',!chosenTypes.includes('sw'));
|
|||
|
document.getElementById('swList').innerHTML=sw.map(s=>`
|
|||
|
<div class='flex flex-col sm:flex-row items-start sm:items-center gap-4 rounded-2xl border border-gray-200 bg-white p-4 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(s.visibility)}'>
|
|||
|
<div class='flex min-w-0 flex-1 items-center gap-4'>
|
|||
|
<div class='flex h-12 w-12 items-center justify-center rounded-xl ${s.kind==='firmware'?'bg-amber-50 text-amber-700 dark:bg-amber-900/20 dark:text-amber-300':'bg-brand-50 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300'}'>
|
|||
|
<svg class='h-6 w-6' fill='none' stroke='currentColor' stroke-width='1.5' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' d='M4 4h16v12H4z'/><path stroke-linecap='round' stroke-linejoin='round' d='M4 20h16'/></svg>
|
|||
|
</div>
|
|||
|
<div class='min-w-0'>
|
|||
|
<div class='font-medium truncate'>${s.title} (${s.version}) — ${s.kind}</div>
|
|||
|
<div class='text-xs text-gray-500'>${(s.platform||'').toUpperCase()} · ${s.size||'—'} · Updated: ${s.updatedAt||'—'} · ${s.checksum||''} · Compat: ${s.compat||'—'}</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class='flex shrink-0 items-center gap-3'>
|
|||
|
${badgeInternal(s.visibility)}
|
|||
|
<a href='${s.notesUrl||'#'}' class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700'>Release notes</a>
|
|||
|
<button class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700' onclick='${s.kind==='firmware'?'openRisk()':'alert("Download started (demo)")'}'>Download</button>
|
|||
|
</div>
|
|||
|
</div>`).join('');
|
|||
|
// kb
|
|||
|
const kb=KB.filter(k=>k.productId===p.id);
|
|||
|
document.getElementById('kbSection').classList.toggle('hidden',!chosenTypes.includes('kb'));
|
|||
|
document.getElementById('kbList').innerHTML=kb.map(k=>`
|
|||
|
<article class='rounded-2xl border border-gray-200 bg-white p-6 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(k.visibility)}'>
|
|||
|
<div class='text-xs text-gray-500'>${(k.tags||[]).join(' · ')}</div>
|
|||
|
<h4 class='mt-1 font-semibold'>${k.title}</h4>
|
|||
|
<p class='mt-1 text-sm text-gray-600 dark:text-gray-300'>${k.summary||''}</p>
|
|||
|
<div class='mt-3 flex items-center gap-3'>${badgeInternal(k.visibility)}<a href='${k.url||'#'}' class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700'>View</a></div>
|
|||
|
</article>`).join('');
|
|||
|
// videos
|
|||
|
const vids=VIDS.filter(v=>v.productId===p.id);
|
|||
|
document.getElementById('vidSection').classList.toggle('hidden',!chosenTypes.includes('vid'));
|
|||
|
document.getElementById('videoList').innerHTML=vids.map(v=>`
|
|||
|
<div class='rounded-2xl border border-gray-200 bg-white p-3 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(v.visibility)}'>
|
|||
|
<div class='aspect-video w-full overflow-hidden rounded-xl bg-gray-100 dark:bg-gray-800'>
|
|||
|
<video controls preload='metadata' poster='${v.poster||''}' class='h-full w-full'>${(v.sources||[]).map(s=>`<source src='${s.src}' type='video/mp4'/>`).join('')}</video>
|
|||
|
</div>
|
|||
|
<div class='mt-3 flex items-center justify-between'>
|
|||
|
<div class='text-sm text-gray-600 dark:text-gray-300'>${v.title||''}</div>
|
|||
|
${badgeInternal(v.visibility)}
|
|||
|
</div>
|
|||
|
</div>`).join('');
|
|||
|
|
|||
|
document.getElementById('emptyState').classList.add('hidden');
|
|||
|
document.getElementById('results').classList.remove('hidden');
|
|||
|
}
|
|||
|
|
|||
|
function startSearch(){ const input=document.getElementById('q').value; const p=resolveProduct(input); if(!p){ alert('No matching product. Try A1'); return; } CURRENT=p; renderProductCard(p); renderResults(); }
|
|||
|
|
|||
|
// Filters change
|
|||
|
document.querySelectorAll('#filters input[type=checkbox][data-type]').forEach(el=>el.addEventListener('change',()=>CURRENT&&renderResults()));
|
|||
|
document.getElementById('langFilter').addEventListener('change',()=>CURRENT&&renderResults());
|
|||
|
document.getElementById('platFilter').addEventListener('change',()=>CURRENT&&renderResults());
|
|||
|
|
|||
|
// Search submit
|
|||
|
document.getElementById('searchForm').addEventListener('submit',e=>{e.preventDefault(); startSearch();});
|
|||
|
document.querySelectorAll('[data-example]').forEach(btn=>btn.addEventListener('click',()=>{document.getElementById('q').value=btn.getAttribute('data-example'); startSearch();}));
|
|||
|
|
|||
|
// Init with q param
|
|||
|
if(q){ startSearch(); }
|
|||
|
renderLatest();
|
|||
|
|
|||
|
// Drawer / Risk modal controls
|
|||
|
function toggleDrawer(v){ document.getElementById('drawer').classList.toggle('hidden',!v); }
|
|||
|
window.openDrawer=function(id){ const list=[{version:'v1.8',date:'2025-07-08',changes:['Updated wiring pinout','Added IT manual']},{version:'v1.7',date:'2025-04-12',changes:['Fix typos','New CE appendix']}];
|
|||
|
document.getElementById('drawerBody').innerHTML=list.map(x=>`<div class='rounded-xl border border-gray-200 p-4 dark:border-gray-800'><div class='font-medium'>${x.version} · ${x.date}</div><ul class='mt-2 list-disc pl-5 text-gray-600 dark:text-gray-300'>${x.changes.map(c=>`<li>${c}</li>`).join('')}</ul><div class='mt-3'><a href='#' class='rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700'>Download</a></div></div>`).join('');
|
|||
|
toggleDrawer(true);
|
|||
|
}
|
|||
|
function toggleRisk(v){ document.getElementById('risk').classList.toggle('hidden',!v); document.getElementById('riskAck').checked=false; document.getElementById('riskGo').disabled=true; }
|
|||
|
window.openRisk=function(){ toggleRisk(true); }
|
|||
|
document.getElementById('riskAck').addEventListener('change',e=>{ document.getElementById('riskGo').disabled=!e.target.checked; });
|
|||
|
document.getElementById('riskGo').addEventListener('click',()=>{ alert('Download started (demo)'); toggleRisk(false); });
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|