archy/neode-ui/src/views/Fleet.vue

110 lines
4.3 KiB
Vue

<template>
<div class="pb-6 mobile-scroll-pad">
<!-- Header -->
<div class="hidden md:block mb-8">
<div class="flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold text-white mb-2">Fleet Dashboard</h1>
<p class="text-white/70">Beta Telemetry monitoring {{ fleet.nodes.value.length }} node{{ fleet.nodes.value.length !== 1 ? 's' : '' }}</p>
</div>
<div class="flex gap-2 items-center">
<span v-if="fleet.autoRefresh.value" class="text-xs text-white/40">Auto-refresh 60s</span>
<button class="glass-button text-sm px-4 py-2" @click="fleet.toggleAutoRefresh">
{{ fleet.autoRefresh.value ? 'Pause' : 'Resume' }}
</button>
<button class="glass-button text-sm px-4 py-2" @click="fleet.refreshAll">
Refresh
</button>
<button class="glass-button text-sm px-4 py-2" @click="fleet.exportFleetData">
Export JSON
</button>
</div>
</div>
</div>
<!-- Mobile Header -->
<div class="md:hidden mb-6">
<h1 class="text-2xl font-bold text-white mb-1">Fleet Dashboard</h1>
<p class="text-white/60 text-sm mb-3">Monitoring {{ fleet.nodes.value.length }} node{{ fleet.nodes.value.length !== 1 ? 's' : '' }}</p>
<div class="flex gap-2">
<button class="glass-button text-xs px-3 py-2 flex-1" @click="fleet.refreshAll">Refresh</button>
<button class="glass-button text-xs px-3 py-2 flex-1" @click="fleet.exportFleetData">Export</button>
</div>
</div>
<!-- Loading State -->
<div v-if="fleet.loading.value" class="flex items-center justify-center py-20">
<div class="text-white/50 text-sm">Loading fleet data...</div>
</div>
<!-- Error State -->
<div v-else-if="fleet.errorMessage.value" class="glass-card p-6 mb-6">
<div class="alert-error rounded-lg mb-4">{{ fleet.errorMessage.value }}</div>
<button class="glass-button text-sm px-4 py-2" @click="fleet.refreshAll">Retry</button>
</div>
<!-- Dashboard Content -->
<template v-else>
<FleetOverviewCards
:node-count="fleet.nodes.value.length"
:online-count="fleet.onlineCount.value"
:offline-count="fleet.offlineCount.value"
:fleet-health-pct="fleet.fleetHealthPct.value"
:healthy-count="fleet.healthyCount.value"
:avg-cpu="fleet.avgCpu.value"
:avg-mem="fleet.avgMem.value"
:avg-disk="fleet.avgDisk.value"
/>
<FleetNodeGrid
:nodes="fleet.nodes.value"
:sorted-nodes="fleet.sortedNodes.value"
:sort-by="fleet.sortBy.value"
:selected-node-id="fleet.selectedNodeId.value"
@update:sort-by="fleet.sortBy.value = $event"
@select-node="fleet.selectNode"
/>
<FleetAlerts
:alerts="fleet.fleetAlerts.value"
:alerts-loading="fleet.alertsLoading.value"
/>
<FleetNodeDetail
v-if="fleet.selectedNodeId.value && fleet.selectedNode.value"
:node="fleet.selectedNode.value"
:node-id="fleet.selectedNodeId.value"
:history-loading="fleet.nodeHistoryLoading.value"
:history-labels="fleet.nodeHistoryLabels.value"
:cpu-datasets="fleet.nodeHistoryCpuDatasets.value"
:mem-datasets="fleet.nodeHistoryMemDatasets.value"
:disk-datasets="fleet.nodeHistoryDiskDatasets.value"
:chart-width="fleet.chartWidth.value"
@close="fleet.selectedNodeId.value = null"
/>
<FleetContainerMatrix
:nodes="fleet.nodes.value"
:sorted-nodes="fleet.sortedNodes.value"
:all-app-ids="fleet.allAppIds.value"
/>
<p class="text-xs text-white/30 mt-4 text-center">
{{ fleet.autoRefresh.value ? 'Auto-refreshing every 60s' : 'Auto-refresh paused' }}
&middot; Last updated {{ fleet.lastRefreshed.value ? timeAgo(fleet.lastRefreshed.value) : 'never' }}
</p>
</template>
</div>
</template>
<script setup lang="ts">
import FleetOverviewCards from './fleet/FleetOverviewCards.vue'
import FleetNodeGrid from './fleet/FleetNodeGrid.vue'
import FleetAlerts from './fleet/FleetAlerts.vue'
import FleetNodeDetail from './fleet/FleetNodeDetail.vue'
import FleetContainerMatrix from './fleet/FleetContainerMatrix.vue'
import { useFleetData, timeAgo } from './fleet/useFleetData'
const fleet = useFleetData()
</script>