mirror of
https://github.com/jpros/tacticalrmm-web.git
synced 2026-02-28 07:13:17 +00:00
Merge pull request #14 from sadnub/feature-policies-alerts
Automation UI improvements and Relations View
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
unelevated
|
||||
no-caps
|
||||
icon="add"
|
||||
@click="showPolicyFormModal = true;"
|
||||
@click="showAddPolicyModal"
|
||||
/>
|
||||
<q-btn
|
||||
ref="edit"
|
||||
@@ -82,9 +82,18 @@
|
||||
<q-td>{{ props.row.name }}</q-td>
|
||||
<q-td>{{ props.row.desc }}</q-td>
|
||||
<q-td>{{ props.row.active }}</q-td>
|
||||
<q-td>{{ props.row.clients.length }}</q-td>
|
||||
<q-td>{{ props.row.sites.length }}</q-td>
|
||||
<q-td>{{ props.row.agents.length }}</q-td>
|
||||
<q-td>
|
||||
<q-btn
|
||||
:label="`See Related (${props.row.clients.length + props.row.sites.length + props.row.agents.length}+)`"
|
||||
color="primary"
|
||||
dense
|
||||
flat
|
||||
unelevated
|
||||
no-caps
|
||||
@click="showRelationsModal(props.row)"
|
||||
size="sm"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
@@ -100,6 +109,9 @@
|
||||
<q-dialog v-model="showPolicyOverviewModal">
|
||||
<PolicyOverview @close="showPolicyOverviewModal = false" />
|
||||
</q-dialog>
|
||||
<q-dialog v-model="showRelationsViewModal">
|
||||
<RelationsView :policy="policy" @close="closeRelationsModal" />
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -109,15 +121,18 @@ import { mapState } from "vuex";
|
||||
import PolicyForm from "@/components/automation/modals/PolicyForm";
|
||||
import PolicyOverview from "@/components/automation/PolicyOverview";
|
||||
import PolicySubTableTabs from "@/components/automation/PolicySubTableTabs";
|
||||
import RelationsView from "@/components/automation/modals/RelationsView";
|
||||
|
||||
export default {
|
||||
name: "AutomationManager",
|
||||
components: { PolicyForm, PolicyOverview, PolicySubTableTabs },
|
||||
components: { PolicyForm, PolicyOverview, PolicySubTableTabs, RelationsView },
|
||||
mixins: [mixins],
|
||||
data() {
|
||||
return {
|
||||
showPolicyFormModal: false,
|
||||
showPolicyOverviewModal: false,
|
||||
showRelationsViewModal: false,
|
||||
policy: null,
|
||||
selected: [],
|
||||
pagination: {
|
||||
rowsPerPage: 0,
|
||||
@@ -148,28 +163,14 @@ export default {
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: "clients",
|
||||
label: "Clients",
|
||||
field: "clients",
|
||||
align: "left",
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: "sites",
|
||||
label: "Sites",
|
||||
field: "sites",
|
||||
align: "left",
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: "agents",
|
||||
label: "Agents",
|
||||
field: "agents",
|
||||
name: "actions",
|
||||
label: "Actions",
|
||||
field: "actions",
|
||||
align: "left",
|
||||
sortable: false
|
||||
}
|
||||
],
|
||||
visibleColumns: ["name", "desc", "active", "clients", "sites", "agents"]
|
||||
visibleColumns: ["name", "desc", "active", "actions"]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -205,6 +206,18 @@ export default {
|
||||
this.notifyError(`An Error occured while deleting policy`);
|
||||
});
|
||||
});
|
||||
},
|
||||
showRelationsModal(policy) {
|
||||
this.policy = policy;
|
||||
this.showRelationsViewModal = true;
|
||||
},
|
||||
closeRelationsModal() {
|
||||
this.policy = null;
|
||||
this.showRelationsViewModal = false;
|
||||
},
|
||||
showAddPolicyModal() {
|
||||
this.clearRow();
|
||||
this.showPolicyFormModal = true;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="Object.keys(checks).length === 0">No policy selected</div>
|
||||
<div v-if="Object.keys(checks).length === 0">No Policy Selected</div>
|
||||
<div class="row" v-else>
|
||||
<div class="col-12">
|
||||
<q-btn size="sm" color="grey-5" icon="fas fa-plus" label="Add Check" text-color="black">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<q-tree
|
||||
ref="Tree"
|
||||
:nodes="clientSiteTree"
|
||||
node-key="label"
|
||||
node-key="id"
|
||||
:selected.sync="selected"
|
||||
selected-color="primary"
|
||||
@update:selected="loadPolicyDetails"
|
||||
@@ -123,14 +123,20 @@ export default {
|
||||
|
||||
var result = [];
|
||||
|
||||
// Used by tree for unique identification
|
||||
let unique_id = 0;
|
||||
|
||||
for (let client in data) {
|
||||
var client_temp = {};
|
||||
|
||||
client_temp["label"] = client;
|
||||
client_temp["id"] = unique_id;
|
||||
client_temp["icon"] = "business";
|
||||
client_temp["selectable"] = false;
|
||||
client_temp["children"] = [];
|
||||
|
||||
unique_id--;
|
||||
|
||||
// Add any policies assigned to client
|
||||
if (data[client].policies.length > 0) {
|
||||
for (let policy in data[client].policies)
|
||||
@@ -145,9 +151,12 @@ export default {
|
||||
for (let site in data[client].sites) {
|
||||
var site_temp = {};
|
||||
site_temp["label"] = site;
|
||||
site_temp["id"] = unique_id;
|
||||
site_temp["icon"] = "apartment";
|
||||
site_temp["selectable"] = false;
|
||||
|
||||
unique_id--;
|
||||
|
||||
// Add any policies assigned to site
|
||||
if (data[client].sites[site].policies.length > 0) {
|
||||
site_temp["children"] = [];
|
||||
|
||||
@@ -27,34 +27,64 @@
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Clients:</div>
|
||||
<div class="col-10">
|
||||
<q-select v-model="selectedClients" :options="clientOptions" filled multiple use-chips>
|
||||
<q-select
|
||||
v-model="selectedClients"
|
||||
:options="clientOptions"
|
||||
filled
|
||||
multiple
|
||||
use-chips
|
||||
options-selected-class="text-green"
|
||||
>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
<q-item-section class="text-grey">No Results</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
<template v-slot:option="props">
|
||||
<q-item
|
||||
v-bind="props.itemProps"
|
||||
v-on="props.itemEvents"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon v-if="props.selected" name="check" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label v-html="props.opt.label" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Sites:</div>
|
||||
<div class="col-10">
|
||||
<q-select v-model="selectedSites" :options="siteOptions" filled multiple use-chips>
|
||||
<q-select
|
||||
v-model="selectedSites"
|
||||
:options="siteOptions"
|
||||
filled
|
||||
multiple
|
||||
use-chips
|
||||
options-selected-class="text-green"
|
||||
>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
<q-item-section class="text-grey">No Results</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Agents:</div>
|
||||
<div class="col-10">
|
||||
<q-select v-model="selectedAgents" :options="agentOptions" filled multiple use-chips>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
<q-item-section class="text-grey">No Results</q-item-section>
|
||||
<template v-slot:option="props">
|
||||
<q-item
|
||||
v-bind="props.itemProps"
|
||||
v-on="props.itemEvents"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon v-if="props.selected" name="check" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<!-- <q-item-label overline>{{ props.opt.client }}</q-item-label> -->
|
||||
<q-item-label v-html="props.opt.label" />
|
||||
<q-item-label caption>{{ props.opt.client }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
@@ -80,12 +110,10 @@ export default {
|
||||
name: "",
|
||||
desc: "",
|
||||
active: false,
|
||||
selectedAgents: [],
|
||||
selectedSites: [],
|
||||
selectedClients: [],
|
||||
clientOptions: [],
|
||||
siteOptions: [],
|
||||
agentOptions: []
|
||||
siteOptions: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -95,23 +123,26 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getPolicy() {
|
||||
this.$store.dispatch("automation/loadPolicy", this.pk).then(r => {
|
||||
this.name = r.data.name;
|
||||
this.desc = r.data.desc;
|
||||
this.active = r.data.active;
|
||||
this.selectedAgents = r.data.agents.map(agent => ({
|
||||
label: agent.hostname,
|
||||
value: agent.pk
|
||||
}) );
|
||||
this.selectedSites = r.data.sites.map(site => ({
|
||||
label: site.site,
|
||||
value: site.id
|
||||
}) );
|
||||
this.selectedClients = r.data.clients.map(client => ({
|
||||
label: client.client,
|
||||
value: client.id
|
||||
}) );
|
||||
});
|
||||
this.$q.loading.show();
|
||||
|
||||
this.$store
|
||||
.dispatch("automation/loadPolicy", this.pk)
|
||||
.then(r => {
|
||||
|
||||
this.$q.loading.hide();
|
||||
|
||||
this.name = r.data.name;
|
||||
this.desc = r.data.desc;
|
||||
this.active = r.data.active;
|
||||
this.selectedSites = r.data.sites.map(site => ({
|
||||
label: site.site,
|
||||
value: site.id
|
||||
}) );
|
||||
this.selectedClients = r.data.clients.map(client => ({
|
||||
label: client.client,
|
||||
value: client.id
|
||||
}) );
|
||||
});
|
||||
},
|
||||
submit() {
|
||||
if (!this.name) {
|
||||
@@ -126,7 +157,6 @@ export default {
|
||||
name: this.name,
|
||||
desc: this.desc,
|
||||
active: this.active,
|
||||
agents: this.selectedAgents.map(agent => agent.value),
|
||||
sites: this.selectedSites.map(site => site.value),
|
||||
clients: this.selectedClients.map(client => client.value)
|
||||
};
|
||||
@@ -164,10 +194,6 @@ export default {
|
||||
.dispatch("loadClients")
|
||||
.then(r => {
|
||||
this.clientOptions = this.formatClients(r.data);
|
||||
})
|
||||
.catch(e => {
|
||||
this.$q.loading.hide();
|
||||
this.notifyError(e.response.data);
|
||||
});
|
||||
},
|
||||
getSites() {
|
||||
@@ -175,21 +201,6 @@ export default {
|
||||
.dispatch("loadSites")
|
||||
.then(r => {
|
||||
this.siteOptions = this.formatSites(r.data);
|
||||
})
|
||||
.catch(e => {
|
||||
this.$q.loading.hide();
|
||||
this.notifyError(e.response.data);
|
||||
});
|
||||
},
|
||||
getAgents() {
|
||||
this.$store
|
||||
.dispatch("loadAgents")
|
||||
.then(r => {
|
||||
this.agentOptions = this.formatAgents(r.data);
|
||||
})
|
||||
.catch(e => {
|
||||
this.$q.loading.hide();
|
||||
this.notifyError(e.response.data);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -201,7 +212,6 @@ export default {
|
||||
|
||||
this.getClients();
|
||||
this.getSites();
|
||||
this.getAgents();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
97
src/components/automation/modals/RelationsView.vue
Normal file
97
src/components/automation/modals/RelationsView.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<q-card style="width: 60vw">
|
||||
<q-card-section class="row items-center">
|
||||
<div class="text-h6">{{ policy.name }} Relations</div>
|
||||
<q-space />
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
dense
|
||||
inline-label
|
||||
class="text-grey"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
align="left"
|
||||
narrow-indicator
|
||||
no-caps
|
||||
>
|
||||
<q-tab name="clients" label="Clients" />
|
||||
<q-tab name="sites" label="Sites" />
|
||||
<q-tab name="agents" label="Agents" />
|
||||
</q-tabs>
|
||||
|
||||
<q-separator />
|
||||
|
||||
<q-tab-panels v-model="tab" :animated="false">
|
||||
<q-tab-panel name="clients">
|
||||
<div class="text-h6">Clients</div>
|
||||
<q-list separator padding >
|
||||
<q-item :key="item.id" v-for="item in related.clients">
|
||||
<q-item-section>
|
||||
<q-item-label>{{ item.client }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="sites">
|
||||
<div class="text-h6">Sites</div>
|
||||
<q-list separator padding >
|
||||
<q-item :key="item.id" v-for="item in related.sites">
|
||||
<q-item-section>
|
||||
<q-item-label>{{ item.site }}</q-item-label>
|
||||
<q-item-label caption>{{ item.client_name }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="agents">
|
||||
<div class="text-h6">Agents</div>
|
||||
<q-list separator padding>
|
||||
<q-item :key="item.pk" v-for="item in related.agents">
|
||||
<q-item-section>
|
||||
<q-item-label>{{ item.hostname }}</q-item-label>
|
||||
<q-item-label caption>{{ item.client }} {{ item.site }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "RelationsView",
|
||||
props: {
|
||||
policy: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab: 'clients',
|
||||
related: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.$q.loading.show();
|
||||
|
||||
this.$store
|
||||
.dispatch("automation/getRelated", this.policy.id)
|
||||
.then(r => {
|
||||
this.$q.loading.hide();
|
||||
this.related = r.data
|
||||
})
|
||||
.catch(e => {
|
||||
this.$q.loading.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,28 +1,19 @@
|
||||
export default {
|
||||
methods: {
|
||||
formatClients(clients) {
|
||||
return clients.map(client => {
|
||||
return {
|
||||
return clients.map(client => ({
|
||||
label: client.client,
|
||||
value: client.id
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
formatSites(sites) {
|
||||
return sites.map(site => {
|
||||
return {
|
||||
label: `${site.client_name}\\${site.site}`,
|
||||
value: site.id
|
||||
};
|
||||
});
|
||||
},
|
||||
formatAgents(agents) {
|
||||
return agents.map(agent => {
|
||||
return {
|
||||
label: `${agent.client}\\${agent.site}\\${agent.hostname}`,
|
||||
value: agent.pk
|
||||
};
|
||||
});
|
||||
},
|
||||
return sites.map(site => ({
|
||||
label: site.site,
|
||||
value: site.id,
|
||||
client: site.client_name
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -56,12 +56,15 @@ export default {
|
||||
return axios.post("/automation/policies/", data);
|
||||
},
|
||||
editPolicy(context, data) {
|
||||
return axios.put(`/automation/policies/${data.id}/`, data)
|
||||
return axios.put(`/automation/policies/${data.id}/`, data);
|
||||
},
|
||||
deletePolicy(context, pk) {
|
||||
return axios.delete(`/automation/policies/${pk}`).then(r => {
|
||||
return axios.delete(`/automation/policies/${pk}/`).then(r => {
|
||||
context.dispatch("loadPolicies");
|
||||
});
|
||||
},
|
||||
getRelated(context, pk) {
|
||||
return axios.get(`/automation/policies/${pk}/related/`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user