web-mojo 2.2.9 → 2.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +828 -9
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.es.js +2 -2
  9. package/dist/chunks/{ChatView-DfhhZKoN.js → ChatView-DIzLD6LF.js} +4 -4
  10. package/dist/chunks/{ChatView-DfhhZKoN.js.map → ChatView-DIzLD6LF.js.map} +1 -1
  11. package/dist/chunks/{Collection-G5_fJcpB.js → Collection-BcCcol8p.js} +2 -2
  12. package/dist/chunks/{Collection-G5_fJcpB.js.map → Collection-BcCcol8p.js.map} +1 -1
  13. package/dist/chunks/{ContextMenu-CMoik8q6.js → ContextMenu-DYLw4o0S.js} +2 -2
  14. package/dist/chunks/{ContextMenu-CMoik8q6.js.map → ContextMenu-DYLw4o0S.js.map} +1 -1
  15. package/dist/chunks/{ListView-DoF-Sr56.js → ListView-PvjChHYI.js} +2 -2
  16. package/dist/chunks/{ListView-DoF-Sr56.js.map → ListView-PvjChHYI.js.map} +1 -1
  17. package/dist/chunks/{MetricsMiniChartWidget-Cg5Hyx28.js → MetricsMiniChartWidget-DL5stA6A.js} +31 -9
  18. package/dist/chunks/MetricsMiniChartWidget-DL5stA6A.js.map +1 -0
  19. package/dist/chunks/{MetricsMiniChartWidget-DNIU3bL-.js → MetricsMiniChartWidget-vXr5pxpm.js} +2 -2
  20. package/dist/chunks/{MetricsMiniChartWidget-DNIU3bL-.js.map → MetricsMiniChartWidget-vXr5pxpm.js.map} +1 -1
  21. package/dist/chunks/{TokenManager-DiQfilqw.js → TokenManager-DC1jXSK4.js} +3 -3
  22. package/dist/chunks/{TokenManager-DiQfilqw.js.map → TokenManager-DC1jXSK4.js.map} +1 -1
  23. package/dist/chunks/version-CAOxcUyu.js +2 -0
  24. package/dist/chunks/version-CAOxcUyu.js.map +1 -0
  25. package/dist/chunks/{version-CHVtoVR0.js → version-CM7fRavU.js} +4 -4
  26. package/dist/chunks/version-CM7fRavU.js.map +1 -0
  27. package/dist/docit.cjs.js +1 -1
  28. package/dist/docit.es.js +4 -4
  29. package/dist/index.cjs.js +1 -1
  30. package/dist/index.es.js +8 -8
  31. package/dist/lightbox.cjs.js +1 -1
  32. package/dist/lightbox.es.js +1 -1
  33. package/dist/map.es.js +1 -1
  34. package/dist/timeline.es.js +2 -2
  35. package/package.json +1 -1
  36. package/dist/chunks/MetricsMiniChartWidget-Cg5Hyx28.js.map +0 -1
  37. package/dist/chunks/version-BlWcQoar.js +0 -2
  38. package/dist/chunks/version-BlWcQoar.js.map +0 -1
  39. package/dist/chunks/version-CHVtoVR0.js.map +0 -1
package/dist/admin.es.js CHANGED
@@ -1,16 +1,16 @@
1
- import { P as Page, U as User, e as UserDataView, g as UserDeviceList, i as UserDeviceLocationList, C as ContextMenu, d as UserForms, c as UserList, a as Group, G as GroupList, b as GroupForms, f as UserDevice } from "./chunks/ContextMenu-CMoik8q6.js";
1
+ import { P as Page, U as User, e as UserDataView, g as UserDeviceList, i as UserDeviceLocationList, C as ContextMenu, d as UserForms, c as UserList, a as Group, G as GroupList, b as GroupForms, f as UserDevice } from "./chunks/ContextMenu-DYLw4o0S.js";
2
2
  import { V as View, M as MOJOUtils, r as rest } from "./chunks/Rest-ChN4Ntac.js";
3
3
  import "./chunks/WebSocketClient-bLYhu2Wv.js";
4
4
  import { D as Dialog$1 } from "./chunks/Dialog-Cu_Dx46k.js";
5
5
  import { W } from "./chunks/Dialog-Cu_Dx46k.js";
6
- import { M as MetricsChart, c as MetricsMiniChartWidget, P as PieChart } from "./chunks/MetricsMiniChartWidget-Cg5Hyx28.js";
7
- import { aj as MemberList, T as TableView, B as IncidentEventList, ap as PushDeviceList, ai as LogList, c as TabView, b as TablePage, M as Member, ak as MemberForms, ay as GeoLocatedIP, az as GeoLocatedIPList, aB as TicketList, J as IncidentList, a0 as IncidentStats, V as IncidentHistoryList, U as IncidentHistory, H as Incident, C as ChatView, K as IncidentForms, I as IncidentEvent, G as IncidentEventForms, aD as TicketNoteList, aC as TicketNote, aA as Ticket, aE as TicketForms, aF as TicketCategories, W as RuleSet, a2 as MatchByOptions, a1 as BundleByOptions, _ as RuleList, X as RuleSetList, k as EmailDomainForms, j as EmailDomainList, E as EmailDomain, n as MailboxForms, m as MailboxList, l as Mailbox, s as EmailTemplate, u as EmailTemplateForms, t as EmailTemplateList, o as SentMessage, q as SentMessageList, av as PushDeliveryList, aw as PushConfigForms, at as PushConfigList, ax as PushTemplateForms, ar as PushTemplateList, a6 as Job, ac as JobEventList, aa as JobLogList, a8 as JobForms, a7 as JobList, af as JobRunnerList, ad as JobsEngineStats, ag as JobRunnerForms, ae as JobRunner, ah as Log, al as MetricsPermission, an as MetricsForms, am as MetricsPermissionList, x as FileManagerForms, w as FileManagerList, y as File, A as FileForms, z as FileList, i as S3BucketForms, h as S3BucketList } from "./chunks/ChatView-DfhhZKoN.js";
6
+ import { M as MetricsChart, c as MetricsMiniChartWidget, P as PieChart } from "./chunks/MetricsMiniChartWidget-DL5stA6A.js";
7
+ import { aj as MemberList, T as TableView, B as IncidentEventList, ap as PushDeviceList, ai as LogList, c as TabView, b as TablePage, M as Member, ak as MemberForms, ay as GeoLocatedIP, az as GeoLocatedIPList, aB as TicketList, J as IncidentList, a0 as IncidentStats, V as IncidentHistoryList, U as IncidentHistory, H as Incident, C as ChatView, K as IncidentForms, I as IncidentEvent, G as IncidentEventForms, aD as TicketNoteList, aC as TicketNote, aA as Ticket, aE as TicketForms, aF as TicketCategories, W as RuleSet, a2 as MatchByOptions, a1 as BundleByOptions, _ as RuleList, X as RuleSetList, k as EmailDomainForms, j as EmailDomainList, E as EmailDomain, n as MailboxForms, m as MailboxList, l as Mailbox, s as EmailTemplate, u as EmailTemplateForms, t as EmailTemplateList, o as SentMessage, q as SentMessageList, av as PushDeliveryList, aw as PushConfigForms, at as PushConfigList, ax as PushTemplateForms, ar as PushTemplateList, a6 as Job, ac as JobEventList, aa as JobLogList, a8 as JobForms, a7 as JobList, af as JobRunnerList, ad as JobsEngineStats, ag as JobRunnerForms, ae as JobRunner, ah as Log, al as MetricsPermission, an as MetricsForms, am as MetricsPermissionList, x as FileManagerForms, w as FileManagerList, y as File, A as FileForms, z as FileList, i as S3BucketForms, h as S3BucketList } from "./chunks/ChatView-DIzLD6LF.js";
8
8
  import DataView from "./chunks/DataView-i7kpJjVQ.js";
