conjur-asset-ui 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.jshintrc +41 -0
  4. data/Gemfile +3 -1
  5. data/README.md +34 -0
  6. data/Rakefile +69 -1
  7. data/bower.json +93 -0
  8. data/conjur-asset-ui.gemspec +1 -1
  9. data/features/navigation_bar.feature +31 -0
  10. data/features/step_definitions/custom_step.rb +32 -0
  11. data/features/support/env.rb +38 -0
  12. data/features/support/hooks.rb +30 -0
  13. data/features/support/world.rb +17 -0
  14. data/gulpfile.js +140 -0
  15. data/lib/conjur/command/ui.rb +1 -1
  16. data/lib/conjur/webserver/server.rb +14 -9
  17. data/lib/conjur-asset-ui-version.rb +1 -1
  18. data/package.json +47 -0
  19. data/preprocessor.js +7 -0
  20. data/public/_client_libs.html +2 -15
  21. data/public/css/styles.less +170 -4
  22. data/public/index.html.erb +5 -7
  23. data/public/js/init.js +183 -97
  24. data/public/js/lib/sorted-set.no-require.js +3 -28
  25. data/public/js/models/groupRecord.js +12 -11
  26. data/public/js/models/hostRecord.js +6 -7
  27. data/public/js/models/layerRecord.js +12 -11
  28. data/public/js/models/namespace.js +2 -0
  29. data/public/js/models/policyList.js +3 -1
  30. data/public/js/models/policyRecord.js +6 -7
  31. data/public/js/models/record.js +24 -23
  32. data/public/js/models/resourceList.js +28 -10
  33. data/public/js/models/userList.js +7 -2
  34. data/public/js/models/userRecord.js +7 -8
  35. data/public/js/models/variableList.js +18 -7
  36. data/public/js/models/variableRecord.js +13 -12
  37. data/public/js/routers.js +72 -26
  38. data/public/js/views/annotations.js +38 -27
  39. data/public/js/views/audit.js +23 -17
  40. data/public/js/views/chart.js +471 -0
  41. data/public/js/views/dashboard.js +94 -58
  42. data/public/js/views/generic.js +16 -9
  43. data/public/js/views/group.js +94 -55
  44. data/public/js/views/groups.js +3 -7
  45. data/public/js/views/host.js +75 -44
  46. data/public/js/views/hosts.js +2 -6
  47. data/public/js/views/layer.js +127 -82
  48. data/public/js/views/layers.js +2 -6
  49. data/public/js/views/mixins/search.js +12 -5
  50. data/public/js/views/mixins/tabs.js +95 -55
  51. data/public/js/views/navSearch.js +16 -5
  52. data/public/js/views/owned.js +14 -8
  53. data/public/js/views/permissions.js +244 -178
  54. data/public/js/views/policies.js +2 -4
  55. data/public/js/views/policy.js +65 -38
  56. data/public/js/views/resource.js +49 -34
  57. data/public/js/views/role.js +52 -37
  58. data/public/js/views/searchResults.js +205 -138
  59. data/public/js/views/time.js +26 -13
  60. data/public/js/views/user.js +178 -55
  61. data/public/js/views/users.js +2 -7
  62. data/public/js/views/variable.js +226 -45
  63. data/public/js/views/variables.js +4 -8
  64. metadata +20 -20
  65. data/public/_client_code.html +0 -42
  66. data/public/css/bootstrap.css +0 -7
  67. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  68. data/public/fonts/glyphicons-halflings-regular.svg +0 -229
  69. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  70. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  71. data/public/js/lib/JSXTransformer.js +0 -10862
  72. data/public/js/lib/async.js +0 -958
  73. data/public/js/lib/backbone.js +0 -2
  74. data/public/js/lib/bootstrap.js +0 -6
  75. data/public/js/lib/less.js +0 -16
  76. data/public/js/lib/moment.js +0 -7768
  77. data/public/js/lib/react-bootstrap.js +0 -5346
  78. data/public/js/lib/react-bootstrap.min.js +0 -4
  79. data/public/js/lib/underscore-min.js +0 -6
  80. data/public/js/lib/underscore.string.min.js +0 -1
  81. data/public/js/main.js +0 -57
