<template>
<section class="page">
<GnPageHeader title="Firmware Catalog" kicker="Firmwares">
<template #actions>
<GnButton
:loading="firmwaresStore.isUpdating"
icon="ph-arrow-clockwise"
@click="handleRefresh"
>
Refresh Catalog
</GnButton>
</template>
</GnPageHeader>
<AppLoadingState v-if="firmwaresStore.isLoadingList" text="Loading firmware catalog" />
<AppErrorState
v-else-if="error"
title="Catalog loading failed"
:error="error"
:retry="reload"
/>
<AppEmptyState
v-else-if="firmwaresStore.firmwares.length === 0"
title="No firmwares found"
message="No firmware packages detected in the firmwares directory."
/>
<div v-else class="firmwares-panel">
<div class="firmwares-summary">
<GnBadge variant="primary">Total: {{ firmwaresStore.firmwares.length }}</GnBadge>
</div>
<div class="firmwares-list">
<div
v-for="fw in firmwaresStore.firmwares"
:key="fw.id"
class="firmware-card"
>
<div class="firmware-header">
<span class="firmware-id">{{ fw.id }}</span>
<GnBadge variant="success">{{ fw.version }}</GnBadge>
</div>
<div class="firmware-meta">
<GnBadge variant="secondary">{{ fw.device_type }}</GnBadge>
<GnBadge v-if="fw.platform" variant="info">{{ fw.platform }}</GnBadge>
<GnBadge v-if="fw.channels" variant="warning">{{ fw.channels }} ch</GnBadge>
</div>
<p v-if="fw.description" class="firmware-desc">{{ fw.description }}</p>
<pre v-if="fw.changelog" class="firmware-changelog">{{ fw.changelog }}</pre>
</div>
</div>
</div>
</section>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useFirmwaresStore } from "../../../stores/firmwares";
import { GnPageHeader, GnButton, GnBadge } from "gnexus-ui-kit/vue";
import AppEmptyState from "../../../components/feedback/AppEmptyState.vue";
import AppErrorState from "../../../components/feedback/AppErrorState.vue";
import AppLoadingState from "../../../components/feedback/AppLoadingState.vue";
const firmwaresStore = useFirmwaresStore();
const error = ref(null);
async function reload() {
error.value = null;
const result = await firmwaresStore.loadFirmwares();
if (!result.ok) {
error.value = result.error?.message || "Failed to load catalog";
}
}
async function handleRefresh() {
error.value = null;
const result = await firmwaresStore.refreshFirmwares();
if (!result.ok) {
error.value = result.error?.message || "Failed to refresh catalog";
}
}
onMounted(reload);
</script>
<style scoped>
.firmwares-summary {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
}
.firmwares-list {
display: grid;
gap: 12px;
}
.firmware-card {
background: var(--color-panel);
border: 1px solid rgba(192, 202, 245, 0.12);
border-radius: 8px;
padding: 12px 16px;
}
.firmware-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.firmware-id {
font-family: monospace;
font-size: 13px;
word-break: break-all;
}
.firmware-meta {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
}
.firmware-desc {
font-size: 13px;
color: var(--color-text-muted);
margin: 0;
}
.firmware-changelog {
font-size: 12px;
background: rgba(15, 23, 42, 0.5);
padding: 8px;
border-radius: 6px;
margin-top: 8px;
white-space: pre-wrap;
word-break: break-word;
}
</style>