2024-03-12 18:31:17 +13:00
< html >
< head >
< meta charset = "utf-8" >
2024-03-26 04:07:27 +13:00
< meta name = "viewport" content = "width=device-width, initial-scale=1, maximum-scale=1" >
2026-02-21 02:03:04 +01:00
< title > DL4AX Meshtastic Map< / title >
< meta name = "title" content = "DL4AX Meshtastic Map" >
2024-03-12 18:31:17 +13:00
< meta name = "description" content = "An interactive map of all Meshtastic nodes." >
2024-05-16 20:30:14 +12:00
< link rel = "icon" type = "image/png" href = "/icon.png" / >
2024-03-12 18:31:17 +13:00
<!-- Open Graph / Facebook -->
< meta property = "og:type" content = "website" >
2026-02-21 02:03:04 +01:00
< meta property = "og:url" content = "https://meshmap.dl4ax.radio" >
< meta property = "og:title" content = "DL4AX Meshtastic Map" >
2024-03-23 16:21:14 +13:00
< meta property = "og:description" content = "An interactive map of all Meshtastic nodes." >
2024-03-12 18:31:17 +13:00
<!-- tailwind css -->
2024-05-16 20:30:14 +12:00
< script src = "assets/js/tailwindcss/tailwind-v3.4.3-forms-v0.5.7.js" > < / script >
2024-03-12 18:31:17 +13:00
<!-- leaflet map -->
2024-05-16 20:30:14 +12:00
< link rel = "stylesheet" href = "assets/js/leaflet@1.9.3/dist/leaflet.css" / >
< script src = "assets/js/leaflet@1.9.3/dist/leaflet.js" > < / script >
2024-05-16 20:43:24 +12:00
<!-- leaflet plugins -->
< script src = "assets/js/leaflet-plugins/leaflet.polylineoffset.js" > < / script >
< script src = "assets/js/leaflet-plugins/leaflet.geometryutil.js" > < / script >
< script src = "assets/js/leaflet-plugins/leaflet-arrowheads.js" > < / script >
<!-- leaflet markercluster -->
< script src = "assets/js/leaflet-plugins/leaflet.markercluster/leaflet.markercluster.js" > < / script >
< link rel = "stylesheet" href = "assets/js/leaflet-plugins/leaflet.markercluster/MarkerCluster.css" / >
< link rel = "stylesheet" href = "assets/js/leaflet-plugins/leaflet.markercluster/MarkerCluster.Default.css" / >
2024-03-12 18:31:17 +13:00
2024-04-17 21:36:41 +12:00
<!-- leaflet groupedlayercontrol -->
2024-05-16 20:43:24 +12:00
< script src = "assets/js/leaflet-plugins/leaflet.groupedlayercontrol/leaflet.groupedlayercontrol.js" > < / script >
< link rel = "stylesheet" href = "assets/js/leaflet-plugins/leaflet.groupedlayercontrol/leaflet.groupedlayercontrol.css" / >
2024-04-17 21:36:41 +12:00
2024-03-12 18:31:17 +13:00
<!-- moment -->
2024-05-16 20:30:14 +12:00
< script src = "assets/js/moment@2.29.1/moment.min.js" > < / script >
2024-03-12 18:31:17 +13:00
2024-03-13 21:02:58 +13:00
<!-- vuejs -->
2024-05-16 20:30:14 +12:00
< script src = "assets/js/vue@3.4.26/dist/vue.global.js" > < / script >
<!-- axios -->
< script src = "assets/js/axios@1.6.8/dist/axios.min.js" > < / script >
2024-03-13 21:02:58 +13:00
2024-03-14 02:39:41 +13:00
<!-- chart js -->
2024-05-16 20:30:14 +12:00
< script src = "assets/js/chart.js@4.4.2/dist/chart.umd.js" > < / script >
2024-09-02 03:26:18 +12:00
< script src = "assets/js/chartjs-adapter-moment/chartjs-adapter-moment.js" > < / script >
2024-03-14 02:39:41 +13:00
2026-02-21 10:27:49 +01:00
<!-- map css style -->
< link rel = "stylesheet" href = "assets/css/styles.css" / >
2024-03-12 18:31:17 +13:00
< / head >
< body class = "h-full bg-gray-200" >
2024-04-07 12:43:02 +12:00
< div id = "app" v-cloak >
2024-03-12 18:31:17 +13:00
2024-03-13 21:02:58 +13:00
< div class = "flex flex-col h-full w-full overflow-hidden" >
< div class = "flex flex-col h-full" >
<!-- header -->
2026-02-21 02:11:39 +01:00
< div class = "flex p-3 h-16" style = "background-color: #67EA94;" >
2024-03-26 04:05:37 +13:00
<!-- close mobile search button -->
< div v-if = "isShowingMobileSearch" class = "my-auto" >
< a @ click = "isShowingMobileSearch = false" href = "javascript:void(0)" class = "rounded-full" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" / >
< / svg >
< / div >
< / a >
< / div >
<!-- icon -->
2026-02-21 02:11:39 +01:00
< div v-if = "!isShowingMobileSearch" class = "sm:block my-auto mr-2 ml-2" >
< img class = "w-10 h-10 rounded border-2 border-[#2C2D3C]" src = "icon.png" / >
2024-03-12 18:31:17 +13:00
< / div >
2024-03-26 04:05:37 +13:00
<!-- search bar -->
< div class = "mx-3 flex-1 relative" :class = "{ 'hidden lg:block': !isShowingMobileSearch }" >
2024-03-17 03:38:06 +13:00
< input v-model = "searchText" type = "text" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" :placeholder = "`Search ${nodes.length} nodes...`" >
< div v-if = "searchText !== ''" class = "absolute z-search bg-white w-full border border-gray-200 rounded-lg shadow-md mt-1 overflow-y-scroll max-h-80 divide-y divide-gray-200" >
< template v-if = "searchedNodes.length > 0" >
2024-09-04 12:32:17 +12:00
< div @ click = "onSearchResultNodeClick(node)" class = "flex space-x-2 p-2 hover:bg-gray-100 cursor-pointer" v-for = "node of searchedNodes" >
< div >
2024-09-07 21:57:23 +12:00
< div class = "flex rounded-full h-12 w-12 text-white shadow" :class = "[ `bg-[${getNodeColour(node.node_id)}]`, `text-[${getNodeTextColour(node.node_id)}]` ]" >
2024-09-04 12:32:17 +12:00
< div class = "mx-auto my-auto drop-shadow-sm" > {{ node.short_name }}< / div >
< / div >
< / div >
< div >
< div class = "text-gray-900" :class = "{ 'text-red-500': node.latitude == null || node.longitude == null }" > {{ node.long_name !== '' ? node.long_name : "-" }}< / div >
< div class = "flex space-x-1 text-sm text-gray-700" >
< div > {{ node.node_id_hex }} / {{ node.node_id }}< / div >
< / div >
2024-03-17 03:38:06 +13:00
< / div >
< / div >
2024-09-04 12:37:51 +12:00
< div v-if = "searchedNodes.length === 500" class = "text-gray-500 text-sm px-2 py-1" >
Only the first 500 results are shown.
< / div >
2024-03-17 03:38:06 +13:00
< / template >
< template v-else >
< div class = "p-2" >
No results found...
< / div >
< / template >
< / div >
< / div >
2024-03-26 04:05:37 +13:00
<!-- header action buttons -->
< div v-if = "!isShowingMobileSearch" class = "flex my-auto ml-auto mr-0 sm:mr-2 space-x-1 sm:space-x-2" >
< a @ click = "isShowingMobileSearch = true" href = "javascript:void(0)" class = "tooltip rounded-full block lg:hidden" >
2024-03-13 21:02:58 +13:00
< div id = "search-button" class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" > < / path >
< path d = "M21 21l-6 -6" > < / path >
< / svg >
< / div >
< div class = "hidden sm:block" >
< span class = "tooltip-text" > Search< / span >
< / div >
< / a >
2026-02-21 02:11:39 +01:00
< a @ click = "isShowingHardwareModels = !isShowingHardwareModels" href = "javascript:void(0)" class = "tooltip rounded-full hidden sm:block" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "M10.5 1.5H8.25A2.25 2.25 0 0 0 6 3.75v16.5a2.25 2.25 0 0 0 2.25 2.25h7.5A2.25 2.25 0 0 0 18 20.25V3.75a2.25 2.25 0 0 0-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3" / >
< / svg >
< / div >
< div class = "hidden sm:block" >
< span class = "tooltip-text" > Devices< / span >
< / div >
< / a >
2024-03-23 17:13:49 +13:00
< a @ click = "isShowingSettings = true" href = "javascript:void(0)" class = "tooltip rounded-full" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" / >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" / >
< / svg >
< / div >
< div class = "hidden sm:block" >
< span class = "tooltip-text" > Settings< / span >
< / div >
< / a >
2024-03-13 21:02:58 +13:00
< a href = "#" class = "tooltip rounded-full" onclick = "reload()" >
< div id = "reload-button" class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M19.933 13.041a8 8 0 1 1 -9.925 -8.788c3.899 -1 7.935 1.007 9.425 4.747" > < / path >
< path d = "M20 4v5h-5" > < / path >
< / svg >
< / div >
< div class = "hidden sm:block" >
< span class = "tooltip-text" > Reload< / span >
< / div >
< / a >
< / div >
2024-03-26 04:05:37 +13:00
2024-03-12 18:31:17 +13:00
< / div >
2024-03-13 21:02:58 +13:00
<!-- map -->
< div id = "map" style = "width:100%;height:100%;" > < / div >
2024-03-12 18:31:17 +13:00
2024-03-13 21:02:58 +13:00
< / div >
2024-03-12 18:31:17 +13:00
< / div >
2024-03-13 21:02:58 +13:00
<!-- hardware models sidebar -->
2024-03-13 23:06:51 +13:00
< div class = "relative z-sidebar" role = "dialog" aria-modal = "true" >
<!-- overlay -->
< transition
enter-active-class="transition-opacity duration-300 ease-linear"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity duration-300 ease-linear"
leave-from-class="opacity-100"
leave-to-class="opacity-0">
< div v-show = "isShowingHardwareModels" @ click = "isShowingHardwareModels = !isShowingHardwareModels" class = "fixed inset-0 bg-gray-900 bg-opacity-75" > < / div >
< / transition >
<!-- sidebar -->
< transition
enter-active-class="transition duration-300 ease-in-out transform"
enter-from-class="-translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="transition duration-300 ease-in-out transform"
leave-from-class="translate-x-0"
leave-to-class="-translate-x-full">
< div v-show = "isShowingHardwareModels" class = "fixed top-0 left-0 bottom-0" >
< div class = "w-screen max-w-md overflow-hidden" >
2024-03-14 19:52:06 +13:00
< div class = "flex h-full flex-col bg-white shadow-xl" >
2024-03-13 23:06:51 +13:00
<!-- slideover header -->
< div class = "p-2 border-b border-gray-200 shadow" >
< div class = "flex items-start justify-between" >
< div >
< h2 class = "font-bold" > Meshtastic Devices< / h2 >
< h3 class = "text-sm" > Ordered by most popular< / h3 >
< / div >
< div class = "my-auto ml-3 flex h-7 items-center" >
< a href = "javascript:void(0)" class = "rounded-full" @ click = "isShowingHardwareModels = !isShowingHardwareModels" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M18 6l-12 12" > < / path >
< path d = "M6 6l12 12" > < / path >
< / svg >
< / div >
< / a >
2024-03-12 18:31:17 +13:00
< / div >
< / div >
2024-03-13 23:06:51 +13:00
< / div >
2024-03-12 18:31:17 +13:00
2024-03-13 23:06:51 +13:00
<!-- list of hardware models -->
< ul role = "list" class = "flex-1 divide-y divide-gray-200 overflow-y-auto" >
< li v-for = "hardwareModel of hardwareModelStats" >
< div class = "group relative flex items-center" >
< a href = "#" class = "block flex-1 px-4 py-2" >
< div class = "absolute inset-0 group-hover:bg-gray-100" aria-hidden = "true" > < / div >
< div class = "relative flex min-w-0 flex-1 items-center" >
< span class = "relative inline-block flex-shrink-0 mr-4" >
2024-05-16 20:30:14 +12:00
< img class = "h-20 w-20 rounded object-contain" :src = "`/images/devices/${hardwareModel.hardware_model_name}.png`" alt = "" onerror = "if(this.src != '/images/no_image.png') this.src = '/images/no_image.png';" >
2024-03-13 23:06:51 +13:00
< / span >
< div class = "truncate" >
< p class = "truncate text-sm font-medium text-gray-900" > {{ hardwareModel.hardware_model_name }}< / p >
< p class = "truncate text-sm text-gray-500" > {{ hardwareModel.count }} nodes on the map< / p >
2024-03-13 21:02:58 +13:00
< / div >
2024-03-13 23:06:51 +13:00
< / div >
< / a >
< / div >
< / li >
< / ul >
2024-03-13 21:02:58 +13:00
2024-03-12 18:31:17 +13:00
< / div >
< / div >
< / div >
2024-03-13 23:06:51 +13:00
< / transition >
2024-03-12 18:31:17 +13:00
< / div >
2024-03-13 21:02:58 +13:00
2024-03-14 00:12:42 +13:00
<!-- node info sidebar -->
< div class = "relative z-sidebar" role = "dialog" aria-modal = "true" >
<!-- overlay -->
< transition
enter-active-class="transition-opacity duration-300 ease-linear"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity duration-300 ease-linear"
leave-from-class="opacity-100"
leave-to-class="opacity-0">
< div v-show = "selectedNode != null" @ click = "selectedNode = null" class = "fixed inset-0 bg-gray-900 bg-opacity-75" > < / div >
< / transition >
<!-- sidebar -->
< transition
enter-active-class="transition duration-300 ease-in-out transform"
enter-from-class="-translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="transition duration-300 ease-in-out transform"
leave-from-class="translate-x-0"
leave-to-class="-translate-x-full">
< div v-show = "selectedNode != null" class = "fixed top-0 left-0 bottom-0" >
< div v-if = "selectedNode != null" class = "w-screen max-w-md overflow-hidden" >
2024-03-14 19:56:19 +13:00
< div class = "flex h-full flex-col bg-white shadow-xl" >
2024-03-14 00:12:42 +13:00
<!-- slideover header -->
< div class = "p-2 border-b border-gray-200 shadow" >
2024-08-28 01:42:44 +12:00
< div class = "flex" >
2024-08-28 02:17:05 +12:00
< div class = "my-auto mr-2" >
2024-09-07 21:57:23 +12:00
< div class = "flex rounded-full h-12 w-12 text-white shadow" :class = "[ `bg-[${getNodeColour(selectedNode.node_id)}]`, `text-[${getNodeTextColour(selectedNode.node_id)}]` ]" >
2024-09-04 12:32:17 +12:00
< div class = "mx-auto my-auto drop-shadow-sm" > {{ selectedNode.short_name }}< / div >
2024-08-28 01:42:44 +12:00
< / div >
< / div >
2024-08-28 02:17:05 +12:00
< div class = "my-auto mr-auto" >
2024-03-14 00:12:42 +13:00
< h2 class = "font-bold" > Node Info< / h2 >
< h3 class = "text-sm" > {{ selectedNode.long_name }}< / h3 >
< / div >
2024-08-28 02:02:28 +12:00
< div class = "my-auto ml-2 flex h-7 items-center space-x-2" >
< a href = "javascript:void(0)" class = "rounded-full" @ click = "copyShareLinkForNode(selectedNode.node_id)" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg xmlns = "http://www.w3.org/2000/svg" fill = "currentColor" viewBox = "0 0 256 256" class = "size-6" >
< path d = "M216,112v96a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V112A16,16,0,0,1,56,96H80a8,8,0,0,1,0,16H56v96H200V112H176a8,8,0,0,1,0-16h24A16,16,0,0,1,216,112ZM93.66,69.66,120,43.31V136a8,8,0,0,0,16,0V43.31l26.34,26.35a8,8,0,0,0,11.32-11.32l-40-40a8,8,0,0,0-11.32,0l-40,40A8,8,0,0,0,93.66,69.66Z" > < / path >
< / svg >
< / div >
< / a >
2024-03-14 00:12:42 +13:00
< a href = "javascript:void(0)" class = "rounded-full" @ click = "selectedNode = null" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M18 6l-12 12" > < / path >
< path d = "M6 6l12 12" > < / path >
< / svg >
< / div >
< / a >
< / div >
< / div >
< / div >
2024-03-14 19:56:19 +13:00
< div class = "overflow-y-auto" >
2024-09-04 12:59:41 +12:00
<!-- no position banner -->
< div v-if = "findNodeMarkerById(selectedNode.node_id) == null" class = "flex bg-orange-500 text-white p-2" >
< div class = "my-auto mr-2" >
< svg xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" fill = "currentColor" class = "size-6" >
< path fill-rule = "evenodd" d = "m11.54 22.351.07.04.028.016a.76.76 0 0 0 .723 0l.028-.015.071-.041a16.975 16.975 0 0 0 1.144-.742 19.58 19.58 0 0 0 2.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 0 0-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 0 0 2.682 2.282 16.975 16.975 0 0 0 1.145.742ZM12 13.5a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" clip-rule = "evenodd" / >
< / svg >
< / div >
< div class = "my-auto" > This node has not reported a position.< / div >
< / div >
2024-03-14 19:56:19 +13:00
< div class = "flex flex-col my-2" >
< div class = "mx-auto" >
2024-05-16 20:30:14 +12:00
< img class = "h-48 w-48 rounded object-contain" :src = "`/images/devices/${selectedNode.hardware_model_name}.png`" alt = "" onerror = "if(this.src != '/images/no_image.png') this.src = '/images/no_image.png';" >
2024-03-14 19:56:19 +13:00
< / div >
2024-03-14 00:12:42 +13:00
< / div >
2024-07-06 01:21:29 +12:00
<!-- action buttons -->
< div class = "flex space-x-6 p-2 justify-center" >
<!-- sent messages -->
< a target = "_blank" :href = "`/api/v1/text-messages/embed?from=${selectedNode.node_id}`" class = "flex flex-col" title = "Messages sent from this Node" >
< div class = "flex mx-auto mb-1" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" class = "size-6" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "m15 11.25-3-3m0 0-3 3m3-3v7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" / >
< / svg >
< / div >
< / div >
< div class = "mx-auto text-sm font-medium text-gray-900" > Sent Msgs< / div >
< / a >
<!-- received messages -->
< a target = "_blank" :href = "`/api/v1/text-messages/embed?to=${selectedNode.node_id}`" class = "flex flex-col" title = "Messages sent to this Node" >
< div class = "flex mx-auto mb-1" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" class = "size-6" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "m9 12.75 3 3m0 0 3-3m-3 3v-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" / >
< / svg >
< / div >
< / div >
< div class = "mx-auto text-sm font-medium text-gray-900" > Received Msgs< / div >
< / a >
<!-- gated messages -->
< a target = "_blank" :href = "`/api/v1/text-messages/embed?gateway_id=${selectedNode.node_id}`" class = "flex flex-col" title = "Messages gated to MQTT by this Node" >
< div class = "flex mx-auto mb-1" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" class = "size-6" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" / >
< / svg >
< / div >
< / div >
< div class = "mx-auto text-sm font-medium text-gray-900" > Gated Msgs< / div >
< / a >
< / div >
2024-03-14 19:56:19 +13:00
<!-- details -->
< div >
< div class = "bg-gray-200 p-2 font-semibold" > Details< / div >
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
2024-09-02 12:37:27 +12:00
<!-- id -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > ID< / div >
< div class = "ml-auto text-sm text-gray-700" > {{ selectedNode.node_id }}< / div >
< / li >
<!-- hex id -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Hex ID< / div >
< div class = "ml-auto text-sm text-gray-700" > {{ selectedNode.node_id_hex }}< / div >
< / li >
2024-09-02 12:18:29 +12:00
<!-- role -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Role< / div >
< div class = "ml-auto text-sm text-gray-700" > {{ selectedNode.role_name }}< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-09-02 12:18:29 +12:00
<!-- hardware -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Hardware< / div >
< div class = "ml-auto text-sm text-gray-700" > {{ selectedNode.hardware_model_name }}< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-03-14 00:12:42 +13:00
2024-09-02 12:18:29 +12:00
<!-- firmware version -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Firmware< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.firmware_version" > {{ selectedNode.firmware_version }}< / span >
< span v-else > ???< / span >
2024-04-02 11:23:18 +13:00
< / div >
< / li >
2024-03-14 19:56:19 +13:00
< / ul >
< / div >
2024-03-14 00:12:42 +13:00
2024-04-07 20:24:15 +12:00
<!-- position -->
< div >
2024-08-04 19:12:51 +02:00
< div @ click . stop class = "flex bg-gray-200 p-2 font-semibold" >
< div class = "my-auto" > Position< / div >
< div class = "ml-auto" >
2024-08-20 18:47:50 +12:00
< button @ click = "showNodePositionHistory(selectedNode.node_id)" type = "button" class = "rounded bg-white px-2 py-1 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50" >
Show History
2024-08-04 19:12:51 +02:00
< / button >
< / div >
< / div >
2024-04-07 20:24:15 +12:00
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
2024-09-02 12:26:53 +12:00
<!-- position -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Lat/Long< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.latitude && selectedNode.longitude" > {{ selectedNode.latitude }}, {{ selectedNode.longitude }}< / span >
< span v-else > ???< / span >
2024-04-07 20:24:15 +12:00
< / div >
< / li >
2024-09-02 12:26:53 +12:00
<!-- altitude -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Altitude< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.altitude" > {{ selectedNode.altitude }}m< / span >
< span v-else > ???< / span >
2024-04-07 20:24:15 +12:00
< / div >
< / li >
< / ul >
< / div >
2024-06-07 00:33:45 +12:00
<!-- device metrics -->
2024-03-14 19:56:19 +13:00
< div >
2024-09-04 11:39:23 +12:00
< div class = "flex bg-gray-200 p-2 font-semibold" >
< div class = "my-auto" > Device Metrics< / div >
< div class = "my-auto ml-auto" >
2024-09-04 12:04:11 +12:00
< select v-model = "deviceMetricsTimeRange" class = "block w-full rounded-md border-0 py-0.5 pl-2 pr-8 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-blue-500 sm:text-sm sm:leading-6" >
2024-09-04 11:39:23 +12:00
< option value = "1d" > 1 Day< / option >
< option value = "3d" > 3 Days< / option >
< option value = "7d" > 7 Days< / option >
2025-03-09 09:40:42 +01:00
< option value = "30d" > 30 Days< / option >
2024-09-04 11:39:23 +12:00
< / select >
< / div >
< / div >
2024-03-14 19:56:19 +13:00
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
2024-09-02 03:26:18 +12:00
<!-- device metrics chart -->
2024-03-14 19:56:19 +13:00
< li >
2024-09-02 03:26:18 +12:00
< div class = "px-4 py-2" >
< div class = "w-full" >
< canvas id = "deviceMetricsChart" style = "height:150px;" > < / canvas >
< div class = "flex" >
< div class = "mx-auto flex space-x-2" >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-blue-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Battery Level< / div >
< / div >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-green-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Channel Utilization< / div >
< / div >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-orange-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Air Util TX< / div >
< / div >
2024-03-14 19:56:19 +13:00
< / div >
2024-03-14 02:39:41 +13:00
< / div >
2024-03-14 00:12:42 +13:00
< / div >
< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-09-02 03:26:18 +12:00
<!-- battery level -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Battery Level< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.battery_level" >
< span v-if = "selectedNode.battery_level > 100" > Plugged In< / span >
< span v-else > {{ selectedNode.battery_level }}%< / span >
< / span >
< span v-else > ???< / span >
2024-03-14 00:12:42 +13:00
< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-09-02 03:26:18 +12:00
<!-- voltage -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Voltage< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.voltage" > {{ Number(selectedNode.voltage).toFixed(2) }}V< / span >
< span v-else > ???< / span >
2024-03-14 00:12:42 +13:00
< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-09-02 03:26:18 +12:00
<!-- channel utilization -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Channel Utilization< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.channel_utilization" > {{ Number(selectedNode.channel_utilization).toFixed(2) }}%< / span >
< span v-else > ???< / span >
2024-03-14 00:12:42 +13:00
< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-03-14 00:12:42 +13:00
2024-09-02 03:26:18 +12:00
<!-- air util tx -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Air Util Tx< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.air_util_tx" > {{ Number(selectedNode.air_util_tx).toFixed(2) }}%< / span >
< span v-else > ???< / span >
< / div >
< / li >
<!-- air util tx -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Uptime< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.uptime_seconds" > {{ formatUptimeSeconds(selectedNode.uptime_seconds) }}< / span >
< span v-else > ???< / span >
2024-04-16 16:32:26 +12:00
< / div >
< / li >
2024-03-14 19:56:19 +13:00
< / ul >
< / div >
2024-03-14 00:12:42 +13:00
2024-06-07 00:51:33 +12:00
<!-- environment metrics -->
< div >
2024-09-08 22:30:22 +12:00
< div class = "flex bg-gray-200 p-2 font-semibold" >
< div class = "my-auto" > Environment Metrics< / div >
< div class = "my-auto ml-auto" >
< select v-model = "environmentMetricsTimeRange" class = "block w-full rounded-md border-0 py-0.5 pl-2 pr-8 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-blue-500 sm:text-sm sm:leading-6" >
< option value = "1d" > 1 Day< / option >
< option value = "3d" > 3 Days< / option >
< option value = "7d" > 7 Days< / option >
2025-03-09 09:40:42 +01:00
< option value = "30d" > 30 Days< / option >
2024-09-08 22:30:22 +12:00
< / select >
< / div >
< / div >
2024-06-07 00:51:33 +12:00
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
2024-09-08 22:30:22 +12:00
<!-- environment metrics chart -->
2024-06-07 00:51:33 +12:00
< li >
2024-09-08 22:30:22 +12:00
< div class = "px-4 py-2" >
< div class = "w-full" >
< canvas id = "environmentMetricsChart" style = "height:150px;" > < / canvas >
< div class = "flex" >
< div class = "mx-auto flex space-x-2" >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-blue-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Temperature< / div >
< / div >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-green-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Humidity< / div >
< / div >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-orange-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Pressure< / div >
< / div >
2025-06-26 22:33:37 +02:00
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-pink-400 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > IAQ< / div >
< / div >
2024-06-07 00:51:33 +12:00
< / div >
< / div >
< / div >
< / div >
< / li >
2024-09-08 22:30:22 +12:00
<!-- temperature -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Temperature< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.temperature" > {{ formatTemperature(selectedNode.temperature) }}< / span >
< span v-else > ???< / span >
2024-06-07 00:51:33 +12:00
< / div >
< / li >
2024-09-08 22:30:22 +12:00
<!-- relative humidity -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Relative Humidity< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.relative_humidity" > {{ Number(selectedNode.relative_humidity).toFixed(0) }}%< / span >
< span v-else > ???< / span >
< / div >
< / li >
<!-- barometric pressure -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Barometric Pressure< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.barometric_pressure" > {{ Number(selectedNode.barometric_pressure).toFixed(1) }}hPa< / span >
< span v-else > ???< / span >
2024-06-07 00:51:33 +12:00
< / div >
< / li >
< / ul >
< / div >
2024-06-08 16:11:24 +12:00
<!-- power metrics -->
< div >
2024-09-04 12:04:11 +12:00
< div class = "flex bg-gray-200 p-2 font-semibold" >
< div class = "my-auto" > Power Metrics< / div >
< div class = "my-auto ml-auto" >
< select v-model = "powerMetricsTimeRange" class = "block w-full rounded-md border-0 py-0.5 pl-2 pr-8 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-blue-500 sm:text-sm sm:leading-6" >
< option value = "1d" > 1 Day< / option >
< option value = "3d" > 3 Days< / option >
< option value = "7d" > 7 Days< / option >
2025-03-09 09:40:42 +01:00
< option value = "30d" > 30 Days< / option >
2024-09-04 12:04:11 +12:00
< / select >
< / div >
< / div >
2024-06-08 16:11:24 +12:00
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
2024-09-02 14:06:58 +12:00
<!-- power metrics chart -->
2024-06-08 16:11:24 +12:00
< li >
2024-09-02 14:06:58 +12:00
< div class = "px-4 py-2" >
< div class = "w-full" >
< canvas id = "powerMetricsChart" style = "height:150px;" > < / canvas >
< div class = "flex" >
< div class = "mx-auto flex space-x-2" >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-blue-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Channel 1< / div >
< / div >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-green-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Channel 2< / div >
< / div >
< div class = "flex mx-auto" >
< div class = "my-auto w-2 h-2 bg-orange-500 rounded-full" > < / div >
< div class = "my-auto ml-1 text-sm text-gray-500" > Channel 3< / div >
< / div >
2024-06-08 16:11:24 +12:00
< / div >
< / div >
< / div >
< / div >
< / li >
2024-09-02 14:06:58 +12:00
<!-- channel 1 -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Channel 1< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNodeLatestPowerMetric" >
< span v-if = "selectedNodeLatestPowerMetric?.ch1_voltage" > {{ Number(selectedNodeLatestPowerMetric.ch1_voltage).toFixed(2) }}V< / span >
< span v-else > ???< / span >
< span v-if = "selectedNodeLatestPowerMetric?.ch1_current" > / {{ Number(selectedNodeLatestPowerMetric.ch1_current).toFixed(2) }}mA< / span >
< / span >
< span v-else > ???< / span >
2024-06-08 16:11:24 +12:00
< / div >
< / li >
2024-09-02 14:06:58 +12:00
<!-- channel 2 -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Channel 2< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNodeLatestPowerMetric" >
< span v-if = "selectedNodeLatestPowerMetric?.ch2_voltage" > {{ Number(selectedNodeLatestPowerMetric.ch2_voltage).toFixed(2) }}V< / span >
< span v-else > ???< / span >
< span v-if = "selectedNodeLatestPowerMetric?.ch2_current" > / {{ Number(selectedNodeLatestPowerMetric.ch2_current).toFixed(2) }}mA< / span >
< / span >
< span v-else > ???< / span >
< / div >
< / li >
<!-- channel 3 -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Channel 3< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNodeLatestPowerMetric" >
< span v-if = "selectedNodeLatestPowerMetric?.ch3_voltage" > {{ Number(selectedNodeLatestPowerMetric.ch3_voltage).toFixed(2) }}V< / span >
< span v-else > ???< / span >
< span v-if = "selectedNodeLatestPowerMetric?.ch3_current" > / {{ Number(selectedNodeLatestPowerMetric.ch3_current).toFixed(2) }}mA< / span >
< / span >
< span v-else > ???< / span >
2024-06-08 16:11:24 +12:00
< / div >
< / li >
< / ul >
< / div >
2024-03-16 19:48:08 +13:00
<!-- mqtt -->
< div >
< div class = "bg-gray-200 p-2" >
< div class = "font-semibold" > MQTT< / div >
2024-03-19 02:33:11 +13:00
< div class = "text-sm text-gray-600" > Topics this node sent packets to< / div >
2024-03-16 19:48:08 +13:00
< / div >
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
< template v-if = "selectedNodeMqttMetrics.length > 0" >
< li v-for = "mqttMetric of selectedNodeMqttMetrics" >
< div class = "relative flex items-center" >
< div class = "block flex-1 px-4 py-2" >
< div class = "relative flex min-w-0 flex-1 items-center" >
< div class = "truncate" >
< p class = "truncate text-sm font-medium text-gray-900" > {{ mqttMetric.mqtt_topic }}< / p >
< div class = "text-sm text-gray-700" > Last packet {{ moment(new Date(mqttMetric.last_packet_at)).fromNow() }}< / div >
< / div >
< / div >
< / div >
< / div >
< / li >
< / template >
< template v-else >
< li >
< div class = "relative flex items-center" >
< div class = "block flex-1 px-4 py-2" >
< div class = "relative flex min-w-0 flex-1 items-center" >
< div class = "truncate" >
< div class = "text-sm text-gray-700" > No packets seen on MQTT< / div >
< / div >
< / div >
< / div >
< / div >
< / li >
< / template >
< / ul >
< / div >
2024-03-19 02:33:11 +13:00
<!-- traceroutes -->
< div >
< div class = "bg-gray-200 p-2" >
< div class = "font-semibold" > Trace Routes< / div >
< div class = "text-sm text-gray-600" > Only 5 most recent are shown< / div >
< / div >
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
< template v-if = "selectedNodeTraceroutes.length > 0" >
< li @ click = "showTraceRoute(traceroute)" v-for = "traceroute of selectedNodeTraceroutes" >
< div class = "relative flex items-center" >
< div class = "block flex-1 px-4 py-2" >
< div class = "relative flex min-w-0 flex-1 items-center" >
< div >
2024-04-16 14:15:25 +12:00
< p class = "text-sm text-gray-900" > < span class = "font-medium" > {{ findNodeById(traceroute.to)?.long_name || '???' }}< / span > to < span class = "font-medium" > {{ findNodeById(traceroute.from)?.long_name || '???' }}< / span > < / p >
< div class = "text-sm text-gray-700" > {{ moment(new Date(traceroute.updated_at)).fromNow() }} - {{ traceroute.route.length }} hops {{ traceroute.channel_id ? `on ${traceroute.channel_id}` : '' }}< / div >
2024-03-19 02:33:11 +13:00
< / div >
< / div >
< / div >
< / div >
< / li >
< / template >
< template v-else >
< li >
< div class = "relative flex items-center" >
< div class = "block flex-1 px-4 py-2" >
< div class = "relative flex min-w-0 flex-1 items-center" >
< div class = "truncate" >
< div class = "text-sm text-gray-700" > No traceroutes seen on MQTT< / div >
< / div >
< / div >
< / div >
< / div >
< / li >
< / template >
< / ul >
< / div >
2024-03-14 19:56:19 +13:00
<!-- other -->
< div >
< div class = "bg-gray-200 p-2 font-semibold" > Other< / div >
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
2024-09-02 04:14:26 +12:00
<!-- first seen -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > First Seen< / div >
< div class = "ml-auto text-sm text-gray-700" > {{ moment(new Date(selectedNode.created_at)).fromNow() }}< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-09-02 04:14:26 +12:00
<!-- last seen -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Last Seen< / div >
< div class = "ml-auto text-sm text-gray-700" > {{ moment(new Date(selectedNode.updated_at)).fromNow() }}< / div >
2024-03-14 19:56:19 +13:00
< / li >
2024-09-02 04:14:26 +12:00
<!-- neighbours updated -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Neighbours Updated< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.neighbours_updated_at" > {{ moment(new Date(selectedNode.neighbours_updated_at)).fromNow() }}< / span >
< span v-else > ???< / span >
2024-03-23 23:57:57 +13:00
< / div >
< / li >
2024-09-02 04:14:26 +12:00
<!-- position updated -->
< li class = "flex p-3" >
< div class = "text-sm font-medium text-gray-900" > Position Updated< / div >
< div class = "ml-auto text-sm text-gray-700" >
< span v-if = "selectedNode.position_updated_at" > {{ moment(new Date(selectedNode.position_updated_at)).fromNow() }}< / span >
< span v-else > ???< / span >
2024-03-23 23:57:57 +13:00
< / div >
< / li >
2024-03-14 19:56:19 +13:00
< / ul >
< / div >
2024-03-14 00:12:42 +13:00
2024-03-14 20:28:50 +13:00
<!-- share -->
< div >
2024-04-04 20:48:24 +13:00
< div class = "flex bg-gray-200 p-2 font-semibold" >
< div class = "my-auto" > Share Link< / div >
< div class = "ml-auto" >
< button @ click = "copyShareLinkForNode(selectedNode.node_id)" type = "button" class = "rounded bg-white px-2 py-1 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50" >
Copy
< / button >
< / div >
< / div >
2024-03-14 20:28:50 +13:00
< ul role = "list" class = "flex-1 divide-y divide-gray-200" >
< li >
< div class = "relative flex items-center" >
< div class = "block flex-1 p-2" >
< div class = "flex space-x-2" >
2024-04-04 20:31:21 +13:00
< input type = "text" readonly class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" :value = "getShareLinkForNode(selectedNode.node_id)" >
2024-03-14 20:28:50 +13:00
< / div >
< / div >
< / div >
< / li >
< / ul >
< / div >
2024-03-14 00:12:42 +13:00
< / div >
< / div >
< / div >
< / div >
< / transition >
< / div >
2024-08-27 21:34:28 +12:00
<!-- traceroute info sidebar -->
2024-03-19 02:33:11 +13:00
< div class = "relative z-sidebar" role = "dialog" aria-modal = "true" >
<!-- overlay -->
< transition
enter-active-class="transition-opacity duration-300 ease-linear"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity duration-300 ease-linear"
leave-from-class="opacity-100"
leave-to-class="opacity-0">
< div v-show = "selectedTraceRoute != null" @ click = "selectedTraceRoute = null" class = "fixed inset-0 bg-gray-900 bg-opacity-75" > < / div >
< / transition >
<!-- sidebar -->
< transition
enter-active-class="transition duration-300 ease-in-out transform"
enter-from-class="-translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="transition duration-300 ease-in-out transform"
leave-from-class="translate-x-0"
leave-to-class="-translate-x-full">
< div v-show = "selectedTraceRoute != null" class = "fixed top-0 left-0 bottom-0" >
< div v-if = "selectedTraceRoute != null" class = "w-screen max-w-md overflow-hidden" >
< div class = "flex h-full flex-col bg-white shadow-xl" >
<!-- slideover header -->
< div class = "p-2 border-b border-gray-200 shadow" >
< div class = "flex items-start justify-between" >
< div >
< h2 class = "font-bold" > Traceroute #{{ selectedTraceRoute.id }}< / h2 >
2024-04-16 14:15:25 +12:00
< h3 class = "text-sm" > {{ moment(new Date(selectedTraceRoute.updated_at)).fromNow() }} - {{ selectedTraceRoute.route.length }} hops {{ selectedTraceRoute.channel_id ? `on ${selectedTraceRoute.channel_id}` : '' }}< / h3 >
2024-03-19 02:33:11 +13:00
< / div >
< div class = "my-auto ml-3 flex h-7 items-center" >
< a href = "javascript:void(0)" class = "rounded-full" @ click = "selectedTraceRoute = null" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M18 6l-12 12" > < / path >
< path d = "M6 6l12 12" > < / path >
< / svg >
< / div >
< / a >
< / div >
< / div >
< / div >
< div class = "overflow-y-auto" >
<!-- details -->
< div class = "p-2" >
2024-09-08 13:34:06 +12:00
< ul role = "list" class = "space-y-3" >
2024-03-19 02:33:11 +13:00
<!-- node that initiated traceroute -->
2024-04-16 14:15:25 +12:00
< li :onclick = "`goToNode(${selectedTraceRoute.to})`" class = "relative flex gap-x-4" >
2024-09-08 13:34:06 +12:00
< div class = "absolute left-0 top-0 flex w-12 justify-center top-3 -bottom-3" >
2024-03-19 02:33:11 +13:00
< div class = "w-px bg-gray-200" > < / div >
< / div >
2024-09-08 13:34:06 +12:00
< div class = "my-auto relative flex flex-none items-center justify-center" >
< div >
< div class = "flex rounded-full h-12 w-12 text-white shadow" :class = "[ `bg-[${getNodeColour(selectedTraceRoute.to)}]`, `text-[${getNodeTextColour(selectedTraceRoute.to)}]` ]" >
< div class = "mx-auto my-auto drop-shadow-sm" > {{ findNodeById(selectedTraceRoute.to)?.short_name ?? "?" }}< / div >
< / div >
< / div >
2024-03-19 02:33:11 +13:00
< / div >
< div class = "flex-auto py-0.5 text-sm leading-5 text-gray-500" >
2024-04-16 14:15:25 +12:00
< div class = "font-medium text-gray-900" > {{ findNodeById(selectedTraceRoute.to)?.long_name || '???' }}< / div >
< div > Hex ID: !{{ Number(selectedTraceRoute.to).toString(16) }}< / div >
2024-03-19 02:33:11 +13:00
< div > Started the traceroute< / div >
< / div >
< / li >
<!-- middleman nodes -->
2024-04-16 14:15:25 +12:00
< li :onclick = "`goToNode(${route})`" v-for = "route of selectedTraceRoute.route" class = "relative flex gap-x-4" >
2024-09-08 13:34:06 +12:00
< div class = "absolute left-0 top-0 flex w-12 justify-center -bottom-3" >
2024-03-19 02:33:11 +13:00
< div class = "w-px bg-gray-200" > < / div >
< / div >
2024-09-08 13:34:06 +12:00
< div class = "my-auto relative flex flex-none items-center justify-center" >
< div >
< div class = "flex rounded-full h-12 w-12 text-white shadow" :class = "[ `bg-[${getNodeColour(route)}]`, `text-[${getNodeTextColour(route)}]` ]" >
< div class = "mx-auto my-auto drop-shadow-sm" > {{ findNodeById(route)?.short_name ?? "?" }}< / div >
< / div >
< / div >
2024-03-19 02:33:11 +13:00
< / div >
< div class = "flex-auto py-0.5 text-sm leading-5 text-gray-500" >
< div class = "font-medium text-gray-900" > {{ findNodeById(route)?.long_name || '???' }}< / div >
< div > Hex ID: !{{ Number(route).toString(16) }}< / div >
< div > Forwarded the packet< / div >
< / div >
< / li >
2024-04-16 14:15:25 +12:00
<!-- node that replied to traceroute -->
2024-04-16 14:34:09 +12:00
< li :onclick = "`goToNode(${selectedTraceRoute.from})`" v-if = "selectedTraceRoute.from" class = "relative flex gap-x-4" >
2024-09-08 13:34:06 +12:00
< div class = "absolute left-0 top-0 flex w-12 justify-center -bottom-3" >
2024-04-15 23:41:16 +01:00
< div class = "w-px bg-gray-200" > < / div >
< / div >
2024-09-08 13:34:06 +12:00
< div class = "my-auto relative flex flex-none items-center justify-center" >
< div >
< div class = "flex rounded-full h-12 w-12 text-white shadow" :class = "[ `bg-[${getNodeColour(selectedTraceRoute.from)}]`, `text-[${getNodeTextColour(selectedTraceRoute.from)}]` ]" >
< div class = "mx-auto my-auto drop-shadow-sm" > {{ findNodeById(selectedTraceRoute.from)?.short_name ?? "?" }}< / div >
< / div >
< / div >
2024-04-15 23:41:16 +01:00
< / div >
< div class = "flex-auto py-0.5 text-sm leading-5 text-gray-500" >
2024-04-16 14:15:25 +12:00
< div class = "font-medium text-gray-900" > {{ findNodeById(selectedTraceRoute.from)?.long_name || '???' }}< / div >
< div > Hex ID: !{{ Number(selectedTraceRoute.from).toString(16) }}< / div >
2024-04-15 23:41:16 +01:00
< div > Replied to traceroute< / div >
< / div >
< / li >
2024-04-16 14:15:25 +12:00
<!-- node that gated traceroute to mqtt -->
2024-04-16 14:34:09 +12:00
< li :onclick = "`goToNode(${selectedTraceRoute.gateway_id})`" v-if = "selectedTraceRoute.gateway_id" class = "relative flex gap-x-4" >
2024-09-08 13:34:06 +12:00
< div class = "absolute left-0 top-0 flex w-12 justify-center h-6" >
2024-03-19 02:33:11 +13:00
< div class = "w-px bg-gray-200" > < / div >
< / div >
2024-09-08 13:34:06 +12:00
< div class = "my-auto relative flex flex-none items-center justify-center" >
< div >
< div class = "flex rounded-full h-12 w-12 text-white shadow" :class = "[ `bg-[${getNodeColour(selectedTraceRoute.gateway_id)}]`, `text-[${getNodeTextColour(selectedTraceRoute.gateway_id)}]` ]" >
< div class = "mx-auto my-auto drop-shadow-sm" > {{ findNodeById(selectedTraceRoute.gateway_id)?.short_name ?? "?" }}< / div >
< / div >
< / div >
2024-03-19 02:33:11 +13:00
< / div >
< div class = "flex-auto py-0.5 text-sm leading-5 text-gray-500" >
< div class = "font-medium text-gray-900" > {{ findNodeById(selectedTraceRoute.gateway_id)?.long_name || '???' }}< / div >
< div > Hex ID: !{{ Number(selectedTraceRoute.gateway_id).toString(16) }}< / div >
< div > Gated the packet to MQTT< / div >
< / div >
< / li >
< / ul >
2024-04-02 21:14:55 +13:00
< / div >
< div >
< div class = "bg-gray-200 p-2 font-semibold" > Raw Data< / div >
< div class = "text-sm text-gray-700" >
< pre class = "bg-gray-100 rounded p-2 overflow-x-auto" > {{ JSON.stringify(selectedTraceRoute, null, 4) }}< / pre >
< / div >
2024-03-19 02:33:11 +13:00
< / div >
< / div >
< / div >
< / div >
< / div >
< / transition >
< / div >
2024-08-27 21:34:28 +12:00
<!-- settings sidebar -->
2024-03-23 17:13:49 +13:00
< div class = "relative z-sidebar" role = "dialog" aria-modal = "true" >
<!-- overlay -->
< transition
enter-active-class="transition-opacity duration-300 ease-linear"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity duration-300 ease-linear"
leave-from-class="opacity-100"
leave-to-class="opacity-0">
< div v-show = "isShowingSettings" @ click = "isShowingSettings = !isShowingSettings" class = "fixed inset-0 bg-gray-900 bg-opacity-75" > < / div >
< / transition >
<!-- sidebar -->
< transition
enter-active-class="transition duration-300 ease-in-out transform"
enter-from-class="-translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="transition duration-300 ease-in-out transform"
leave-from-class="translate-x-0"
leave-to-class="-translate-x-full">
< div v-show = "isShowingSettings" class = "fixed top-0 left-0 bottom-0" >
< div v-if = "isShowingSettings" class = "w-screen max-w-md overflow-hidden" >
< div class = "flex h-full flex-col bg-white shadow-xl" >
<!-- slideover header -->
< div class = "p-2 border-b border-gray-200 shadow" >
< div class = "flex items-start justify-between" >
< div >
< h2 class = "font-bold" > Settings< / h2 >
< h3 class = "text-sm" > Changes are only saved in this browser.< / h3 >
< / div >
< div class = "my-auto ml-3 flex h-7 items-center" >
< a href = "javascript:void(0)" class = "rounded-full" @ click = "isShowingSettings = false" >
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M18 6l-12 12" > < / path >
< path d = "M6 6l12 12" > < / path >
< / svg >
< / div >
< / a >
< / div >
< / div >
< / div >
< div class = "overflow-y-auto divide-y divide-gray-200" >
2024-03-26 03:07:08 +13:00
<!-- configNodesMaxAgeInSeconds -->
< div class = "p-2" >
2024-03-26 03:11:44 +13:00
< label class = "block text-sm font-medium text-gray-900" > Nodes Max Age< / label >
< div class = "text-xs text-gray-600 mb-2" > Nodes not updated within this time are hidden. Reload to update map.< / div >
2024-03-26 03:07:08 +13:00
< select v-model = "configNodesMaxAgeInSeconds" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
< option :value = "null" > Show All< / option >
< option value = "900" > 15 minutes< / option >
< option value = "1800" > 30 minutes< / option >
< option value = "3600" > 1 hour< / option >
< option value = "10800" > 3 hours< / option >
< option value = "21600" > 6 hours< / option >
< option value = "43200" > 12 hours< / option >
< option value = "86400" > 24 hours< / option >
< option value = "172800" > 2 days< / option >
< option value = "259200" > 3 days< / option >
< option value = "345600" > 4 days< / option >
< option value = "432000" > 5 days< / option >
< option value = "518400" > 6 days< / option >
< option value = "604800" > 7 days< / option >
< / select >
< / div >
2024-04-05 11:59:33 +13:00
<!-- configNodesOfflineAgeInSeconds -->
< div class = "p-2" >
< label class = "block text-sm font-medium text-gray-900" > Nodes Offline Age< / label >
< div class = "text-xs text-gray-600 mb-2" > Nodes not updated within this time will show as red icons. Reload to update map.< / div >
< select v-model = "configNodesOfflineAgeInSeconds" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
< option :value = "null" > Don't show as offline< / option >
< option value = "900" > 15 minutes< / option >
< option value = "1800" > 30 minutes< / option >
< option value = "2700" > 45 minutes< / option >
< option value = "3600" > 1 hour< / option >
2024-04-05 12:48:49 +13:00
< option value = "7200" > 2 hours< / option >
2024-04-05 11:59:33 +13:00
< option value = "10800" > 3 hours< / option >
< option value = "21600" > 6 hours< / option >
< option value = "43200" > 12 hours< / option >
< option value = "86400" > 24 hours< / option >
< option value = "172800" > 2 days< / option >
< option value = "259200" > 3 days< / option >
< option value = "345600" > 4 days< / option >
< option value = "432000" > 5 days< / option >
< option value = "518400" > 6 days< / option >
< option value = "604800" > 7 days< / option >
< / select >
< / div >
2024-04-05 15:08:31 +13:00
<!-- configWaypointsMaxAgeInSeconds -->
< div class = "p-2" >
< label class = "block text-sm font-medium text-gray-900" > Waypoints Max Age< / label >
< div class = "text-xs text-gray-600 mb-2" > Waypoints not updated within this time are hidden. Reload to update map.< / div >
< select v-model = "configWaypointsMaxAgeInSeconds" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
< option :value = "null" > Show All< / option >
< option value = "900" > 15 minutes< / option >
< option value = "1800" > 30 minutes< / option >
< option value = "3600" > 1 hour< / option >
< option value = "10800" > 3 hours< / option >
< option value = "21600" > 6 hours< / option >
< option value = "43200" > 12 hours< / option >
< option value = "86400" > 24 hours< / option >
< option value = "172800" > 2 days< / option >
< option value = "259200" > 3 days< / option >
< option value = "345600" > 4 days< / option >
< option value = "432000" > 5 days< / option >
< option value = "518400" > 6 days< / option >
< option value = "604800" > 7 days< / option >
< / select >
< / div >
2026-01-08 18:33:16 +01:00
<!-- configConnectionsTimePeriodInSeconds -->
2025-08-10 15:17:04 +02:00
< div class = "p-2" >
2026-01-08 19:03:29 +01:00
< label class = "block text-sm font-medium text-gray-900" > Connections Max Age< / label >
< div class = "text-xs text-gray-600 mb-2" > Edges from traceroutes and neighbour info within this time period are shown in the Connections layer. Reload to update map.< / div >
2026-01-08 18:33:16 +01:00
< select v-model = "configConnectionsTimePeriodInSeconds" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
< option value = "300" > 5 minutes< / option >
2025-08-10 15:17:04 +02:00
< option value = "900" > 15 minutes< / option >
< option value = "3600" > 1 hour< / option >
< option value = "21600" > 6 hours< / option >
< option value = "43200" > 12 hours< / option >
< option value = "86400" > 24 hours< / option >
< option value = "172800" > 2 days< / option >
< option value = "259200" > 3 days< / option >
< option value = "604800" > 7 days< / option >
2026-01-08 18:33:16 +01:00
< option value = "1209600" > 14 days< / option >
< option value = "2592000" > 30 days< / option >
2025-08-10 15:17:04 +02:00
< / select >
< / div >
2026-01-08 18:33:16 +01:00
<!-- configConnectionsColoredLines -->
2024-03-23 17:13:49 +13:00
< div class = "p-2" >
2026-01-08 18:33:16 +01:00
< div class = "flex items-start" >
< div class = "flex items-center h-5" >
< input type = "checkbox" v-model = "configConnectionsColoredLines" class = "w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300" required >
< / div >
< label class = "ml-2 text-sm font-medium text-gray-900" > Colored Connection Lines< / label >
< / div >
< div class = "text-xs text-gray-600" > Colors the connection lines by the average SNR in the worst direction. Reload to update map.< / div >
< / div >
2026-01-10 13:43:11 +01:00
<!-- configConnectionsBidirectionalOnly -->
< div class = "p-2" >
< div class = "flex items-start" >
< div class = "flex items-center h-5" >
< input type = "checkbox" v-model = "configConnectionsBidirectionalOnly" class = "w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300" required >
< / div >
< label class = "ml-2 text-sm font-medium text-gray-900" > Bidirectional Connections Only< / label >
< / div >
< div class = "text-xs text-gray-600" > Only show connections where data flows in both directions. Reload to update map.< / div >
< / div >
<!-- configConnectionsMinSnrDb -->
< div class = "p-2" >
< label class = "block text-sm font-medium text-gray-900" > Connections Minimum SNR (dB)< / label >
< div class = "text-xs text-gray-600 mb-2" > Only show connections where at least one direction has SNR above this threshold. Leave empty to show all connections. Reload to update map.< / div >
< input type = "number" v-model = "configConnectionsMinSnrDb" placeholder = "e.g. -10" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
< div class = "mt-2 flex items-start" >
< div class = "flex items-center h-5" >
< input type = "checkbox" v-model = "configConnectionsBidirectionalMinSnr" class = "w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300" required >
< / div >
< label class = "ml-2 text-sm font-medium text-gray-900" > Bidirectional Minimum SNR< / label >
< / div >
< div class = "text-xs text-gray-600 ml-6" > If checked, all existing directions must meet the minimum SNR threshold (both directions if bidirectional, single direction if unidirectional).< / div >
< / div >
2026-01-08 18:33:16 +01:00
<!-- configConnectionsMaxDistanceInMeters -->
< div class = "p-2" >
< label class = "block text-sm font-medium text-gray-900" > Connections Max Distance (meters)< / label >
< div class = "text-xs text-gray-600 mb-2" > Connections further than this are hidden. Reload to update map.< / div >
< input type = "number" v-model = "configConnectionsMaxDistanceInMeters" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
2024-03-23 17:13:49 +13:00
< / div >
2024-03-23 22:43:55 +13:00
<!-- configZoomLevelGoToNode -->
< div class = "p-2" >
2024-03-26 03:11:44 +13:00
< label class = "block text-sm font-medium text-gray-900" > Zoom Level (go to node)< / label >
< div class = "text-xs text-gray-600 mb-2" > How far to zoom map when navigating to a node.< / div >
2024-03-23 22:43:55 +13:00
< input type = "number" v-model = "configZoomLevelGoToNode" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
< / div >
2024-06-24 17:14:46 +12:00
<!-- configTemperatureFormat -->
< div class = "p-2" >
< label class = "block text-sm font-medium text-gray-900" > Temperature Format< / label >
< div class = "text-xs text-gray-600 mb-2" > Metrics will be shown in the selected format.< / div >
< select v-model = "configTemperatureFormat" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" >
2025-04-25 00:20:39 -04:00
< option value = "celsius" > Celsius (°C)< / option >
< option value = "fahrenheit" > Fahrenheit (°F)< / option >
2024-06-24 17:14:46 +12:00
< / select >
< / div >
2024-03-30 16:42:44 +13:00
<!-- configAutoUpdatePositionInUrl -->
< div class = "p-2" >
< div class = "flex items-start" >
< div class = "flex items-center h-5" >
< input type = "checkbox" v-model = "configAutoUpdatePositionInUrl" class = "w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300" required >
< / div >
< label class = "ml-2 text-sm font-medium text-gray-900" > Auto Update Position in URL< / label >
< / div >
< div class = "text-xs text-gray-600" > Sets lat/lng/zoom as query parameters.< / div >
< / div >
2024-04-04 20:55:41 +13:00
<!-- configEnableMapAnimations -->
< div class = "p-2" >
< div class = "flex items-start" >
< div class = "flex items-center h-5" >
< input type = "checkbox" v-model = "configEnableMapAnimations" class = "w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300" required >
< / div >
< label class = "ml-2 text-sm font-medium text-gray-900" > Enable Map Animations< / label >
< / div >
< div class = "text-xs text-gray-600" > Map will animate flying to nodes.< / div >
< / div >
2024-03-23 17:13:49 +13:00
< / div >
< / div >
< / div >
< / div >
< / transition >
< / div >
2026-01-08 18:33:16 +01:00
<!-- node connections modal -->
2024-04-07 12:30:27 +12:00
< div class = "relative z-sidebar" role = "dialog" aria-modal = "true" >
<!-- sidebar -->
< transition
enter-active-class="transition duration-300 ease-in-out transform"
enter-from-class="translate-y-full"
enter-to-class="translate-y-0"
leave-active-class="transition duration-300 ease-in-out transform"
leave-from-class="translate-y-0"
leave-to-class="translate-y-full">
2026-01-08 18:33:16 +01:00
< div v-show = "selectedNodeToShowConnections != null" class = "fixed left-0 right-0 bottom-0" >
< div v-if = "selectedNodeToShowConnections != null" class = "mx-auto w-screen max-w-md p-4" >
2024-04-07 12:41:28 +12:00
< div class = "flex h-full flex-col bg-white shadow-xl rounded-xl border" >
2024-04-07 12:30:27 +12:00
< div class = "p-2" >
< div class = "flex items-start justify-between" >
< div >
2026-01-08 18:33:16 +01:00
< h2 class = "font-bold" > {{ selectedNodeToShowConnections.short_name }} Connections< / h2 >
2024-04-07 12:30:27 +12:00
< / div >
< div class = "my-auto ml-3 flex h-7 items-center" >
2026-01-08 18:33:16 +01:00
< a href = "javascript:void(0)" class = "rounded-full" @ click = "dismissShowingNodeConnections" >
2024-04-07 12:30:27 +12:00
< div class = "bg-gray-100 hover:bg-gray-200 p-2 rounded-full" >
< svg class = "w-6 h-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M18 6l-12 12" > < / path >
< path d = "M6 6l12 12" > < / path >
< / svg >
< / div >
< / a >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< / transition >
< / div >
2024-08-04 19:12:51 +02:00
<!-- node position history modal -->
< div class = "relative z-sidebar" role = "dialog" aria-modal = "true" >
<!-- sidebar -->
< transition
enter-active-class="transition duration-300 ease-in-out transform"
enter-from-class="translate-y-full"
enter-to-class="translate-y-0"
leave-active-class="transition duration-300 ease-in-out transform"
leave-from-class="translate-y-0"
leave-to-class="translate-y-full">
< div v-show = "selectedNodeToShowPositionHistory != null" class = "fixed left-0 right-0 bottom-0" >
< div v-if = "selectedNodeToShowPositionHistory != null" class = "mx-auto w-screen max-w-md p-4" >
< div class = "flex h-full flex-col bg-white shadow-xl rounded-xl border" >
2024-08-20 18:47:50 +12:00
< div >
2024-09-04 18:01:54 +12:00
< div class = "flex p-2" >
< div class = "flex my-auto mr-3 space-x-2" >
< a href = "javascript:void(0)" @ click = "isPositionHistoryModalExpanded = !isPositionHistoryModalExpanded" class = "rounded-full" >
< div class = "bg-gray-100 hover:bg-gray-200 p-1 rounded-full" >
< svg v-if = "isPositionHistoryModalExpanded" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" class = "size-6" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "m19.5 8.25-7.5 7.5-7.5-7.5" / >
< / svg >
< svg v-else xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" class = "size-6" >
< path stroke-linecap = "round" stroke-linejoin = "round" d = "m8.25 4.5 7.5 7.5-7.5 7.5" / >
< / svg >
< / div >
< / a >
< / div >
2024-08-20 18:47:50 +12:00
< div class = "my-auto mr-auto font-bold" > {{ selectedNodeToShowPositionHistory.short_name }} Position History< / div >
2024-09-04 18:01:54 +12:00
< div class = "flex my-auto ml-3 space-x-2" >
2024-08-04 19:12:51 +02:00
< a href = "javascript:void(0)" class = "rounded-full" @ click = "dismissShowingNodePositionHistory" >
2024-08-20 18:47:50 +12:00
< div class = "bg-gray-100 hover:bg-gray-200 p-1 rounded-full" >
2024-09-04 18:01:54 +12:00
< svg class = "size-6" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor" fill = "none" stroke-linecap = "round" stroke-linejoin = "round" >
2024-08-04 19:12:51 +02:00
< path stroke = "none" d = "M0 0h24v24H0z" fill = "none" > < / path >
< path d = "M18 6l-12 12" > < / path >
< path d = "M6 6l12 12" > < / path >
< / svg >
< / div >
< / a >
< / div >
< / div >
2024-09-04 18:01:54 +12:00
< div v-if = "isPositionHistoryModalExpanded" class = "divide-y border-t" >
2024-08-20 18:47:50 +12:00
2024-08-21 23:08:59 +12:00
<!-- quick range -->
< div class = "flex p-2 space-x-2" >
< button @ click = "onPositionHistoryQuickRangeClick('1h')" type = "button" class = "w-full bg-gray-100 rounded border shadow px-2 py-1 text-sm hover:bg-gray-200 text-center" > 1 Hour< / button >
< button @ click = "onPositionHistoryQuickRangeClick('24h')" type = "button" class = "w-full bg-gray-100 rounded border shadow px-2 py-1 text-sm hover:bg-gray-200 text-center" > 24 Hours< / button >
< button @ click = "onPositionHistoryQuickRangeClick('7d')" type = "button" class = "w-full bg-gray-100 rounded border shadow px-2 py-1 text-sm hover:bg-gray-200 text-center" > 7 Days< / button >
2024-08-20 18:47:50 +12:00
< / div >
2024-08-21 23:08:59 +12:00
<!-- manual range -->
< div class = "p-2 space-y-1" >
<!-- from -->
< div class = "flex items-center" >
< label class = "text-sm pr-1 min-w-12 text-right" > From:< / label >
< input v-model = "positionHistoryDateTimeFrom" @ change = "loadNodePositionHistory(selectedNodeToShowPositionHistory.node_id)" type = "datetime-local" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-1" >
< / div >
<!-- to -->
< div class = "flex items-center" >
< label class = "text-sm pr-1 min-w-12 text-right" > To:< / label >
< input v-model = "positionHistoryDateTimeTo" @ change = "loadNodePositionHistory(selectedNodeToShowPositionHistory.node_id)" type = "datetime-local" class = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-1" >
< / div >
2024-08-20 18:47:50 +12:00
< / div >
< / div >
2024-08-04 19:12:51 +02:00
< / div >
< / div >
< / div >
< / div >
< / transition >
< / div >
2024-03-12 18:31:17 +13:00
< / div >
2026-02-21 10:38:46 +01:00
< script src = "assets/js/config.js" > < / script >
< script src = "assets/js/app.js" > < / script >
2026-02-21 10:43:01 +01:00
< script src = "assets/js/map.js" > < / script >
2024-03-13 17:21:18 +13:00
2024-03-12 18:31:17 +13:00
< / body >
2024-04-04 11:09:24 -04:00
< / html >