Compare commits
1 Commits
702da27468
...
improvemen
Author | SHA1 | Date | |
---|---|---|---|
2a55bd056f |
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { selectedNodeLatestPowerMetric } from '@/store';
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
channel: Number, // Channel number (1, 2, or 3)
|
channel: Number, // Channel number (1, 2, or 3),
|
||||||
|
latest: Array,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -10,13 +10,13 @@
|
|||||||
<li class="flex p-3">
|
<li class="flex p-3">
|
||||||
<div class="text-sm font-medium text-gray-900">Channel {{ channel }}</div>
|
<div class="text-sm font-medium text-gray-900">Channel {{ channel }}</div>
|
||||||
<div class="ml-auto text-sm text-gray-700">
|
<div class="ml-auto text-sm text-gray-700">
|
||||||
<span v-if="selectedNodeLatestPowerMetric">
|
<span v-if="latest">
|
||||||
<span v-if="selectedNodeLatestPowerMetric[`ch${channel}_voltage`]" >
|
<span v-if="latest[`ch${channel}_voltage`]" >
|
||||||
{{ Number(selectedNodeLatestPowerMetric[`ch${channel}_voltage`]).toFixed(2) }}V
|
{{ Number(latest[`ch${channel}_voltage`]).toFixed(2) }}V
|
||||||
</span>
|
</span>
|
||||||
<span v-else>???</span>
|
<span v-else>???</span>
|
||||||
<span v-if="selectedNodeLatestPowerMetric[`ch${channel}_current`]">
|
<span v-if="latest[`ch${channel}_current`]">
|
||||||
/ {{ Number(selectedNodeLatestPowerMetric[`ch${channel}_current`]).toFixed(2) }}mA
|
/ {{ Number(latest[`ch${channel}_current`]).toFixed(2) }}mA
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>???</span>
|
<span v-else>???</span>
|
||||||
|
@ -20,16 +20,62 @@ import { copyShareLinkForNode, getTimeSpan, buildPath } from '@/utils';
|
|||||||
import { useConfigStore } from '@/stores/configStore';
|
import { useConfigStore } from '@/stores/configStore';
|
||||||
|
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const emit = defineEmits(['showPositionHistory']);
|
const emit = defineEmits(['showPositionHistory', 'showTraceRoute']);
|
||||||
const mapData = useMapStore();
|
const mapData = useMapStore();
|
||||||
|
|
||||||
function showPositionHistory(id) {
|
const mqttMetrics = ref([]);
|
||||||
emit('showPositionHistory', id);
|
const traceroutes = ref([]);
|
||||||
|
const deviceMetrics = ref([]);
|
||||||
|
const environmentMetrics = ref([]);
|
||||||
|
const powerMetrics = ref([]);
|
||||||
|
|
||||||
|
// Generalized function for loading data
|
||||||
|
async function loadData(path, params = {}, targetRef, metrics = false) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(buildPath(path), { params });
|
||||||
|
|
||||||
|
// Grab the first key from the response (assuming it's always an object with a single data array)
|
||||||
|
const key = Object.keys(response.data)[0];
|
||||||
|
const rawData = response.data[key] ?? [];
|
||||||
|
|
||||||
|
// Reverse if it's metric data, otherwise just assign
|
||||||
|
targetRef.value = metrics ? rawData.slice().reverse() : rawData;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error loading data from ${path}:`, error);
|
||||||
|
targetRef.value = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showTraceRoute(traceroute) {
|
// Utility function to calculate the timeFrom value based on a given time range
|
||||||
emit('showTraceRoute', traceroute);
|
function calculateTimeFrom(timeRange) {
|
||||||
state.selectedTraceRoute = traceroute;
|
const time = getTimeSpan(timeRange).amount;
|
||||||
|
return new Date().getTime() - (time * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load MQTT metrics
|
||||||
|
function loadNodeMqttMetrics(nodeId) {
|
||||||
|
loadData(`/api/v1/nodes/${nodeId}/mqtt-metrics`, {}, mqttMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Traceroutes
|
||||||
|
function loadNodeTraceroutes(nodeId) {
|
||||||
|
loadData(`/api/v1/nodes/${nodeId}/traceroutes`, { count: 5 }, traceroutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Device Metrics
|
||||||
|
function loadNodeDeviceMetrics(nodeId) {
|
||||||
|
loadData(`/api/v1/nodes/${nodeId}/device-metrics`, { time_from: calculateTimeFrom(configStore.deviceMetricsTimeRange) }, deviceMetrics, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Environment Metrics
|
||||||
|
function loadNodeEnvironmentMetrics(nodeId) {
|
||||||
|
loadData(`/api/v1/nodes/${nodeId}/environment-metrics`, { time_from: calculateTimeFrom(configStore.environmentMetricsTimeRange) }, environmentMetrics, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Power Metrics
|
||||||
|
function loadNodePowerMetrics(nodeId) {
|
||||||
|
loadData(`/api/v1/nodes/${nodeId}/power-metrics`, { time_from: calculateTimeFrom(configStore.powerMetricsTimeRange) }, powerMetrics, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadNode(nodeId) {
|
function loadNode(nodeId) {
|
||||||
@ -40,73 +86,6 @@ function loadNode(nodeId) {
|
|||||||
loadNodePowerMetrics(nodeId);
|
loadNodePowerMetrics(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadNodeMqttMetrics(nodeId) {
|
|
||||||
state.selectedNodeMqttMetrics = [];
|
|
||||||
axios.get(buildPath(`/api/v1/nodes/${nodeId}/mqtt-metrics`)).then((response) => {
|
|
||||||
state.selectedNodeMqttMetrics = response.data.mqtt_metrics;
|
|
||||||
}).catch(() => {
|
|
||||||
// do nothing
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadNodeTraceroutes(nodeId) {
|
|
||||||
state.selectedNodeTraceroutes = [];
|
|
||||||
axios.get(buildPath(`/api/v1/nodes/${nodeId}/traceroutes`), {
|
|
||||||
params: {
|
|
||||||
count: 5,
|
|
||||||
},
|
|
||||||
}).then((response) => {
|
|
||||||
state.selectedNodeTraceroutes = response.data.traceroutes;
|
|
||||||
}).catch(() => {
|
|
||||||
// do nothing
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadNodeDeviceMetrics(nodeId) {
|
|
||||||
const time = getTimeSpan(configStore.deviceMetricsTimeRange).amount
|
|
||||||
const timeFrom = new Date().getTime() - (time * 1000);
|
|
||||||
axios.get(buildPath(`/api/v1/nodes/${nodeId}/device-metrics`), {
|
|
||||||
params: {
|
|
||||||
time_from: timeFrom,
|
|
||||||
},
|
|
||||||
}).then((response) => {
|
|
||||||
// reverse response, as it's newest to oldest, but we want oldest to newest
|
|
||||||
state.selectedNodeDeviceMetrics = response.data.device_metrics.reverse();
|
|
||||||
}).catch(() => {
|
|
||||||
state.selectedNodeDeviceMetrics = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadNodeEnvironmentMetrics(nodeId) {
|
|
||||||
const time = getTimeSpan(configStore.environmentMetricsTimeRange).amount
|
|
||||||
const timeFrom = new Date().getTime() - (time * 1000);
|
|
||||||
axios.get(buildPath(`/api/v1/nodes/${nodeId}/environment-metrics`), {
|
|
||||||
params: {
|
|
||||||
time_from: timeFrom,
|
|
||||||
},
|
|
||||||
}).then((response) => {
|
|
||||||
// reverse response, as it's newest to oldest, but we want oldest to newest
|
|
||||||
state.selectedNodeEnvironmentMetrics = response.data.environment_metrics.reverse();
|
|
||||||
}).catch(() => {
|
|
||||||
state.selectedNodeEnvironmentMetrics = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadNodePowerMetrics(nodeId) {
|
|
||||||
const time = getTimeSpan(configStore.powerMetricsTimeRange).amount
|
|
||||||
const timeFrom = new Date().getTime() - (time * 1000);
|
|
||||||
axios.get(buildPath(`/api/v1/nodes/${nodeId}/power-metrics`), {
|
|
||||||
params: {
|
|
||||||
time_from: timeFrom,
|
|
||||||
},
|
|
||||||
}).then((response) => {
|
|
||||||
// reverse response, as it's newest to oldest, but we want oldest to newest
|
|
||||||
state.selectedNodePowerMetrics = response.data.power_metrics.reverse();
|
|
||||||
}).catch(() => {
|
|
||||||
state.selectedNodePowerMetrics = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => state.selectedNode,
|
() => state.selectedNode,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
@ -263,17 +242,17 @@ watch(
|
|||||||
<!-- lora config -->
|
<!-- lora config -->
|
||||||
<LoraConfig :node="state.selectedNode"/>
|
<LoraConfig :node="state.selectedNode"/>
|
||||||
<!-- position -->
|
<!-- position -->
|
||||||
<Position @show-position-history="showPositionHistory" :node="state.selectedNode"/>
|
<Position @show-position-history="(nodeId) => $emit('showPositionHistory', nodeId)" :node="state.selectedNode"/>
|
||||||
<!-- device metrics -->
|
<!-- device metrics -->
|
||||||
<DeviceMetricsChart :node="state.selectedNode"/>
|
<DeviceMetricsChart :node="state.selectedNode" :data="deviceMetrics"/>
|
||||||
<!-- environment metrics -->
|
<!-- environment metrics -->
|
||||||
<EnvironmentMetricsChart :node="state.selectedNode"/>
|
<EnvironmentMetricsChart :node="state.selectedNode" :data="environmentMetrics"/>
|
||||||
<!-- power metrics -->
|
<!-- power metrics -->
|
||||||
<PowerMetricsChart/>
|
<PowerMetricsChart :data="powerMetrics"/>
|
||||||
<!-- mqtt -->
|
<!-- mqtt -->
|
||||||
<MqttHistory />
|
<MqttHistory :data="mqttMetrics"/>
|
||||||
<!-- traceroutes -->
|
<!-- traceroutes -->
|
||||||
<Traceroutes @show-trace-route="showTraceRoute"/>
|
<Traceroutes @show-trace-route="(traceroute) => $emit('showTraceRoute', traceroute)" :data="traceroutes"/>
|
||||||
<!-- other -->
|
<!-- other -->
|
||||||
<OtherInfo :node="state.selectedNode"/>
|
<OtherInfo :node="state.selectedNode"/>
|
||||||
<!-- share -->
|
<!-- share -->
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps(['node']);
|
const props = defineProps(['node', 'data']);
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { state } from '@/store';
|
|
||||||
import MetricsChart from '@/components/Chart/Metrics.vue';
|
import MetricsChart from '@/components/Chart/Metrics.vue';
|
||||||
|
|
||||||
const chartData = computed(() => {
|
const chartData = computed(() => {
|
||||||
const metrics = state.selectedNodeDeviceMetrics;
|
const metrics = props.data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
labels: metrics.map(m => m.created_at),
|
labels: metrics.map(m => m.created_at),
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps(['node']);
|
const props = defineProps(['node', 'data']);
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { state } from '@/store';
|
|
||||||
import { formatTemperature } from '@/utils';
|
import { formatTemperature } from '@/utils';
|
||||||
import MetricsChart from '@/components/Chart/Metrics.vue';
|
import MetricsChart from '@/components/Chart/Metrics.vue';
|
||||||
|
|
||||||
// Chart data prep
|
// Chart data prep
|
||||||
const labels = computed(() => state.selectedNodeEnvironmentMetrics.map(m => m.created_at));
|
const labels = computed(() => props.data.map(m => m.created_at));
|
||||||
const temperatureMetrics = computed(() => state.selectedNodeEnvironmentMetrics.map(m => m.temperature));
|
const temperatureMetrics = computed(() => props.data.map(m => m.temperature));
|
||||||
const relativeHumidityMetrics = computed(() => state.selectedNodeEnvironmentMetrics.map(m => m.relative_humidity));
|
const relativeHumidityMetrics = computed(() => props.data.map(m => m.relative_humidity));
|
||||||
const barometricPressureMetrics = computed(() => state.selectedNodeEnvironmentMetrics.map(m => m.barometric_pressure));
|
const barometricPressureMetrics = computed(() => props.data.map(m => m.barometric_pressure));
|
||||||
|
|
||||||
const chartData = computed(() => ({
|
const chartData = computed(() => ({
|
||||||
labels: labels.value,
|
labels: labels.value,
|
||||||
@ -109,7 +107,7 @@ const legendData = {
|
|||||||
<li class="flex p-3">
|
<li class="flex p-3">
|
||||||
<div class="text-sm font-medium text-gray-900">Temperature</div>
|
<div class="text-sm font-medium text-gray-900">Temperature</div>
|
||||||
<div class="ml-auto text-sm text-gray-700">
|
<div class="ml-auto text-sm text-gray-700">
|
||||||
<span v-if="state.selectedNode?.temperature">{{ formatTemperature(state.selectedNode.temperature) }}</span>
|
<span v-if="props.node?.temperature">{{ formatTemperature(props.node.temperature) }}</span>
|
||||||
<span v-else>???</span>
|
<span v-else>???</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@ -117,8 +115,8 @@ const legendData = {
|
|||||||
<li class="flex p-3">
|
<li class="flex p-3">
|
||||||
<div class="text-sm font-medium text-gray-900">Relative Humidity</div>
|
<div class="text-sm font-medium text-gray-900">Relative Humidity</div>
|
||||||
<div class="ml-auto text-sm text-gray-700">
|
<div class="ml-auto text-sm text-gray-700">
|
||||||
<span v-if="state.selectedNode?.relative_humidity">
|
<span v-if="props.node?.relative_humidity">
|
||||||
{{ Number(state.selectedNode.relative_humidity).toFixed(0) }}%
|
{{ Number(props.node.relative_humidity).toFixed(0) }}%
|
||||||
</span>
|
</span>
|
||||||
<span v-else>???</span>
|
<span v-else>???</span>
|
||||||
</div>
|
</div>
|
||||||
@ -127,8 +125,8 @@ const legendData = {
|
|||||||
<li class="flex p-3">
|
<li class="flex p-3">
|
||||||
<div class="text-sm font-medium text-gray-900">Barometric Pressure</div>
|
<div class="text-sm font-medium text-gray-900">Barometric Pressure</div>
|
||||||
<div class="ml-auto text-sm text-gray-700">
|
<div class="ml-auto text-sm text-gray-700">
|
||||||
<span v-if="state.selectedNode?.barometric_pressure">
|
<span v-if="props.node?.barometric_pressure">
|
||||||
{{ Number(state.selectedNode.barometric_pressure).toFixed(1) }} hPa
|
{{ Number(props.node.barometric_pressure).toFixed(1) }} hPa
|
||||||
</span>
|
</span>
|
||||||
<span v-else>???</span>
|
<span v-else>???</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { state } from '@/store';
|
const props = defineProps(['data']);
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const mqttMetrics = computed(() => state.selectedNodeMqttMetrics);
|
const mqttMetrics = computed(() => props.data);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { state, selectedNodeLatestPowerMetric } from '@/store';
|
|
||||||
import MetricsChart from '@/components/Chart/Metrics.vue';
|
import MetricsChart from '@/components/Chart/Metrics.vue';
|
||||||
import ChannelData from '@/components/Chart/PowerMetrics/ChannelData.vue';
|
import ChannelData from '@/components/Chart/PowerMetrics/ChannelData.vue';
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
|
||||||
const legendData = {
|
const legendData = {
|
||||||
'Channel 1': 'bg-blue-500',
|
'Channel 1': 'bg-blue-500',
|
||||||
@ -10,9 +10,14 @@ const legendData = {
|
|||||||
'Channel 3': 'bg-orange-500',
|
'Channel 3': 'bg-orange-500',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const latestMetric = computed(() => {
|
||||||
|
const [ latest ] = (props.data ?? []).slice(-1);
|
||||||
|
return latest;
|
||||||
|
});
|
||||||
|
|
||||||
// Computed dataset for the chart container
|
// Computed dataset for the chart container
|
||||||
const chartData = computed(() => {
|
const chartData = computed(() => {
|
||||||
const metrics = state.selectedNodePowerMetrics;
|
const metrics = props.data;
|
||||||
const labels = metrics.map(m => m.created_at);
|
const labels = metrics.map(m => m.created_at);
|
||||||
|
|
||||||
const colors = ['#3b82f6', '#22c55e', '#f97316'];
|
const colors = ['#3b82f6', '#22c55e', '#f97316'];
|
||||||
@ -109,6 +114,6 @@ const chartConfig = {
|
|||||||
:legendData="legendData"
|
:legendData="legendData"
|
||||||
:chartConfig="chartConfig"
|
:chartConfig="chartConfig"
|
||||||
>
|
>
|
||||||
<ChannelData v-for="i in [1, 2, 3]" :key="i" :channel="i" />
|
<ChannelData v-for="i in [1, 2, 3]" :key="i" :channel="i" :latest="latestMetric"/>
|
||||||
</MetricsChart>
|
</MetricsChart>
|
||||||
</template>
|
</template>
|
@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
const props = defineProps(['data']);
|
||||||
const emit = defineEmits(['showTraceRoute']);
|
const emit = defineEmits(['showTraceRoute']);
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { state } from '@/store';
|
|
||||||
import { useMapStore } from '@/stores/mapStore';
|
import { useMapStore } from '@/stores/mapStore';
|
||||||
const mapData = useMapStore();
|
const mapData = useMapStore();
|
||||||
</script>
|
</script>
|
||||||
@ -12,8 +12,8 @@ const mapData = useMapStore();
|
|||||||
<div class="text-sm text-gray-600">Only 5 most recent are shown</div>
|
<div class="text-sm text-gray-600">Only 5 most recent are shown</div>
|
||||||
</div>
|
</div>
|
||||||
<ul role="list" class="flex-1 divide-y divide-gray-200">
|
<ul role="list" class="flex-1 divide-y divide-gray-200">
|
||||||
<template v-if="state.selectedNodeTraceroutes.length > 0">
|
<template v-if="props.data.length > 0">
|
||||||
<li @click="$emit('showTraceRoute', traceroute)" v-for="traceroute of state.selectedNodeTraceroutes">
|
<li @click="$emit('showTraceRoute', traceroute)" v-for="traceroute of props.data">
|
||||||
<div class="relative flex items-center">
|
<div class="relative flex items-center">
|
||||||
<div class="block flex-1 px-4 py-2">
|
<div class="block flex-1 px-4 py-2">
|
||||||
<div class="relative flex min-w-0 flex-1 items-center">
|
<div class="relative flex min-w-0 flex-1 items-center">
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const emit = defineEmits(['goTo']);
|
const emit = defineEmits(['goTo', 'dismiss']);
|
||||||
|
const props = defineProps(['data']);
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { state } from '@/store';
|
|
||||||
import { useMapStore } from '@/stores/mapStore';
|
import { useMapStore } from '@/stores/mapStore';
|
||||||
import NodeEntry from '@/components/Traceroute/NodeEntry.vue';
|
import NodeEntry from '@/components/Traceroute/NodeEntry.vue';
|
||||||
const mapData = useMapStore();
|
const mapData = useMapStore();
|
||||||
// selected node IDs
|
// selected node IDs
|
||||||
const toNode = computed(() => mapData.findNodeById(state.selectedTraceRoute.to));
|
const toNode = computed(() => mapData.findNodeById(props.data.to));
|
||||||
const fromNode = computed(() => mapData.findNodeById(state.selectedTraceRoute.from));
|
const fromNode = computed(() => mapData.findNodeById(props.data.from));
|
||||||
const gatewayNode = computed(() => mapData.findNodeById(state.selectedTraceRoute.gateway_id));
|
const gatewayNode = computed(() => mapData.findNodeById(props.data.gateway_id));
|
||||||
// pre-resolve the route nodes into an array
|
// pre-resolve the route nodes into an array
|
||||||
const routeNodes = computed(() =>
|
const routeNodes = computed(() =>
|
||||||
state.selectedTraceRoute.route?.map(id => ({
|
props.data.route?.map(id => ({
|
||||||
id,
|
id,
|
||||||
node: mapData.findNodeById(id),
|
node: mapData.findNodeById(id),
|
||||||
})) ?? []
|
})) ?? []
|
||||||
@ -29,7 +29,7 @@ const routeNodes = computed(() =>
|
|||||||
leave-active-class="transition-opacity duration-300 ease-linear"
|
leave-active-class="transition-opacity duration-300 ease-linear"
|
||||||
leave-from-class="opacity-100"
|
leave-from-class="opacity-100"
|
||||||
leave-to-class="opacity-0">
|
leave-to-class="opacity-0">
|
||||||
<div v-show="state.selectedTraceRoute != null" @click="state.selectedTraceRoute = null" class="fixed inset-0 bg-gray-900/75"></div>
|
<div v-show="props.data != null" @click="$emit('dismiss')" class="fixed inset-0 bg-gray-900/75"></div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
<!-- sidebar -->
|
<!-- sidebar -->
|
||||||
@ -40,19 +40,19 @@ const routeNodes = computed(() =>
|
|||||||
leave-active-class="transition duration-300 ease-in-out transform"
|
leave-active-class="transition duration-300 ease-in-out transform"
|
||||||
leave-from-class="translate-x-0"
|
leave-from-class="translate-x-0"
|
||||||
leave-to-class="-translate-x-full">
|
leave-to-class="-translate-x-full">
|
||||||
<div v-show="state.selectedTraceRoute != null" class="fixed top-0 left-0 bottom-0">
|
<div v-show="props.data != null" class="fixed top-0 left-0 bottom-0">
|
||||||
<div v-if="state.selectedTraceRoute != null" class="w-screen h-full max-w-md overflow-hidden">
|
<div v-if="props.data != null" class="w-screen h-full max-w-md overflow-hidden">
|
||||||
<div class="flex h-full flex-col bg-white shadow-xl">
|
<div class="flex h-full flex-col bg-white shadow-xl">
|
||||||
|
|
||||||
<!-- slideover header -->
|
<!-- slideover header -->
|
||||||
<div class="p-2 border-b border-gray-200 shadow-sm">
|
<div class="p-2 border-b border-gray-200 shadow-sm">
|
||||||
<div class="flex items-start justify-between">
|
<div class="flex items-start justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="font-bold">Traceroute #{{ state.selectedTraceRoute.id }}</h2>
|
<h2 class="font-bold">Traceroute #{{ props.data.id }}</h2>
|
||||||
<h3 class="text-sm">{{ moment(new Date(state.selectedTraceRoute.updated_at)).fromNow() }} - {{ state.selectedTraceRoute.route.length }} hops {{ state.selectedTraceRoute.channel_id ? `on ${state.selectedTraceRoute.channel_id}` : '' }}</h3>
|
<h3 class="text-sm">{{ moment(new Date(props.data.updated_at)).fromNow() }} - {{ props.data.route.length }} hops {{ props.data.channel_id ? `on ${props.data.channel_id}` : '' }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-auto ml-3 flex h-7 items-center">
|
<div class="my-auto ml-3 flex h-7 items-center">
|
||||||
<a href="javascript:void(0)" class="rounded-full" @click="state.selectedTraceRoute = null">
|
<a href="javascript:void(0)" class="rounded-full" @click="$emit('dismiss')">
|
||||||
<div class="bg-gray-100 hover:bg-gray-200 p-2 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" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
<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 stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||||
@ -83,7 +83,7 @@ const routeNodes = computed(() =>
|
|||||||
<div>
|
<div>
|
||||||
<div class="bg-gray-200 p-2 font-semibold">Raw Data</div>
|
<div class="bg-gray-200 p-2 font-semibold">Raw Data</div>
|
||||||
<div class="text-sm text-gray-700">
|
<div class="text-sm text-gray-700">
|
||||||
<pre class="bg-gray-100 rounded-sm p-2 overflow-x-auto">{{ JSON.stringify(state.selectedTraceRoute, null, 4) }}</pre>
|
<pre class="bg-gray-100 rounded-sm p-2 overflow-x-auto">{{ JSON.stringify(props.data, null, 4) }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
import { reactive, computed } from 'vue';
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
export const state = reactive({
|
export const state = reactive({
|
||||||
// state
|
// state
|
||||||
searchText: '', // moved to ui store, maybe should not be there though?
|
searchText: '', // moved to ui store, maybe should not be there though?
|
||||||
selectedNodeOutlineCircle: null,
|
selectedNodeOutlineCircle: null,
|
||||||
selectedNodeMqttMetrics: [],
|
|
||||||
selectedNodeTraceroutes: [],
|
|
||||||
selectedNodeDeviceMetrics: [],
|
|
||||||
selectedNodePowerMetrics: [],
|
|
||||||
selectedNodeEnvironmentMetrics: [],
|
|
||||||
|
|
||||||
// new selected node stuff
|
// new selected node/data stuff
|
||||||
positionHistoryNode: null,
|
positionHistoryNode: null,
|
||||||
neighborsNode: null,
|
neighborsNode: null,
|
||||||
|
traceRouteData: null,
|
||||||
|
|
||||||
// new modal specific stuff
|
// new modal specific stuff
|
||||||
neighborsModalType: null,
|
neighborsModalType: null,
|
||||||
@ -21,8 +17,3 @@ export const state = reactive({
|
|||||||
positionHistoryDateTimeTo: null,
|
positionHistoryDateTimeTo: null,
|
||||||
positionHistoryDateTimeFrom: null,
|
positionHistoryDateTimeFrom: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectedNodeLatestPowerMetric = computed(() => {
|
|
||||||
const [ latestPowerMetric ] = state.selectedNodePowerMetrics.slice(-1);
|
|
||||||
return latestPowerMetric;
|
|
||||||
});
|
|
@ -469,6 +469,14 @@ function goToNode(id, animate, zoom){
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showTraceRoute(traceroute) {
|
||||||
|
state.traceRouteData = traceroute;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTraceRoute() {
|
||||||
|
state.traceRouteData = null;
|
||||||
|
}
|
||||||
|
|
||||||
function onSearchResultNodeClick(node) {
|
function onSearchResultNodeClick(node) {
|
||||||
// clear search
|
// clear search
|
||||||
ui.search('');
|
ui.search('');
|
||||||
@ -967,10 +975,10 @@ onMounted(() => {
|
|||||||
<InfoModal />
|
<InfoModal />
|
||||||
<HardwareModelList />
|
<HardwareModelList />
|
||||||
<Settings />
|
<Settings />
|
||||||
<NodeInfo @show-position-history="showNodePositionHistory"/>
|
<NodeInfo @show-position-history="showNodePositionHistory" @show-trace-route="showTraceRoute"/>
|
||||||
<NodeNeighborsModal @dismiss="resetNodeNeighbors" :node="state.neighborsNode"/>
|
<NodeNeighborsModal @dismiss="resetNodeNeighbors" :node="state.neighborsNode"/>
|
||||||
<NodePositionHistoryModal @dismiss="resetPositionHistory" :node="state.positionHistoryNode"/>
|
<NodePositionHistoryModal @dismiss="resetPositionHistory" :node="state.positionHistoryNode"/>
|
||||||
<TracerouteInfo @go-to="goToNode" />
|
<TracerouteInfo @go-to="goToNode" @dismiss="resetTraceRoute" :data="state.traceRouteData"/>
|
||||||
<Teleport v-if="popupTarget && selectedNode" :to="popupTarget">
|
<Teleport v-if="popupTarget && selectedNode" :to="popupTarget">
|
||||||
<NodeTooltip :node="selectedNode" @show-neighbors="showNodeNeighbors"/>
|
<NodeTooltip :node="selectedNode" @show-neighbors="showNodeNeighbors"/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
Reference in New Issue
Block a user