Skip to content
This repository has been archived by the owner on Oct 20, 2021. It is now read-only.

Commit

Permalink
feat: Dynamic global dashboards of analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
aelamrani committed Oct 24, 2019
1 parent 09b0968 commit af09230
Show file tree
Hide file tree
Showing 50 changed files with 1,493 additions and 1,152 deletions.
11 changes: 10 additions & 1 deletion docs/management-configuration-analytics.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# Analytics

You can manage analytics.
## Settings

You can manage analytics settings.

## Dashboards

Here you are able to manage the dashboards you want to display.

You can manage finely the dashboards to display on APIs, applications or on the platform.

You can add, delete, edit/reorder or enabled/disable dashboards and configure the widgets you want to display on each.
5 changes: 5 additions & 0 deletions docs/management-configuration-dashboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Dashboard

Here you can organize your dashboard by moving and resizing the widgets.

You can also add and delete widgets to display the metrics you want/need.
4 changes: 2 additions & 2 deletions src/components/dashboard/dashboard-timeframe.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<md-card-content>
<div layout="row" layout-align="space-around center" style="margin-bottom: 10px">
<div class="btn-group float-right">
<button class="btn btn-default"
<button class="btn btn-default" type="button"
ng-repeat="timeframe in $ctrl.timeframes track by timeframe.id"
ng-click="$ctrl.updateTimeframe(timeframe.id)"
ng-class="{'gravitee-analytics-timeframe-active' : timeframe === $ctrl.timeframe}">
Expand All @@ -81,7 +81,7 @@
ng-model="$ctrl.pickerEndDate"
ng-model-options="{ updateOn: 'blur' }">
</md-input-container>
<md-button class="md-raised" ng-click="$ctrl.updateRangeDate()">Apply</md-button>
<md-button type="button" class="md-raised" ng-click="$ctrl.updateRangeDate()">Apply</md-button>
</div>
</div>
</md-card-content>
Expand Down
24 changes: 5 additions & 19 deletions src/components/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,22 @@
* limitations under the License.
*/
import * as _ from 'lodash';

const DashboardComponent: ng.IComponentOptions = {
template: require('./dashboard.html'),
bindings: {
model: '<',
accessLogs: '<',
onFilterChange: '&',
onTimeframeChange: '&',
onViewLogClick: '&'
onViewLogClick: '&',
updateMode: '<'
},
controller: function($scope) {
'ngInject';

this.initialEventCounter = 2;
this.initialTimeFrame;
this.initialQuery;


this.dashboardOptions = {
margins: [10, 10],
columns: 6,
Expand Down Expand Up @@ -101,21 +99,9 @@ const DashboardComponent: ng.IComponentOptions = {
}
};

this.$onInit = () => {
_.each(this.model, widget => {
widget.$uid = this.guid();
});
};

this.guid = function() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
$scope.$on('onWidgetDelete', (event, widget) => {
_.remove(this.model.definition, widget);
});
}
};

Expand Down
21 changes: 13 additions & 8 deletions src/components/dashboard/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,21 @@
limitations under the License.
-->
<div class="gv-form-content" layout="column">
<div class="gv-form-content" layout="column" ng-if="$ctrl.model.definition && $ctrl.model.definition.length">
<div ng-if="!$ctrl.updateMode">
<gv-dashboard-timeframe on-timeframe-change="$ctrl.timeframeChange(timeframe)"></gv-dashboard-timeframe>
<gv-dashboard-filter on-filter-change="$ctrl.queryFilterChange(query, widget)"></gv-dashboard-filter>
<div layout="row" flex="100" layout-align="end center" ng-if="$ctrl.accessLogs">
<md-button md-no-ink class="md-primary" ng-click="$ctrl.viewLogs()">View logs</md-button>
<md-button md-no-ink class="md-primary" ng-click="$ctrl.viewLogs()">View logs</md-button>
</div>
</div>

