Skip to content

Commit 97df8a8

Browse files
committed
Improve duplicates UI
1 parent 8387404 commit 97df8a8

10 files changed

Lines changed: 205 additions & 44 deletions

OpenFlow/src/public/CommonControllers.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ export class entitiesCtrl<T> {
250250
public orderby: any = { _id: -1 };
251251
public autorefresh: boolean = false;
252252
public autorefreshinterval: number = 30 * 1000;
253+
public pagesize: number = 100;
253254
public autorefreshpromise: any = null;
254255
public preloadData: any = null;
255256
public postloadData: any = null;
@@ -320,11 +321,11 @@ export class entitiesCtrl<T> {
320321
query = { $and: [query, { $or: finalor.concat() }] };
321322
}
322323
}
323-
this.models = await NoderedUtil.Query(this.collection, query, this.baseprojection, this.orderby, 100, 0, null, this.basequeryas);
324+
this.models = await NoderedUtil.Query(this.collection, query, this.baseprojection, this.orderby, this.pagesize, 0, null, this.basequeryas);
324325
this.loading = false;
325326
if (this.autorefresh) {
326-
if (this.models.length >= 100) {
327-
// console.warn("Disabling auto refresh, result has more than 100 entries");
327+
if (this.models.length >= this.pagesize) {
328+
// console.warn("Disabling auto refresh, result has more than pagesize entries");
328329
} else {
329330
if (this.autorefreshpromise == null && this.searchstring === "") {
330331
//if (this.autorefreshpromise == null) {

OpenFlow/src/public/Controllers.ts

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,9 @@ export class RoleCtrl extends entityCtrl<Role> {
12351235
if (this.model._id) {
12361236
await NoderedUtil.UpdateOne(this.collection, null, this.model, 1, false, null);
12371237
} else {
1238-
await NoderedUtil.InsertOne(this.collection, this.model, 1, false, null);
1238+
this.model = await NoderedUtil.InsertOne(this.collection, this.model, 1, false, null);
1239+
// this.model = await NoderedUtil.InsertOne(this.collection, this.model, 1, false, null);
1240+
// this.model = await NoderedUtil.UpdateOne(this.collection, null, this.model, 1, false, null);
12391241
}
12401242
this.$location.path("/Roles");
12411243
if (!this.$scope.$$phase) { this.$scope.$apply(); }
@@ -4034,10 +4036,18 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
40344036
super($scope, $location, $routeParams, $interval, WebSocketClientService, api, userdata);
40354037
console.debug("DuplicatesCtrl");
40364038
this.autorefresh = true;
4037-
this.basequery = { _id: 'notthere' };
4039+
this.basequery = {};
40384040
this.collection = $routeParams.collection;
4039-
this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1 };
4041+
// this.baseprojection = { _type: 1, type: 1, name: 1, _created: 1, _createdby: 1, _modified: 1 };
4042+
this.pagesize = 1;
40404043
this.postloadData = this.processdata;
4044+
var checkList = document.getElementById('list1');
4045+
(checkList.getElementsByClassName('anchor')[0] as any).onclick = function (evt) {
4046+
if (checkList.classList.contains('visible'))
4047+
checkList.classList.remove('visible');
4048+
else
4049+
checkList.classList.add('visible');
4050+
}
40414051
if (this.userdata.data.DuplicatesCtrl) {
40424052
this.basequery = this.userdata.data.DuplicatesCtrl.basequery;
40434053
this.uniqeness = this.userdata.data.DuplicatesCtrl.uniqeness;
@@ -4071,14 +4081,23 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
40714081
this.loadData();
40724082
});
40734083
}
4084+
public keys: string[] = [];
40744085
async processdata() {
4086+
if (this.models.length > 0) {
4087+
this.keys = Object.keys(this.models[0]);
4088+
for (var i: number = this.keys.length - 1; i >= 0; i--) {
4089+
if (this.keys[i].startsWith('_') && this.keys[i] != "_type") this.keys.splice(i, 1);
4090+
}
4091+
this.keys.sort();
4092+
this.keys.reverse();
4093+
} else { this.keys = []; }
40754094
if (!NoderedUtil.IsNullEmpty(this.uniqeness)) {
40764095
var pipe: any[] = [];
40774096
var arr = this.uniqeness.split(",");
40784097
var group: any = { _id: {}, count: { "$sum": 1 } };
40794098
//if ("111".toLowerCase() == "22") {
40804099
group.items = {
4081-
$push: '$$ROOT._id'
4100+
$push: { "_id": '$$ROOT._id', "name": '$$ROOT.name' }
40824101
}
40834102
//}
40844103
arr.forEach(field => {
@@ -4108,15 +4127,33 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
41084127
this.userdata.data.DuplicatesCtrl.basequeryas = this.basequeryas;
41094128
if (!this.$scope.$$phase) { this.$scope.$apply(); }
41104129
}
4130+
ToggleUniqeness(model) {
4131+
var arr = [];
4132+
if (this.uniqeness != null && this.uniqeness != "") arr = this.uniqeness.split(',');
4133+
var index = arr.indexOf(model);
4134+
if (index > -1) {
4135+
arr.splice(index, 1);
4136+
this.uniqeness = arr.join(',');
4137+
} else {
4138+
arr.push(model);
4139+
this.uniqeness = arr.join(',');
4140+
}
4141+
if (!this.$scope.$$phase) { this.$scope.$apply(); }
4142+
this.loadData();
4143+
}
41114144
async ShowData(model) {
41124145
var modal: any = $("#exampleModal");
41134146
modal.modal();
41144147
this.model = model;
41154148
}
4116-
OpenEntity(id) {
4149+
async CloseModal() {
41174150
var modal: any = $("#exampleModal");
41184151
modal.modal('hide');
4119-
this.$location.path("/Entity/" + this.collection + "/" + id);
4152+
}
4153+
OpenEntity(model) {
4154+
var modal: any = $("#exampleModal");
4155+
modal.modal('hide');
4156+
this.$location.path("/Entity/" + this.collection + "/" + model._id);
41204157
if (!this.$scope.$$phase) { this.$scope.$apply(); }
41214158
return;
41224159

@@ -4125,8 +4162,8 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
41254162
this.loading = true;
41264163
for (var x = 0; x < this.models.length; x++) {
41274164
var item = (this.models[x] as any);
4128-
console.log("deleting " + item.items[0]);
4129-
await NoderedUtil.DeleteOne(this.collection, item.items[0], null);
4165+
console.log("deleting ", item.items[0]);
4166+
await NoderedUtil.DeleteOne(this.collection, item.items[0]._id, null);
41304167
}
41314168
this.loading = false;
41324169
this.loadData();
@@ -4136,8 +4173,8 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
41364173
for (var x = 0; x < this.models.length; x++) {
41374174
var item = (this.models[x] as any);
41384175
for (var y = 1; y < item.items.length; y++) {
4139-
console.log("deleting " + item.items[y]);
4140-
await NoderedUtil.DeleteOne(this.collection, item.items[y], null);
4176+
console.log("deleting ", item.items[y]);
4177+
await NoderedUtil.DeleteOne(this.collection, item.items[y]._id, null);
41414178
}
41424179
}
41434180
this.loading = false;
@@ -4148,8 +4185,8 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
41484185
for (var x = 0; x < this.models.length; x++) {
41494186
var item = (this.models[x] as any);
41504187
for (var y = 0; y < item.items.length; y++) {
4151-
console.log("deleting " + item.items[y]);
4152-
await NoderedUtil.DeleteOne(this.collection, item.items[y], null);
4188+
console.log("deleting ", item.items[y]);
4189+
await NoderedUtil.DeleteOne(this.collection, item.items[y]._id, null);
41534190
}
41544191
}
41554192
this.loading = false;
@@ -4160,8 +4197,19 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
41604197
if (NoderedUtil.IsNullUndefinded(model.items)) return;
41614198
if (model.items.length < 2) return;
41624199
this.loading = true;
4163-
console.log("deleting " + model.items[0]);
4164-
await NoderedUtil.DeleteOne(this.collection, model.items[0], null);
4200+
console.log("deleting ", model.items[0]);
4201+
await NoderedUtil.DeleteOne(this.collection, model.items[0]._id, null);
4202+
this.loading = false;
4203+
this.loadData();
4204+
}
4205+
async DeleteAllButOne(model) {
4206+
if (NoderedUtil.IsNullUndefinded(model)) return;
4207+
if (NoderedUtil.IsNullUndefinded(model.items)) return;
4208+
this.loading = true;
4209+
for (var i = 1; i < model.items.length; i++) {
4210+
console.log("deleting ", model.items[i]);
4211+
await NoderedUtil.DeleteOne(this.collection, model.items[i]._id, null);
4212+
}
41654213
this.loading = false;
41664214
this.loadData();
41674215
}
@@ -4170,10 +4218,20 @@ export class DuplicatesCtrl extends entitiesCtrl<Base> {
41704218
if (NoderedUtil.IsNullUndefinded(model.items)) return;
41714219
this.loading = true;
41724220
for (var i = 0; i < model.items.length; i++) {
4173-
console.log("deleting " + model.items[i]);
4174-
await NoderedUtil.DeleteOne(this.collection, model.items[i], null);
4221+
console.log("deleting ", model.items[i]);
4222+
await NoderedUtil.DeleteOne(this.collection, model.items[i]._id, null);
41754223
}
41764224
this.loading = false;
41774225
this.loadData();
41784226
}
4227+
async ModalDeleteOne(model) {
4228+
this.loading = true;
4229+
await NoderedUtil.DeleteOne(this.collection, model._id, null);
4230+
var arr: any[] = (this.model as any).items;
4231+
arr = arr.filter(x => x._id != model._id);
4232+
(this.model as any).items = arr;
4233+
this.loading = false;
4234+
if (!this.$scope.$$phase) { this.$scope.$apply(); }
4235+
this.loadData();
4236+
}
41794237
}

OpenFlow/src/public/Duplicates.html

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,56 @@ <h1 translate lib="web">duplicates tracking</h1>
2424
</div>
2525
</div>
2626
</div>
27+
<div class="form-group form-horizontal">
28+
<div class="input-group unframed-addons col-sm-9" ng-show="ctrl.keys.length < 8">
29+
<div ng-repeat="model in ctrl.keys" class="border">
30+
<input type="checkbox" ng-model="ctrl.uniqeness.split(',').indexOf(model) > -1"
31+
ng-click="ctrl.ToggleUniqeness(model)" class="form-control input-md" ng-model-options="{debounce: 400}" />
32+
{{model}}
33+
</div>
34+
</div>
35+
</div>
36+
<div class="form-group form-horizontal" ng-show="ctrl.keys.length >= 8">
37+
<div class="input-group unframed-addons col-sm-9 ">
38+
<div id="list1" class="dropdown-check-list" tabindex="100">
39+
<span class="anchor">Select fields</span>
40+
<ul class="items">
41+
<li ng-repeat="model in ctrl.keys"><input type="checkbox"
42+
ng-model="ctrl.uniqeness.split(',').indexOf(model) > -1" ng-click="ctrl.ToggleUniqeness(model)" />{{model}}
43+
</li>
44+
</ul>
45+
</div>
46+
</div>
47+
</div>
2748

2849
</div>
2950
<table id=" table1" class="table table-striped table-hover table-sm" when-scrolled="ctrl.more()" style="width: 100%;">
3051
<thead class="thead-dark">
3152
<tr>
3253
<th scope="col" ng-click="ctrl.ToggleOrder('_id')"><b>_id</b></th>
54+
<th scope="col" ng-click="ctrl.ToggleOrder('name')"><b>name</b></th>
3355
<th scope="col" ng-click="ctrl.ToggleOrder('count')"><b>count</b></th>
34-
<th></th>
35-
<th></th>
36-
<th></th>
56+
<th scope="col"></th>
57+
<th scope="col"></th>
58+
<th scope="col"></th>
3759
</tr>
3860
</thead>
3961
<tbody>
40-
<tr ng-repeat="model in ctrl.models" ng-click="ctrl.ShowData(model)">
41-
<td>{{model._id}}</td>
62+
<tr ng-repeat="model in ctrl.models">
63+
<td><a ng-click="ctrl.ShowData(model)">{{model._id}}</a></td>
64+
<td><a ng-click="ctrl.ShowData(model)">{{model.items[0].name}}</a></td>
4265
<td>{{model.count}}</td>
43-
<td class="btn-cell">
44-
<!-- <a ng-href="#/History/{{ctrl.collection}}/{{model._id}}" class="table-btn">Hist</a> -->
45-
</td>
4666
<td class="btn-cell">
4767
<a href ng-click="ctrl.DeleteOnlyOne(model)" ng-disabled="ctrl.loading==true" class="table-btn"><i
4868
class="az-trash"></i> one</a>
4969
</td>
5070
<td class="btn-cell">
51-
<a href ng-click="ctrl.DeleteAll(model)" ng-disabled="ctrl.loading==true" class="table-btn"><i
52-
class="az-trash"></i> all</a>
71+
<a href ng-click="ctrl.DeleteAllButOne(model)" ng-disabled="ctrl.loading==true" class="table-btn"><i
72+
class="az-trash"></i> all but one</a>
73+
</td>
74+
<td class="btn-cell">
75+
<a href ng-click="ctrl.DeleteAll(model)" ng-disabled="ctrl.loading==true" ng-disabled="ctrl.loading==true"
76+
class="table-btn"><i class="az-trash"></i> all</a>
5377
</td>
5478
</tr>
5579
</tbody>
@@ -67,14 +91,92 @@ <h5 class="modal-title">{{ctrl.model.name}} </h5>
6791
</button>
6892
</div>
6993
<div class="modal-body">
70-
<div ng-repeat="model in ctrl.model.items">
71-
<a ng-click="ctrl.OpenEntity(model)" class="table-btn"><i class="az-edit"></i>{{ model }}</a>
72-
94+
<table id=" table1" class="table table-striped table-hover table-sm" when-scrolled="ctrl.more()"
95+
style="width: 100%;">
96+
<thead class="thead-dark">
97+
<th scope="col"><b>id</b></th>
98+
<th scope="col"><b>name</b></th>
99+
<th></th>
100+
</thead>
101+
<tbody>
102+
<tr ng-repeat="model in ctrl.model.items">
103+
<td class="btn-cell">
104+
<!-- <a ng-click="ctrl.OpenEntity(model)" class="table-btn"><i class="az-edit"></i>{{ model }}</a> -->
105+
<a ng-href="#/Entity/{{ctrl.collection}}/{{model._id}}" ng-click="ctrl.CloseModal()"
106+
class="table-btn"><i class="az-edit"></i>{{ model._id }}</a>
107+
</td>
108+
<td class="btn-cell">
109+
{{ model.name }}
110+
</td>
111+
<td class="btn-cell">
112+
<a href ng-click="ctrl.ModalDeleteOne(model)" ng-disabled="ctrl.loading==true" class="table-btn"><i
113+
class="az-trash"></i></a>
114+
</td>
115+
</tr>
116+
</tbody>
117+
</table>
118+
<div>
73119
</div>
74120
</div>
75121
<div class="modal-footer">
76122
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
77123
</div>
78124
</div>
79125
</div>
80-
</div>
126+
</div>
127+
128+
129+
130+
<style>
131+
.dropdown-check-list {
132+
display: inline-block;
133+
}
134+
135+
.dropdown-check-list .anchor {
136+
position: relative;
137+
cursor: pointer;
138+
display: inline-block;
139+
padding: 5px 50px 5px 10px;
140+
border: 1px solid #ccc;
141+
}
142+
143+
.dropdown-check-list .anchor:after {
144+
position: absolute;
145+
content: "";
146+
border-left: 2px solid black;
147+
border-top: 2px solid black;
148+
padding: 5px;
149+
right: 10px;
150+
top: 20%;
151+
-moz-transform: rotate(-135deg);
152+
-ms-transform: rotate(-135deg);
153+
-o-transform: rotate(-135deg);
154+
-webkit-transform: rotate(-135deg);
155+
transform: rotate(-135deg);
156+
}
157+
158+
.dropdown-check-list .anchor:active:after {
159+
right: 8px;
160+
top: 21%;
161+
}
162+
163+
.dropdown-check-list ul.items {
164+
padding: 2px;
165+
display: none;
166+
margin: 0;
167+
border: 1px solid #ccc;
168+
border-top: none;
169+
}
170+
171+
.dropdown-check-list ul.items li {
172+
list-style: none;
173+
}
174+
175+
.dropdown-check-list.visible .anchor {
176+
color: #0094ff;
177+
}
178+
179+
.dropdown-check-list.visible .items {
180+
display: block;
181+
}
182+
</style>

OpenFlowNodeRED/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openflow-nodered",
3-
"version": "1.1.24",
3+
"version": "1.1.26",
44
"description": "Simple wrapper around NodeRed, RabbitMQ and MongoDB to support a more scaleable NodeRed implementation.\r Also the \"backend\" for [OpenRPA](https://github.com/skadefro/OpenRPA)",
55
"main": "index.js",
66
"scripts": {

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.24
1+
1.1.26

docker-compose-toolbox.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ services:
4545
- "traefik.http.routers.web.rule=Host(`toolbox.openrpa.dk`)"
4646
- "traefik.http.routers.web.entrypoints=web"
4747
- "traefik.frontend.passHostHeader=true"
48-
image: "cloudhack/openflow:1.1.24"
48+
image: "cloudhack/openflow:1.1.26"
4949
container_name: "web"
5050
environment:
5151
- update_acl_based_on_groups=true
@@ -82,7 +82,7 @@ services:
8282
- "traefik.http.routers.nodered.rule=Host(`nodered1.toolbox.openrpa.dk`)"
8383
- "traefik.http.routers.nodered.entrypoints=web"
8484
- "traefik.http.services.nodered.loadbalancer.server.port=1880"
85-
image: "cloudhack/openflownodered:1.1.24"
85+
image: "cloudhack/openflownodered:1.1.26"
8686
container_name: "nodered"
8787
environment:
8888
# - nodered_id=1

0 commit comments

Comments
 (0)