Newer
Older
smart-home-server / webclient-vue / src / components / script / ScriptTable.vue
<template>
  <GnTable
    :columns="columns"
    :rows="scripts"
    :caption="caption"
  >
    <template #cell-alias="{ row }">
      <router-link
        :to="{ name: 'script-detail', params: { type: row.type || scriptType, 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 v-if="showActions" #cell-actions="{ row }">
      <GnButton
        v-if="row.state === 'enabled'"
        variant="secondary"
        icon="ph-pause"
        @click="toggle(row.alias, false, row)"
      >
        Disable
      </GnButton>
      <GnButton
        v-else
        variant="primary"
        icon="ph-play"
        @click="toggle(row.alias, true, row)"
      >
        Enable
      </GnButton>
    </template>
  </GnTable>
</template>

<script setup>
import { computed } from "vue";
import { useScriptsStore } from "../../stores/scripts";
import { useAreasStore } from "../../stores/areas";
import { GnTable, GnBadge, GnButton } from "gnexus-ui-kit/vue";

const props = defineProps({
  scripts: { type: Array, required: true },
  scriptType: { type: String, default: "regular" },
  showArea: { type: Boolean, default: false },
  showScope: { type: Boolean, default: true },
  showFilename: { type: Boolean, default: true },
  showActions: { type: Boolean, default: true },
  caption: { type: String, default: "Scripts" },
});

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 columns = computed(() => {
  const cols = [
    { key: "alias", label: "Alias" },
    { key: "name", label: "Name" },
  ];
  if (props.showScope) {
    cols.push({ key: "scope", label: "Scope" });
  }
  if (props.showArea) {
    cols.push({ key: "area", label: "Area" });
  }
  cols.push({ key: "state", label: "State" });
  if (props.showFilename) {
    cols.push({ key: "filename", label: "File" });
  }
  if (props.showActions) {
    cols.push({ key: "actions", label: "Actions" });
  }
  return cols;
});

function toggle(alias, enabled, row) {
  const type = row.type || props.scriptType;
  if (type === "action") {
    scriptsStore.setActionState(alias, enabled);
  } else if (type === "regular") {
    scriptsStore.setRegularState(alias, enabled);
  }
}
</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>