Newer
Older
smart-home-server / webclient-vue / src / features / devices / pages / DevicesListPage.vue
<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"
      :message="devicesStore.error.message"
      :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"
        :message="devicesStore.stateError.message"
      />

      <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>