Completed
Push — master ( 78d2f9...949122 )
by greg
42s
created

src/server/public/scripts/modules/UserList.js   D

Complexity

Total Complexity 63
Complexity/F 2.25

Size

Lines of Code 386
Function Count 28

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
wmc 63
nc 1
mnd 3
bc 54
fnc 28
dl 0
loc 386
rs 4.7037
bpm 1.9285
cpm 2.25
noi 3
c 1
b 0
f 0

12 Functions

Rating   Name   Duplication   Size   Complexity  
A usersList._update 0 58 2
A usersList._edit 0 9 1
A usersList._remove 0 20 2
A usersList._validateEmail 0 4 1
D usersList._add 0 116 14
B usersList._deactivate 0 24 1
B usersList._formUserRoleSubmit 0 29 1
B usersList._activate 0 24 1
A usersList._closeUpdate 0 3 1
A usersList._closeFormUpdate 0 7 1
B usersList._bindEvents 0 37 3
B usersList.init 0 48 6

How to fix   Complexity   

Complexity

Complex classes like src/server/public/scripts/modules/UserList.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*global document, confirm, $ */
2
3
import Nanoajax from 'nanoajax'
4
import qs from 'qs'
5
6
var usersList = {
7
  init: function () {
8
    var scope = document.querySelector('.user-list')
9
    if (scope != null) {
10
      this._ajax = Nanoajax.ajax
11
12
      this._scope = scope
13
      this._table = this._scope.querySelector('#filtered-list tbody')
14
      this._alert = document.querySelector('.alert')
15
      this._handleActivate = this._activate.bind(this)
16
      this._handleDeactivate = this._deactivate.bind(this)
17
      this._handleRemove = this._remove.bind(this)
18
      this._handleEdit = this._edit.bind(this)
19
      this._handleUpdate = this._update.bind(this)
20
      this._handleCloseUpdate = this._closeUpdate.bind(this)
21
      this._handleAdd = this._add.bind(this)
22
23
      this._bindEvents()
24
    }
25
26
    if ($('#filtered-list').size() > 0) {
27
      var orderables = document.querySelectorAll('#filtered-list thead th')
28
      var columns = []
29
      Array.prototype.forEach.call(orderables, (orderable) => {
30
        var order = orderable.getAttribute('data-orderable')
31
        if (order != null) {
32
          columns.push({ 'orderable': (order == 'true') ? true : false })
33
        }else {
34
          columns.push(null)
35
        }
36
      })
37
      this.table = $('#filtered-list').DataTable({
38
        paging: false,
39
        'info': false,
40
        'columns': columns
41
      })
42
    }
43
44
    if ($('#filtered-list-url').size() > 0) {
45
      this._handleFormUserRoleSubmit = this._formUserRoleSubmit.bind(this)
46
47
      this._formUserRole = document.querySelector('[data-user-role]')
48
      this._formUserRoleSave = document.querySelector('[data-save-user-role]')
49
50
      if(typeof this._formUserRole !== 'undefined' && this._formUserRole !== null) {
51
        this._formUserRole.addEventListener('submit', this._handleFormUserRoleSubmit)
52
      }
53
    }
54
  },
55
  _bindEvents: function () {
56
    this._activateBtn = this._scope.querySelectorAll('[data-activate]')
57
    this._deactivateBtn = this._scope.querySelectorAll('[data-deactivate]')
58
    this._removeBtn = this._scope.querySelectorAll('[data-remove]')
59
    this._editBtn = this._scope.querySelectorAll('[data-edit]')
60
    this._updateBtn = this._scope.querySelectorAll('[data-update]')
61
    this._addBtn = this._scope.querySelector('[data-add-user]')
62
63
    Array.prototype.forEach.call(this._activateBtn, (btn) => {
64
      btn.removeEventListener('click', this._handleActivate)
65
      btn.addEventListener('click', this._handleActivate)
66
    })
67
68
    Array.prototype.forEach.call(this._deactivateBtn, (btn) => {
69
      btn.removeEventListener('click', this._handleDeactivate)
70
      btn.addEventListener('click', this._handleDeactivate)
71
    })
72
73
    Array.prototype.forEach.call(this._removeBtn, (btn) => {
74
      btn.removeEventListener('click', this._handleRemove)
75
      btn.addEventListener('click', this._handleRemove)
76
    })
77
78
    Array.prototype.forEach.call(this._editBtn, (btn) => {
79
      btn.removeEventListener('click', this._handleEdit, true)
80
      btn.addEventListener('click', this._handleEdit, true)
81
    })
82
83
    Array.prototype.forEach.call(this._updateBtn, (btn) => {
84
      btn.removeEventListener('click', this._handleUpdate, true)
85
      btn.addEventListener('click', this._handleUpdate, true)
86
    })
87
88
    if(typeof this._addBtn !== 'undefined' && this._addBtn !== null) {
89
      this._addBtn.addEventListener('click', this._handleAdd)
90
    }
91
  },
92
  _formUserRoleSubmit: function (e) {
93
    e.preventDefault()
94
95
    var inputs = this._formUserRole.querySelectorAll('input[type=checkbox]')
96
    var data = {}
97
    Array.prototype.forEach.call(inputs, (input) => {
98
      if (!input.disabled) {
99
        var name = input.getAttribute('name')
100
        if (data[name] == null) {
101
          data[name] = []
102
        }
103
        if (input.checked) {
104
          data[name].push(input.getAttribute('value'))
105
        }
106
      }
107
    })
108
109
    var toSave = qs.stringify(data)
110
    
111
    this._ajax(
112
      {
113
        url: '/abe/list-url/save',
114
        body: toSave,
115
        method: 'post'
116
      },
117
      () => {
118
        
119
      })
120
  },
121
  _activate: function (e) {
122
    var target = e.currentTarget
123
    var id = target.getAttribute('data-user-id')
124
125
    var toSave = qs.stringify({
126
      id: id
127
    })
128
129
    this._ajax(
130
      {
131
        url: '/abe/users/activate',
132
        body: toSave,
133
        method: 'post'
134
      },
135
      () => {
136
        var childGlyph = target.querySelector('.glyphicon')
137
        childGlyph.classList.remove('glyphicon-eye-open', 'text-info')
138
        childGlyph.classList.add('glyphicon-eye-close', 'text-danger')
139
        target.classList.remove('glyphicon-eye-close', 'text-danger')
140
        target.classList.add('glyphicon-eye-open', 'text-info')
141
        target.removeEventListener('click', this._handleActivate)
142
        target.addEventListener('click', this._handleDeactivate)
143
      })
144
  },
145
  _deactivate: function (e) {
146
    var target = e.currentTarget
147
    var id = target.getAttribute('data-user-id')
148
149
    var toSave = qs.stringify({
150
      id: id
151
    })
152
    
153
    this._ajax(
154
      {
155
        url: '/abe/users/deactivate',
156
        body: toSave,
157
        method: 'post'
158
      },
159
      () => {
160
        var childGlyph = target.querySelector('.glyphicon')
161
        childGlyph.classList.remove('glyphicon-eye-close', 'text-danger')
162
        childGlyph.classList.add('glyphicon-eye-open', 'text-info')
163
        target.classList.remove('glyphicon-eye-open', 'text-info')
164
        target.classList.add('glyphicon-eye-close', 'text-danger')
165
        target.removeEventListener('click', this._handleDeactivate)
166
        target.addEventListener('click', this._handleActivate)
167
      })
168
  },
169
  _edit: function (e) {
170
    var parent = e.currentTarget.parentNode.parentNode
171
    e.currentTarget.removeEventListener('click', this._handleEdit, true)
172
173
    parent.classList.add('editing')
174
    var closeUpdateBtn = parent.querySelector('[data-close-update]')
175
    closeUpdateBtn.removeEventListener('click', this._handleCloseUpdate)
176
    closeUpdateBtn.addEventListener('click', this._handleCloseUpdate)
177
  },
178
  _closeFormUpdate(target) {
179
    var parent = target.parentNode.parentNode.parentNode
180
    var edit = parent.querySelector('[data-edit]')
181
    parent.classList.remove('editing')
182
    edit.addEventListener('click', this._handleEdit, true)
183
    target.removeEventListener('click', this._handleCloseUpdate)
184
  },
185
  _closeUpdate: function (e) {
186
    this._closeFormUpdate(e.currentTarget)
187
  },
188
  _update: function (e) {
189
    var parent = e.currentTarget.parentNode.parentNode.parentNode
190
    var target = e.currentTarget
191
    var data = {
192
      id: target.getAttribute('data-user-id')
193
    }
194
195
    var inputs = parent.querySelectorAll('.form-control')
196
    var msg = ''
197
    var hasError = false
198
    Array.prototype.forEach.call(inputs, function(input) {
199
      data[input.name] = input.value
200
201
      if(input.name === 'email' && !this._validateEmail(input.value)) {
202
        hasError = true
203
        input.parentNode.classList.add('has-error')
204
        this._alert.classList.remove('hidden')
205
        msg += 'email is invalid<br />'
206
        return
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
207
      }else if (input.value.trim() === '') {
208
        hasError = true
209
        input.parentNode.classList.add('has-error')
210
        this._alert.classList.remove('hidden')
211
        msg += input.name + ' is invalid<br />'
212
        return
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
213
      }else {
214
        input.parentNode.classList.remove('has-error')
215
      }
216
    }.bind(this))
217
218
    if (hasError) {
219
      this._alert.innerHTML = msg
220
      return
221
    }else {
222
      this._alert.classList.add('hidden')
223
      this._alert.innerHTML = ''
224
    }
225
    var toSave = qs.stringify(data)
226
    
227
    this._ajax(
228
      {
229
        url: '/abe/users/update',
230
        body: toSave,
231
        method: 'post'
232
      },
233
      (code, responseText) => {
234
        var response = JSON.parse(responseText)
235
        if (response.success === 1) {
236
          Array.prototype.forEach.call(inputs, function(input) {
237
            input.parentNode.parentNode.querySelector('.value').innerHTML = input.value
238
          })
239
          this._closeFormUpdate(target)
240
        }else {
241
          this._alert.classList.remove('hidden')
242
          this._alert.innerHTML = response.message
243
        }
244
      })
245
  },
246
  _remove: function (e) {
247
    var confirmDelete = confirm(e.currentTarget.getAttribute('data-text'))
248
    if (!confirmDelete) return
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
249
250
    var target = e.currentTarget
251
    var id = target.getAttribute('data-user-id')
252
    var toSave = qs.stringify({
253
      id: id
254
    })
255
    
256
    this._ajax(
257
      {
258
        url: '/abe/users/remove',
259
        body: toSave,
260
        method: 'post'
261
      },
262
      () => {
263
        target.parentNode.parentNode.remove()
264
      })
265
  },
266
  _validateEmail: function(email) {
267
    var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
268
    return re.test(email)
269
  },
270
  _add: function () {
271
    this._alert.classList.add('hidden')
272
    var username = document.querySelector('[data-add-user-username]')
273
    if(typeof username.value === 'undefined' || username.value === null || username.value === '') {
274
      username.parentNode.classList.add('has-error')
275
      return 
276
    }
277
    username.parentNode.classList.remove('has-error')
278
279
    var name = document.querySelector('[data-add-user-name]')
280
    if(typeof name.value === 'undefined' || name.value === null || name.value === '') {
281
      name.parentNode.classList.add('has-error')
282
      return 
283
    }
284
    name.parentNode.classList.remove('has-error')
285
286
    var email = document.querySelector('[data-add-user-email]')
287
    if(typeof email.value === 'undefined' || email.value === null || email.value === '') {
288
      email.parentNode.classList.add('has-error')
289
      return 
290
    }
291
    if(!this._validateEmail(email.value)) {
292
      email.parentNode.classList.add('has-error')
293
      this._alert.classList.remove('hidden')
294
      this._alert.innerHTML = 'email is invalid'
295
      return
296
    }
297
    email.parentNode.classList.remove('has-error')
298
299
    var password = document.querySelector('[data-add-user-password]')
300
    if(typeof password.value === 'undefined' || password.value === null || password.value === '') {
301
      password.parentNode.classList.add('has-error')
302
      return 
303
    }
304
    
305
    password.parentNode.classList.remove('has-error')
306
307
    var role = document.querySelector('[data-add-user-role]')
308
    var toSave = qs.stringify({
309
      username: username.value,
310
      name: name.value,
311
      email: email.value,
312
      password: password.value,
313
      role: role.value
314
    })
315
    
316
    this._ajax(
317
      {
318
        url: '/abe/users/add',
319
        body: toSave,
320
        method: 'post'
321
      },
322
      (code, responseText) => {
323
        var data = JSON.parse(responseText)
324
        if(data.success === 1) {
325
          var tr = document.createElement('tr')
326
          var oldTr = document.querySelector('[data-user-base]')
327
          if(typeof oldTr !== 'undefined' && oldTr !== null) {
328
            tr.innerHTML = oldTr.innerHTML
329
330
            var tdUsername = tr.querySelector('.td-username')
331
            tdUsername.querySelector('.value').innerHTML = data.user.username
332
            tdUsername.querySelector('input').value = data.user.username
333
334
            var tdName = tr.querySelector('.td-name')
335
            tdName.querySelector('.value').innerHTML = data.user.name
336
            tdName.querySelector('input').value = data.user.name
337
338
            var tdEmail = tr.querySelector('.td-email')
339
            tdEmail.querySelector('.value').innerHTML = data.user.email
340
            tdEmail.querySelector('input').value = data.user.email
341
342
            var tdRole = tr.querySelector('.td-role')
343
            tdRole.querySelector('.value').innerHTML = data.user.role.name
344
            var tdRoleOptions = tdRole.querySelectorAll('select option')
345
            Array.prototype.forEach.call(tdRoleOptions, function(option) {
346
              if(option.value === data.user.role.name) {
347
                option.selected = 'selected'
348
              }
349
            })
350
351
            var tdActive = tr.querySelector('.td-active')
352
            var glypEyeClose = tdActive.querySelector('.glyphicon-eye-close')
353
            glypEyeClose.addEventListener('click', this._handleActivate, true)
354
            glypEyeClose.setAttribute('data-user-id', data.user.id)
355
356
            var tdActions = tr.querySelector('.td-actions')
357
            var glypEdit = tdActions.querySelector('.glyphicon-pencil')
358
            glypEdit.addEventListener('click', this._handleEdit, true)
359
            glypEdit.setAttribute('data-user-id', data.user.id)
360
361
            var glypOk = tdActions.querySelector('.glyphicon-ok')
362
            glypOk.addEventListener('click', this._handleUpdate, true)
363
            glypOk.setAttribute('data-user-id', data.user.id)
364
365
            var glypCloseUpdate = tdActions.querySelector('.glyphicon-remove')
366
            glypCloseUpdate.setAttribute('data-user-id', data.user.id)
367
            // glypCloseUpdate.addEventListener('click', this._handleCloseUpdate, true)
368
369
            var glypRemove = tdActions.querySelector('.glyphicon-trash')
370
            glypRemove.setAttribute('data-user-id', data.user.id)
371
            glypRemove.addEventListener('click', this._handleRemove, true)
372
          }
373
374
          this._table.appendChild(tr)
375
      
376
          username.value = ''
377
          name.value = ''
378
          email.value = ''
379
          password.value = ''
380
        }else {
381
          this._alert.classList.remove('hidden')
382
          this._alert.innerHTML = data.message
383
        }
384
      })
385
  }
386
}
387
388
export default usersList
389
390
// var userListEle = document.querySelector('.user-list');
391
// if(typeof userListEle !== 'undefined' && userListEle !== null) {
392
//   usersList.init(userListEle)
393
// }