diff --git a/webclient-vue/src/components/layout/PageActionsDropdown.vue b/webclient-vue/src/components/layout/PageActionsDropdown.vue
new file mode 100644
index 0000000..e147d71
--- /dev/null
+++ b/webclient-vue/src/components/layout/PageActionsDropdown.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/webclient-vue/src/features/areas/pages/AreaDetailPage.vue b/webclient-vue/src/features/areas/pages/AreaDetailPage.vue
index ea28edf..a585e5f 100644
--- a/webclient-vue/src/features/areas/pages/AreaDetailPage.vue
+++ b/webclient-vue/src/features/areas/pages/AreaDetailPage.vue
@@ -19,22 +19,7 @@
- Rename
-
- {{ area.parent_id > 0 ? 'Change parent area' : 'Assign to area' }}
-
-
- {{ isFavorite ? 'Unstar' : 'Star' }}
-
- Remove
+
@@ -172,6 +157,7 @@
import AreaAssignSection from "../../../components/area/AreaAssignSection.vue";
import DeviceTable from "../../../components/device/DeviceTable.vue";
import ScriptTable from "../../../components/script/ScriptTable.vue";
+import PageActionsDropdown from "../../../components/layout/PageActionsDropdown.vue";
const route = useRoute();
const router = useRouter();
@@ -189,6 +175,22 @@
const area = computed(() => areasStore.areasById[String(route.params.id)] || null);
const isFavorite = computed(() => (area.value ? favoritesStore.has(area.value.id) : false));
+
+const areaActions = computed(() => [
+ { label: "Rename", icon: "ph-pencil", onSelect: openRename },
+ {
+ label: area.value?.parent_id > 0 ? "Change parent area" : "Assign to area",
+ icon: "ph-map-pin",
+ onSelect: openAssign,
+ },
+ {
+ label: isFavorite.value ? "Unstar" : "Star",
+ icon: "ph-star",
+ onSelect: () => favoritesStore.toggle(area.value?.id),
+ },
+ { label: "Remove", icon: "ph-trash", danger: true, onSelect: openRemove },
+]);
+
const parentArea = computed(() => {
if (!area.value || area.value.parent_id <= 0) return null;
return areasStore.areasById[String(area.value.parent_id)] || null;
diff --git a/webclient-vue/src/features/devices/pages/DeviceDetailPage.vue b/webclient-vue/src/features/devices/pages/DeviceDetailPage.vue
index 133fc33..2d97298 100644
--- a/webclient-vue/src/features/devices/pages/DeviceDetailPage.vue
+++ b/webclient-vue/src/features/devices/pages/DeviceDetailPage.vue
@@ -12,23 +12,7 @@
- Edit
-
- {{ device.area_id ? 'Change area' : 'Assign to area' }}
-
-
- Reboot
-
- Remove
+
@@ -198,6 +182,7 @@
import AppLoadingState from "../../../components/feedback/AppLoadingState.vue";
import AreaBadgeLink from "../../../components/area/AreaBadgeLink.vue";
import AreaAssignSection from "../../../components/area/AreaAssignSection.vue";
+import PageActionsDropdown from "../../../components/layout/PageActionsDropdown.vue";
const route = useRoute();
const router = useRouter();
@@ -243,6 +228,22 @@
{ key: "type", label: "Type" },
];
+const deviceActions = computed(() => [
+ { label: "Edit", icon: "ph-pencil", onSelect: openEdit },
+ {
+ label: device.value?.area_id ? "Change area" : "Assign to area",
+ icon: "ph-map-pin",
+ onSelect: openAssign,
+ },
+ {
+ label: "Reboot",
+ icon: "ph-arrow-clockwise",
+ disabled: devicesStore.isRebooting(device.value?.id),
+ onSelect: reboot,
+ },
+ { label: "Remove", icon: "ph-trash", danger: true, onSelect: remove },
+]);
+
function openEdit() {
if (!device.value) return;
editForm.name = device.value.name || "";
diff --git a/webclient-vue/src/features/scripts/pages/ScriptDetailPage.vue b/webclient-vue/src/features/scripts/pages/ScriptDetailPage.vue
index 566bc3b..c6f994f 100644
--- a/webclient-vue/src/features/scripts/pages/ScriptDetailPage.vue
+++ b/webclient-vue/src/features/scripts/pages/ScriptDetailPage.vue
@@ -17,24 +17,7 @@
label="Enabled"
@update:model-value="toggleState($event)"
/>
-
- {{ script.area_id ? 'Change area' : 'Assign to area' }}
-
-
- Run
-
+
@@ -197,6 +180,7 @@
import AppEmptyState from "../../../components/feedback/AppEmptyState.vue";
import AreaBadgeLink from "../../../components/area/AreaBadgeLink.vue";
import AreaAssignSection from "../../../components/area/AreaAssignSection.vue";
+import PageActionsDropdown from "../../../components/layout/PageActionsDropdown.vue";
const route = useRoute();
const scriptsStore = useScriptsStore();
@@ -219,6 +203,26 @@
const isRegular = computed(() => scriptType.value === "regular");
const isScope = computed(() => scriptType.value === "scopes");
+const scriptActions = computed(() => {
+ const actions = [];
+ if (!isScope.value) {
+ actions.push({
+ label: script.value?.area_id ? "Change area" : "Assign to area",
+ icon: "ph-map-pin",
+ onSelect: openAssign,
+ });
+ }
+ if (isAction.value) {
+ actions.push({
+ label: "Run",
+ icon: "ph-play",
+ disabled: script.value?.state !== "enabled" || scriptsStore.isRunning(script.value?.alias),
+ onSelect: run,
+ });
+ }
+ return actions;
+});
+
const kicker = computed(() => {
if (isAction.value) return "Actions";
if (isRegular.value) return "Regular";