From 8170d5ea730fc23b77dc75107e1f16ca6b5841b5 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Tue, 9 Mar 2021 03:17:43 +0000 Subject: [PATCH] feat: add client tree sorting closes #316 --- .../migrations/0013_user_client_tree_sort.py | 18 ++++++++++++ api/tacticalrmm/accounts/models.py | 10 ++++++- api/tacticalrmm/accounts/serializers.py | 12 ++++++++ api/tacticalrmm/accounts/tests.py | 12 ++------ api/tacticalrmm/accounts/views.py | 26 ++++------------- api/tacticalrmm/core/views.py | 2 +- .../modals/coresettings/UserPreferences.vue | 29 ++++++++++++++++++- web/src/store/index.js | 18 +++++++++--- web/src/views/Dashboard.vue | 5 +++- 9 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 api/tacticalrmm/accounts/migrations/0013_user_client_tree_sort.py diff --git a/api/tacticalrmm/accounts/migrations/0013_user_client_tree_sort.py b/api/tacticalrmm/accounts/migrations/0013_user_client_tree_sort.py new file mode 100644 index 00000000..89f94844 --- /dev/null +++ b/api/tacticalrmm/accounts/migrations/0013_user_client_tree_sort.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.7 on 2021-03-09 02:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0012_user_agents_per_page'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='client_tree_sort', + field=models.CharField(choices=[('alphafail', 'Move failing clients to the top'), ('alpha', 'Sort alphabetically')], default='alphafail', max_length=50), + ), + ] diff --git a/api/tacticalrmm/accounts/models.py b/api/tacticalrmm/accounts/models.py index 7ab03728..b50ea91d 100644 --- a/api/tacticalrmm/accounts/models.py +++ b/api/tacticalrmm/accounts/models.py @@ -15,6 +15,11 @@ AGENT_TBL_TAB_CHOICES = [ ("mixed", "Mixed"), ] +CLIENT_TREE_SORT_CHOICES = [ + ("alphafail", "Move failing clients to the top"), + ("alpha", "Sort alphabetically"), +] + class User(AbstractUser, BaseAuditModel): is_active = models.BooleanField(default=True) @@ -27,7 +32,10 @@ class User(AbstractUser, BaseAuditModel): default_agent_tbl_tab = models.CharField( max_length=50, choices=AGENT_TBL_TAB_CHOICES, default="server" ) - agents_per_page = models.PositiveIntegerField(default=50) + agents_per_page = models.PositiveIntegerField(default=50) # not currently used + client_tree_sort = models.CharField( + max_length=50, choices=CLIENT_TREE_SORT_CHOICES, default="alphafail" + ) agent = models.OneToOneField( "agents.Agent", diff --git a/api/tacticalrmm/accounts/serializers.py b/api/tacticalrmm/accounts/serializers.py index baebb7a3..003b2e57 100644 --- a/api/tacticalrmm/accounts/serializers.py +++ b/api/tacticalrmm/accounts/serializers.py @@ -4,6 +4,18 @@ from rest_framework.serializers import ModelSerializer, SerializerMethodField from .models import User +class UserUISerializer(ModelSerializer): + class Meta: + model = User + fields = [ + "dark_mode", + "show_community_scripts", + "agent_dblclick_action", + "default_agent_tbl_tab", + "client_tree_sort", + ] + + class UserSerializer(ModelSerializer): class Meta: model = User diff --git a/api/tacticalrmm/accounts/tests.py b/api/tacticalrmm/accounts/tests.py index 10c6d4bc..eb56e749 100644 --- a/api/tacticalrmm/accounts/tests.py +++ b/api/tacticalrmm/accounts/tests.py @@ -271,19 +271,13 @@ class TestUserAction(TacticalTestCase): def test_user_ui(self): url = "/accounts/users/ui/" - data = {"dark_mode": False} - r = self.client.patch(url, data, format="json") - self.assertEqual(r.status_code, 200) - - data = {"show_community_scripts": True} - r = self.client.patch(url, data, format="json") - self.assertEqual(r.status_code, 200) data = { - "userui": True, + "dark_mode": True, + "show_community_scripts": True, "agent_dblclick_action": "editagent", "default_agent_tbl_tab": "mixed", - "agents_per_page": 1000, + "client_tree_sort": "alpha", } r = self.client.patch(url, data, format="json") self.assertEqual(r.status_code, 200) diff --git a/api/tacticalrmm/accounts/views.py b/api/tacticalrmm/accounts/views.py index db60d576..00eb1230 100644 --- a/api/tacticalrmm/accounts/views.py +++ b/api/tacticalrmm/accounts/views.py @@ -14,7 +14,7 @@ from logs.models import AuditLog from tacticalrmm.utils import notify_error from .models import User -from .serializers import TOTPSetupSerializer, UserSerializer +from .serializers import TOTPSetupSerializer, UserSerializer, UserUISerializer class CheckCreds(KnoxLoginView): @@ -184,23 +184,9 @@ class TOTPSetup(APIView): class UserUI(APIView): def patch(self, request): - user = request.user - - if "dark_mode" in request.data.keys(): - user.dark_mode = request.data["dark_mode"] - user.save(update_fields=["dark_mode"]) - - if "show_community_scripts" in request.data.keys(): - user.show_community_scripts = request.data["show_community_scripts"] - user.save(update_fields=["show_community_scripts"]) - - if "userui" in request.data.keys(): - user.agent_dblclick_action = request.data["agent_dblclick_action"] - user.default_agent_tbl_tab = request.data["default_agent_tbl_tab"] - user.save(update_fields=["agent_dblclick_action", "default_agent_tbl_tab"]) - - if "agents_per_page" in request.data.keys(): - user.agents_per_page = request.data["agents_per_page"] - user.save(update_fields=["agents_per_page"]) - + serializer = UserUISerializer( + instance=request.user, data=request.data, partial=True + ) + serializer.is_valid(raise_exception=True) + serializer.save() return Response("ok") diff --git a/api/tacticalrmm/core/views.py b/api/tacticalrmm/core/views.py index 3c939cb2..af07a5cd 100644 --- a/api/tacticalrmm/core/views.py +++ b/api/tacticalrmm/core/views.py @@ -63,7 +63,7 @@ def dashboard_info(request): "show_community_scripts": request.user.show_community_scripts, "dbl_click_action": request.user.agent_dblclick_action, "default_agent_tbl_tab": request.user.default_agent_tbl_tab, - "agents_per_page": request.user.agents_per_page, + "client_tree_sort": request.user.client_tree_sort, } ) diff --git a/web/src/components/modals/coresettings/UserPreferences.vue b/web/src/components/modals/coresettings/UserPreferences.vue index bbffed77..dde086fa 100644 --- a/web/src/components/modals/coresettings/UserPreferences.vue +++ b/web/src/components/modals/coresettings/UserPreferences.vue @@ -46,6 +46,20 @@ class="col-4" /> + +
Client Sort:
+
+ +
@@ -68,8 +82,19 @@ export default { return { agentDblClickAction: "", defaultAgentTblTab: "", + clientTreeSort: "", tab: "ui", splitterModel: 20, + clientTreeSortOptions: [ + { + label: "Sort alphabetically, moving failing clients to the top", + value: "alphafail", + }, + { + label: "Sort alphabetically only", + value: "alpha", + }, + ], agentDblClickOptions: [ { label: "Edit Agent", @@ -105,17 +130,19 @@ export default { this.$axios.get("/core/dashinfo/").then(r => { this.agentDblClickAction = r.data.dbl_click_action; this.defaultAgentTblTab = r.data.default_agent_tbl_tab; + this.clientTreeSort = r.data.client_tree_sort; }); }, editUserPrefs() { const data = { - userui: true, agent_dblclick_action: this.agentDblClickAction, default_agent_tbl_tab: this.defaultAgentTblTab, + client_tree_sort: this.clientTreeSort, }; this.$axios.patch("/accounts/users/ui/", data).then(r => { this.notifySuccess("Preferences were saved!"); this.$emit("edited"); + this.$store.dispatch("loadTree"); this.$emit("close"); }); }, diff --git a/web/src/store/index.js b/web/src/store/index.js index e2e9a801..c5c58687 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -32,6 +32,7 @@ export default function () { showCommunityScripts: false, agentDblClickAction: "", defaultAgentTblTab: "server", + clientTreeSort: "alphafail", }, getters: { loggedIn(state) { @@ -139,6 +140,9 @@ export default function () { }, SET_DEFAULT_AGENT_TBL_TAB(state, tab) { state.defaultAgentTblTab = tab + }, + SET_CLIENT_TREE_SORT(state, val) { + state.clientTreeSort = val } }, actions: { @@ -215,7 +219,7 @@ export default function () { loadSites(context) { return axios.get("/clients/sites/"); }, - loadTree({ commit }) { + loadTree({ commit, state }) { axios.get("/clients/tree/").then(r => { if (r.data.length === 0) { @@ -263,9 +267,15 @@ export default function () { output.push(clientNode); } - // move failing clients to the top - const sortedByFailing = output.sort(a => a.color === "negative" ? -1 : 1) - commit("loadTree", sortedByFailing); + + if (state.clientTreeSort === "alphafail") { + // move failing clients to the top + const sortedByFailing = output.sort(a => a.color === "negative" ? -1 : 1); + commit("loadTree", sortedByFailing); + } else { + commit("loadTree", output); + } + }); }, checkVer(context) { diff --git a/web/src/views/Dashboard.vue b/web/src/views/Dashboard.vue index ee7e7e83..c028a067 100644 --- a/web/src/views/Dashboard.vue +++ b/web/src/views/Dashboard.vue @@ -712,7 +712,10 @@ export default { }, getDashInfo(edited = true) { this.$store.dispatch("getDashInfo").then(r => { - if (edited) this.$store.commit("SET_DEFAULT_AGENT_TBL_TAB", r.data.default_agent_tbl_tab); + if (edited) { + this.$store.commit("SET_DEFAULT_AGENT_TBL_TAB", r.data.default_agent_tbl_tab); + this.$store.commit("SET_CLIENT_TREE_SORT", r.data.client_tree_sort); + } this.darkMode = r.data.dark_mode; this.$q.dark.set(this.darkMode); this.currentTRMMVersion = r.data.trmm_version;