How We Built the Intelligence Monitor
A practical look at how we built the live dashboard on our site, including the data flow, map setup, and frontend timing choices.
How We Built the Intelligence Monitor
We built a live dashboard that pulls together geography, weather, and market data on our homepage. This article walks through the server-side proxy routes, the Leaflet map setup, and the state handling we used to keep the page responsive.
System Objectives
We aimed for three things:
- Visual density: Show live geography, charts, and data streams without crowding the page.
- Performance: Keep the page fast and avoid layout shifts.
- Resilience: Handle API rate limits and network delays without breaking the view.
Architectural Stack
We chose tools that keep the page light and simple to maintain:
- Nuxt (Vue 3): Serves as the web framework. We use server-side rendering (SSR) for fast initial loads and page hydration.
- Leaflet: Provides a lightweight open-source mapping engine. We styled the map using CartoDB Dark Matter tiles to match our dark aesthetic.
- Vanilla CSS: Handles responsive layouts, grid systems, and animations.
Implementation Stages
1. Data Aggregation and Server-Side API Routing
To protect API keys and avoid client-side rate limits, we created dedicated server-side endpoints in Nuxt (/server/api/dashboard/*). These routes aggregate data from public sources:
- Open-Meteo API: For local weather updates.
- MarineTraffic: For interactive Strait of Hormuz vessel tracking.
- Financial Indicators: Market indexes updated dynamically (subject to disclosures).
- Dynamic Events: Simulated security incidents distributed globally.
2. Client-Side Rendering Strategy
Leaflet requires direct access to the browser window object. In Nuxt, we wrapped the initialization code inside onMounted and dynamically imported the library:
// DataDashboard.vue initialization pattern
onMounted(async () => {
if (process.client) {
const L = await import('leaflet')
// Initialize map container and load CartoDB tile layer
}
})
3. Transition Optimization
To match the dark visual style we wanted, we added a fade-in, zoom, and desaturation transition to the hero background with Nuxt state:
<img src="/images/bg.webp"
class="hero-video transition-all duration-1000 ease-out transform"
:class="showHeroCard ? 'opacity-100 scale-100 grayscale-0 blur-none' : 'opacity-0 scale-105 grayscale blur-md'" />
Lessons Learned
- CORS & Proxying: Proxying external API calls through Nuxt server routes prevents client-side CORS issues.
- Leaflet Hydration: Always initialize mapping tools inside browser-only hooks to prevent Nuxt build errors.
- Polling Intervals: Adjusting update intervals (e.g. to 5 minutes) keeps data fresh without overloading backend resources.
