From 8e128500d025dc4b128d66cd8e516a9f638646e3 Mon Sep 17 00:00:00 2001 From: Zaid Abdulla Date: Tue, 24 Apr 2018 01:29:37 -0700 Subject: [PATCH] Implement reboot, reset, and delete --- client/components.js | 2 + client/components/device-actions.js | 73 +++++++++++++++ client/css/app.css | 10 ++- client/devices-page.js | 133 +++++++++++++++++++++++++++- config.json | 3 +- server/api.js | 2 +- 6 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 client/components/device-actions.js diff --git a/client/components.js b/client/components.js index 277f209..16c30e5 100644 --- a/client/components.js +++ b/client/components.js @@ -8,6 +8,7 @@ import container from "./components/container"; import summonButton from "./components/summon-button"; import deviceFaults from "./components/device-faults"; import allParameters from "./components/all-parameters"; +import deviceActions from "./components/device-actions"; function get(name) { if (name === "parameter") return parameter; @@ -18,6 +19,7 @@ function get(name) { else if (name === "summon-button") return summonButton; else if (name === "device-faults") return deviceFaults; else if (name === "all-parameters") return allParameters; + else if (name === "device-actions") return deviceActions; else throw new Error(`No such component '${name}'`); } diff --git a/client/components/device-actions.js b/client/components/device-actions.js new file mode 100644 index 0000000..30b4bfd --- /dev/null +++ b/client/components/device-actions.js @@ -0,0 +1,73 @@ +"use strict"; + +import m from "mithril"; +import * as taskQueue from "../task-queue"; +import * as notifications from "../notifications"; +import * as store from "../store"; + +const component = { + view: vnode => { + const device = vnode.attrs.device; + + const buttons = []; + + buttons.push( + m( + "button.primary", + { + title: "Reboot device", + onclick: () => { + taskQueue.queueTask({ + name: "reboot", + device: device["DeviceID.ID"].value[0] + }); + } + }, + "Reboot" + ) + ); + + buttons.push( + m( + "button.critical", + { + title: "Factory reset device", + onclick: () => { + taskQueue.queueTask({ + name: "factoryReset", + device: device["DeviceID.ID"].value[0] + }); + } + }, + "Reset" + ) + ); + + buttons.push( + m( + "button.primary", + { + title: "Delete device", + onclick: () => { + if (!confirm("Deleting this device. Are you sure?")) return; + + store + .deleteResource("devices", device["DeviceID.ID"].value[0]) + .then(() => { + notifications.push("success", "Device deleted"); + m.route.set("/devices"); + }) + .catch(err => { + notifications.push("error", err.message); + }); + } + }, + "Delete" + ) + ); + + return m(".device-actions", buttons); + } +}; + +export default component; diff --git a/client/css/app.css b/client/css/app.css index 0a27ddd..0da3c05 100644 --- a/client/css/app.css +++ b/client/css/app.css @@ -324,8 +324,7 @@ button { &.critical { border: 1px solid var(--color1); background-color: var(--color3); - font-weight: bold; - color: #900; + color: #700; } &:hover:enabled { background-color: var(--color4); @@ -490,3 +489,10 @@ span.na { } } } + +.device-actions { + margin-top: 10px; + & > button { + margin-right: 10px; + } +} diff --git a/client/devices-page.js b/client/devices-page.js index 0bf2109..4645494 100644 --- a/client/devices-page.js +++ b/client/devices-page.js @@ -8,6 +8,8 @@ import * as filterParser from "../common/filter-parser"; import Filter from "../common/filter"; import * as store from "./store"; import * as components from "./components"; +import * as taskQueue from "./task-queue"; +import * as notifications from "./notifications"; function init(args) { return new Promise(resolve => { @@ -126,6 +128,134 @@ function renderTable( ]); } +function renderActions(selected) { + const buttons = []; + + buttons.push( + m( + "button.primary", + { + title: "Reboot selected devices", + disabled: !selected.size, + onclick: () => { + for (let d of selected) + taskQueue.queueTask({ + name: "reboot", + device: d + }); + } + }, + "Reboot" + ) + ); + + buttons.push( + m( + "button.critical", + { + title: "Factory reset selected devices", + disabled: !selected.size, + onclick: () => { + for (let d of selected) + taskQueue.queueTask({ + name: "factoryReset", + device: d + }); + } + }, + "Reset" + ) + ); + + buttons.push( + m( + "button.primary", + { + title: "Delete selected devices", + disabled: !selected.size, + onclick: () => { + const ids = Array.from(selected); + if (!confirm(`Deleting ${ids.length} devices. Are you sure?`)) return; + + Promise.all(ids.map(id => store.deleteResource("devices", id))) + .then(() => { + notifications.push("success", `${ids.size} devices deleted`); + store.fulfill(0, Date.now()); + }) + .catch(err => { + notifications.push("error", err.message); + }); + } + }, + "Delete" + ) + ); + + buttons.push( + m( + "button.primary", + { + title: "Tag selected devices", + disabled: !selected.size, + onclick: () => { + const ids = Array.from(selected); + const tag = prompt(`Enter tag to assign to ${ids.length} devices:`); + if (!tag) return; + + Promise.all( + ids.map(id => { + store.updateTags(id, { [tag]: true }); + }) + ) + .then(() => { + notifications.push( + "success", + `Tag '${tag}' assigned to ${ids.length} devices` + ); + store.fulfill(0, Date.now()); + }) + .catch(err => notifications.push("error", err.message)); + } + }, + "Tag" + ) + ); + + buttons.push( + m( + "button.primary", + { + title: "Untag selected devices", + disabled: !selected.size, + onclick: () => { + const ids = Array.from(selected); + const tag = prompt( + `Enter tag to unassign from ${ids.length} devices:` + ); + if (!tag) return; + + Promise.all( + ids.map(id => { + store.updateTags(id, { [tag]: false }); + }) + ) + .then(() => { + notifications.push( + "success", + `Tag '${tag}' unassigned from ${ids.length} devices` + ); + store.fulfill(0, Date.now()); + }) + .catch(err => notifications.push("error", err.message)); + } + }, + "Untag" + ) + ); + + return m(".device-actions", buttons); +} + const component = { view: vnode => { document.title = "Devices - GenieACS"; @@ -168,7 +298,8 @@ const component = { count.value, selected, showMore - ) + ), + renderActions(selected) ]; } }; diff --git a/config.json b/config.json index c7aa865..ac6c194 100644 --- a/config.json +++ b/config.json @@ -94,5 +94,6 @@ "ui.device.2.parameters.3.label": "MAC address", "ui.device.2.parameters.3.parameter": "MACAddress", "ui.device.3.type": "device-faults", - "ui.device.4.type": "all-parameters" + "ui.device.4.type": "all-parameters", + "ui.device.5.type": "device-actions" } diff --git a/server/api.js b/server/api.js index 8c6ccc2..3b99eb6 100644 --- a/server/api.js +++ b/server/api.js @@ -8,7 +8,7 @@ const Filter = require("../common/filter"); const router = new Router(); const resources = { - devices: false, + devices: true, presets: true, provisions: true, files: true,