clients and sites rework and custom fields
This commit is contained in:
parent
0227519eab
commit
9bd7c8edd1
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 3.1.7 on 2021-03-17 14:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0014_customfield'),
|
||||
('agents', '0031_agent_alert_template'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AgentCustomField',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.TextField(blank=True, null=True)),
|
||||
('agent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_fields', to='agents.agent')),
|
||||
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='agent_fields', to='core.customfield')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -4,7 +4,7 @@ import re
|
|||
import time
|
||||
from collections import Counter
|
||||
from distutils.version import LooseVersion
|
||||
from typing import Any, Union
|
||||
from typing import Any
|
||||
|
||||
import msgpack
|
||||
import validators
|
||||
|
@ -18,7 +18,6 @@ from django.utils import timezone as djangotime
|
|||
from loguru import logger
|
||||
from nats.aio.client import Client as NATS
|
||||
from nats.aio.errors import ErrTimeout
|
||||
from packaging import version as pyver
|
||||
|
||||
from core.models import TZ_CHOICES, CoreSettings
|
||||
from logs.models import BaseAuditModel
|
||||
|
@ -837,3 +836,19 @@ class Note(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return self.agent.hostname
|
||||
|
||||
|
||||
class AgentCustomField(models.Model):
|
||||
agent = models.ForeignKey(
|
||||
Agent,
|
||||
related_name="custom_fields",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
field = models.ForeignKey(
|
||||
"core.CustomField",
|
||||
related_name="agent_fields",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
value = models.TextField(null=True, blank=True)
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
import pytz
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
|
||||
from clients.serializers import ClientSerializer
|
||||
from winupdate.serializers import WinUpdatePolicySerializer
|
||||
|
||||
from .models import Agent, Note
|
||||
from .models import Agent, AgentCustomField, Note
|
||||
from core.models import CustomField
|
||||
|
||||
|
||||
class AgentCustomFieldSerializer(serializers.ModelSerializer):
|
||||
agent_fields = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = AgentCustomField
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class AgentSerializer(serializers.ModelSerializer):
|
||||
|
@ -123,6 +133,11 @@ class AgentEditSerializer(serializers.ModelSerializer):
|
|||
winupdatepolicy = WinUpdatePolicySerializer(many=True, read_only=True)
|
||||
all_timezones = serializers.SerializerMethodField()
|
||||
client = ClientSerializer(read_only=True)
|
||||
customfields = SerializerMethodField()
|
||||
|
||||
def get_customfields(self, instance):
|
||||
customfields = CustomField.objects.filter(model="agent")
|
||||
return AgentCustomFieldSerializer(customfields, many=True).data
|
||||
|
||||
def get_all_timezones(self, obj):
|
||||
return pytz.all_timezones
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 3.1.7 on 2021-03-17 14:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0014_customfield'),
|
||||
('clients', '0009_auto_20210212_1408'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SiteCustomField',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.TextField(blank=True, null=True)),
|
||||
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='site_fields', to='core.customfield')),
|
||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_fields', to='clients.site')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ClientCustomField',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.TextField(blank=True, null=True)),
|
||||
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_fields', to='clients.client')),
|
||||
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='client_fields', to='core.customfield')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.1.7 on 2021-03-21 15:11
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('clients', '0010_clientcustomfield_sitecustomfield'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='site',
|
||||
unique_together={('client', 'name')},
|
||||
),
|
||||
]
|
|
@ -159,6 +159,7 @@ class Site(BaseAuditModel):
|
|||
|
||||
class Meta:
|
||||
ordering = ("name",)
|
||||
unique_together = (("client", "name"),)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -233,3 +234,35 @@ class Deployment(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return f"{self.client} - {self.site} - {self.mon_type}"
|
||||
|
||||
|
||||
class ClientCustomField(models.Model):
|
||||
client = models.ForeignKey(
|
||||
Client,
|
||||
related_name="custom_fields",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
field = models.ForeignKey(
|
||||
"core.CustomField",
|
||||
related_name="client_fields",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
value = models.TextField(null=True, blank=True)
|
||||
|
||||
|
||||
class SiteCustomField(models.Model):
|
||||
site = models.ForeignKey(
|
||||
Site,
|
||||
related_name="custom_fields",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
field = models.ForeignKey(
|
||||
"core.CustomField",
|
||||
related_name="site_fields",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
value = models.TextField(null=True, blank=True)
|
|
@ -8,17 +8,19 @@ class SiteSerializer(ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Site
|
||||
fields = "__all__"
|
||||
fields = (
|
||||
"id",
|
||||
"name",
|
||||
"server_policy",
|
||||
"workstation_policy",
|
||||
"client_name",
|
||||
"client",
|
||||
)
|
||||
|
||||
def validate(self, val):
|
||||
if "name" in val.keys() and "|" in val["name"]:
|
||||
raise ValidationError("Site name cannot contain the | character")
|
||||
|
||||
if self.context:
|
||||
client = Client.objects.get(pk=self.context["clientpk"])
|
||||
if Site.objects.filter(client=client, name=val["name"]).exists():
|
||||
raise ValidationError(f"Site {val['name']} already exists")
|
||||
|
||||
return val
|
||||
|
||||
|
||||
|
@ -27,16 +29,9 @@ class ClientSerializer(ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Client
|
||||
fields = "__all__"
|
||||
fields = ("id", "name", "server_policy", "workstation_policy", "sites")
|
||||
|
||||
def validate(self, val):
|
||||
|
||||
if "site" in self.context:
|
||||
if "|" in self.context["site"]:
|
||||
raise ValidationError("Site name cannot contain the | character")
|
||||
if len(self.context["site"]) > 255:
|
||||
raise ValidationError("Site name too long")
|
||||
|
||||
if "name" in val.keys() and "|" in val["name"]:
|
||||
raise ValidationError("Client name cannot contain the | character")
|
||||
|
||||
|
|
|
@ -4,10 +4,12 @@ from . import views
|
|||
|
||||
urlpatterns = [
|
||||
path("clients/", views.GetAddClients.as_view()),
|
||||
path("<int:pk>/client/", views.GetUpdateDeleteClient.as_view()),
|
||||
path("<int:pk>/client/", views.GetUpdateClient.as_view()),
|
||||
path("<int:pk>/<int:sitepk>/", views.DeleteClient.as_view()),
|
||||
path("tree/", views.GetClientTree.as_view()),
|
||||
path("sites/", views.GetAddSites.as_view()),
|
||||
path("<int:pk>/site/", views.GetUpdateDeleteSite.as_view()),
|
||||
path("sites/<int:pk>/", views.GetUpdateSite.as_view()),
|
||||
path("sites/<int:pk>/<int:sitepk>/", views.DeleteSite.as_view()),
|
||||
path("deployments/", views.AgentDeployment.as_view()),
|
||||
path("<int:pk>/deployment/", views.AgentDeployment.as_view()),
|
||||
path("<str:uid>/deploy/", views.GenerateAgent.as_view()),
|
||||
|
|
|
@ -30,27 +30,37 @@ class GetAddClients(APIView):
|
|||
|
||||
def post(self, request):
|
||||
|
||||
if "initialsetup" in request.data:
|
||||
client = {"name": request.data["client"]["client"].strip()}
|
||||
site = {"name": request.data["client"]["site"].strip()}
|
||||
serializer = ClientSerializer(data=client, context=request.data["client"])
|
||||
serializer.is_valid(raise_exception=True)
|
||||
# create client
|
||||
client_serializer = ClientSerializer(data=request.data["client"])
|
||||
client_serializer.is_valid(raise_exception=True)
|
||||
client = client_serializer.save()
|
||||
|
||||
# create site
|
||||
site_serializer = SiteSerializer(
|
||||
data={"client": client.id, "name": request.data["site"]["name"]}
|
||||
)
|
||||
|
||||
# make sure site serializer doesn't return errors and save
|
||||
if site_serializer.is_valid():
|
||||
site_serializer.save()
|
||||
else:
|
||||
# delete client since site serializer was invalid
|
||||
client.delete()
|
||||
site_serializer.is_valid(raise_exception=True)
|
||||
|
||||
if "initialsetup" in request.data.keys():
|
||||
core = CoreSettings.objects.first()
|
||||
core.default_time_zone = request.data["timezone"]
|
||||
core.save(update_fields=["default_time_zone"])
|
||||
else:
|
||||
client = {"name": request.data["client"].strip()}
|
||||
site = {"name": request.data["site"].strip()}
|
||||
serializer = ClientSerializer(data=client, context=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
obj = serializer.save()
|
||||
Site(client=obj, name=site["name"]).save()
|
||||
|
||||
return Response(f"{obj} was added!")
|
||||
return Response(f"{client} was added!")
|
||||
|
||||
|
||||
class GetUpdateDeleteClient(APIView):
|
||||
class GetUpdateClient(APIView):
|
||||
def get(self, request, pk):
|
||||
client = get_object_or_404(Client, pk=pk)
|
||||
return Response(ClientSerializer(client).data)
|
||||
|
||||
def put(self, request, pk):
|
||||
client = get_object_or_404(Client, pk=pk)
|
||||
|
||||
|
@ -58,16 +68,27 @@ class GetUpdateDeleteClient(APIView):
|
|||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
||||
return Response("The Client was renamed")
|
||||
return Response("The Client was updated")
|
||||
|
||||
|
||||
class DeleteClient(APIView):
|
||||
def delete(self, request, pk, sitepk):
|
||||
from automation.tasks import generate_all_agent_checks_task
|
||||
|
||||
def delete(self, request, pk):
|
||||
client = get_object_or_404(Client, pk=pk)
|
||||
agent_count = Agent.objects.filter(site__client=client).count()
|
||||
if agent_count > 0:
|
||||
agents = Agent.objects.filter(site__client=client)
|
||||
|
||||
if not sitepk:
|
||||
return notify_error(
|
||||
f"Cannot delete {client} while {agent_count} agents exist in it. Move the agents to another client first."
|
||||
"There needs to be a site specified to move existing agents to"
|
||||
)
|
||||
|
||||
site = get_object_or_404(Site, pk=sitepk)
|
||||
agents.update(site=site)
|
||||
|
||||
generate_all_agent_checks_task.delay("workstation", create_tasks=True)
|
||||
generate_all_agent_checks_task.delay("server", create_tasks=True)
|
||||
|
||||
client.delete()
|
||||
return Response(f"{client.name} was deleted!")
|
||||
|
||||
|
@ -84,39 +105,50 @@ class GetAddSites(APIView):
|
|||
return Response(SiteSerializer(sites, many=True).data)
|
||||
|
||||
def post(self, request):
|
||||
name = request.data["name"].strip()
|
||||
serializer = SiteSerializer(
|
||||
data={"name": name, "client": request.data["client"]},
|
||||
context={"clientpk": request.data["client"]},
|
||||
)
|
||||
serializer = SiteSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
||||
return Response("ok")
|
||||
|
||||
|
||||
class GetUpdateDeleteSite(APIView):
|
||||
class GetUpdateSite(APIView):
|
||||
def put(self, request, pk):
|
||||
|
||||
site = get_object_or_404(Site, pk=pk)
|
||||
|
||||
if site.client.id != request.data["client"] and site.client.sites.count() == 1:
|
||||
return notify_error("A client must have at least one site")
|
||||
|
||||
serializer = SiteSerializer(instance=site, data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
||||
return Response("ok")
|
||||
|
||||
def delete(self, request, pk):
|
||||
|
||||
class DeleteSite(APIView):
|
||||
def delete(self, request, pk, sitepk):
|
||||
from automation.tasks import generate_all_agent_checks_task
|
||||
|
||||
site = get_object_or_404(Site, pk=pk)
|
||||
if site.client.sites.count() == 1:
|
||||
return notify_error(f"A client must have at least 1 site.")
|
||||
|
||||
agent_count = Agent.objects.filter(site=site).count()
|
||||
agents = Agent.objects.filter(site=site)
|
||||
|
||||
if agent_count > 0:
|
||||
if not sitepk:
|
||||
return notify_error(
|
||||
f"Cannot delete {site.name} while {agent_count} agents exist in it. Move the agents to another site first."
|
||||
f"There needs to be a site specified to move the agents to"
|
||||
)
|
||||
|
||||
agent_site = get_object_or_404(Site, pk=sitepk)
|
||||
|
||||
agents.update(site=agent_site)
|
||||
|
||||
generate_all_agent_checks_task.delay("workstation", create_tasks=True)
|
||||
generate_all_agent_checks_task.delay("server", create_tasks=True)
|
||||
|
||||
site.delete()
|
||||
return Response(f"{site.name} was deleted!")
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import CoreSettings
|
||||
from .models import CoreSettings, CustomField
|
||||
|
||||
admin.site.register(CoreSettings)
|
||||
admin.site.register(CustomField)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 3.1.7 on 2021-03-17 14:45
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0013_coresettings_alert_template'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomField',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('order', models.PositiveIntegerField()),
|
||||
('model', models.CharField(choices=[('client', 'Client'), ('site', 'Site'), ('agent', 'Agent')], max_length=25)),
|
||||
('type', models.CharField(choices=[('text', 'Text'), ('number', 'Number'), ('single', 'Single'), ('multiple', 'Multiple'), ('checkbox', 'Checkbox'), ('datetime', 'DateTime')], default='text', max_length=25)),
|
||||
('options', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=255, null=True), blank=True, default=list, null=True, size=None)),
|
||||
('name', models.TextField(blank=True, null=True)),
|
||||
('default_value', models.TextField(blank=True, null=True)),
|
||||
('required', models.BooleanField(blank=True, default=False)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1.7 on 2021-03-18 20:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0014_customfield'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customfield',
|
||||
name='order',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.1.7 on 2021-03-19 15:36
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0015_auto_20210318_2034'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customfield',
|
||||
unique_together={('model', 'name')},
|
||||
),
|
||||
]
|
|
@ -216,3 +216,34 @@ class CoreSettings(BaseAuditModel):
|
|||
from .serializers import CoreSerializer
|
||||
|
||||
return CoreSerializer(core).data
|
||||
|
||||
|
||||
FIELD_TYPE_CHOICES = (
|
||||
("text", "Text"),
|
||||
("number", "Number"),
|
||||
("single", "Single"),
|
||||
("multiple", "Multiple"),
|
||||
("checkbox", "Checkbox"),
|
||||
("datetime", "DateTime"),
|
||||
)
|
||||
|
||||
MODEL_CHOICES = (("client", "Client"), ("site", "Site"), ("agent", "Agent"))
|
||||
|
||||
|
||||
class CustomField(models.Model):
|
||||
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
model = models.CharField(max_length=25, choices=MODEL_CHOICES)
|
||||
type = models.CharField(max_length=25, choices=FIELD_TYPE_CHOICES, default="text")
|
||||
options = ArrayField(
|
||||
models.CharField(max_length=255, null=True, blank=True),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
)
|
||||
name = models.TextField(null=True, blank=True)
|
||||
default_value = models.TextField(null=True, blank=True)
|
||||
required = models.BooleanField(blank=True, default=False)
|
||||
|
||||
class Meta:
|
||||
unique_together = (("model", "name"),)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from django.db.models import fields
|
||||
import pytz
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import CoreSettings
|
||||
from .models import CoreSettings, CustomField
|
||||
|
||||
|
||||
class CoreSettingsSerializer(serializers.ModelSerializer):
|
||||
|
@ -21,3 +22,9 @@ class CoreSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = CoreSettings
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class CustomFieldSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CustomField
|
||||
fields = "__all__"
|
|
@ -10,4 +10,6 @@ urlpatterns = [
|
|||
path("emailtest/", views.email_test),
|
||||
path("dashinfo/", views.dashboard_info),
|
||||
path("servermaintenance/", views.server_maintenance),
|
||||
path("customfields/", views.GetAddCustomFields.as_view()),
|
||||
path("customfields/<int:pk>/", views.GetUpdateDeleteCustomFields.as_view()),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.conf import settings
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import api_view
|
||||
|
@ -10,8 +11,8 @@ from rest_framework.views import APIView
|
|||
|
||||
from tacticalrmm.utils import notify_error
|
||||
|
||||
from .models import CoreSettings
|
||||
from .serializers import CoreSettingsSerializer
|
||||
from .models import CoreSettings, CustomField
|
||||
from .serializers import CoreSettingsSerializer, CustomFieldSerializer
|
||||
|
||||
|
||||
class UploadMeshAgent(APIView):
|
||||
|
@ -133,3 +134,46 @@ def server_maintenance(request):
|
|||
return Response(f"{records_count} records were pruned from the database")
|
||||
|
||||
return notify_error("The data is incorrect")
|
||||
|
||||
|
||||
class GetAddCustomFields(APIView):
|
||||
def get(self, request):
|
||||
fields = CustomField.objects.all()
|
||||
return Response(CustomFieldSerializer(fields, many=True).data)
|
||||
|
||||
def patch(self, request):
|
||||
if "model" in request.data.keys():
|
||||
fields = CustomField.objects.filter(model=request.data["model"])
|
||||
return Response(CustomFieldSerializer(fields, many=True).data)
|
||||
else:
|
||||
return notify_error("The request was invalid")
|
||||
|
||||
def post(self, request):
|
||||
serializer = CustomFieldSerializer(data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
||||
return Response("ok")
|
||||
|
||||
|
||||
class GetUpdateDeleteCustomFields(APIView):
|
||||
def get(self, request, pk):
|
||||
custom_field = get_object_or_404(CustomField, pk=pk)
|
||||
|
||||
return Response(CustomFieldSerializer(custom_field).data)
|
||||
|
||||
def put(self, request, pk):
|
||||
custom_field = get_object_or_404(CustomField, pk=pk)
|
||||
|
||||
serializer = CustomFieldSerializer(
|
||||
instance=custom_field, data=request.data, partial=True
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
||||
return Response("ok")
|
||||
|
||||
def delete(self, request, pk):
|
||||
get_object_or_404(CustomField, pk=pk).delete()
|
||||
|
||||
return Response("ok")
|
||||
|
|
|
@ -133,7 +133,6 @@ export default {
|
|||
})
|
||||
.catch(e => {
|
||||
this.$q.loading.hide();
|
||||
console.log({ e });
|
||||
this.notifyError("There was an issue resolving alert");
|
||||
});
|
||||
},
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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");
|
||||
},
|
||||
|
|
|
@ -1,189 +1,142 @@
|
|||
<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-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: "",
|
||||
},
|
||||
localClient: {
|
||||
name: "",
|
||||
site: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
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,
|
||||
};
|
||||
this.$axios
|
||||
.post("/clients/clients/", data)
|
||||
.post("/clients/clients/", { site: this.site, client: this.localClient })
|
||||
.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,
|
||||
};
|
||||
this.$axios
|
||||
.put(`/clients/${this.client.id}/client/`, this.client)
|
||||
.put(`/clients/${this.client.id}/client/`, this.localClient)
|
||||
.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);
|
||||
console.log({ e });
|
||||
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" },
|
||||
})
|
||||
.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);
|
||||
});
|
||||
});
|
||||
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();
|
||||
// Copy client prop locally
|
||||
if (this.editing) {
|
||||
this.localClient.id = this.client.id;
|
||||
this.localClient.name = this.client.name;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -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>
|
|
@ -1,197 +1,158 @@
|
|||
<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-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: {
|
||||
customFields: [],
|
||||
clientOptions: [],
|
||||
localSite: {
|
||||
id: null,
|
||||
client: null,
|
||||
name: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
this.$axios
|
||||
.post("/clients/sites/", data)
|
||||
.then(() => {
|
||||
this.$emit("close");
|
||||
this.$store.dispatch("loadTree");
|
||||
.post("/clients/sites/", this.localSite)
|
||||
.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,
|
||||
};
|
||||
|
||||
this.$axios
|
||||
.put(`/clients/${this.site.id}/site/`, data)
|
||||
.then(() => {
|
||||
this.$emit("edited");
|
||||
this.$emit("close");
|
||||
.put(`/clients/sites/${this.site.id}/`, this.localSite)
|
||||
.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" },
|
||||
})
|
||||
.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);
|
||||
});
|
||||
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();
|
||||
|
||||
// Copy site prop locally
|
||||
if (this.editing) {
|
||||
this.localSite.id = this.site.id;
|
||||
this.localSite.name = this.site.name;
|
||||
this.localSite.client = this.site.client;
|
||||
}
|
||||
|
||||
if (this.client) this.localSite.client = this.client;
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Notify, date } from "quasar";
|
||||
import axios from 'axios'
|
||||
|
||||
export function notifySuccessConfig(msg, timeout = 2000) {
|
||||
return {
|
||||
|
@ -139,6 +140,15 @@ export default {
|
|||
},
|
||||
capitalize(string) {
|
||||
return string[0].toUpperCase() + string.substring(1)
|
||||
},
|
||||
getCustomFields(model) {
|
||||
axios.patch("/core/customfields/", { model: model }).then(r => {
|
||||
return r.data
|
||||
})
|
||||
.catch(e => {
|
||||
this.notifyError("There was an issue getting Client Custom Fields")
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue