<template>
<section class="page">
<GnPageHeader title="Device Matrix" kicker="Devices">
<template #actions>
<GnButton
:loading="devicesStore.isLoading || devicesStore.isLoadingStates"
icon="ph-arrow-clockwise"
@click="reload"
>
Refresh
</GnButton>
</template>
</GnPageHeader>
<AppLoadingState v-if="devicesStore.isLoading" text="Loading devices" />
<AppErrorState
v-else-if="devicesStore.error"
title="Devices loading failed"
:error="devicesStore.error"
:retry="reload"
/>
<AppEmptyState
v-else-if="devicesStore.devices.length === 0"
title="No active devices"
message="No active devices found."
/>
<div v-else class="devices-panel">
<div class="devices-summary">
<GnBadge variant="primary">Total: {{ devicesStore.total }}</GnBadge>
<GnBadge v-if="devicesStore.isLoadingStates" variant="secondary">States loading</GnBadge>
<GnBadge v-else variant="success">States settled</GnBadge>
<GnBadge v-if="isStale" variant="warning">Stale data</GnBadge>
</div>
<AppErrorState
v-if="devicesStore.stateError"
title="Device states loading failed"
:error="devicesStore.stateError"
/>
<DeviceTable :devices="devicesStore.devices" caption="Registered devices" />
</div>
</section>
</template>
<script setup>
import { onMounted, computed } from "vue";
import { useDevicesStore } from "../../../stores/devices";
import { GnPageHeader, GnButton, GnBadge } from "gnexus-ui-kit/vue";
const STALE_THRESHOLD_MS = 5 * 60 * 1000;
import AppEmptyState from "../../../components/feedback/AppEmptyState.vue";
import AppErrorState from "../../../components/feedback/AppErrorState.vue";
import AppLoadingState from "../../../components/feedback/AppLoadingState.vue";
import DeviceTable from "../../../components/device/DeviceTable.vue";
const devicesStore = useDevicesStore();
const isStale = computed(() => {
if (!devicesStore.lastLoadedAt) return false;
const loaded = new Date(devicesStore.lastLoadedAt).getTime();
return Date.now() - loaded > STALE_THRESHOLD_MS;
});
async function reload() {
const result = await devicesStore.loadDevices();
if (result.ok) {
await devicesStore.loadDeviceStates();
}
}
onMounted(reload);
</script>
<style scoped>
.devices-summary {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
}
</style>