Newer
Older
smart-home-server / webclient-vue / src / features / scripts / pages / ScriptsRegularPage.vue
<template>
  <section class="page">
    <GnPageHeader title="Regular" kicker="Scripts">
      <template #actions>
        <GnBadge variant="primary">{{ scriptsStore.totalRegular }} scripts</GnBadge>
      </template>
    </GnPageHeader>

    <AppLoadingState v-if="scriptsStore.isLoadingRegular" text="Loading regular scripts" />

    <AppErrorState
      v-else-if="scriptsStore.errorRegular"
      title="Regular scripts loading failed"
      :message="scriptsStore.errorRegular.message"
      :retry="scriptsStore.loadRegular"
    />

    <AppEmptyState
      v-else-if="scriptsStore.regular.length === 0"
      title="No regular scripts"
      message="No regular scripts registered."
    />

    <div v-else class="devices-panel">
      <GnTable
        :columns="tableColumns"
        :rows="scriptsStore.regular"
        caption="Regular scripts"
      >
        <template #cell-alias="{ row }">
          <router-link
            :to="{ name: 'script-detail', params: { type: 'regular', id: row.alias } }"
            class="script-link"
          >
            {{ row.alias }}
          </router-link>
        </template>

        <template #cell-scope="{ row }">
          <router-link
            v-if="row.scope"
            :to="{ name: 'script-detail', params: { type: 'scopes', id: row.scope } }"
            class="scope-link"
          >
            <span class="scope-label">Scope</span>
            <span class="scope-name">{{ row.scope }}</span>
            <i class="ph ph-arrow-right"></i>
          </router-link>
          <span v-else class="muted">—</span>
        </template>

        <template #cell-area="{ row }">
          <GnBadge v-if="row.area_id" variant="primary">{{ areaNameFor(row) }}</GnBadge>
          <span v-else class="muted">—</span>
        </template>

        <template #cell-state="{ row }">
          <GnBadge :variant="row.state === 'enabled' ? 'success' : 'secondary'"
          >{{ row.state }}</GnBadge>
        </template>

        <template #cell-actions="{ row }">
          <GnButton
            v-if="row.state === 'enabled'"
            variant="secondary"
            icon="ph-pause"
            @click="toggle(row.alias, false)"
          >
            Disable
          </GnButton>
          <GnButton
            v-else
            variant="primary"
            icon="ph-play"
            @click="toggle(row.alias, true)"
          >
            Enable
          </GnButton>
        </template>
      </GnTable>
    </div>
  </section>
</template>

<script setup>
import { onMounted, computed } from "vue";
import { useRouter } from "vue-router";
import { useScriptsStore } from "../../../stores/scripts";
import { useAreasStore } from "../../../stores/areas";
import { GnPageHeader, GnBadge, GnTable, GnButton } 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 router = useRouter();
const scriptsStore = useScriptsStore();
const areasStore = useAreasStore();

const areaById = computed(() => {
  const map = {};
  for (const a of areasStore.areas) {
    map[a.id] = a;
  }
  return map;
});

function areaNameFor(row) {
  if (!row.area_id) return null;
  return areaById.value[row.area_id]?.display_name || `Area ${row.area_id}`;
}

const tableColumns = [
  { key: "alias", label: "Alias" },
  { key: "name", label: "Name" },
  { key: "scope", label: "Scope" },
  { key: "area", label: "Area" },
  { key: "state", label: "State" },
  { key: "filename", label: "File" },
  { key: "actions", label: "Actions" },
];

function goToDetail(alias) {
  router.push({ name: "script-detail", params: { type: "regular", id: alias } });
}

function toggle(alias, enabled) {
  scriptsStore.setRegularState(alias, enabled);
}

onMounted(() => {
  scriptsStore.loadRegular();
  if (areasStore.areas.length === 0) {
    areasStore.loadAreas();
  }
});
</script>

<style scoped>
.script-link {
  color: var(--color-primary);
  text-decoration: none;
}

.scope-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--color-primary);
  color: var(--color-primary);
  text-decoration: none;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}

.scope-link:hover {
  background: var(--color-primary);
  color: var(--color-bg);
}

.scope-label {
  text-transform: uppercase;
  font-size: 11px;
  font-weight: 700;
  opacity: 0.8;
}

.muted {
  color: var(--color-muted);
}
</style>