Merge pull request #337 from sadnub/develop

clients and sites rework and custom fields
This commit is contained in:
Dan
2021-03-25 22:24:43 -07:00
committed by GitHub
17 changed files with 1485 additions and 413 deletions

View File

@@ -133,7 +133,6 @@ export default {
})
.catch(e => {
this.$q.loading.hide();
console.log({ e });
this.notifyError("There was an issue resolving alert");
});
},

View File

@@ -0,0 +1,187 @@
<template>
<q-dialog ref="dialog" @hide="onHide">
<div class="q-dialog-plugin" style="width: 90vw; max-width: 90vw">
<q-card>
<q-bar>
<q-btn @click="getClients" class="q-mr-sm" dense flat push icon="refresh" />Clients Manager
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<div class="q-pa-sm" style="min-height: 65vh; max-height: 65vh">
<div class="q-gutter-sm">
<q-btn label="New" dense flat push unelevated no-caps icon="add" @click="showAddClient" />
</div>
<q-table
dense
:data="clients"
:columns="columns"
:pagination.sync="pagination"
row-key="id"
binary-state-sort
hide-pagination
virtual-scroll
:rows-per-page-options="[0]"
no-data-label="No Clients"
>
<!-- body slots -->
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer" @dblclick="showEditClient(props.row)">
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="showEditClient(props.row)">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showClientDeleteModal(props.row)">
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
<q-item-section>Delete</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup @click="showAddSite(props.row)">
<q-item-section side>
<q-icon name="add" />
</q-item-section>
<q-item-section>Add Site</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup>
<q-item-section>Close</q-item-section>
</q-item>
</q-list>
</q-menu>
<!-- name -->
<q-td>
{{ props.row.name }}
</q-td>
<q-td>
<span
style="cursor: pointer; text-decoration: underline"
class="text-primary"
@click="showSitesTable(props.row)"
>Show Sites</span
>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</q-card>
</div>
</q-dialog>
</template>
<script>
import mixins from "@/mixins/mixins";
import ClientsForm from "@/components/modals/clients/ClientsForm";
import SitesForm from "@/components/modals/clients/SitesForm";
import DeleteClient from "@/components/modals/clients/DeleteClient";
import SitesTable from "@/components/modals/clients/SitesTable";
export default {
name: "ClientsManager",
mixins: [mixins],
data() {
return {
clients: [],
columns: [
{ name: "name", label: "Name", field: "name", align: "left" },
{ name: "sites", label: "Sites", field: "sites", align: "left" },
],
pagination: {
rowsPerPage: 0,
sortBy: "name",
descending: true,
},
};
},
methods: {
getClients() {
this.$q.loading.show();
this.$axios
.get("clients/clients/")
.then(r => {
this.clients = r.data;
this.$q.loading.hide();
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("Unable to get Clients.");
});
},
showClientDeleteModal(client) {
this.$q
.dialog({
component: DeleteClient,
parent: this,
object: client,
type: "client",
})
.onOk(() => {
this.getClients();
});
},
showEditClient(client) {
this.$q
.dialog({
component: ClientsForm,
parent: this,
client: client,
})
.onOk(() => {
this.getClients();
});
},
showAddClient() {
this.$q
.dialog({
component: ClientsForm,
parent: this,
})
.onOk(() => {
this.getClients();
});
},
showAddSite(client) {
this.$q
.dialog({
component: SitesForm,
parent: this,
client: client.id,
})
.onOk(() => {
this.getClients();
});
},
showSitesTable(client) {
this.$q.dialog({
component: SitesTable,
parent: this,
client: client,
});
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
},
mounted() {
this.getClients();
},
};
</script>

View File

@@ -0,0 +1,92 @@
<template>
<q-input
v-if="field.type === 'text'"
ref="input"
outlined
dense
:label="field.name"
type="text"
:value="value"
@input="value => $emit('input', value)"
:rules="[...validationRules]"
reactive-rules
/>
<q-input
v-else-if="field.type === 'number'"
ref="input"
outlined
dense
:label="field.name"
type="number"
:value="value"
@input="value => $emit('input', value)"
:rules="[...validationRules]"
reactive-rules
/>
<q-toggle
v-else-if="field.type === 'checkbox'"
:label="field.name"
:value="value"
@input="value => $emit('input', value)"
/>
<q-input v-else-if="field.type === 'datetime'" outlined dense :value="value" @input="value => $emit('input', value)">
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy transition-show="scale" transition-hide="scale">
<q-date v-model="value" mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat />
</div>
</q-date>
</q-popup-proxy>
</q-icon>
<q-icon name="access_time" class="cursor-pointer">
<q-popup-proxy transition-show="scale" transition-hide="scale">
<q-time v-model="value" mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat />
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-select
v-else-if="field.type === 'single' || field.type === 'multiple'"
:value="value"
@input="value => $emit('input', value)"
outlined
dense
:options="field.options"
:multiple="field.type === 'multiple'"
:rules="[...validationRules]"
reactive-rules
/>
</template>
<script>
export default {
name: "CustomField",
props: ["field", "value"],
methods: {
validate(...args) {
return this.$refs.input.validate(...args);
},
},
computed: {
validationRules() {
const rules = [];
if (this.field.required) {
rules.push(val => !!val || `${this.field.name} is required`);
}
return rules;
},
},
};
</script>

View File

@@ -12,28 +12,11 @@
</q-item-section>
<q-menu anchor="top right" self="top left">
<q-list dense style="min-width: 100px">
<q-item clickable v-close-popup @click="showClientsFormModal('client', 'add')">
<q-item-section>Add Client</q-item-section>
<q-item clickable v-close-popup @click="showAddClientModal">
<q-item-section>Client</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showClientsFormModal('site', 'add')">
<q-item-section>Add Site</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-item>
<q-item clickable>
<q-item-section>Delete</q-item-section>
<q-item-section side>
<q-icon name="keyboard_arrow_right" />
</q-item-section>
<q-menu anchor="top right" self="top left">
<q-list dense style="min-width: 100px">
<q-item clickable v-close-popup @click="showClientsFormModal('client', 'delete')">
<q-item-section>Delete Client</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showClientsFormModal('site', 'delete')">
<q-item-section>Delete Site</q-item-section>
<q-item clickable v-close-popup @click="showAddSiteModal">
<q-item-section>Site</q-item-section>
</q-item>
</q-list>
</q-menu>
@@ -51,19 +34,6 @@
</q-list>
</q-menu>
</q-btn>
<!-- edit -->
<q-btn size="md" dense no-caps flat label="Edit">
<q-menu>
<q-list dense style="min-width: 100px">
<q-item clickable v-close-popup @click="showClientsFormModal('client', 'edit')">
<q-item-section>Edit Clients</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showClientsFormModal('site', 'edit')">
<q-item-section>Edit Sites</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
<!-- view -->
<q-btn size="md" dense no-caps flat label="View">
<q-menu auto-close>
@@ -95,6 +65,10 @@
<q-btn size="md" dense no-caps flat label="Settings">
<q-menu auto-close>
<q-list dense style="min-width: 100px">
<!-- clients manager -->
<q-item clickable v-close-popup @click="showClientsManager">
<q-item-section>Clients Manager</q-item-section>
</q-item>
<!-- script manager -->
<q-item clickable v-close-popup @click="showScriptManager = true">
<q-item-section>Script Manager</q-item-section>
@@ -143,14 +117,6 @@
</q-btn>
</q-btn-group>
<q-space />
<!-- client form modal -->
<q-dialog v-model="showClientFormModal" @hide="closeClientsFormModal">
<ClientsForm @close="closeClientsFormModal" :op="clientOp" @edited="edited" />
</q-dialog>
<!-- site form modal -->
<q-dialog v-model="showSiteFormModal" @hide="closeClientsFormModal">
<SitesForm @close="closeClientsFormModal" :op="clientOp" @edited="edited" />
</q-dialog>
<!-- edit core settings modal -->
<q-dialog v-model="showEditCoreSettingsModal">
<EditCoreSettings @close="showEditCoreSettingsModal = false" />
@@ -220,6 +186,7 @@
<script>
import LogModal from "@/components/modals/logs/LogModal";
import PendingActions from "@/components/modals/logs/PendingActions";
import ClientsManager from "@/components/ClientsManager";
import ClientsForm from "@/components/modals/clients/ClientsForm";
import SitesForm from "@/components/modals/clients/SitesForm";
import UpdateAgents from "@/components/modals/agents/UpdateAgents";
@@ -240,8 +207,6 @@ export default {
components: {
LogModal,
PendingActions,
ClientsForm,
SitesForm,
UpdateAgents,
ScriptManager,
EditCoreSettings,
@@ -253,13 +218,9 @@ export default {
Deployment,
ServerMaintenance,
},
props: ["clients"],
data() {
return {
showServerMaintenance: false,
showClientFormModal: false,
showSiteFormModal: false,
clientOp: null,
showUpdateAgentsModal: false,
showEditCoreSettingsModal: false,
showAdminManager: false,
@@ -275,20 +236,6 @@ export default {
};
},
methods: {
showClientsFormModal(type, op) {
this.clientOp = op;
if (type === "client") {
this.showClientFormModal = true;
} else if (type === "site") {
this.showSiteFormModal = true;
}
},
closeClientsFormModal() {
this.clientOp = null;
this.showClientFormModal = null;
this.showSiteFormModal = null;
},
showBulkActionModal(mode) {
this.bulkMode = mode;
this.showBulkAction = true;
@@ -309,6 +256,24 @@ export default {
parent: this,
});
},
showClientsManager() {
this.$q.dialog({
component: ClientsManager,
parent: this,
});
},
showAddClientModal() {
this.$q.dialog({
component: ClientsForm,
parent: this,
});
},
showAddSiteModal() {
this.$q.dialog({
component: SitesForm,
parent: this,
});
},
edited() {
this.$emit("edited");
},

View File

@@ -131,6 +131,9 @@
<q-checkbox v-model="agent.overdue_email_alert" label="Get overdue email alerts" />
<q-checkbox v-model="agent.overdue_text_alert" label="Get overdue sms alerts" />
</q-card-section>
<q-card-section v-for="field in customFields" :key="field.id">
<CustomField v-model="custom_fields[field.name]" :field="field" />
</q-card-section>
</q-tab-panel>
<!-- patch -->
<q-tab-panel name="patch">
@@ -151,13 +154,16 @@
import { mapGetters } from "vuex";
import mixins from "@/mixins/mixins";
import PatchPolicyForm from "@/components/modals/agents/PatchPolicyForm";
import CustomField from "@/components/CustomField";
export default {
name: "EditAgent",
components: { PatchPolicyForm },
components: { PatchPolicyForm, CustomField },
mixins: [mixins],
data() {
return {
customFields: [],
custom_fields: {},
agentLoaded: false,
clientsLoaded: false,
agent: {},
@@ -192,6 +198,14 @@ export default {
this.agent.client = { label: r.data.client.name, id: r.data.client.id, sites: r.data.client.sites };
this.agentLoaded = true;
for (let field of this.customFields) {
const value = r.data.custom_fields.find(value => value.field === field.id);
if (!!value) this.$set(this.custom_fields, field.name, value.value);
else if (!!field.default_value) this.$set(this.custom_fields, field.name, field.default_value);
else this.$set(this.custom_fields, field.name, "");
}
});
},
getClientsSites() {
@@ -219,7 +233,10 @@ export default {
}
this.$axios
.patch("/agents/editagent/", this.agent)
.patch("/agents/editagent/", {
...this.agent,
custom_fields: this.formatCustomFields(this.customFields, this.custom_fields),
})
.then(r => {
this.$emit("close");
this.$emit("edited");
@@ -237,6 +254,10 @@ export default {
},
},
created() {
// Get custom fields
this.getCustomFields("agent").then(r => {
this.customFields = r.data;
});
this.getAgentInfo();
this.getClientsSites();
},

View File

@@ -1,189 +1,180 @@
<template>
<q-card style="min-width: 400px">
<q-card-section class="row">
<q-card-actions align="left">
<div class="text-h6">{{ modalTitle }}</div>
</q-card-actions>
<q-space />
<q-card-actions align="right">
<q-btn v-close-popup flat round dense icon="close" />
</q-card-actions>
</q-card-section>
<q-card-section>
<q-form @submit.prevent="submit">
<q-card-section v-if="op === 'edit' || op === 'delete'">
<q-select
:rules="[val => !!val || '*Required']"
outlined
options-dense
label="Select client"
v-model="selected_client"
:options="client_options"
/>
</q-card-section>
<q-card-section v-if="op === 'add'">
<q-input
outlined
v-model="client.name"
label="Client"
:rules="[val => (val && val.length > 0) || '*Required']"
/>
</q-card-section>
<q-card-section v-if="op === 'add' || op === 'edit'">
<q-input
v-if="op === 'add'"
:rules="[val => !!val || '*Required']"
outlined
v-model="client.site"
label="Default first site"
/>
<q-input
v-else-if="op === 'edit'"
:rules="[val => !!val || '*Required']"
outlined
v-model="client.name"
label="Rename client"
/>
</q-card-section>
<q-card-actions align="left">
<q-btn
:label="capitalize(op)"
:color="op === 'delete' ? 'negative' : 'primary'"
type="submit"
class="full-width"
/>
</q-card-actions>
</q-form>
</q-card-section>
</q-card>
<q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="width: 60vw">
<q-bar>
{{ title }}
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-card-section>
<q-form @submit.prevent="submit">
<q-card-section>
<q-input
outlined
dense
v-model="localClient.name"
label="Name"
:rules="[val => (val && val.length > 0) || '*Required']"
/>
</q-card-section>
<q-card-section v-if="!editing">
<q-input
:rules="[val => !!val || '*Required']"
outlined
dense
v-model="site.name"
label="Default first site"
/>
</q-card-section>
<q-card-section v-for="field in customFields" :key="field.id">
<CustomField v-model="custom_fields[field.name]" :field="field" />
</q-card-section>
<q-card-actions align="right">
<q-btn dense flat label="Cancel" v-close-popup />
<q-btn dense flat label="Save" color="primary" type="submit" />
</q-card-actions>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
</template>
<script>
import CustomField from "@/components/CustomField";
import mixins from "@/mixins/mixins";
export default {
name: "ClientsForm",
components: {
CustomField,
},
mixins: [mixins],
props: {
op: !String,
clientpk: Number,
client: !Object,
},
data() {
return {
client_options: [],
selected_client: {},
client: {
id: null,
customFields: [],
site: {
name: "",
site: "",
},
localClient: {
name: "",
},
custom_fields: {},
};
},
watch: {
selected_client(newClient, oldClient) {
this.client.id = newClient.value;
this.client.name = newClient.label;
},
},
computed: {
modalTitle() {
if (this.op === "add") return "Add Client";
if (this.op === "edit") return "Edit Client";
if (this.op === "delete") return "Delete Client";
title() {
return this.editing ? "Edit Client" : "Add Client";
},
editing() {
return !!this.client;
},
},
methods: {
submit() {
if (this.op === "add") this.addClient();
if (this.op === "edit") this.editClient();
if (this.op === "delete") this.deleteClient();
},
getClients() {
this.$axios.get("/clients/clients/").then(r => {
this.client_options = r.data.map(client => ({ label: client.name, value: client.id }));
if (this.clientpk !== undefined && this.clientpk !== null) {
let client = this.client_options.find(client => client.value === this.clientpk);
this.selected_client = client;
} else {
this.selected_client = this.client_options[0];
}
});
if (!this.editing) this.addClient();
else this.editClient();
},
addClient() {
this.$q.loading.show();
const data = {
client: this.client.name,
site: this.client.site,
client: this.localClient,
site: this.site,
custom_fields: this.formatCustomFields(this.customFields, this.custom_fields),
};
this.$axios
.post("/clients/clients/", data)
.then(r => {
this.$emit("close");
this.$store.dispatch("loadTree");
this.$store.dispatch("getUpdatedSites");
this.refreshDashboardTree();
this.$q.loading.hide();
this.onOk();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
if (e.response.data.client) {
this.notifyError(e.response.data.client);
if (e.response.data.name) {
this.notifyError(e.response.data.name);
} else {
this.notifyError(e.response.data.non_field_errors);
this.notifyError(e.response.data);
}
});
},
editClient() {
this.$q.loading.show();
const data = {
id: this.client.id,
name: this.client.name,
client: this.localClient,
custom_fields: this.formatCustomFields(this.customFields, this.custom_fields),
};
this.$axios
.put(`/clients/${this.client.id}/client/`, this.client)
.put(`/clients/${this.client.id}/client/`, data)
.then(r => {
this.$emit("edited");
this.$emit("close");
this.refreshDashboardTree();
this.onOk();
this.$q.loading.hide();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
if (e.response.data.client) {
this.notifyError(e.response.data.client);
if (e.response.data.name) {
this.notifyError(e.response.data.name);
} else {
this.notifyError(e.response.data.non_field_errors);
this.notifyError(e.response.data);
}
});
},
deleteClient() {
this.$q
.dialog({
title: "Are you sure?",
message: `Delete client ${this.client.name}`,
cancel: true,
ok: { label: "Delete", color: "negative" },
getClient() {
this.$q.loading.show();
this.$axios
.get(`/clients/${this.client.id}/client/`)
.then(r => {
this.$q.loading.hide();
this.localClient.name = r.data.name;
for (let field of this.customFields) {
const value = r.data.custom_fields.find(value => value.field === field.id);
if (!!value) this.$set(this.custom_fields, field.name, value.value);
else if (!!field.default_value) this.$set(this.custom_fields, field.name, field.default_value);
else this.$set(this.custom_fields, field.name, "");
}
})
.onOk(() => {
this.$q.loading.show();
this.$axios
.delete(`/clients/${this.client.id}/client/`)
.then(r => {
this.$q.loading.hide();
this.$emit("edited");
this.$emit("close");
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data, 6000);
});
.catch(e => {
this.$q.loading.hide();
});
},
refreshDashboardTree() {
this.$store.dispatch("loadTree");
this.$store.dispatch("getUpdatedSites");
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
onOk() {
this.$emit("ok");
this.hide();
},
},
created() {
if (this.op !== "add") this.getClients();
// Get custom fields
this.getCustomFields("client").then(r => {
this.customFields = r.data;
});
// Copy client prop locally
if (this.editing) {
this.getClient();
}
},
};
</script>

View File

@@ -0,0 +1,161 @@
<template>
<q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin">
<q-bar>
Delete {{ object.name }}
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-form @submit="submit">
<q-card-section>
<q-select
label="Site to move agents to"
dense
options-dense
outlined
v-model="selectedSite"
:options="siteOptions"
map-options
emit-value
:rules="[val => !!val || 'Select the site that the agents should be moved to']"
>
<template v-slot:option="scope">
<q-item v-if="!scope.opt.category" v-bind="scope.itemProps" v-on="scope.itemEvents" class="q-pl-lg">
<q-item-section>
<q-item-label v-html="scope.opt.label"></q-item-label>
</q-item-section>
</q-item>
<q-item-label v-if="scope.opt.category" v-bind="scope.itemProps" header class="q-pa-sm">{{
scope.opt.category
}}</q-item-label>
</template>
</q-select>
</q-card-section>
<q-card-actions align="right">
<q-btn dense flat label="Cancel" v-close-popup />
<q-btn dense flat label="Delete" color="negative" type="submit" />
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<script>
import mixins from "@/mixins/mixins";
export default {
name: "DeleteClient",
mixins: [mixins],
props: {
object: !Object,
type: !String,
},
data() {
return {
siteOptions: [],
selectedSite: null,
};
},
methods: {
submit() {
if (this.type === "client") this.deleteClient();
else this.deleteSite();
},
deleteClient() {
this.$q
.dialog({
title: "Are you sure?",
message: `Delete client ${this.object.name}. Agents from all sites will be moved to the selected site`,
cancel: true,
ok: { label: "Delete", color: "negative" },
})
.onOk(() => {
this.$q.loading.show();
this.$axios
.delete(`/clients/${this.object.id}/${this.selectedSite}/`)
.then(r => {
this.refreshDashboardTree();
this.$q.loading.hide();
this.onOk();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data, 6000);
});
});
},
deleteSite() {
this.$q
.dialog({
title: "Are you sure?",
message: `Delete site ${this.object.name}. Agents from all sites will be moved to the selected site`,
cancel: true,
ok: { label: "Delete", color: "negative" },
})
.onOk(() => {
this.$q.loading.show();
this.$axios
.delete(`/clients/sites/${this.object.id}/${this.selectedSite}/`)
.then(r => {
this.refreshDashboardTree();
this.$q.loading.hide();
this.onOk();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data, 6000);
});
});
},
getSites() {
this.$axios.get("/clients/clients/").then(r => {
r.data.forEach(client => {
// remove client that is being deleted from options
if (this.type === "client") {
if (client.id !== this.object.id) {
this.siteOptions.push({ category: client.name });
client.sites.forEach(site => {
this.siteOptions.push({ label: site.name, value: site.id });
});
}
} else {
this.siteOptions.push({ category: client.name });
client.sites.forEach(site => {
if (site.id !== this.object.id) {
this.siteOptions.push({ label: site.name, value: site.id });
} else if (client.sites.length === 1) {
this.siteOptions.pop();
}
});
}
});
});
},
refreshDashboardTree() {
this.$store.dispatch("loadTree");
this.$store.dispatch("getUpdatedSites");
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
onOk() {
this.$emit("ok");
this.hide();
},
},
created() {
this.getSites();
},
};
</script>

View File

@@ -1,197 +1,195 @@
<template>
<q-card style="min-width: 400px">
<q-card-section class="row">
<q-card-actions align="left">
<div class="text-h6">{{ modalTitle }}</div>
</q-card-actions>
<q-space />
<q-card-actions align="right">
<q-btn v-close-popup flat round dense icon="close" />
</q-card-actions>
</q-card-section>
<q-card-section>
<q-form @submit.prevent="submit">
<q-card-section>
<q-select
:rules="[val => !!val || '*Required']"
outlined
options-dense
label="Select client"
v-model="selected_client"
:options="client_options"
@input="op === 'edit' || op === 'delete' ? (selected_site = sites[0]) : () => {}"
/>
</q-card-section>
<q-card-section v-if="op === 'edit' || op === 'delete'">
<q-select
:rules="[val => !!val || '*Required']"
outlined
options-dense
label="Select site"
v-model="selected_site"
:options="sites"
/>
</q-card-section>
<q-card-section v-if="op === 'add' || op === 'edit'">
<q-input
v-if="op === 'add'"
outlined
v-model="site.name"
label="Site"
:rules="[val => !!val || '*Required']"
/>
<q-input
v-else-if="op === 'edit'"
:rules="[val => !!val || '*Required']"
outlined
v-model="site.name"
label="Rename site"
/>
</q-card-section>
<q-card-actions align="left">
<q-btn
:label="capitalize(op)"
:color="op === 'delete' ? 'negative' : 'primary'"
type="submit"
class="full-width"
/>
</q-card-actions>
</q-form>
</q-card-section>
</q-card>
<q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="width: 60vw">
<q-bar>
{{ title }}
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-card-section>
<q-form @submit.prevent="submit">
<q-card-section>
<q-select
v-model="localSite.client"
label="Client"
:options="clientOptions"
outlined
dense
options-dense
map-options
emit-value
:rules="[val => !!val || 'Client is required']"
/>
</q-card-section>
<q-card-section>
<q-input
:rules="[val => !!val || 'Name is required']"
outlined
dense
v-model="localSite.name"
label="Name"
/>
</q-card-section>
<q-card-section v-for="field in customFields" :key="field.id">
<CustomField v-model="custom_fields[field.name]" :field="field" />
</q-card-section>
<q-card-actions align="right">
<q-btn dense flat label="Cancel" v-close-popup />
<q-btn dense flat label="Save" color="primary" type="submit" />
</q-card-actions>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
</template>
<script>
import CustomField from "@/components/CustomField";
import mixins from "@/mixins/mixins";
export default {
name: "SitesForm",
name: "ClientsForm",
components: {
CustomField,
},
mixins: [mixins],
props: {
op: !String,
sitepk: Number,
site: !Object,
client: !Number,
},
data() {
return {
client_options: [],
selected_client: null,
selected_site: null,
site: {
id: null,
customFields: [],
clientOptions: [],
localSite: {
client: null,
name: "",
},
custom_fields: {},
};
},
watch: {
selected_site(newSite, oldSite) {
this.site.id = newSite.value;
this.site.name = newSite.label;
},
},
computed: {
sites() {
return !!this.selected_client ? this.formatSiteOptions(this.selected_client.sites) : [];
title() {
return this.editing ? "Edit Site" : "Add Site";
},
modalTitle() {
if (this.op === "add") return "Add Site";
if (this.op === "edit") return "Edit Site";
if (this.op === "delete") return "Delete Site";
editing() {
return !!this.site;
},
},
methods: {
submit() {
if (this.op === "add") this.addSite();
if (this.op === "edit") this.editSite();
if (this.op === "delete") this.deleteSite();
},
getClients() {
this.$axios.get("/clients/clients/").then(r => {
this.client_options = this.formatClientOptions(r.data);
if (this.sitepk !== undefined && this.sitepk !== null) {
this.client_options.forEach(client => {
let site = client.sites.find(site => site.id === this.sitepk);
if (site !== undefined) {
this.selected_client = client;
this.selected_site = { value: site.id, label: site.name };
}
});
} else {
this.selected_client = this.client_options[0];
if (this.op !== "add") this.selected_site = this.sites[0];
}
});
if (!this.editing) this.addSite();
else this.editSite();
},
addSite() {
this.$q.loading.show();
const data = {
client: this.selected_client.value,
name: this.site.name,
site: this.localSite,
custom_fields: this.formatCustomFields(this.customFields, this.custom_fields),
};
this.$axios
.post("/clients/sites/", data)
.then(() => {
this.$emit("close");
this.$store.dispatch("loadTree");
.then(r => {
this.refreshDashboardTree();
this.$q.loading.hide();
this.notifySuccess(`Site ${this.site.name} was added!`);
this.onOk();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data.non_field_errors);
if (e.response.data.name) {
this.notifyError(e.response.data.name);
} else {
this.notifyError(e.response.data);
}
});
},
editSite() {
this.$q.loading.show();
const data = {
id: this.site.id,
name: this.site.name,
client: this.selected_client.value,
site: this.localSite,
custom_fields: this.formatCustomFields(this.customFields, this.custom_fields),
};
this.$axios
.put(`/clients/${this.site.id}/site/`, data)
.then(() => {
this.$emit("edited");
this.$emit("close");
.put(`/clients/sites/${this.site.id}/`, data)
.then(r => {
this.refreshDashboardTree();
this.onOk();
this.$q.loading.hide();
this.notifySuccess("Site was edited");
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data.non_field_errors);
if (e.response.data.name) {
this.notifyError(e.response.data.name);
} else {
this.notifyError(e.response.data);
}
});
},
deleteSite() {
this.$q
.dialog({
title: "Are you sure?",
message: `Delete site ${this.site.name}`,
cancel: true,
ok: { label: "Delete", color: "negative" },
getSite() {
this.$q.loading.show();
this.$axios
.get(`/clients/sites/${this.site.id}/`)
.then(r => {
this.$q.loading.hide();
this.localSite.name = r.data.name;
this.localSite.client = r.data.client;
for (let field of this.customFields) {
const value = r.data.custom_fields.find(value => value.field === field.id);
if (!!value) this.$set(this.custom_fields, field.name, value.value);
else if (!!field.default_value) this.$set(this.custom_fields, field.name, field.default_value);
else this.$set(this.custom_fields, field.name, "");
}
})
.onOk(() => {
this.$q.loading.show();
this.$axios
.delete(`/clients/${this.site.id}/site/`)
.then(r => {
this.$emit("edited");
this.$emit("close");
this.$q.loading.hide();
this.notifySuccess(r.data);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data, 6000);
});
.catch(e => {
this.$q.loading.hide();
});
},
refreshDashboardTree() {
this.$store.dispatch("loadTree");
this.$store.dispatch("getUpdatedSites");
},
getClients() {
this.$axios.get("/clients/clients/").then(r => {
r.data.forEach(client => {
this.clientOptions.push({ label: client.name, value: client.id });
});
});
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
onOk() {
this.$emit("ok");
this.hide();
},
},
created() {
this.getClients();
// Get custom fields
this.getCustomFields("site").then(r => {
this.customFields = r.data;
});
// Copy site prop locally
if (this.editing) {
this.getSite();
} else {
if (this.client) this.localSite.client = this.client;
}
},
};
</script>

View File

@@ -0,0 +1,152 @@
<template>
<q-dialog ref="dialog" @hide="onHide">
<div class="q-dialog-plugin" style="width: 60vw; max-width: 60vw">
<q-card>
<q-bar>
<q-btn @click="getSites" class="q-mr-sm" dense flat push icon="refresh" />Sites for {{ client.name }}
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<div class="q-pa-sm" style="min-height: 40vh; max-height: 40vh">
<div class="q-gutter-sm">
<q-btn label="New" dense flat push unelevated no-caps icon="add" @click="showAddSite" />
</div>
<q-table
dense
:data="sites"
:columns="columns"
:pagination.sync="pagination"
row-key="id"
binary-state-sort
hide-pagination
virtual-scroll
:rows-per-page-options="[0]"
no-data-label="No Sites"
>
<!-- body slots -->
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer" @dblclick="showEditSite(props.row)">
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="showEditSite(props.row)">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showSiteDeleteModal(props.row)">
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
<q-item-section>Delete</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup>
<q-item-section>Close</q-item-section>
</q-item>
</q-list>
</q-menu>
<!-- name -->
<q-td>
{{ props.row.name }}
</q-td>
</q-tr>
</template>
</q-table>
</div>
</q-card>
</div>
</q-dialog>
</template>
<script>
import mixins from "@/mixins/mixins";
import SitesForm from "@/components/modals/clients/SitesForm";
import DeleteClient from "@/components/modals/clients/DeleteClient";
export default {
name: "SitesTable",
mixins: [mixins],
props: {
client: !Object,
},
data() {
return {
sites: [],
columns: [{ name: "name", label: "Name", field: "name", align: "left" }],
pagination: {
rowsPerPage: 0,
sortBy: "name",
descending: true,
},
};
},
methods: {
getSites() {
this.$q.loading.show();
this.$axios
.get(`clients/${this.client.id}/client/`)
.then(r => {
this.sites = r.data.sites;
this.$q.loading.hide();
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("Unable to get Sites.");
});
},
showSiteDeleteModal(site) {
this.$q
.dialog({
component: DeleteClient,
parent: this,
object: site,
type: "site",
})
.onOk(() => {
this.getSites();
});
},
showEditSite(site) {
this.$q
.dialog({
component: SitesForm,
parent: this,
site: site,
client: site.client,
})
.onOk(() => {
this.getSites();
});
},
showAddSite() {
this.$q
.dialog({
component: SitesForm,
parent: this,
client: this.client.id,
})
.onOk(() => {
this.getSites();
});
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
},
mounted() {
this.getSites();
},
};
</script>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div class="row">
<div class="text-subtitle2">Custom Fields</div>
<q-space />
<q-btn
size="sm"
color="grey-5"
icon="fas fa-plus"
text-color="black"
label="Add custom field"
@click="addCustomField"
/>
</div>
<hr />
<div>
<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="client" label="Clients" />
<q-tab name="site" label="Sites" />
<q-tab name="agent" label="Agents" />
</q-tabs>
<q-separator />
<q-scroll-area :thumb-style="thumbStyle" style="height: 50vh">
<q-tab-panels v-model="tab" :animated="false">
<q-tab-panel name="client">
<CustomFieldsTable @refresh="getCustomFields" :data="clientFields" />
</q-tab-panel>
<q-tab-panel name="site">
<CustomFieldsTable @refresh="getCustomFields" :data="siteFields" />
</q-tab-panel>
<q-tab-panel name="agent">
<CustomFieldsTable @refresh="getCustomFields" :data="agentFields" />
</q-tab-panel>
</q-tab-panels>
</q-scroll-area>
</div>
</div>
</template>
<script>
import CustomFieldsTable from "@/components/modals/coresettings/CustomFieldsTable";
import CustomFieldsForm from "@/components/modals/coresettings/CustomFieldsForm";
export default {
name: "CustomFields",
components: {
CustomFieldsTable,
},
data() {
return {
tab: "client",
customFields: [],
thumbStyle: {
right: "2px",
borderRadius: "5px",
backgroundColor: "#027be3",
width: "5px",
opacity: 0.75,
},
};
},
computed: {
agentFields() {
return this.customFields.filter(field => field.model === "agent");
},
siteFields() {
return this.customFields.filter(field => field.model === "site");
},
clientFields() {
return this.customFields.filter(field => field.model === "client");
},
},
methods: {
getCustomFields() {
this.$q.loading.show();
this.$axios
.get(`/core/customfields/`)
.then(r => {
this.$q.loading.hide();
this.customFields = r.data;
})
.catch(e => {
this.$q.loading.hide();
});
},
addCustomField() {
this.$q
.dialog({
component: CustomFieldsForm,
parent: this,
model: this.tab,
})
.onOk(() => {
this.getCustomFields();
});
},
},
mounted() {
this.getCustomFields();
},
};
</script>

View File

@@ -0,0 +1,244 @@
<template>
<q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="width: 60vw">
<q-bar>
{{ title }}
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-form @submit="submit">
<q-card-section>
<q-select
label="Target"
:options="modelOptions"
map-options
emit-value
outlined
dense
v-model="localField.model"
:rules="[val => !!val || '*Required']"
/>
</q-card-section>
<q-card-section>
<q-input label="Name" outlined dense v-model="localField.name" :rules="[val => !!val || '*Required']" />
</q-card-section>
<q-card-section>
<q-select
label="Field Type"
:options="typeOptions"
@input="clear"
map-options
emit-value
outlined
dense
v-model="localField.type"
:rules="[val => !!val || '*Required']"
/>
</q-card-section>
<q-card-section v-if="localField.type === 'single' || localField.type == 'multiple'">
<q-select
dense
label="Input Options (press Enter after typing each option)"
filled
v-model="localField.options"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<!-- For datetime field -->
<q-input
v-if="localField.type === 'datetime'"
outlined
dense
v-model="localField.default_value"
:rules="[...defaultValueRules]"
reactive-rules
>
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy transition-show="scale" transition-hide="scale">
<q-date v-model="localField.default_value" mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat />
</div>
</q-date>
</q-popup-proxy>
</q-icon>
<q-icon name="access_time" class="cursor-pointer">
<q-popup-proxy transition-show="scale" transition-hide="scale">
<q-time v-model="localField.default_value" mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat />
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<!-- For Checkbox -->
<q-toggle
v-else-if="localField.type == 'checkbox'"
label="Default Value"
v-model="localField.default_value"
color="green"
/>
<!-- For dropdowns -->
<q-select
v-else-if="localField.type === 'single' || localField.type === 'multiple'"
label="Default Value"
:options="localField.options"
outlined
dense
:multiple="localField.type === 'multiple'"
v-model="localField.default_value"
:rules="[...defaultValueRules]"
reactive-rules
/>
<!-- For everything else -->
<q-input
v-else
label="Default Value"
:type="localField.type === 'text' ? 'text' : 'number'"
outlined
dense
v-model="localField.default_value"
:rules="[...defaultValueRules]"
reactive-rules
/>
</q-card-section>
<q-card-section>
<q-toggle
v-if="localField.type !== 'checkbox'"
label="Required"
v-model="localField.required"
color="green"
/>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Cancel" v-close-popup />
<q-btn flat label="Submit" color="primary" type="submit" />
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<script>
import mixins from "@/mixins/mixins";
export default {
name: "CustomFieldsForm",
mixins: [mixins],
props: { field: Object, model: String },
data() {
return {
localField: {
name: "",
model: "",
type: "",
options: [],
default_value: "",
required: false,
},
modelOptions: [
{ label: "Client", value: "client" },
{ label: "Site", value: "site" },
{ label: "Agent", value: "agent" },
],
typeOptions: [
{ label: "Text", value: "text" },
{ label: "Number", value: "number" },
{ label: "Dropdown Single", value: "single" },
{ label: "Dropdown Multiple", value: "multiple" },
{ label: "DateTime", value: "datetime" },
{ label: "Checkbox", value: "checkbox" },
],
};
},
computed: {
title() {
return this.editing ? "Edit Custom Field" : "Add Custom Field";
},
editing() {
return !!this.field;
},
defaultValueRules() {
if (this.localField.required) {
return [val => !!val || `Default Value needs to be set for required fields`];
}
},
},
methods: {
submit() {
this.$q.loading.show();
let data = {
...this.localField,
};
if (this.editing) {
this.$axios
.put(`/core/customfields/${data.id}/`, data)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Custom field edited!");
})
.catch(e => {
this.$q.loading.hide();
this.onOk();
this.notifyError("There was an error editing the custom field");
});
} else {
this.$axios
.post("/core/customfields/", data)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Custom field added!");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an error adding the custom field");
});
}
},
clear() {
this.localField.options = [];
this.localField.default_value =
this.localField.type === "single" || this.localField.type === "multiple" ? [] : "";
this.localField.required = false;
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
onOk() {
this.$emit("ok");
this.hide();
},
},
mounted() {
// If pk prop is set that means we are editting
if (this.field) Object.assign(this.localField, this.field);
// Set model to current tab
if (this.model) this.localField.model = this.model;
},
};
</script>

View File

@@ -0,0 +1,136 @@
<template>
<q-table
dense
:data="data"
:columns="columns"
:pagination.sync="pagination"
row-key="id"
binary-state-sort
hide-pagination
virtual-scroll
:rows-per-page-options="[0]"
no-data-label="No Custom Fields"
>
<!-- body slots -->
<template v-slot:body="props">
<q-tr
:props="props"
class="cursor-pointer"
@contextmenu="selectedTemplate = props.row"
@dblclick="editCustomField(props.row)"
>
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="editCustomField(props.row)">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="deleteCustomField(props.row)">
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
<q-item-section>Delete</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup>
<q-item-section>Close</q-item-section>
</q-item>
</q-list>
</q-menu>
<!-- name -->
<q-td>
{{ props.row.name }}
</q-td>
<!-- type -->
<q-td>
{{ props.row.type }}
</q-td>
<!-- default value -->
<q-td>
{{ props.row.default_value }}
</q-td>
<!-- required -->
<q-td>
<q-icon v-if="props.row.required" name="check" />
</q-td>
</q-tr>
</template>
</q-table>
</template>
<script>
import CustomFieldsForm from "@/components/modals/coresettings/CustomFieldsForm";
import mixins from "@/mixins/mixins"
export default {
name: "CustomFieldsTable",
mixins: [mixins],
props: {
data: !Array,
},
data() {
return {
pagination: {
rowsPerPage: 0,
sortBy: "name",
descending: true,
},
columns: [
{
name: "name",
label: "Name",
field: "name",
align: "left",
sortable: true,
},
{ name: "type", label: "Field Type", field: "type", align: "left", sortable: true, format: (string => this.capitalize(string)) },
{ name: "default_value", label: "Default Value", field: "default_value", align: "left", sortable: true },
{ name: "required", label: "Required", field: "required", align: "left", sortable: true },
],
};
},
methods: {
editCustomField(field) {
this.$q
.dialog({
component: CustomFieldsForm,
parent: this,
field: field,
})
.onOk(() => {
this.refresh();
});
},
deleteCustomField(field) {
this.$q
.dialog({
title: `Delete custom field ${field.name}?`,
cancel: true,
ok: { label: "Delete", color: "negative" },
})
.onOk(() => {
this.$q.loading.show();
this.$axios
.delete(`core/customfields/${field.id}/`)
.then(r => {
this.refresh();
this.$q.loading.hide();
this.notifySuccess(`Custom Field ${field.name} was deleted!`);
})
.catch(error => {
this.$q.loading.hide();
this.notifyError(`An Error occured while deleting custom field: ${field.name}`);
});
});
},
refresh() {
this.$emit("refresh");
},
},
};
</script>

View File

@@ -7,6 +7,7 @@
<q-tab name="emailalerts" label="Email Alerts" />
<q-tab name="smsalerts" label="SMS Alerts" />
<q-tab name="meshcentral" label="MeshCentral" />
<q-tab name="customfields" label="Custom Fields" />
</q-tabs>
</template>
<template v-slot:after>
@@ -289,10 +290,13 @@
<q-input dense filled v-model="settings.mesh_token" class="col-6" />
</q-card-section>
</q-tab-panel>
<q-tab-panel name="customfields">
<CustomFields />
</q-tab-panel>
</q-tab-panels>
</q-scroll-area>
<q-card-section class="row items-center">
<q-btn label="Save" color="primary" type="submit" />
<q-btn v-show="tab !== 'customfields'" label="Save" color="primary" type="submit" />
<q-btn
v-show="tab === 'emailalerts'"
label="Save and Test"
@@ -311,9 +315,13 @@
<script>
import mixins from "@/mixins/mixins";
import ResetPatchPolicy from "@/components/modals/coresettings/ResetPatchPolicy";
import CustomFields from "@/components/modals/coresettings/CustomFields";
export default {
name: "EditCoreSettings",
components: {
CustomFields,
},
mixins: [mixins],
data() {
return {

View File

@@ -1,4 +1,5 @@
import { Notify, date } from "quasar";
import axios from 'axios'
export function notifySuccessConfig(msg, timeout = 2000) {
return {
@@ -139,6 +140,23 @@ export default {
},
capitalize(string) {
return string[0].toUpperCase() + string.substring(1)
}
},
getCustomFields(model) {
return axios.patch("/core/customfields/", { model: model })
.catch(e => {
this.notifyError("There was an issue getting Custom Fields")
})
},
formatCustomFields(fields, values) {
let tempArray = []
for (let field of fields) {
let obj = { value: values[field.name], field: field.id }
tempArray.push(obj)
}
return tempArray
},
}
};

View File

@@ -238,6 +238,7 @@ export default function () {
raw: `Site|${site.id}`,
header: "generic",
icon: "apartment",
client: client.id,
server_policy: site.server_policy,
workstation_policy: site.workstation_policy,
alert_template: site.alert_template

View File

@@ -89,7 +89,7 @@
</q-header>
<q-page-container>
<FileBar :clients="clients" @edited="refreshEntireSite" />
<FileBar />
<q-splitter v-model="outsideModel">
<template v-slot:before>
<div v-if="!treeReady" class="q-pa-sm q-gutter-sm text-center" style="height: 30vh">
@@ -119,13 +119,13 @@
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="showEditModal(props.node, 'edit')">
<q-item clickable v-close-popup @click="showEditModal(props.node)">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showDeleteModal(props.node, 'delete')">
<q-item clickable v-close-popup @click="showDeleteModal(props.node)">
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
@@ -134,6 +134,18 @@
<q-separator></q-separator>
<q-item
v-if="props.node.children"
clickable
v-close-popup
@click="showAddSiteModal(props.node)"
>
<q-item-section side>
<q-icon name="add" />
</q-item-section>
<q-item-section>Add Site</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showToggleMaintenance(props.node)">
<q-item-section side>
<q-icon name="construction" />
@@ -338,24 +350,6 @@
</q-splitter>
</q-page-container>
<!-- client form modal -->
<q-dialog v-model="showClientsFormModal" @hide="closeClientsFormModal">
<ClientsForm
@close="closeClientsFormModal"
:op="clientOp"
:clientpk="deleteEditModalPk"
@edited="refreshEntireSite"
/>
</q-dialog>
<!-- edit site modal -->
<q-dialog v-model="showSitesFormModal" @hide="closeClientsFormModal">
<SitesForm
@close="closeClientsFormModal"
:op="clientOp"
:sitepk="deleteEditModalPk"
@edited="refreshEntireSite"
/>
</q-dialog>
<!-- install agent modal -->
<q-dialog v-model="showInstallAgentModal" @hide="closeInstallAgent">
<InstallAgent @close="closeInstallAgent" :sitepk="parseInt(sitePk)" />
@@ -369,7 +363,6 @@
<script>
import mixins from "@/mixins/mixins";
import { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
import { mapState, mapGetters } from "vuex";
import FileBar from "@/components/FileBar";
import AgentTable from "@/components/AgentTable";
@@ -378,6 +371,7 @@ import AlertsIcon from "@/components/AlertsIcon";
import PolicyAdd from "@/components/automation/modals/PolicyAdd";
import ClientsForm from "@/components/modals/clients/ClientsForm";
import SitesForm from "@/components/modals/clients/SitesForm";
import DeleteClient from "@/components/modals/clients/DeleteClient";
import InstallAgent from "@/components/modals/agents/InstallAgent";
import UserPreferences from "@/components/modals/coresettings/UserPreferences";
import AlertTemplateAdd from "@/components/modals/alerts/AlertTemplateAdd";
@@ -388,8 +382,6 @@ export default {
AgentTable,
SubTableTabs,
AlertsIcon,
ClientsForm,
SitesForm,
InstallAgent,
UserPreferences,
},
@@ -397,12 +389,8 @@ export default {
data() {
return {
darkMode: true,
showClientsFormModal: false,
showSitesFormModal: false,
deleteEditModalPk: null,
showInstallAgentModal: false,
sitePk: null,
clientOp: null,
serverCount: 0,
serverOfflineCount: 0,
workstationCount: 0,
@@ -621,53 +609,45 @@ export default {
});
},
showPolicyAdd(node) {
if (node.children) {
this.$q
.dialog({
component: PolicyAdd,
parent: this,
type: "client",
object: node,
})
.onOk(() => {
this.getTree();
});
} else {
this.$q
.dialog({
component: PolicyAdd,
parent: this,
type: "site",
object: node,
})
.onOk(() => {
this.getTree();
});
}
this.$q
.dialog({
component: PolicyAdd,
parent: this,
type: node.children ? "client" : "site",
object: node,
})
.onOk(() => {
this.getTree();
});
},
showEditModal(node, op) {
this.deleteEditModalPk = node.id;
this.clientOp = op;
if (node.children) {
this.showClientsFormModal = true;
} else {
this.showSitesFormModal = true;
}
showAddSiteModal(node) {
this.$q.dialog({
component: SitesForm,
parent: this,
client: node.id,
});
},
showDeleteModal(node, op) {
this.deleteEditModalPk = node.id;
this.clientOp = op;
showEditModal(node) {
let props = {};
if (node.children) {
this.showClientsFormModal = true;
props.client = { id: node.id, name: node.label };
} else {
this.showSitesFormModal = true;
props.site = { id: node.id, name: node.label, client: node.client };
}
this.$q.dialog({
component: node.children ? ClientsForm : SitesForm,
parent: this,
...props,
});
},
closeClientsFormModal() {
this.showClientsFormModal = false;
this.showSitesFormModal = false;
this.deleteEditModalPk = null;
this.clientOp = null;
showDeleteModal(node) {
this.$q.dialog({
component: DeleteClient,
parent: this,
object: { id: node.id, name: node.label },
type: node.children ? "client" : "site",
});
},
showInstallAgent(node) {
this.sitePk = node.id;
@@ -734,11 +714,11 @@ export default {
this.$store
.dispatch("toggleMaintenanceMode", data)
.then(response => {
this.$q.notify(notifySuccessConfig(text));
this.notifySuccess(text);
this.getTree();
})
.catch(error => {
this.$q.notify(notifyErrorConfig("An Error occured. Please try again"));
this.notifyError("An Error occured. Please try again");
});
},
menuMaintenanceText(node) {

View File

@@ -10,7 +10,7 @@
<q-form @submit.prevent="finish">
<q-card-section>
<div>Add Client:</div>
<q-input dense outlined v-model="client.client" :rules="[val => !!val || '*Required']">
<q-input dense outlined v-model="client.name" :rules="[val => !!val || '*Required']">
<template v-slot:prepend>
<q-icon name="business" />
</template>
@@ -18,7 +18,7 @@
</q-card-section>
<q-card-section>
<div>Add Site:</div>
<q-input dense outlined v-model="client.site" :rules="[val => !!val || '*Required']">
<q-input dense outlined v-model="site.name" :rules="[val => !!val || '*Required']">
<template v-slot:prepend>
<q-icon name="apartment" />
</template>
@@ -66,8 +66,10 @@ export default {
data() {
return {
client: {
client: null,
site: null,
name: "",
},
site: {
name: "",
},
meshagent: null,
allTimezones: [],
@@ -80,6 +82,7 @@ export default {
this.$q.loading.show();
const data = {
client: this.client,
site: this.site,
timezone: this.timezone,
initialsetup: true,
};