9
9
  import { F as FormView, a as applyFileDropMixin } from "./chunks/FormView-B_90L1RY.js";
10
10
  import { MapView } from "./map.es.js";
11
- import { M as Model, C as Collection } from "./chunks/Collection-G5_fJcpB.js";
11
+ import { M as Model, C as Collection } from "./chunks/Collection-BcCcol8p.js";
12
12
  import { L as LightboxGallery, P as PDFViewer } from "./chunks/PDFViewer--jlqnuVw.js";
13
- import { B, a, V, b, c, d } from "./chunks/version-CHVtoVR0.js";
13
+ import { B, a, V, b, c, d } from "./chunks/version-CM7fRavU.js";
14
14
  class AdminHeaderView extends View {
15
15
  constructor(options = {}) {
16
16
  super({
@@ -2249,6 +2249,795 @@ class GeoLocatedIPTablePage extends TablePage {
2249
2249
  }
2250
2250
  }
2251
2251
  }
2252
+ class MapLibreView extends View {
2253
+ constructor(options = {}) {
2254
+ super({
2255
+ className: "maplibre-view",
2256
+ ...options
2257
+ });
2258
+ this.markers = options.markers || [];
2259
+ this.center = options.center || null;
2260
+ this.zoom = options.zoom || 13;
2261
+ this.height = options.height || 400;
2262
+ this.pitch = options.pitch || 0;
2263
+ this.bearing = options.bearing || 0;
2264
+ this.mapStyle = options.style || "streets";
2265
+ this.showNavigationControl = options.showNavigationControl !== false;
2266
+ this.autoFitBounds = options.autoFitBounds !== false;
2267
+ this.map = null;
2268
+ this.mapMarkers = [];
2269
+ this.pendingMarkers = [...this.markers];
2270
+ this.lineSources = options.lineSources || [];
2271
+ this.pendingLineData = /* @__PURE__ */ new Map();
2272
+ this.template = `
2273
+ <div class="maplibre-container">
2274
+ <div id="maplibre-{{id}}" style="height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;"></div>
2275
+ </div>
2276
+ `;
2277
+ }
2278
+ async onAfterRender() {
2279
+ await this.loadMapLibre();
2280
+ await this.initializeMap();
2281
+ }
2282
+ async loadMapLibre() {
2283
+ if (window.maplibregl) return;
2284
+ const link = document.createElement("link");
2285
+ link.rel = "stylesheet";
2286
+ link.href = "https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css";
2287
+ document.head.appendChild(link);
2288
+ return new Promise((resolve, reject) => {
2289
+ const script = document.createElement("script");
2290
+ script.src = "https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js";
2291
+ script.onload = resolve;
2292
+ script.onerror = reject;
2293
+ document.head.appendChild(script);
2294
+ });
2295
+ }
2296
+ getMapStyle() {
2297
+ const styles = {
2298
+ streets: "https://demotiles.maplibre.org/style.json",
2299
+ dark: {
2300
+ version: 8,
2301
+ sources: {
2302
+ osm: {
2303
+ type: "raster",
2304
+ tiles: ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"],
2305
+ tileSize: 256,
2306
+ attribution: "© OpenStreetMap contributors"
2307
+ }
2308
+ },
2309
+ layers: [{
2310
+ id: "osm",
2311
+ type: "raster",
2312
+ source: "osm"
2313
+ }],
2314
+ glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf"
2315
+ },
2316
+ light: {
2317
+ version: 8,
2318
+ sources: {
2319
+ osm: {
2320
+ type: "raster",
2321
+ tiles: ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"],
2322
+ tileSize: 256,
2323
+ attribution: "© OpenStreetMap contributors"
2324
+ }
2325
+ },
2326
+ layers: [{
2327
+ id: "osm",
2328
+ type: "raster",
2329
+ source: "osm"
2330
+ }],
2331
+ glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf"
2332
+ },
2333
+ satellite: {
2334
+ version: 8,
2335
+ sources: {
2336
+ satellite: {
2337
+ type: "raster",
2338
+ tiles: ["https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"],
2339
+ tileSize: 256,
2340
+ attribution: "© Esri"
2341
+ }
2342
+ },
2343
+ layers: [{
2344
+ id: "satellite",
2345
+ type: "raster",
2346
+ source: "satellite"
2347
+ }],
2348
+ glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf"
2349
+ },
2350
+ terrain: {
2351
+ version: 8,
2352
+ sources: {
2353
+ terrain: {
2354
+ type: "raster",
2355
+ tiles: ["https://a.tile.opentopomap.org/{z}/{x}/{y}.png"],
2356
+ tileSize: 256,
2357
+ attribution: "© OpenTopoMap contributors"
2358
+ }
2359
+ },
2360
+ layers: [{
2361
+ id: "terrain",
2362
+ type: "raster",
2363
+ source: "terrain"
2364
+ }],
2365
+ glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf"
2366
+ }
2367
+ };
2368
+ return styles[this.mapStyle] || styles.streets;
2369
+ }
2370
+ async initializeMap() {
2371
+ const mapElement = this.element.querySelector(`#maplibre-${this.id}`);
2372
+ if (!mapElement || !window.maplibregl) return;
2373
+ let mapCenter = this.center;
2374
+ if (!mapCenter && this.markers.length > 0) {
2375
+ mapCenter = [this.markers[0].lng, this.markers[0].lat];
2376
+ }
2377
+ if (!mapCenter) {
2378
+ mapCenter = [0, 0];
2379
+ }
2380
+ this.map = new window.maplibregl.Map({
2381
+ container: mapElement,
2382
+ style: this.getMapStyle(),
2383
+ center: mapCenter,
2384
+ zoom: this.zoom,
2385
+ pitch: this.pitch,
2386
+ bearing: this.bearing
2387
+ });
2388
+ if (this.showNavigationControl) {
2389
+ this.map.addControl(new window.maplibregl.NavigationControl(), "top-right");
2390
+ }
2391
+ this.map.on("load", () => {
2392
+ const initialMarkers = this.pendingMarkers.length ? this.pendingMarkers : this.markers;
2393
+ this.updateMarkers(initialMarkers);
2394
+ this.lineSources.forEach((source) => this.addLineSource(source));
2395
+ this.pendingLineData.forEach((source) => this.addLineSource(source));
2396
+ this.pendingLineData.clear();
2397
+ });
2398
+ }
2399
+ addMarkers(markers) {
2400
+ if (!this.map || !Array.isArray(markers)) {
2401
+ this.pendingMarkers = Array.isArray(markers) ? markers : [];
2402
+ return;
2403
+ }
2404
+ markers.forEach((markerData) => {
2405
+ const { lng, lat, popup, color, icon, size } = markerData;
2406
+ if (!lng || !lat) return;
2407
+ const el = document.createElement("div");
2408
+ el.className = "maplibre-marker";
2409
+ const markerSize = Number(size) || 30;
2410
+ el.style.width = `${markerSize}px`;
2411
+ el.style.height = `${markerSize}px`;
2412
+ el.style.borderRadius = "50%";
2413
+ el.style.backgroundColor = color || "#3b82f6";
2414
+ el.style.border = "3px solid white";
2415
+ el.style.boxShadow = "0 2px 4px rgba(0,0,0,0.3)";
2416
+ el.style.cursor = "pointer";
2417
+ el.style.display = "flex";
2418
+ el.style.alignItems = "center";
2419
+ el.style.justifyContent = "center";
2420
+ if (icon) {
2421
+ el.innerHTML = `<i class="${icon}" style="color: white;"></i>`;
2422
+ }
2423
+ const marker = new window.maplibregl.Marker({ element: el }).setLngLat([lng, lat]).addTo(this.map);
2424
+ if (popup) {
2425
+ const mapPopup = new window.maplibregl.Popup({ offset: 25 }).setHTML(popup);
2426
+ marker.setPopup(mapPopup);
2427
+ }
2428
+ this.mapMarkers.push(marker);
2429
+ });
2430
+ }
2431
+ fitBounds() {
2432
+ if (!this.map || this.markers.length === 0) return;
2433
+ const bounds = new window.maplibregl.LngLatBounds();
2434
+ this.markers.forEach((marker) => {
2435
+ bounds.extend([marker.lng, marker.lat]);
2436
+ });
2437
+ this.map.fitBounds(bounds, {
2438
+ padding: 50,
2439
+ maxZoom: 15
2440
+ });
2441
+ }
2442
+ updateMarkers(newMarkers) {
2443
+ const markers = Array.isArray(newMarkers) ? newMarkers : [];
2444
+ this.markers = markers;
2445
+ this.pendingMarkers = markers;
2446
+ if (!this.map) return;
2447
+ this.clearMarkers();
2448
+ this.addMarkers(markers);
2449
+ if (this.autoFitBounds && markers.length > 1) {
2450
+ this.fitBounds();
2451
+ } else if (markers.length === 1 && this.map) {
2452
+ this.map.flyTo({
2453
+ center: [markers[0].lng, markers[0].lat],
2454
+ zoom: this.zoom
2455
+ });
2456
+ }
2457
+ }
2458
+ clearMarkers() {
2459
+ this.mapMarkers.forEach((marker) => {
2460
+ marker.remove();
2461
+ });
2462
+ this.mapMarkers = [];
2463
+ }
2464
+ addLineSource({ id, data, paint, layout }) {
2465
+ if (!id || !data) return;
2466
+ const sourceId = `${id}-source`;
2467
+ if (!this.map) {
2468
+ this.pendingLineData.set(id, { id, data, paint, layout });
2469
+ return;
2470
+ }
2471
+ if (this.map.getLayer(id)) {
2472
+ this.map.removeLayer(id);
2473
+ }
2474
+ if (this.map.getSource(sourceId)) {
2475
+ this.map.removeSource(sourceId);
2476
+ }
2477
+ this.map.addSource(sourceId, {
2478
+ type: "geojson",
2479
+ data
2480
+ });
2481
+ this.map.addLayer({
2482
+ id,
2483
+ type: "line",
2484
+ source: sourceId,
2485
+ paint: {
2486
+ "line-color": "#3b82f6",
2487
+ "line-width": 2,
2488
+ "line-opacity": 0.6,
2489
+ ...paint
2490
+ },
2491
+ layout: {
2492
+ "line-cap": "round",
2493
+ "line-join": "round",
2494
+ ...layout
2495
+ }
2496
+ });
2497
+ }
2498
+ updateLineSource(id, { data, paint, layout } = {}) {
2499
+ if (!id || !data) return;
2500
+ const sourceId = `${id}-source`;
2501
+ if (!this.map) {
2502
+ this.pendingLineData.set(id, { id, data, paint, layout });
2503
+ return;
2504
+ }
2505
+ if (this.map.getSource(sourceId)) {
2506
+ this.map.getSource(sourceId).setData(data);
2507
+ if (paint || layout) {
2508
+ if (paint) {
2509
+ Object.entries(paint).forEach(([key, value]) => {
2510
+ this.map.setPaintProperty(id, key, value);
2511
+ });
2512
+ }
2513
+ if (layout) {
2514
+ Object.entries(layout).forEach(([key, value]) => {
2515
+ this.map.setLayoutProperty(id, key, value);
2516
+ });
2517
+ }
2518
+ }
2519
+ } else {
2520
+ this.addLineSource({ id, data, paint, layout });
2521
+ }
2522
+ }
2523
+ setView(lng, lat, zoom = null) {
2524
+ if (!this.map) return;
2525
+ this.map.flyTo({
2526
+ center: [lng, lat],
2527
+ zoom: zoom || this.zoom
2528
+ });
2529
+ }
2530
+ setZoom(zoom) {
2531
+ if (!this.map) return;
2532
+ this.map.setZoom(zoom);
2533
+ }
2534
+ setPitch(pitch) {
2535
+ if (!this.map) return;
2536
+ this.map.setPitch(pitch);
2537
+ }
2538
+ setBearing(bearing) {
2539
+ if (!this.map) return;
2540
+ this.map.setBearing(bearing);
2541
+ }
2542
+ async onBeforeDestroy() {
2543
+ if (this.map) {
2544
+ this.map.remove();
2545
+ this.map = null;
2546
+ }
2547
+ await super.onBeforeDestroy();
2548
+ }
2549
+ }
2550
+ const COUNTRY_CENTROIDS = {
2551
+ "AD": { name: "Andorra", lat: 42.54859834854764, lng: 1.5802243611232873 },
2552
+ "AE": { name: "United Arab Emirates", lat: 24.18250292309135, lng: 54.27920525789581 },
2553
+ "AF": { name: "Afghanistan", lat: 34.13402601376932, lng: 66.59216131095278 },
2554
+ "AG": { name: "Antigua and Barbuda", lat: 17.07146759372967, lng: -61.78530823226373 },
2555
+ "AI": { name: "Anguilla", lat: 18.222874004219086, lng: -63.06008343771806 },
2556
+ "AL": { name: "Albania", lat: 41.14165894891656, lng: 20.061082767269493 },
2557
+ "AM": { name: "Armenia", lat: 40.17841274230679, lng: 45.05490831965259 },
2558
+ "AO": { name: "Angola", lat: -12.167424062667942, lng: 17.651768783079 },
2559
+ "AQ": { name: "Antarctica", lat: -77.16987521415838, lng: -177.56451613408842 },
2560
+ "AR": { name: "Argentina", lat: -35.697270518120085, lng: -64.53238503843076 },
2561
+ "AS": { name: "American Samoa", lat: -14.305711987770538, lng: -170.7007316174498 },
2562
+ "AT": { name: "Austria", lat: 47.631858269895794, lng: 13.797778364631036 },
2563
+ "AU": { name: "Australia", lat: -25.697337673983082, lng: 134.02277170916162 },
2564
+ "AW": { name: "Aruba", lat: 12.515625722992898, lng: -69.97564014284046 },
2565
+ "AZ": { name: "Azerbaijan", lat: 40.3920509942049, lng: 48.634592670644324 },
2566
+ "BA": { name: "Bosnia and Herzegovina", lat: 44.14415356126429, lng: 17.83467240787538 },
2567
+ "BB": { name: "Barbados", lat: 13.183219369337529, lng: -59.557383949150285 },
2568
+ "BD": { name: "Bangladesh", lat: 23.673728665121, lng: 90.43212562608613 },
2569
+ "BE": { name: "Belgium", lat: 50.6182138854095, lng: 4.675010154696485 },
2570
+ "BF": { name: "Burkina Faso", lat: 12.108709036312737, lng: -1.6932816211842325 },
2571
+ "BG": { name: "Bulgaria", lat: 42.82043677302438, lng: 25.251739122561908 },
2572
+ "BH": { name: "Bahrain", lat: 26.04798501537066, lng: 50.540695402276775 },
2573
+ "BI": { name: "Burundi", lat: -3.261251993278643, lng: 29.88518227845293 },
2574
+ "BJ": { name: "Benin", lat: 9.503013199615893, lng: 2.305714528830206 },
2575
+ "BL": { name: "Saint Barthelemy", lat: 17.90561691241738, lng: -62.83051610005156 },
2576
+ "BM": { name: "Bermuda", lat: 32.315067430740726, lng: -64.7458500599169 },
2577
+ "BN": { name: "Brunei Darussalam", lat: 4.543205889917609, lng: 114.6430958360464 },
2578
+ "BO": { name: "Bolivia", lat: -16.7312488393574, lng: -64.45209597511206 },
2579
+ "BQ": { name: "Bonaire", lat: 12.180844982440338, lng: -68.29350445958761 },
2580
+ "BQ": { name: "Saba", lat: 17.632512616389718, lng: -63.23739481909494 },
2581
+ "BQ": { name: "Saint Eustatius", lat: 17.4919042294197, lng: -62.978230589445026 },
2582
+ "BR": { name: "Brazil", lat: -11.524630416426652, lng: -54.355206608256424 },
2583
+ "BS": { name: "Bahamas", lat: 24.72162633646784, lng: -78.07275370060313 },
2584
+ "BT": { name: "Bhutan", lat: 27.42163933959824, lng: 90.46716647173861 },
2585
+ "BV": { name: "Bouvet Island", lat: -54.42316679395248, lng: 3.411969465057627 },
2586
+ "BW": { name: "Botswana", lat: -22.236609002062902, lng: 23.85779956995608 },
2587
+ "BY": { name: "Belarus", lat: 53.46791374543163, lng: 27.964252054715104 },
2588
+ "BZ": { name: "Belize", lat: 17.24252476647155, lng: -88.68273510023441 },
2589
+ "CA": { name: "Canada", lat: 57.550480044655636, lng: -98.41680517868062 },
2590
+ "CC": { name: "Cocos Islands", lat: -12.171249450199545, lng: 96.83688767323002 },
2591
+ "CD": { name: "Congo DRC", lat: -3.338629596207896, lng: 23.419827574282188 },
2592
+ "CF": { name: "Central African Republic", lat: 6.331390033944319, lng: 20.520743419397256 },
2593
+ "CG": { name: "Congo", lat: -0.7294391595233845, lng: 14.879732849491393 },
2594
+ "CH": { name: "Switzerland", lat: 46.73678128684938, lng: 8.286928794895285 },
2595
+ "CI": { name: "Côte d'Ivoire", lat: 7.536779279421307, lng: -5.571710194917734 },
2596
+ "CK": { name: "Cook Islands", lat: -21.222613253399842, lng: -159.78768870952257 },
2597
+ "CL": { name: "Chile", lat: -37.82938283049967, lng: -70.76863431739216 },
2598
+ "CM": { name: "Cameroon", lat: 6.294168487480992, lng: 12.948474142398263 },
2599
+ "CN": { name: "China", lat: 38.07325481105728, lng: 104.69113855849604 },
2600
+ "CO": { name: "Colombia", lat: 4.187753877352739, lng: -72.6445066243485 },
2601
+ "CR": { name: "Costa Rica", lat: 9.863467407406214, lng: -84.14673625701816 },
2602
+ "CU": { name: "Cuba", lat: 21.476176522869448, lng: -79.69817857618705 },
2603
+ "CV": { name: "Cabo Verde", lat: 15.076411518651643, lng: -23.63401005900474 },
2604
+ "CW": { name: "Curacao", lat: 12.199996647939832, lng: -68.96939768599042 },
2605
+ "CX": { name: "Christmas Island", lat: -10.446440802183416, lng: 105.70209512200549 },
2606
+ "CY": { name: "Cyprus", lat: 35.11700416345239, lng: 33.375346009199205 },
2607
+ "CZ": { name: "Czech Republic", lat: 49.74917370930982, lng: 15.383273292023533 },
2608
+ "DE": { name: "Germany", lat: 51.08304539800482, lng: 10.426171427430804 },
2609
+ "DJ": { name: "Djibouti", lat: 11.750235727618804, lng: 42.613496898789506 },
2610
+ "DK": { name: "Denmark", lat: 56.00118817971057, lng: 9.378670542409406 },
2611
+ "DM": { name: "Dominica", lat: 15.429269860940513, lng: -61.360471946942994 },
2612
+ "DO": { name: "Dominican Republic", lat: 18.77954818522993, lng: -70.43495198520012 },
2613
+ "DZ": { name: "Algeria", lat: 28.350969744889056, lng: 2.6558464719769135 },
2614
+ "EC": { name: "Ecuador", lat: -1.5642721388853116, lng: -78.4630326109714 },
2615
+ "EE": { name: "Estonia", lat: 58.648108311231034, lng: 25.916870250633806 },
2616
+ "EG": { name: "Egypt", lat: 26.60517034450628, lng: 30.240135435012338 },
2617
+ "ER": { name: "Eritrea", lat: 15.005533147667684, lng: 39.2672401449901 },
2618
+ "ES": { name: "Spain", lat: 40.365008336683836, lng: -3.6516251409956983 },
2619
+ "ES": { name: "Canarias", lat: 28.297665106525546, lng: -16.53799441021647 },
2620
+ "ET": { name: "Ethiopia", lat: 8.729389557048396, lng: 39.914902886544276 },
2621
+ "FI": { name: "Finland", lat: 65.01578959749911, lng: 25.65738433454702 },
2622
+ "FJ": { name: "Fiji", lat: -17.822470952336204, lng: 177.98144613732626 },
2623
+ "FK": { name: "Falkland Islands", lat: -51.75901312766726, lng: -58.746646363799854 },
2624
+ "FM": { name: "Micronesia", lat: 6.8789448129255435, lng: 158.2291899444527 },
2625
+ "FO": { name: "Faroe Islands", lat: 62.130896281495346, lng: -6.9811060913122835 },
2626
+ "FR": { name: "France", lat: 46.6423682169416, lng: 2.1940236627886227 },
2627
+ "GA": { name: "Gabon", lat: -0.628448459921234, lng: 11.839410898545754 },
2628
+ "GB": { name: "United Kingdom", lat: 53.97844735080214, lng: -2.852943909329258 },
2629
+ "GD": { name: "Grenada", lat: 12.112926656338907, lng: -61.67937937204098 },
2630
+ "GE": { name: "Georgia", lat: 42.17986277737226, lng: 43.378866534112234 },
2631
+ "GF": { name: "French Guiana", lat: 3.857429742497078, lng: -53.32232307156624 },
2632
+ "GG": { name: "Guernsey", lat: 49.45870771378872, lng: -2.576392582891568 },
2633
+ "GH": { name: "Ghana", lat: 7.94530467243628, lng: -1.219233362526581 },
2634
+ "GI": { name: "Gibraltar", lat: 36.14022671336082, lng: -5.345549484594358 },
2635
+ "GL": { name: "Greenland", lat: 74.16847218965994, lng: -42.07567788066985 },
2636
+ "GM": { name: "Gambia", lat: 13.428617959189328, lng: -15.383380385869662 },
2637
+ "GN": { name: "Guinea", lat: 10.255986541378112, lng: -10.986948848040218 },
2638
+ "GP": { name: "Guadeloupe", lat: 16.24420002705553, lng: -61.54382262282755 },
2639
+ "GQ": { name: "Equatorial Guinea", lat: 1.5954643936590733, lng: 10.425456672353823 },
2640
+ "GR": { name: "Greece", lat: 39.42012261727978, lng: 23.110368936161876 },
2641
+ "GS": { name: "South Georgia and South Sandwich Islands", lat: -54.37666443862139, lng: -36.77509575898928 },
2642
+ "GT": { name: "Guatemala", lat: 15.820878515352684, lng: -90.31219349119617 },
2643
+ "GU": { name: "Guam", lat: 13.445430479945276, lng: 144.78024458298802 },
2644
+ "GW": { name: "Guinea-Bissau", lat: 11.980075324820504, lng: -14.980186756910847 },
2645
+ "GY": { name: "Guyana", lat: 4.68211981385056, lng: -58.91352612754667 },
2646
+ "HM": { name: "Heard Island and McDonald Islands", lat: -53.084170035513736, lng: 73.49298560844045 },
2647
+ "HN": { name: "Honduras", lat: 14.740370695750006, lng: -86.49251679006962 },
2648
+ "HR": { name: "Croatia", lat: 44.91192100856702, lng: 16.625761129583374 },
2649
+ "HT": { name: "Haiti", lat: 18.883520486983567, lng: -72.89291379842 },
2650
+ "HU": { name: "Hungary", lat: 47.22527332486294, lng: 19.39620048366142 },
2651
+ "ID": { name: "Indonesia", lat: 0.15591979959037652, lng: 113.96538246847302 },
2652
+ "IE": { name: "Ireland", lat: 53.30489539816495, lng: -8.241128545554096 },
2653
+ "IL": { name: "Israel", lat: 31.513542220043195, lng: 35.027923472437024 },
2654
+ "IM": { name: "Isle of Man", lat: 54.22855301008011, lng: -4.532995055468449 },
2655
+ "IN": { name: "India", lat: 23.586300567746722, lng: 81.17300408530181 },
2656
+ "IO": { name: "British Indian Ocean Territory", lat: -7.323548444385743, lng: 72.43501618476016 },
2657
+ "IQ": { name: "Iraq", lat: 33.105075667527906, lng: 43.832529181056884 },
2658
+ "IR": { name: "Iran", lat: 32.906023742890056, lng: 54.237077001065444 },
2659
+ "IS": { name: "Iceland", lat: 65.12360920205514, lng: -19.05682967106099 },
2660
+ "IT": { name: "Italy", lat: 42.98201127614267, lng: 12.763657166123137 },
2661
+ "JE": { name: "Jersey", lat: 49.215396925724306, lng: -2.1291601162653575 },
2662
+ "JM": { name: "Jamaica", lat: 18.12207889341651, lng: -77.30358894542778 },
2663
+ "JO": { name: "Jordan", lat: 31.387064884449156, lng: 36.95728884547246 },
2664
+ "JP": { name: "Japan", lat: 36.76738832597829, lng: 137.46934234351835 },
2665
+ "KE": { name: "Kenya", lat: 0.6899182318376179, lng: 37.95309411262371 },
2666
+ "KG": { name: "Kyrgyzstan", lat: 41.35698905438358, lng: 74.17532903468319 },
2667
+ "KH": { name: "Cambodia", lat: 12.699186865507775, lng: 105.03973078680701 },
2668
+ "KI": { name: "Kiribati", lat: 1.8676673749241066, lng: -157.39024189323504 },
2669
+ "KM": { name: "Comoros", lat: -11.658861474508491, lng: 43.34826587429403 },
2670
+ "KN": { name: "Saint Kitts and Nevis", lat: 17.314736299587768, lng: -62.74560385895787 },
2671
+ "KP": { name: "North Korea", lat: 40.19198091470839, lng: 127.3379805653744 },
2672
+ "KR": { name: "South Korea", lat: 36.402386712544114, lng: 127.76224551357649 },
2673
+ "KW": { name: "Kuwait", lat: 29.281360965443092, lng: 47.56311109320184 },
2674
+ "KY": { name: "Cayman Islands", lat: 19.311231805620288, lng: -81.25203208977878 },
2675
+ "KZ": { name: "Kazakhstan", lat: 47.641465195176835, lng: 66.3759193479301 },
2676
+ "LA": { name: "Laos", lat: 18.117282736873282, lng: 103.76375899026448 },
2677
+ "LB": { name: "Lebanon", lat: 33.91160170722086, lng: 35.89651946324749 },
2678
+ "LC": { name: "Saint Lucia", lat: 13.895749185129906, lng: -60.9689510935251 },
2679
+ "LI": { name: "Liechtenstein", lat: 47.14627562133036, lng: 9.547674672376376 },
2680
+ "LK": { name: "Sri Lanka", lat: 7.696630939329944, lng: 80.66931169770622 },
2681
+ "LR": { name: "Liberia", lat: 6.52012979398834, lng: -9.258988935497618 },
2682
+ "LS": { name: "Lesotho", lat: -29.60168116924817, lng: 28.24475317864227 },
2683
+ "LT": { name: "Lithuania", lat: 55.29437393417175, lng: 23.946021605013534 },
2684
+ "LU": { name: "Luxembourg", lat: 49.77523454542369, lng: 6.103230338458876 },
2685
+ "LV": { name: "Latvia", lat: 56.813853047554154, lng: 24.693671325654403 },
2686
+ "LY": { name: "Libya", lat: 27.202915771690794, lng: 17.91133392454237 },
2687
+ "MA": { name: "Morocco", lat: 28.687598134979325, lng: -8.817212587250811 },
2688
+ "MC": { name: "Monaco", lat: 43.74798224995656, lng: 7.412820873271196 },
2689
+ "MD": { name: "Moldova", lat: 47.0725674580696, lng: 28.391111865941348 },
2690
+ "ME": { name: "Montenegro", lat: 42.73694835210454, lng: 19.29505087156758 },
2691
+ "MF": { name: "Saint Martin", lat: 18.078012113224464, lng: -63.06678525361946 },
2692
+ "MG": { name: "Madagascar", lat: -19.04163612493041, lng: 46.68493466832544 },
2693
+ "MH": { name: "Marshall Islands", lat: 7.307929900994344, lng: 168.72016025351076 },
2694
+ "MK": { name: "North Macedonia", lat: 41.59402890143112, lng: 21.70998923872772 },
2695
+ "ML": { name: "Mali", lat: 17.168146208584837, lng: -4.346399841781153 },
2696
+ "MM": { name: "Myanmar", lat: 19.901227931399873, lng: 97.08892285397344 },
2697
+ "MN": { name: "Mongolia", lat: 47.08644454604851, lng: 103.3987360327455 },
2698
+ "MP": { name: "Northern Mariana Islands", lat: 15.178063516432115, lng: 145.74119737203134 },
2699
+ "MQ": { name: "Martinique", lat: 14.642697353597645, lng: -61.01432380875083 },
2700
+ "MR": { name: "Mauritania", lat: 20.466731212820022, lng: -10.495079045035716 },
2701
+ "MS": { name: "Montserrat", lat: 16.735363391460357, lng: -62.18693281256255 },
2702
+ "MT": { name: "Malta", lat: 35.890522650899314, lng: 14.441922442508494 },
2703
+ "MU": { name: "Mauritius", lat: -20.28142317475198, lng: 57.56415671066546 },
2704
+ "MV": { name: "Maldives", lat: -0.6065577168009924, lng: 73.10076245140479 },
2705
+ "MW": { name: "Malawi", lat: -13.128986464184024, lng: 34.23441182298881 },
2706
+ "MX": { name: "Mexico", lat: 23.87436068093592, lng: -101.55399731630118 },
2707
+ "MY": { name: "Malaysia", lat: 3.6716608556387857, lng: 114.63330303992869 },
2708
+ "MZ": { name: "Mozambique", lat: -17.525230309488748, lng: 35.208577031290176 },
2709
+ "NA": { name: "Namibia", lat: -21.90858163281473, lng: 18.16451345845268 },
2710
+ "NC": { name: "New Caledonia", lat: -21.33003372660827, lng: 165.50767040438612 },
2711
+ "NE": { name: "Niger", lat: 17.08105392407292, lng: 8.86863247002646 },
2712
+ "NF": { name: "Norfolk Island", lat: -29.037654445046282, lng: 167.95259597483337 },
2713
+ "NG": { name: "Nigeria", lat: 9.61029352034213, lng: 8.147714845256194 },
2714
+ "NI": { name: "Nicaragua", lat: 12.893566631930554, lng: -85.016088327669 },
2715
+ "NL": { name: "Netherlands", lat: 52.134054128923886, lng: 5.554136426128487 },
2716
+ "NO": { name: "Norway", lat: 64.97775882947745, lng: 16.670259272390894 },
2717
+ "NP": { name: "Nepal", lat: 28.300920699755657, lng: 84.1338898313567 },
2718
+ "NR": { name: "Nauru", lat: -0.5221021440668993, lng: 166.92937633139178 },
2719
+ "NU": { name: "Niue", lat: -19.05230921680393, lng: -169.86878106699083 },
2720
+ "NZ": { name: "New Zealand", lat: -43.82765432544426, lng: 170.69035541428696 },
2721
+ "OM": { name: "Oman", lat: 20.7242833183209, lng: 55.841088119829 },
2722
+ "PA": { name: "Panama", lat: 8.439536749576892, lng: -80.14428761482796 },
2723
+ "PE": { name: "Peru", lat: -8.522717984240291, lng: -74.11416196781884 },
2724
+ "PF": { name: "French Polynesia", lat: -17.674684080120677, lng: -149.40041671099763 },
2725
+ "PG": { name: "Papua New Guinea", lat: -7.156912819152135, lng: 144.8348942994695 },
2726
+ "PH": { name: "Philippines", lat: 15.586542251094242, lng: 121.82208941416745 },
2727
+ "PK": { name: "Pakistan", lat: 30.116188371410882, lng: 69.08835090769651 },
2728
+ "PL": { name: "Poland", lat: 52.06848055692473, lng: 19.43573279234468 },
2729
+ "PM": { name: "Saint Pierre and Miquelon", lat: 46.95153913615198, lng: -56.32465353437558 },
2730
+ "PN": { name: "Pitcairn", lat: -24.366121747565458, lng: -128.3149848627581 },
2731
+ "PR": { name: "Puerto Rico", lat: 18.216224086610914, lng: -66.49425339593509 },
2732
+ "PS": { name: "Palestinian Territory", lat: 31.930818736453883, lng: 35.24251184154588 },
2733
+ "PT": { name: "Portugal", lat: 39.67529214702138, lng: -7.933662183874006 },
2734
+ "PW": { name: "Palau", lat: 7.534775852547396, lng: 134.57965086721052 },
2735
+ "PY": { name: "Paraguay", lat: -23.42190559259428, lng: -58.38906357428651 },
2736
+ "QA": { name: "Qatar", lat: 25.318528486425386, lng: 51.19794918743203 },
2737
+ "RE": { name: "Réunion", lat: -21.119825460665105, lng: 55.54393506194689 },
2738
+ "RO": { name: "Romania", lat: 45.82454894397586, lng: 25.094158201563292 },
2739
+ "RS": { name: "Serbia", lat: 44.02679870131969, lng: 20.85677444395745 },
2740
+ "RU": { name: "Russian Federation", lat: 59.039434214106194, lng: 98.6704990698032 },
2741
+ "RW": { name: "Rwanda", lat: -2.014687460047154, lng: 29.919439681764082 },
2742
+ "SA": { name: "Saudi Arabia", lat: 24.136038144757897, lng: 44.600958178225596 },
2743
+ "SB": { name: "Solomon Islands", lat: -9.613104734596515, lng: 160.16475795033884 },
2744
+ "SC": { name: "Seychelles", lat: -4.660002318822744, lng: 55.47250789595527 },
2745
+ "SD": { name: "Sudan", lat: 15.67060230984256, lng: 29.951458283594064 },
2746
+ "SE": { name: "Sweden", lat: 62.73420986108448, lng: 17.062431988004956 },
2747
+ "SG": { name: "Singapore", lat: 1.3528251890006349, lng: 103.81025757634053 },
2748
+ "SH": { name: "Saint Helena", lat: -15.962963318340398, lng: -5.717391620813109 },
2749
+ "SI": { name: "Slovenia", lat: 46.13759229564504, lng: 14.890636899973781 },
2750
+ "SJ": { name: "Svalbard", lat: 78.57318936469626, lng: 16.036378851505283 },
2751
+ "SK": { name: "Slovakia", lat: 48.69808390520484, lng: 19.581015362490966 },
2752
+ "SL": { name: "Sierra Leone", lat: 8.561330384750587, lng: -11.78656695731115 },
2753
+ "SM": { name: "San Marino", lat: 43.942820729097896, lng: 12.461278349581722 },
2754
+ "SN": { name: "Senegal", lat: 14.228861491763402, lng: -14.610875368352305 },
2755
+ "SO": { name: "Somalia", lat: 6.524534573103924, lng: 45.40037867243972 },
2756
+ "SR": { name: "Suriname", lat: 4.098723595920171, lng: -55.855514311561286 },
2757
+ "SS": { name: "South Sudan", lat: 7.657782041763295, lng: 30.3851856901788 },
2758
+ "ST": { name: "Sao Tome and Principe", lat: 0.22744704294793774, lng: 6.606158796857607 },
2759
+ "SV": { name: "El Salvador", lat: 13.758041517055418, lng: -88.85911489238985 },
2760
+ "SX": { name: "Sint Maarten", lat: 18.03942608463078, lng: -63.06883135915303 },
2761
+ "SY": { name: "Syria", lat: 35.09751106058316, lng: 38.5117323139514 },
2762
+ "SZ": { name: "Eswatini", lat: -26.562540935608702, lng: 31.510685746082007 },
2763
+ "TC": { name: "Turks and Caicos Islands", lat: 21.799865427483745, lng: -71.74058946811508 },
2764
+ "TD": { name: "Chad", lat: 15.283493546654503, lng: 18.427113900363025 },
2765
+ "TF": { name: "Juan De Nova Island", lat: -17.06449193630804, lng: 42.74374761089645 },
2766
+ "TF": { name: "French Southern Territories", lat: -49.26329687105643, lng: 69.54686984724839 },
2767
+ "TF": { name: "Glorioso Islands", lat: -11.566224871643417, lng: 47.290948081543384 },
2768
+ "TG": { name: "Togo", lat: 8.660743037717811, lng: 0.8990857571109684 },
2769
+ "TH": { name: "Thailand", lat: 13.66222784745538, lng: 101.08675116214552 },
2770
+ "TJ": { name: "Tajikistan", lat: 38.56933138382972, lng: 70.94215281065698 },
2771
+ "TK": { name: "Tokelau", lat: -9.195174767256544, lng: -171.85265950722743 },
2772
+ "TL": { name: "Timor-Leste", lat: -8.809894630601962, lng: 125.95024049562659 },
2773
+ "TM": { name: "Turkmenistan", lat: 39.06069118001429, lng: 58.4577357627054 },
2774
+ "TN": { name: "Tunisia", lat: 34.08636179565723, lng: 9.65587551697984 },
2775
+ "TO": { name: "Tonga", lat: -21.15927212823853, lng: -175.20415878511247 },
2776
+ "TR": { name: "Turkey", lat: 38.93207363123396, lng: 35.56886764076691 },
2777
+ "TT": { name: "Trinidad and Tobago", lat: 10.415515638298093, lng: -61.37236579976247 },
2778
+ "TV": { name: "Tuvalu", lat: -8.514701506447222, lng: 179.217833977593 },
2779
+ "TZ": { name: "Tanzania", lat: -6.355794440041147, lng: 34.81832206060381 },
2780
+ "UA": { name: "Ukraine", lat: 48.657532515563794, lng: 31.27377208442636 },
2781
+ "UG": { name: "Uganda", lat: 1.2821729218416205, lng: 32.34371768463123 },
2782
+ "UM": { name: "United States Minor Outlying Islands", lat: 19.302045812215958, lng: 166.63800339749642 },
2783
+ "US": { name: "United States", lat: 38.8208089190304, lng: -96.33161660829639 },
2784
+ "UY": { name: "Uruguay", lat: -32.78195043831093, lng: -56.01919523458085 },
2785
+ "UZ": { name: "Uzbekistan", lat: 41.4879065244633, lng: 63.8548297593985 },
2786
+ "VA": { name: "Vatican City", lat: 41.90402351316735, lng: 12.451312917026133 },
2787
+ "VC": { name: "Saint Vincent and the Grenadines", lat: 13.254808122970651, lng: -61.193766354393034 },
2788
+ "VE": { name: "Venezuela", lat: 7.148324760507107, lng: -66.36492135985132 },
2789
+ "VG": { name: "British Virgin Islands", lat: 18.42195819619707, lng: -64.62406519257699 },
2790
+ "VI": { name: "US Virgin Islands", lat: 17.738009708772523, lng: -64.76155341409797 },
2791
+ "VN": { name: "Vietnam", lat: 16.517347170839393, lng: 105.91338832758704 },
2792
+ "VU": { name: "Vanuatu", lat: -15.189132121699332, lng: 166.84912735669738 },
2793
+ "WF": { name: "Wallis and Futuna", lat: -14.283442307826677, lng: -178.12735555777184 },
2794
+ "WS": { name: "Samoa", lat: -13.634252953274622, lng: -172.44107655740137 },
2795
+ "YE": { name: "Yemen", lat: 16.001392616307445, lng: 47.46815793206386 },
2796
+ "YT": { name: "Mayotte", lat: -12.824468416301052, lng: 45.128142327031064 },
2797
+ "ZA": { name: "South Africa", lat: -28.55361930679731, lng: 24.75252746489084 },
2798
+ "ZM": { name: "Zambia", lat: -13.162832953186246, lng: 27.75521363430896 },
2799
+ "ZW": { name: "Zimbabwe", lat: -18.92700121981475, lng: 29.717829640720844 }
2800
+ };
2801
+ class MetricsCountryMapView extends View {
2802
+ constructor(options = {}) {
2803
+ const mapHeight = options.height || 320;
2804
+ super({
2805
+ className: "metrics-country-map-view",
2806
+ ...options
2807
+ });
2808
+ this.endpoint = options.endpoint || "/api/metrics/fetch";
2809
+ this.account = options.account || "global";
2810
+ this.category = options.category || null;
2811
+ this.slugs = options.slugs || null;
2812
+ this.granularity = options.granularity || "days";
2813
+ this.maxCountries = options.maxCountries || 12;
2814
+ this.metricLabel = options.metricLabel || "Events";
2815
+ this.height = mapHeight;
2816
+ this.mapStyle = options.mapStyle || "dark";
2817
+ this.mapOptions = options.mapOptions || {};
2818
+ this.showRoutes = options.showRoutes !== false;
2819
+ this.routeOrigin = options.routeOrigin || { lng: -77.346, lat: 38.958, name: "Reston, VA" };
2820
+ this._refreshing = false;
2821
+ }
2822
+ async getTemplate() {
2823
+ return `
2824
+ <div class="metrics-country-map">
2825
+ <div class="map-container mb-3" data-container="${this.id}-map" style="height:${this.height}px"></div>
2826
+ <div class="map-legend small" data-region="legend"></div>
2827
+ </div>
2828
+ `;
2829
+ }
2830
+ async onInit() {
2831
+ this.statusEl = document.createElement("div");
2832
+ this.statusEl.className = "text-muted small px-3 pb-2";
2833
+ this.element.appendChild(this.statusEl);
2834
+ this.mapView = new MapLibreView({
2835
+ containerId: `${this.id}-map`,
2836
+ height: this.height,
2837
+ style: this.mapStyle,
2838
+ zoom: this.mapOptions.zoom ?? 1.3,
2839
+ center: this.mapOptions.center || [10, 20],
2840
+ pitch: this.mapOptions.pitch ?? 20,
2841
+ bearing: this.mapOptions.bearing ?? 0,
2842
+ showNavigationControl: this.mapOptions.showNavigationControl ?? true,
2843
+ autoFitBounds: this.mapOptions.autoFitBounds ?? false
2844
+ });
2845
+ this.addChild(this.mapView);
2846
+ await this.refresh();
2847
+ }
2848
+ async refresh() {
2849
+ if (this._refreshing) return;
2850
+ this._refreshing = true;
2851
+ this.setStatus("Loading hotspots…");
2852
+ try {
2853
+ const metrics = await this.fetchMetrics();
2854
+ await this.applyMetrics(metrics);
2855
+ this.setStatus("");
2856
+ } catch (error) {
2857
+ console.error("MetricsCountryMapView refresh error", error);
2858
+ this.setStatus("Unable to load country metrics.");
2859
+ } finally {
2860
+ this._refreshing = false;
2861
+ }
2862
+ }
2863
+ async fetchMetrics() {
2864
+ const rest2 = this.getApp()?.rest;
2865
+ if (!rest2) {
2866
+ throw new Error("REST client unavailable");
2867
+ }
2868
+ const params = {
2869
+ account: this.account,
2870
+ granularity: this.granularity,
2871
+ with_labels: true
2872
+ };
2873
+ if (this.category) params.category = this.category;
2874
+ if (this.slugs) {
2875
+ const slugsArray = Array.isArray(this.slugs) ? this.slugs : [this.slugs];
2876
+ params["slugs[]"] = slugsArray;
2877
+ }
2878
+ const response = await rest2.GET(this.endpoint, params);
2879
+ if (!response.success || !response.data?.status) {
2880
+ throw new Error(response.data?.error || "Metrics API error");
2881
+ }
2882
+ return response.data.data;
2883
+ }
2884
+ async applyMetrics(data) {
2885
+ const metrics = data?.data || {};
2886
+ const labels = data?.labels || [];
2887
+ const entries = [];
2888
+ Object.entries(metrics).forEach(([iso, series]) => {
2889
+ const total = series.reduce((sum, value) => sum + (Number(value) || 0), 0);
2890
+ if (!total) return;
2891
+ const centroid = COUNTRY_CENTROIDS[iso.toUpperCase()];
2892
+ if (!centroid) return;
2893
+ entries.push({
2894
+ code: iso.toUpperCase(),
2895
+ total,
2896
+ values: series,
2897
+ centroid
2898
+ });
2899
+ });
2900
+ if (!entries.length) {
2901
+ this.mapView.updateMarkers([]);
2902
+ this.renderLegend([]);
2903
+ this.setStatus("No country data available for the selected range.");
2904
+ return;
2905
+ }
2906
+ entries.sort((a2, b2) => b2.total - a2.total);
2907
+ const topEntries = entries.slice(0, this.maxCountries);
2908
+ const maxValue = topEntries[0]?.total || 1;
2909
+ const markers = topEntries.map((entry) => {
2910
+ const intensity = entry.total / maxValue;
2911
+ const markerSize = Math.round(18 + intensity * 24);
2912
+ return {
2913
+ lng: entry.centroid.lng,
2914
+ lat: entry.centroid.lat,
2915
+ size: markerSize,
2916
+ color: this.getMarkerColor(intensity),
2917
+ popup: `
2918
+ <div class="text-center">
2919
+ <strong>${entry.centroid.name}</strong><br/>
2920
+ <span class="text-muted">${entry.total.toLocaleString()} ${this.metricLabel}</span>
2921
+ </div>
2922
+ `
2923
+ };
2924
+ });
2925
+ if (this.showRoutes && this.routeOrigin?.lng && this.routeOrigin?.lat) {
2926
+ markers.push({
2927
+ lng: this.routeOrigin.lng,
2928
+ lat: this.routeOrigin.lat,
2929
+ size: 26,
2930
+ color: "#0d6efd",
2931
+ icon: "bi bi-broadcast-pin",
2932
+ popup: `
2933
+ <div class="text-center">
2934
+ <strong>${this.routeOrigin.name || "Operations Hub"}</strong><br/>
2935
+ <span class="text-muted">Origin</span>
2936
+ </div>
2937
+ `
2938
+ });
2939
+ }
2940
+ this.mapView.updateMarkers(markers);
2941
+ this.renderLegend(topEntries, labels);
2942
+ if (this.showRoutes) {
2943
+ this.renderRoutes(topEntries, maxValue);
2944
+ }
2945
+ }
2946
+ getMarkerColor(intensity) {
2947
+ const start = [32, 201, 151];
2948
+ const end = [255, 193, 7];
2949
+ const mix = start.map(
2950
+ (value, idx) => Math.round(value + (end[idx] - value) * intensity)
2951
+ );
2952
+ return `rgba(${mix[0]}, ${mix[1]}, ${mix[2]}, 0.9)`;
2953
+ }
2954
+ renderLegend(entries) {
2955
+ const legendEl = this.element.querySelector('[data-region="legend"]');
2956
+ if (!legendEl) return;
2957
+ if (!entries.length) {
2958
+ legendEl.innerHTML = "";
2959
+ return;
2960
+ }
2961
+ const maxValue = entries[0]?.total || 1;
2962
+ const rows = entries.map((entry) => {
2963
+ const percent = (entry.total / maxValue * 100).toFixed(0);
2964
+ return `
2965
+ <div class="d-flex justify-content-between align-items-center py-1 border-top">
2966
+ <div>
2967
+ <span class="fw-semibold">${entry.centroid.name}</span>
2968
+ <span class="text-muted ms-1">(${entry.code})</span>
2969
+ </div>
2970
+ <div class="text-end">
2971
+ <div class="fw-semibold">${entry.total.toLocaleString()}</div>
2972
+ <small class="text-muted">${percent}% of top</small>
2973
+ </div>
2974
+ </div>
2975
+ `;
2976
+ }).join("");
2977
+ legendEl.innerHTML = rows;
2978
+ }
2979
+ renderRoutes(entries, maxValue) {
2980
+ const origin = this.routeOrigin || null;
2981
+ if (!origin || !origin.lng || !origin.lat || !this.mapView) return;
2982
+ const features = entries.map((entry) => ({
2983
+ type: "Feature",
2984
+ geometry: {
2985
+ type: "LineString",
2986
+ coordinates: [
2987
+ [origin.lng, origin.lat],
2988
+ [entry.centroid.lng, entry.centroid.lat]
2989
+ ]
2990
+ },
2991
+ properties: {
2992
+ total: entry.total,
2993
+ intensity: entry.total / maxValue
2994
+ }
2995
+ }));
2996
+ const geojson = {
2997
+ type: "FeatureCollection",
2998
+ features
2999
+ };
3000
+ const paint = {
3001
+ "line-color": [
3002
+ "interpolate",
3003
+ ["linear"],
3004
+ ["get", "intensity"],
3005
+ 0,
3006
+ "rgba(32, 201, 151, 0.6)",
3007
+ 1,
3008
+ "rgba(255, 193, 7, 0.95)"
3009
+ ],
3010
+ "line-width": [
3011
+ "interpolate",
3012
+ ["linear"],
3013
+ ["get", "intensity"],
3014
+ 0,
3015
+ 1.75,
3016
+ 1,
3017
+ 6
3018
+ ],
3019
+ "line-opacity": [
3020
+ "interpolate",
3021
+ ["linear"],
3022
+ ["get", "intensity"],
3023
+ 0,
3024
+ 0.45,
3025
+ 1,
3026
+ 0.95
3027
+ ]
3028
+ };
3029
+ this.mapView.lineSources = this.mapView.lineSources.filter((src) => src.id !== `${this.id}-routes`);
3030
+ this.mapView.updateLineSource(`${this.id}-routes`, {
3031
+ data: geojson,
3032
+ paint
3033
+ });
3034
+ }
3035
+ setStatus(message) {
3036
+ if (!this.statusEl) return;
3037
+ this.statusEl.textContent = message || "";
3038
+ this.statusEl.style.display = message ? "block" : "none";
3039
+ }
3040
+ }
2252
3041
  class IncidentDashboardHeader extends View {
2253
3042
  constructor(options = {}) {
2254
3043
  super({
@@ -2367,6 +3156,23 @@ class IncidentDashboardPage extends Page {
2367
3156
  <div class="col-xl-6 col-lg-12" data-container="incidents-widget"></div>
2368
3157
  </div>
2369
3158
 
3159
+
3160
+ <div class="row g-4 mt-1">
3161
+ <div class="col-12">
3162
+ <div class="card shadow-sm h-100">
3163
+ <div class="card-header border-0 bg-transparent d-flex align-items-center justify-content-between">
3164
+ <div>
3165
+ <h6 class="mb-0 text-uppercase small text-muted">Global Event Hotspots</h6>
3166
+ <span class="text-muted small">Clusters sized by total events</span>
3167
+ </div>
3168
+ <span class="badge bg-info-subtle text-info">Interactive</span>
3169
+ </div>
3170
+ <div class="card-body p-0" data-container="events-country-map"></div>
3171
+ </div>
3172
+ </div>
3173
+ </div>
3174
+
3175
+
2370
3176
  <div class="row g-4 mt-1">
2371
3177
  <div class="col-lg-6">
2372
3178
  <div class="card shadow-sm h-100">
@@ -2489,11 +3295,12 @@ class IncidentDashboardPage extends Page {
2489
3295
  endpoint: "/api/metrics/fetch",
2490
3296
  account: "incident",
2491
3297
  category: "incident_events_by_country",
2492
- granularity: "hours",
2493
- chartType: "bar",
3298
+ granularity: "days",
3299
+ chartType: "line",
2494
3300
  showDateRange: false,
2495
3301
  showMetricsFilter: false,
2496
3302
  height: 220,
3303
+ maxDatasets: 10,
2497
3304
  colors: [
2498
3305
  "rgba(32, 201, 151, 0.85)"
2499
3306
  ],
@@ -2510,11 +3317,12 @@ class IncidentDashboardPage extends Page {
2510
3317
  endpoint: "/api/metrics/fetch",
2511
3318
  account: "incident",
2512
3319
  category: "incident_by_country",
2513
- granularity: "hours",
2514
- chartType: "bar",
3320
+ granularity: "days",
3321
+ chartType: "line",
2515
3322
  showDateRange: false,
2516
3323
  showMetricsFilter: false,
2517
3324
  height: 220,
3325
+ maxDatasets: 10,
2518
3326
  colors: [
2519
3327
  "rgba(255, 193, 7, 0.85)"
2520
3328
  ],
@@ -2526,6 +3334,16 @@ class IncidentDashboardPage extends Page {
2526
3334
  containerId: "incidents-by-country-chart"
2527
3335
  });
2528
3336
  this.addChild(this.incidentsByCountryChart);
3337
+ this.eventsCountryMap = new MetricsCountryMapView({
3338
+ containerId: "events-country-map",
3339
+ category: "incident_events_by_country",
3340
+ account: "incident",
3341
+ maxCountries: 20,
3342
+ metricLabel: "Events",
3343
+ height: 360,
3344
+ mapStyle: "dark"
3345
+ });
3346
+ this.addChild(this.eventsCountryMap);
2529
3347
  const myTicketsCollection = new TicketList({
2530
3348
  params: {
2531
3349
  status: "new"
@@ -2570,6 +3388,7 @@ class IncidentDashboardPage extends Page {
2570
3388
  this.incidentsWidget?.refresh(),
2571
3389
  this.eventsByCountryChart?.refresh(),
2572
3390
  this.incidentsByCountryChart?.refresh(),
3391
+ this.eventsCountryMap?.refresh(),
2573
3392
  this.myTicketsTable?.collection?.fetch(),
2574
3393
  this.highPriorityIncidentsTable?.collection?.fetch()
2575
3394
  ].filter(Boolean);