@@ -0,0 +1,471 @@
1
+ /** @jsx React.DOM */
2
+ /* global conjur, React, d3, _ */
3
+
4
+ (function(conjur, React, d3, _) {
5
+ 'use strict';
6
+
7
+ var bisector = d3.bisector(function(d) { return d.date; }).left;
8
+
9
+ function _translate(x, y) {
10
+ return 'translate(' + x + ',' + y + ')';
11
+ }
12
+
13
+ this.Chart = React.createClass({
14
+ getDefaultProps: function() {
15
+ return {
16
+ defaultOptions: {
17
+ legend: {
18
+
19
+ },
20
+ axis: {
21
+ x: {
22
+ label: 'X axis'
23
+ },
24
+ y: {
25
+ label: 'Y axis'
26
+ }
27
+ },
28
+ margin: {
29
+ top: 20,
30
+ right: 120,
31
+ bottom: 30,
32
+ left: 50
33
+ }
34
+ }
35
+ };
36
+ },
37
+
38
+ el: undefined,
39
+ svg: {},
40
+
41
+ componentDidMount: function() {
42
+ this.el = this.getDOMNode();
43
+
44
+ var options = _.deepExtend({}, this.props.options, this.props.defaultOptions);
45
+
46
+ this.setState(options, function() {
47
+ this._calculateInitialState(function() {
48
+ this._create();
49
+ this._update();
50
+ }.bind(this));
51
+ }.bind(this));
52
+ },
53
+
54
+ componentDidUpdate: function() {
55
+ if (typeof this.svg.svg === 'undefined') {
56
+ return;
57
+ }
58
+
59
+ this._update();
60
+
61
+ return;
62
+ },
63
+
64
+ componentWillUnmount: function() {
65
+
66
+ },
67
+
68
+ render: function() {
69
+ return (
70
+ <div className="b-chart"></div>
71
+ );
72
+ },
73
+
74
+ _getHeight: function() {
75
+ var height;
76
+
77
+ if (this.el.offsetHeight) {
78
+ height = this.el.offsetHeight;
79
+ } else if (this.el.style.pixelHeight) {
80
+ height = this.el.style.pixelHeight;
81
+ }
82
+
83
+ height = window.parseFloat(height) -
84
+ this.state.margin.top - this.state.margin.bottom;
85
+
86
+ return height;
87
+ },
88
+
89
+ _getWidth: function() {
90
+ var width;
91
+
92
+ if (this.el.offsetWidth) {
93
+ width = this.el.offsetWidth;
94
+ } else if (this.el.style.pixelWidth) {
95
+ width = this.el.style.pixelWidth;
96
+ }
97
+
98
+ width = window.parseFloat(width) -
99
+ this.state.margin.left - this.state.margin.right;
100
+
101
+ return width;
102
+ },
103
+
104
+ _create: function() {
105
+ var svg = d3.select(this.el)
106
+ .append('svg')
107
+ .attr('class', 'd3-line-chart')
108
+ .attr('width', this.state.fullWidth)
109
+ .attr('height', this.state.fullHeight)
110
+ .append('g')
111
+ .attr('class', 'd3-line-chart__inner')
112
+ .attr('transform', _translate(this.state.margin.left, this.state.margin.top));
113
+
114
+ this.svg.svg = svg;
115
+
116
+ this.svg.axis = svg.append('g')
117
+ .attr('class', 'd3-axis');
118
+
119
+ this.svg.axis.append('g')
120
+ .attr('class', 'd3-axis__x');
121
+
122
+ this.svg.axis.append('g')
123
+ .attr('class', 'd3-axis__y');
124
+
125
+ this.svg.legend = svg.append('g')
126
+ .attr('class', 'd3-legend');
127
+
128
+ this.svg.graph = svg.append('g')
129
+ .attr('class', 'd3-graph');
130
+
131
+ this.svg.focus = this.svg.graph.append('g')
132
+ .attr('class', 'd3-graph__focus');
133
+ },
134
+
135
+ _update: function() {
136
+ this._drawAxisX();
137
+ this._drawAxisY();
138
+ this._drawLegend();
139
+ this._drawLine();
140
+ this._drawFocus();
141
+ },
142
+
143
+ _calculateInitialState: function(cb) {
144
+ var props = this.props;
145
+
146
+ var width = this._getWidth(),
147
+ height = this._getHeight(),
148
+ fullWidth = (
149
+ width +
150
+ this.state.margin.left +
151
+ this.state.margin.right
152
+ ),
153
+ fullHeight = (
154
+ height +
155
+ this.state.margin.top +
156
+ this.state.margin.bottom
157
+ );
158
+
159
+ var color = d3.scale.category10();
160
+
161
+ color.domain(d3.keys(props.data[0]).filter(function(key) { return key !== 'date'; }));
162
+
163
+ var data = color.domain().map(function(name) {
164
+ return {
165
+ name: name,
166
+ values: props.data.map(function(d) {
167
+ return {date: d.date, value: +d[name]};
168
+ })
169
+ };
170
+ });
171
+
172
+ var x = this._getScaleX('time')
173
+ .range([0, width])
174
+ .domain(d3.extent(props.data, function(d) { return d.date; })),
175
+
176
+ y = this._getScaleX('linear')
177
+ .range([height, 0])
178
+ .domain([
179
+ d3.min(data, function(c) {
180
+ return d3.min(c.values, function(v) { return v.value; });
181
+ }),
182
+ d3.max(data, function(c) {
183
+ return d3.max(c.values, function(v) { return v.value; });
184
+ })
185
+ ]),
186
+
187
+ legendScale = d3.scale.ordinal()
188
+ .domain([])
189
+ .range([0, 20, 40, 60, 80, 100, 120, 140]),
190
+
191
+ scales = {x: x, y: y, legend: legendScale};
192
+
193
+ var line = this._getLine(scales);
194
+
195
+ this.setState({
196
+ width: width,
197
+ height: height,
198
+ fullWidth: fullWidth,
199
+ fullHeight: fullHeight,
200
+ color: color,
201
+ scales: scales,
202
+ lineStencil: line,
203
+ data: data
204
+ }, cb);
205
+ },
206
+
207
+ _getScaleX: function(type) {
208
+ if (type === 'linear') {
209
+ return d3.scale.linear();
210
+ } else if (type === 'time') {
211
+ return d3.time.scale();
212
+ }
213
+
214
+ return null;
215
+ },
216
+
217
+ _getLine: function(scales) {
218
+ return d3.svg.line()
219
+ .x(function(d) { return scales.x(d.date); })
220
+ .y(function(d) { return scales.y(d.value); });
221
+ },
222
+
223
+ _drawAxisX: function() {
224
+ var xAxis = d3.svg.axis()
225
+ .scale(this.state.scales.x)
226
+ .orient('bottom');
227
+
228
+ this.svg.axis.select('.d3-axis__x')
229
+ .attr('transform', _translate(0, this.state.height))
230
+ .call(xAxis);
231
+ },
232
+
233
+ _drawAxisY: function() {
234
+ var yAxis = d3.svg.axis()
235
+ .scale(this.state.scales.y)
236
+ .orient('left');
237
+
238
+ var axis = this.svg.axis.select('.d3-axis__y')
239
+ .call(yAxis);
240
+
241
+ if (typeof(this.state.yAxisLabel) === 'string') {
242
+ this._drawAxisLabel(axis, this.state.yAxisLabel, true);
243
+ }
244
+ },
245
+
246
+ _drawAxisLabel: function(axis, text, rotate) {
247
+ var label = axis.append('text');
248
+
249
+ if (typeof(rotate) === 'boolean' && rotate === true) {
250
+ label.attr('transform', 'rotate(-90)')
251
+ .attr('y', 0 - this.state.margin.left)
252
+ .attr('x', 0 - (this.state.height / 2))
253
+ .attr('dy', '1em')
254
+ .style('text-anchor', 'middle')
255
+ .text(text);
256
+ } else {
257
+ label.attr('transform',
258
+ _translate(this.state.width / 2,
259
+ this.state.height + this.state.margin.bottom))
260
+ .style('text-anchor', 'middle')
261
+ .text(text);
262
+ }
263
+ },
264
+
265
+ _drawLegend: function() {
266
+ var self = this;
267
+
268
+ var legend = this.svg.legend
269
+ .selectAll('.d3-legend__item')
270
+ .data(this.state.data);
271
+
272
+ var enter = legend
273
+ .enter()
274
+ .append('g')
275
+ .style('opacity', 0.9)
276
+ .attr('class', 'd3-legend__item')
277
+ .attr('data-graph-line-id', function(d) {
278
+ return 'd3-legend__item--' + d.name;
279
+ })
280
+ .on('mouseover', function(d) {
281
+ self._handleLegendMouseover(this, d);
282
+ })
283
+ .on('mouseout', function(d) {
284
+ self._handleLegendMouseout(this, d);
285
+ })
286
+ .on('click', function(d) {
287
+ self._handleLegendClick(this, d);
288
+ });
289
+
290
+ enter.append('circle')
291
+ .attr('cx', this.state.width + 30)
292
+ .attr('cy', function(d) {
293
+ return self.state.scales.legend(d.values[d.values.length-1].value) - 3.1;
294
+ })
295
+ .attr('r', 5.5)
296
+ .style('fill', function(d) {
297
+ return self.state.color(d.name);
298
+ });
299
+
300
+ enter.append('text')
301
+ .attr('x', this.state.width + 40)
302
+ .attr('y', function(d) {
303
+ return self.state.scales.legend(d.values[d.values.length-1].value);
304
+ })
305
+ .text(function(d) {
306
+ return self.state.legend[d.name];
307
+ });
308
+ },
309
+
310
+ _handleLegendMouseover: function(self) {
311
+ var opacity = parseFloat(self.style.opacity);
312
+
313
+ if (opacity >= 0.9) {
314
+ d3.select(self)
315
+ .style('opacity', 1);
316
+ } else {
317
+ d3.select(self)
318
+ .style('opacity', 0.5);
319
+ }
320
+ },
321
+
322
+ _handleLegendMouseout: function(self) {
323
+ var opacity = parseFloat(self.style.opacity);
324
+
325
+ if (opacity >= 0.9) {
326
+ d3.select(self)
327
+ .style('opacity', 0.9);
328
+ } else {
329
+ d3.select(self)
330
+ .style('opacity', 0.2);
331
+ }
332
+ },
333
+
334
+ _handleLegendClick: function(self) {
335
+ var line = document.getElementById(self.getAttribute('data-graph-line-id'));
336
+
337
+ if (line.style.opacity !== '0') {
338
+ d3.select(line).transition()
339
+ .duration(500)
340
+ .style('opacity', 0);
341
+
342
+ d3.select(self).transition()
343
+ .duration(500)
344
+ .style('opacity', 0.2);
345
+ } else {
346
+ d3.select(line).transition()
347
+ .duration(500)
348
+ .style('opacity', 1);
349
+
350
+ d3.select(self).transition()
351
+ .duration(500)
352
+ .style('opacity', 0.9);
353
+ }
354
+ },
355
+
356
+ _drawLine: function() {
357
+ var self = this;
358
+
359
+ var line = this.svg.graph.selectAll('.d3-graph__line')
360
+ .data(this.state.data)
361
+ .enter()
362
+ .append('g')
363
+ .attr('class', 'd3-graph__line')
364
+ .attr('id', function(d) {
365
+ return 'd3-legend__item--' + d.name;
366
+ });
367
+
368
+ line.append('path')
369
+ .attr('class', 'line')
370
+ .attr('d', function(d) {
371
+ return self.state.lineStencil(d.values);
372
+ })
373
+ .style('stroke', function(d) {
374
+ return self.state.color(d.name);
375
+ });
376
+ },
377
+
378
+ _drawFocus: function() {
379
+ var self = this;
380
+
381
+ var circle = this.svg.focus.selectAll('.d3-graph__focus-circle')
382
+ .data(this.state.data)
383
+ .enter()
384
+ .append('g')
385
+ .attr('class', 'd3-graph__focus-circle')
386
+ .attr('id', function(d) {
387
+ return 'd3-graph__focus-circle--' + d.name;
388
+ })
389
+ .style('display', 'none');
390
+
391
+ circle.append('circle')
392
+ .attr('r', 4.5);
393
+
394
+ circle.append('text')
395
+ .attr('x', 9)
396
+ .attr('dy', '.35em');
397
+
398
+ this.svg.focusLine = this.svg.focus.append('line')
399
+ .attr('x1', 0)
400
+ .attr('y1', 0)
401
+ .attr('x2', 0)
402
+ .attr('y2', this.state.height)
403
+ .style('stroke', 'gray')
404
+ .style('stroke-width', 0.5)
405
+ .style('stroke-dasharray', '5,10');
406
+
407
+ this.svg.graph.append('rect')
408
+ .attr('class', 'd3-graph__overlay')
409
+ .attr('width', this.state.width)
410
+ .attr('height', this.state.height)
411
+ .on('mouseover', function() {
412
+ self.svg.focusLine.style('display', null);
413
+ })
414
+ .on('mouseout', function() {
415
+ self.svg.focusLine.style('display', 'none');
416
+
417
+ self.svg.focus.selectAll('.d3-graph__focus-circle')
418
+ .style('display', 'none');
419
+ })
420
+ .on('mousemove', function() {
421
+ self._handleFocusMousemove(this);
422
+ });
423
+ },
424
+
425
+ _handleFocusMousemove: function(overlay) {
426
+ var self = this,
427
+ mouse = d3.mouse(overlay)[0],
428
+ x0 = this.state.scales.x.invert(mouse);
429
+
430
+ var bisectData = function(data) {
431
+ var i = bisector(data.values, x0, 1),
432
+ d0 = data.values[i - 1],
433
+ d1 = data.values[i],
434
+ d = x0 - d0.date > d1.date - x0 ? d1 : d0;
435
+
436
+ return {name: data.name, data: d};
437
+ };
438
+
439
+ this.svg.focusLine.attr('transform',
440
+ _translate(this.state.scales.x(x0),
441
+ 0));
442
+
443
+ this.state.data.map(function(e) {
444
+ return bisectData(e);
445
+ }).map(function(d) {
446
+ var circle = document.getElementById('d3-graph__focus-circle--' + d.name),
447
+ line = document.getElementById('d3-legend__item--' + d.name);
448
+
449
+ if (d.data.value > 0 && line.style.opacity !== '0') {
450
+ d3.select(circle)
451
+ .style('display', null)
452
+ .attr('transform',
453
+ _translate(self.state.scales.x(d.data.date),
454
+ self.state.scales.y(d.data.value)))
455
+
456
+ .select('text').text(d3.format(',.2f')(d.data.value));
457
+ } else {
458
+ d3.select(circle)
459
+ .style('display', 'none');
460
+ }
461
+ });
462
+ }
463
+ });
464
+
465
+ }).bind(conjur.views)
466
+ (
467
+ conjur,
468
+ React,
469
+ d3,
470
+ _
471
+ );
@@ -1,67 +1,103 @@
1
1
  /**@jsx React.DOM*/
