First version with collapsable device groups

Ylian Saint-Hilaire committed Dec 23, 2019 at 17:40 UTC 81fd0451a7affbcc23e7a939891236e3ddd4dcf0
4 files changed +31 -4
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.4.6-m",
3 + "version": "0.4.6-n",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/images/c1.png
Binary files /dev/null and b/public/images/c1.png differ
public/images/c2.png
Binary files /dev/null and b/public/images/c2.png differ
views/default.handlebars
+30 -3
@@ -2785,6 +2785,7 @@
2785 if (sort == 0) {
2786 // Mesh header
2787 if (node.meshid != current) {
2788 + if ((view == 1) && (current != null)) { r += '</div>'; } // Close collapse div
2789 deviceHeaderSet();
2790 var extra = '';
2791 if (view == 2) { r += '<tr><td colspan=5>'; }
@@ -2793,11 +2794,15 @@
2794 if (view == 2) { r += '<div>'; }
2795 r += '<div class=DevSt style=width:100%;padding-top:4px><span style=float:right>';
2796 r += '<span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx></span>' + extra;
2796 - r += '</span><span id=MxMESH tabindex=0 style=cursor:pointer onclick=gotoMesh("' + node.meshid + '") onkeypress="if (event.key==\'Enter\') gotoMesh(\'' + node.meshid + '\')">' + EscapeHtml(meshes[node.meshid].name) + '</span>' + getMeshActions(mesh2, meshrights) + '</div>';
2797 + r += '</span>';
2798 + var collapsed = CollapsedGroups[node.meshid];
2799 + r += '<img id=\"DevxColImg' + deviceHeaderId + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + deviceHeaderId + '\",\"' + node.meshid + '\")></img>'; // Collapse action
2800 + r += '<span id=MxMESH tabindex=0 style=cursor:pointer onclick=gotoMesh("' + node.meshid + '") onkeypress="if (event.key==\'Enter\') gotoMesh(\'' + node.meshid + '\')">' + EscapeHtml(meshes[node.meshid].name) + '</span>' + getMeshActions(mesh2, meshrights) + '</div>';
2801 if (view == 2) { r += '</div>'; }
2802 current = node.meshid;
2803 displayedMeshes[current] = 1;
2804 c = 0;
2805 + if (view == 1) { r += '<div id=DevxCol' + deviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
2806 }
2807 } else if (sort == 1) {
2808 // Power header
@@ -2876,6 +2881,8 @@
2881 if (typeof deviceHeaderCount[node.state] == 'undefined') { deviceHeaderCount[node.state] = 1; } else { deviceHeaderCount[node.state]++; }
2882 }
2883
2884 + if ((view == 1) && (current != null)) { r += '</div>'; } // Close collapse div
2885 +
2886 // Above 32 devices, gray out the auto connect feature.
2887 if (kvmDivs.length >= 32) { Q('autoConnectDesktopCheckbox').checked = false; }
2888 QE('autoConnectDesktopCheckbox', kvmDivs.length < 32);
@@ -2908,13 +2915,21 @@
2915
2916 // Display all empty device groups, we need to do this because users can add devices to these at any time.
2917 if ((sort == 0) && (Q('SearchInput').value == '') && (view < 3)) {
2918 + var deviceHeaderId2 = deviceHeaderId;
2919 for (var i in meshes) {
2920 var mesh = meshes[i], meshlink = mesh.links[userinfo._id];
2921 if (meshlink != null) {
2922 var meshrights = meshlink.rights;
2923 if (displayedMeshes[mesh._id] == null) {
2924 if ((current != '') && (r != '')) { r += '</tr></table>'; }
2917 - r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
2925 + r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt>';
2926 +
2927 + // Collapsing header & start collapsing area
2928 + deviceHeaderId2++;
2929 + var collapsed = CollapsedGroups[mesh._id];
2930 + r += '<img id=\"DevxColImg' + deviceHeaderId2 + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + deviceHeaderId2 + '\",\"' + mesh._id + '\")></img>'; // Collapse action
2931 +
2932 + r += '<span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
2933 r += getMeshActions(mesh, meshrights);
2934 r += '</span></td></tr><tr>';
2935 if (mesh.mtype == 1) {
@@ -2922,10 +2937,14 @@
2937 if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addDeviceToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
2938 }
2939 if (mesh.mtype == 2) {
2925 - r += '<td><div style=padding:10px><i>' + "No devices in this group";
2940 + r += '<td>';
2941 + if (view == 1) { r += '<div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
2942 + r += '<div style=padding:10px><i>' + "No devices in this group";
2943 if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addAgentToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
2944 }
2945 r += '.</i></div></td>';
2946 + if (view == 1) { r += '</div>'; } // End collapsing area
2947 +
2948 current = mesh._id;
2949 count++;
2950 }
@@ -3036,6 +3055,14 @@
3055 oldviewmode = view;
3056 }
3057
3058 + var CollapsedGroups = {};
3059 + function toggleCollapseGroup(id, id2) {
3060 + var x = (QS('DevxCol' + id)['display'] == 'none');
3061 + if (x) { delete CollapsedGroups[id2]; } else { CollapsedGroups[id2] = true; }
3062 + Q('DevxColImg' + id).src = x?'images/c2.png':'images/c1.png';
3063 + QV('DevxCol' + id, x);
3064 + }
3065 +
3066 function toggleKvmDevice(node) {
3067 if (typeof node == 'string') { node = getNodeFromId(node); } // Convert nodeid to node if needed
3068 var mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights;