<template>
<section class="page">
<GnPageHeader title="Actions" kicker="Scripts">
<template #actions>
<GnBadge variant="primary">{{ scriptsStore.totalActions }} scripts</GnBadge>
</template>
</GnPageHeader>
<AppLoadingState v-if="scriptsStore.isLoadingActions" text="Loading actions" />
<AppErrorState
v-else-if="scriptsStore.errorActions"
title="Actions loading failed"
:message="scriptsStore.errorActions.message"
:retry="scriptsStore.loadActions"
/>
<AppEmptyState
v-else-if="scriptsStore.actions.length === 0"
title="No action scripts"
message="No action scripts registered."
/>
<div v-else class="area-grid">
<GnActionCard
v-for="script in scriptsStore.actions"
:key="script.alias"
:title="script.name"
@click="goToDetail(script.alias)"
>
<template #default>
<div v-html="script.icon" class="script-icon" />
<p v-if="script.description">{{ script.description }}</p>
<div class="script-meta">
<GnBadge :variant="script.state === 'enabled' ? 'success' : 'secondary'"
>{{ script.state }}</GnBadge>
<GnBadge v-if="areaFor(script)" variant="primary">{{ areaFor(script).display_name }}</GnBadge>
</div>
<small>{{ script.author }}</small>
</template>
<template #actions>
<GnButton
variant="primary"
icon="ph-play"
:loading="scriptsStore.isRunning(script.alias)"
:disabled="script.state !== 'enabled'"
@click.stop="run(script.alias)"
>
Run
</GnButton>
</template>
</GnActionCard>
</div>
<GnAlert v-if="resultAlert" :variant="resultAlert.variant" class="result-alert">
<strong>{{ resultAlert.title }}</strong>
<p v-if="resultAlert.message">{{ resultAlert.message }}</p>
</GnAlert>
</section>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
import { useRouter } from "vue-router";
import { useScriptsStore } from "../../../stores/scripts";
import { useAreasStore } from "../../../stores/areas";
import {
GnPageHeader,
GnBadge,
GnActionCard,
GnButton,
GnAlert,
} 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 resultAlert = ref(null);
function areaFor(script) {
if (!script.area_id) return null;
return areasStore.areas.find((a) => a.id === script.area_id) || null;
}
const resultAlertComputed = computed(() => {
const r = scriptsStore.lastRunResult;
if (!r) return null;
if (r.ok) {
return {
variant: "success",
title: `Ran ${r.alias}`,
message: r.execTime ? `Exec time: ${r.execTime}` : undefined,
};
}
return {
variant: "danger",
title: `Failed ${r.alias}`,
message: r.error?.message || "Unknown error",
};
});
async function run(alias) {
resultAlert.value = null;
const result = await scriptsStore.runScript(alias);
resultAlert.value = resultAlertComputed.value;
}
function goToDetail(alias) {
router.push({ name: "script-detail", params: { type: "actions", id: alias } });
}
onMounted(() => {
scriptsStore.loadActions();
if (areasStore.areas.length === 0) {
areasStore.loadAreas();
}
});
</script>
<style scoped>
.script-icon {
font-size: 32px;
}
.script-meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.result-alert {
margin-top: 24px;
}
</style>