2
+ /* global conjur, React, jQuery */
2
3
 
3
- var DashboardSearchForm = React.createClass({
4
- mixins: [ conjur.views.mixins.Search ],
4
+ (function(conjur, React, $) {
5
+ 'use strict';
5
6
 
6
- render: function() {
7
- return (
8
- <form id="dashboard-search" className="form-inline search" role="search" onSubmit={this.handleSubmit}>
9
- <div className="form-group">
10
- <input ref="input" type="text" className="form-control" placeholder="Search Conjur" value={this.props.searchText}>
11
- </input>
12
- </div>
13
- <button type="submit" className="btn btn-default search-button">Search</button>
14
- </form>
15
- );
16
- }
17
- });
7
+ var OwnedResourcesBox = conjur.views.OwnedResourcesBox,
8
+ Search = conjur.views.mixins.Search;
18
9
 
19
- var DashboardFrequent = React.createClass({
20
- getInitialState: function() {
21
- return {resources: [], loaded: false}
22
- },
10
+ var DashboardSearchForm = React.createClass({
11
+ mixins: [Search],
23
12
 
24
- componentWillMount: function() {
25
- $.get("/api/authz/" + conjur.app.configuration.account + "/resources?owner=" + encodeURIComponent(conjur.app.userId), function(data) {
26
- var filtered_data = data.filter( function(item) {
27
- return item.id.split(':')[1]!="secret"; // assets of 'secret' kind are internal
28
- });
29
- this.setState({resources: filtered_data, loaded: true});
30
- }.bind(this));
31
- },
13
+ render: function() {
14
+ return (
15
+ <form id="dashboard-search"
16
+ className="form-inline search"
17
+ role="search"
18
+ onSubmit={this.handleSubmit}>
19
+ <div className="form-group">
20
+ <input ref="input"
21
+ type="text"
22
+ className="form-control"
23
+ placeholder="Search Conjur"
24
+ value={this.props.searchText} />
25
+ </div>
26
+ <button type="submit" className="btn btn-default search-button">
27
+ Search
28
+ </button>
29
+ </form>
30
+ );
31
+ }
32
+ });
32
33
 
33
- render: function() {
34
- var content,
35
- OwnedResourcesBox = conjur.views.OwnedResourcesBox;
34
+ var DashboardFrequent = React.createClass({
35
+ getInitialState: function() {
36
+ return {resources: [], loaded: false};
37
+ },
36
38
 
37
- if (this.state.loaded)
38
- content = <OwnedResourcesBox resources={this.state.resources} />
39
- else
40
- content = <span>Loading...</span>
39
+ componentWillMount: function() {
40
+ $.get('/api/authz/' +
41
+ conjur.app.configuration.account +
42
+ '/resources?owner=' +
43
+ encodeURIComponent(conjur.app.userId),
44
+ function(data) {
45
+ var filteredData = data.filter(function(item) {
46
+ // assets of 'secret' kind are internal
47
+ return item.id.split(':')[1] !== 'secret';
48
+ });
41
49
 
42
- return <section>
43
- <h3>Owned assets</h3>
44
- {content}
45
- </section>;
46
- }
47
- });
50
+ this.setState({resources: filteredData, loaded: true});
51
+ }.bind(this));
52
+ },
48
53
 
49
- var Dashboard = React.createClass({
50
- render: function(){
51
- return (
52
- <div className="dashboard">
53
- <div className="row">
54
- <DashboardSearchForm />
55
- </div>
56
- <div className="row summary">
57
- <div className="col-xs-6">
58
- <DashboardFrequent />
59
- </div>
60
- <div className="col-xs-6 audit">
61
- <AuditTable src={'/api/audit/all'} caption={'Recent activity'} compact={true}/>
62
- </div>
63
- </div>
64
- </div>
65
- );
66
- }
67
- });
54
+ render: function() {
55
+ var content;
56
+
57
+ if (this.state.loaded) {
58
+ content = (
59
+ <OwnedResourcesBox resources={this.state.resources} />
60
+ );
61
+ } else {
62
+ content = (
63
+ <span>Loading...</span>
64
+ );
65
+ }
66
+
67
+ return (
68
+ <section>
69
+ <h3>Owned assets</h3>
70
+ {content}
71
+ </section>
72
+ );
73
+ }
74
+ });
75
+
76
+ this.Dashboard = React.createClass({
77
+ render: function() {
78
+ var AuditTable = window.AuditTable;
79
+
80
+ return (
81
+ <div className="dashboard">
82
+ <div className="row">
83
+ <DashboardSearchForm />
84
+ </div>
85
+ <div className="row summary">
86
+ <div className="col-xs-6">
87
+ <DashboardFrequent />
88
+ </div>
89
+ <div className="col-xs-6 audit">
90
+ <AuditTable src={'/api/audit/all'} caption={'Recent activity'} compact={true}/>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ );
95
+ }
96
+ });
97
+
98
+ }).bind(conjur.views)
99
+ (
100
+ conjur,
101
+ React,
102
+ jQuery
103
+ );
@@ -1,14 +1,17 @@
1
1
  /** @jsx React.DOM */