<div gridster="$ctrl.dashboardOptions" layout="row" style="width:100%">
<md-card gridster-item="widget" ng-repeat="widget in $ctrl.model">
<gv-widget widget="widget"></gv-widget>
</md-card>
</div>
</div>
<div gridster="$ctrl.dashboardOptions" layout="row" style="width:100%">
<md-card gridster-item="widget" ng-repeat="widget in $ctrl.model.definition">
<gv-widget widget="widget" update-mode="$ctrl.updateMode" global-query="$ctrl.model.query_filter"></gv-widget>
</md-card>
</div>
</div>

<gravitee-empty-state ng-if="!$ctrl.model.definition || !$ctrl.model.definition.length" icon="insert_chart" model="Dashboard"
message="{{$ctrl.updateMode?'Start adding widgets': 'No widget defined'}}"></gravitee-empty-state>
20 changes: 15 additions & 5 deletions src/components/widget/_widget.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.widget-card {
height: 100%;
overflow: scroll;
padding-bottom: 10px;
& > md-card-title {
padding-bottom: 0px;
span.md-title {
Expand Down Expand Up @@ -27,6 +30,17 @@
a:hover {
color: #5a646d;
}

& > md-card-content md-input-container, & > .gravitee-widget-draggable md-input-container {
margin-bottom: 0;
margin-top: 15px;
label {
margin-bottom: 0;
}
md-checkbox {
margin: 0;
}
}
}

.gravitee-widget-draggable {
Expand All @@ -45,11 +59,7 @@
}

.gv-widget-stats-container {
margin-top: 40px;
}

.gv-widget-stats-container-sm {
margin-top: 20px;
margin-top: 10px;
}

.gv-widget-stats {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import DashboardService from "../../../services/dashboard.service";
import * as _ from "lodash";
const WidgetChartLineConfigurationComponent: ng.IComponentOptions = {
template: require('./widget-chart-line-configuration.html'),
bindings: {
chart: '<'
},
controller: function (DashboardService: DashboardService) {
'ngInject';
this.fields = DashboardService.getAggregateFields();

this.$onInit = () => {
this.data = [];
if (this.chart.request) {
if (this.chart.request.aggs) {
this.data = this.chart.request.aggs.split('%3B');
}
} else {
_.merge(this.chart, {
request: {
type: 'date_histo',
aggs: ''
},
labels: []
});
}
};

this.onDataChanged = () => {
this.chart.request.aggs = '';
this.chart.labels = [];
let last = _.last(this.data);
_.forEach(this.data, (data) => {
this.chart.request.aggs += data + (last === data ? '' : '%3B');
this.chart.labels.push(_.find(this.fields, {aggValue: data}).label);
});
};
}
};

export default WidgetChartLineConfigurationComponent;
39 changes: 39 additions & 0 deletions src/components/widget/line/widget-chart-line-configuration.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!--
Copyright (C) 2015 The Gravitee team (http://gravitee.io)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div layout="column">

<md-input-container>
<label>Field</label>
<md-select ng-model="$ctrl.data" ng-change="$ctrl.onDataChanged()" multiple="true">
<md-option ng-repeat="field in $ctrl.fields" ng-value="field.aggValue">{{field.aggLabel}}</md-option>
</md-select>
</md-input-container>

<md-input-container>
<md-checkbox ng-model="$ctrl.chart.stacked">
Display stacked
</md-checkbox>
</md-input-container>

<md-input-container>
<md-checkbox ng-model="$ctrl.chart.selectable">
Allows to check a field to filter
</md-checkbox>
</md-input-container>

</div>
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ const WidgetChartLineComponent: ng.IComponentOptions = {
'ngInject';

this.$scope = $scope;
let that = this;

this.$onInit = function() {
this.$onInit = () => {
this.widget = this.parent.widget;
};

this.$onChanges = function(changes) {
this.$onChanges = (changes) => {
if (changes.data) {
let data = changes.data.currentValue;
let values = [], i;
Expand All @@ -48,14 +47,9 @@ const WidgetChartLineComponent: ng.IComponentOptions = {
year: '%b'
}
},
plotOptions: {
areaspline: {
stacking: this.parent.widget.chart.stacked ? 'normal' : null
}
},
chart: {
events: {
selection: function (event) {
selection: (event) => {
if (!event.resetSelection) {
$rootScope.$broadcast("timeframeZoom", {
from: Math.floor(event.xAxis[0].min),
Expand All @@ -68,20 +62,19 @@ const WidgetChartLineComponent: ng.IComponentOptions = {
};

if (data.values && data.values.length > 0) {
_.forEach(data.values, function (value, idx) {
_.forEach(value.buckets, function (bucket) {
_.forEach(data.values, (value, idx) => {
_.forEach(value.buckets, (bucket) => {
if (bucket) {
i++;
let lineColor = ChartService.colorByBucket[i % ChartService.colorByBucket.length];
let bgColor = ChartService.bgColorByBucket[i % ChartService.bgColorByBucket.length];
let label = that.parent.widget.chart.labels ? that.parent.widget.chart.labels[idx] : (bucket.name);
if (value.metadata && value.metadata[bucket.name]) {
label = value.metadata[bucket.name].name;
}
let isFieldRequest = _.includes(this.parent.widget.chart.request.aggs.split('%3B')[idx], 'field:');
let label = this.parent.widget.chart.labels[idx];

values.push({
name: label || bucket.name, data: bucket.data, color: lineColor, fillColor: bgColor,
labelPrefix: that.parent.widget.chart.labelPrefix,
name: isFieldRequest ? bucket.name : label,
data: bucket.data, color: lineColor, fillColor: bgColor,
labelPrefix: isFieldRequest ? label : '',
id: bucket.name
});
}
Expand All @@ -93,35 +86,36 @@ const WidgetChartLineComponent: ng.IComponentOptions = {
let plotOptions = {
series: {
pointStart: timestamp.from,
pointInterval: timestamp.interval
pointInterval: timestamp.interval,
stacking: this.parent.widget.chart.stacked ? 'normal' : null
}
} as any;

if (this.parent.widget.chart.selectable) {
plotOptions.series.events = {
legendItemClick: function (event) {
legendItemClick: (event) => {
// If all series are visible, keep only the one selected
let selected = event.target.chart.series[event.target.index];
let visibles = _.filter(event.target.chart.series, { 'visible': true });

if (visibles.length === that.result.series.length) {
if (visibles.length === this.result.series.length) {
// Do not disable selected item but disable others
event.preventDefault();

_(visibles)
.filter((serie: any) => { return serie.name !== event.target.name; })
.forEach((serie: any) => serie.hide());
that.updateQuery(selected, selected.visible);
this.updateQuery(selected, selected.visible);
} else {
if (selected.visible) {
event.preventDefault();
}
that.updateQuery(selected, !selected.visible);
this.updateQuery(selected, !selected.visible);
}
},
hide: function(event) {
hide: (event) => {
let hidden = _.filter(event.target.chart.series, { 'visible': false });
if (hidden.length === that.result.series.length) {
if (hidden.length === this.result.series.length) {
// Do not disable selected item but disable others
event.preventDefault();

Expand All @@ -133,29 +127,29 @@ const WidgetChartLineComponent: ng.IComponentOptions = {
};
}

that.result = _.assign(that.result, {
this.result = _.assign(this.result, {
series: values,
plotOptions: plotOptions
});
} else {
that.result.series = [];
this.result.series = [];
}
}
};

this.updateQuery = function(item, add) {
let removeFn = function() {
this.updateQuery = (item, add) => {
let removeFn = () => {
// Filter has been removed, so let's hide the serie
if (this.visible) {
this.hide();
}
};

//console.log('filter :' + item.name + '(' + add + ')');
that.$scope.$emit('filterItemChange', {
widget: that.widget.$uid,
field: that.widget.chart.request.field,
fieldLabel: that.widget.chart.request.fieldLabel,
this.$scope.$emit('filterItemChange', {
widget: this.widget.id,
field: this.widget.chart.request.field,
fieldLabel: this.widget.chart.request.fieldLabel,
key: item.userOptions.id,
name: item.name,
mode: (add) ? 'add' : 'remove',
Expand Down
Loading

0 comments on commit af09230

Please sign in to comment.