2
+ /* global conjur, React, _ */
2
3
 
3
- (function(conjur, React) {
4
+ (function(conjur, React, _) {
4
5
  'use strict';
5
6
 
7
+ var RoleLink = conjur.views.RoleLink;
8
+
6
9
  var GenericListItem = React.createClass({
7
10
  render: function() {
8
- var recordUrl = '/ui/'
9
- + this.props.data.kind
10
- + '/'
11
- + window.encodeURIComponent(this.props.data.record.identifier);
11
+ var recordUrl = '/ui/' +
12
+ this.props.data.kind +
13
+ '/' +
14
+ window.encodeURIComponent(this.props.data.record.identifier);
12
15
 
13
16
  return (
14
17
  <tr>
@@ -25,7 +28,7 @@
25
28
  }
26
29
  });
27
30
 
28
- var GenericList = this.GenericList = React.createClass({
31
+ this.GenericList = React.createClass({
29
32
  getInitialState: function() {
30
33
  return {
31
34
  sort: {
@@ -53,7 +56,7 @@
53
56
 
54
57
  var order = (this.state.sort.order === 'asc') ? 'desc' : 'asc';
55
58
 
56
- if (this.state.sort.column != column) {
59
+ if (this.state.sort.column !== column) {
57
60
  order = this.state.columns[column].defaultSortOrder;
58
61
  }
59
62
 
@@ -74,7 +77,10 @@
74
77
  });
75
78
 
76
79
  items = _.sortBy(items, this.state.sort.column);
77
- if (this.state.sort.order === 'desc') items.reverse();
80
+
81
+ if (this.state.sort.order === 'desc') {
82
+ items.reverse();
83
+ }
78
84
 
79
85
  var rows = items.map(function (o) {
80
86
  var componentName = _.str.capitalize(
@@ -111,5 +117,6 @@
111
117
  }).bind(conjur.views)
112
118
  (
113
119
  conjur,
114
- React
120
+ React,
121
+ _
115
122
  );