Added a new dashboard (Analytics)

This commit is contained in:
Sercan Yemen 2018-02-01 15:30:44 +03:00
parent dfd430712d
commit 8f5e947c28
7 changed files with 697 additions and 16 deletions

View File

@ -8,6 +8,10 @@ const routes = [
path : 'dashboards/project', path : 'dashboards/project',
loadChildren: './dashboards/project/project.module#FuseProjectDashboardModule' loadChildren: './dashboards/project/project.module#FuseProjectDashboardModule'
}, },
{
path : 'dashboards/analytics',
loadChildren: './dashboards/analytics/analytics.module#FuseAnalyticsDashboardModule'
},
{ {
path : 'mail', path : 'mail',
loadChildren: './mail/mail.module#FuseMailModule' loadChildren: './mail/mail.module#FuseMailModule'

View File

@ -0,0 +1,489 @@
<div id="dashboard-analytics" class="page-layout blank grey-100-bg" fusePerfectScrollbar>
<!--<div fxLayout="row" fxLayoutAlign="start center" class="p-24">
<div fxFlex="33" class="position-relative h-200 pb-16 mat-elevation-z2 mr-16 p-16 white-bg">
<div class="h2 mb-8">Site Traffic</div>
<div fxLayout="row" fxLayoutAlign="start center">
<div>
<div class="h3 secondary-text">Overall Growth</div>
<div fxLayout="row" fxLayoutAlign="start center">
<span *ngIf="widgets.widget1.overallGrowthTrend === 'increase'">
<mat-icon class="green-fg mr-8">trending_up</mat-icon>
</span>
<span *ngIf="widgets.widget1.overallGrowthTrend === 'decrease'">
<mat-icon class="red-fg mr-8">trending_down</mat-icon>
</span>
<span class="h2 text-bold">{{widgets.widget1.overallGrowthPercentage}}</span>
</div>
</div>
</div>
</div>
</div>-->
<div class="main-widget">
<div class="position-relative p-24 mat-blue-600-bg"
fxLayout="row" fxLayoutAlign="space-between center">
<div fxLayout="column" fxLayoutAlign="start start">
<span class="h2">Visitors</span>
<span class="h5 secondary-text">Unique visitors by month</span>
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<div class="py-8 px-12 border-radius-2 line-height-1 mr-8 cursor-pointer"
(click)="widget1SelectedYear = '2015'"
[ngClass]="{'blue-300-bg': widget1SelectedYear === '2015'}">
2015
</div>
<div class="py-8 px-12 border-radius-2 line-height-1 mr-8 cursor-pointer"
(click)="widget1SelectedYear = '2016'"
[ngClass]="{'blue-300-bg': widget1SelectedYear === '2016'}">
2016
</div>
<div class="py-8 px-12 border-radius-2 line-height-1 cursor-pointer"
(click)="widget1SelectedYear = '2017'"
[ngClass]="{'blue-300-bg': widget1SelectedYear === '2017'}">
2017
</div>
</div>
</div>
<div class="position-relative h-256 pb-16 mat-blue-600-bg">
<canvas baseChart
[datasets]="widgets.widget1.datasets[widget1SelectedYear]"
[labels]="widgets.widget1.labels"
[colors]="widgets.widget1.colors"
[options]="widgets.widget1.options"
[chartType]="widgets.widget1.chartType">
</canvas>
</div>
</div>
<div class="content">
<div class="left mr-32">
<div class="pb-24 font-size-18 font-weight-300">
How are your active users trending over time?
</div>
<div fxLayout="row" fxLayoutAlign="start start">
<!-- Widget 2 -->
<div class="widget">
<div class="fuse-card auto-width mr-32">
<div class="p-16">
<div class="h5 secondary-text">Conversion</div>
<div class="font-size-54 font-weight-300 line-height-1 mt-8">
{{widgets.widget2.conversion.value}}
</div>
</div>
<div class="p-16 pt-0">
<span class="green-fg" *ngIf="widgets.widget2.conversion.ofTarget > 0">
{{widgets.widget2.conversion.ofTarget}}%
</span>
<span class="red-fg" *ngIf="widgets.widget2.conversion.ofTarget < 0">
{{widgets.widget2.conversion.ofTarget}}%
</span>
<span> of target</span>
</div>
<div class="h-64">
<ngx-charts-bar-vertical
[scheme]="widgets.widget2.scheme"
[results]="widgets.widget2.data">
</ngx-charts-bar-vertical>
</div>
</div>
</div>
<!-- / Widget 2 -->
<!-- Widget 3 -->
<div class="widget">
<div class="fuse-card auto-width mr-32">
<div class="p-16">
<div class="h5 secondary-text">Impressions</div>
<div class="font-size-54 font-weight-300 line-height-1 mt-8">
{{widgets.widget3.impressions.value}}
</div>
</div>
<div class="p-16 pt-0">
<span class="green-fg" *ngIf="widgets.widget3.impressions.ofTarget > 0">
{{widgets.widget3.impressions.ofTarget}}%
</span>
<span class="red-fg" *ngIf="widgets.widget3.impressions.ofTarget < 0">
{{widgets.widget3.impressions.ofTarget}}%
</span>
<span> of target</span>
</div>
<div class="h-64">
<ngx-charts-line-chart
[scheme]="widgets.widget3.scheme"
[results]="widgets.widget3.data">
</ngx-charts-line-chart>
</div>
</div>
</div>
<!-- / Widget 3 -->
<!-- Widget 4 -->
<div class="widget">
<div class="fuse-card auto-width">
<div class="p-16">
<div class="h5 secondary-text">Visits</div>
<div class="font-size-54 font-weight-300 line-height-1 mt-8">
{{widgets.widget4.visits.value}}
</div>
</div>
<div class="p-16 pt-0">
<span class="green-fg" *ngIf="widgets.widget4.visits.ofTarget > 0">
{{widgets.widget4.visits.ofTarget}}%
</span>
<span class="red-fg" *ngIf="widgets.widget4.visits.ofTarget < 0">
{{widgets.widget4.visits.ofTarget}}%
</span>
<span> of target</span>
</div>
<div class="h-64">
<ngx-charts-bar-vertical
[scheme]="widgets.widget4.scheme"
[results]="widgets.widget4.data">
</ngx-charts-bar-vertical>
</div>
</div>
</div>
<!-- / Widget 4 -->
</div>
<!-- Widget 5 -->
<div class="pt-32 pb-24 font-size-18 font-weight-300">
How many pages your users visit?
</div>
<div class="white-bg mat-elevation-z2">
<div class="position-relative p-24"
fxLayout="row" fxLayoutAlign="space-between center">
<div fxLayout="column" fxLayoutAlign="start start">
<span class="h2">Visitors & Page views</span>
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<div class="py-8 px-12 border-radius-2 line-height-1 mr-8 cursor-pointer"
(click)="widget5SelectedDay = 'yesterday'"
[ngClass]="{'grey-300-bg': widget5SelectedDay === 'yesterday'}">
Yesterday
</div>
<div class="py-8 px-12 border-radius-2 line-height-1 mr-8 cursor-pointer"
(click)="widget5SelectedDay = 'today'"
[ngClass]="{'grey-300-bg': widget5SelectedDay === 'today'}">
Today
</div>
</div>
</div>
<div class="position-relative h-368 pb-16">
<canvas baseChart
[datasets]="widgets.widget5.datasets[widget5SelectedDay]"
[labels]="widgets.widget5.labels"
[colors]="widgets.widget5.colors"
[options]="widgets.widget5.options"
[chartType]="widgets.widget5.chartType">
</canvas>
</div>
</div>
<!-- / Widget 5 -->
<!-- Widget 6 -->
<div class="pt-32 pb-24 font-size-18 font-weight-300">
Where are your users?
</div>
<div>
<agm-map class="h-640 w-100-p"
[minZoom]="2"
[maxZoom]="2"
[mapDraggable]="false"
[fullscreenControl]="false"
[panControl]="false"
[rotateControl]="false"
[zoomControl]="false"
[scaleControl]="false"
[streetViewControl]="false"
[scrollwheel]="false">
<agm-marker
*ngFor="let marker of widgets.widget6.markers"
[latitude]="marker.lat"
[longitude]="marker.lng">
<agm-info-window>
<strong>{{marker.label}}</strong>
</agm-info-window>
</agm-marker>
</agm-map>
</div>
<!-- / Widget 6 -->
</div>
<div class="right">
<div fxLayout="column">
<!-- Widget 7 -->
<div class="pb-24 font-size-18 font-weight-300">
What are your top devices?
</div>
<div class="fuse-card mb-32">
<div class="p-16">
<div class="h1 font-weight-300">Sessions by device</div>
</div>
<div class="h-200">
<ngx-charts-pie-chart
[scheme]="widgets.widget7.scheme"
[results]="widgets.widget7.devices"
[doughnut]="true">
</ngx-charts-pie-chart>
</div>
<div class="p-16" fxLayout="row" fxLayoutAlign="center center">
<div class="px-16" fxLayout="column" fxLayoutAlign="start center"
*ngFor="let device of widgets.widget7.devices">
<div class="h4 secondary-text">{{device.name}}</div>
<div class="h2 font-weight-300 py-8">{{device.value}}%</div>
<div fxLayout="row" fxLayoutAlign="center center">
<mat-icon class="s-18 pr-4 red-fg"
*ngIf="device.change < 0">
arrow_downward
</mat-icon>
<mat-icon class="s-18 pr-4 green-fg"
*ngIf="device.change > 0">
arrow_upward
</mat-icon>
<div class="h5 red-fg"
[ngClass]="{'red-fg': device.change < 0, 'green-fg': device.change > 0}">
{{device.change}}%
</div>
</div>
</div>
</div>
<div class="card-divider mb-0"></div>
<div class="px-16" fxLayout="row" fxLayoutAlign="space-between center">
<mat-form-field>
<mat-select class="simplified" value="7days">
<mat-option value="today">Today</mat-option>
<mat-option value="yesterday">Yesterday</mat-option>
<mat-option value="7days">Last 7 days</mat-option>
<mat-option value="28days">Last 28 days</mat-option>
<mat-option value="90days">Last 90 days</mat-option>
</mat-select>
</mat-form-field>
<button mat-button color="accent">OVERVIEW</button>
</div>
</div>
<!-- / Widget 7 -->
<!-- Widget 8 -->
<div class="pb-24 font-size-18 font-weight-300">
How are your sales?
</div>
<div class="fuse-card mb-32">
<div class="mat-light-blue-600-bg">
<div class="p-16" fxLayout="row" fxLayoutAlign="space-between center">
<div class="pr-16">
<div class="h1 font-weight-300">Sales</div>
<div class="h5 secondary-text">Lifetime sum of your sales</div>
</div>
<div>
<button mat-icon-button [matMenuTriggerFor]="card19Menu" aria-label="more">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #card19Menu="matMenu">
<button mat-menu-item>
<mat-icon>trending_up</mat-icon>
<span>Trend</span>
</button>
<button mat-menu-item>
<mat-icon>history</mat-icon>
<span>History</span>
</button>
<button mat-menu-item>
<mat-icon>notifications_off</mat-icon>
<span>Disable alerts</span>
</button>
</mat-menu>
</div>
</div>
<div class="p-16 pt-8" fxLayout="row" fxLayoutAlign="space-between end">
<div class="font-size-48 font-weight-300 line-height-1">{{widgets.widget8.today}}</div>
<div fxLayout="row" fxLayoutAlign="start center">
<mat-icon *ngIf="widgets.widget8.change.value > 0">trending_up</mat-icon>
<mat-icon *ngIf="widgets.widget8.change.value < 0">trending_down</mat-icon>
<div class="ml-8">{{widgets.widget8.change.value}}
({{widgets.widget8.change.percentage}}%)
</div>
</div>
</div>
</div>
<mat-tab-group backgroundColor="accent">
<mat-tab label="1DAY">
<div class="h-200 my-16">
<ngx-charts-line-chart
*fuseIfOnDom
[scheme]="widgets.widget8.scheme"
[results]="widgets.widget8.data"
[xAxis]="false"
[yAxis]="true"
[yScaleMin]="widgets.widget8.dataMin"
[yScaleMax]="widgets.widget8.dataMax">
</ngx-charts-line-chart>
</div>
</mat-tab>
<mat-tab label="1WEEK">
<div class="h-200 my-16">
<ngx-charts-line-chart
*fuseIfOnDom
[scheme]="widgets.widget8.scheme"
[results]="widgets.widget8.data"
[xAxis]="false"
[yAxis]="true"
[yScaleMin]="widgets.widget8.dataMin"
[yScaleMax]="widgets.widget8.dataMax">
</ngx-charts-line-chart>
</div>
</mat-tab>
<mat-tab label="1MONTH">
<div class="h-200 my-16">
<ngx-charts-line-chart
*fuseIfOnDom
[scheme]="widgets.widget8.scheme"
[results]="widgets.widget8.data"
[xAxis]="false"
[yAxis]="true"
[yScaleMin]="widgets.widget8.dataMin"
[yScaleMax]="widgets.widget8.dataMax">
</ngx-charts-line-chart>
</div>
</mat-tab>
</mat-tab-group>
</div>
<!-- / Widget 8 -->
<!-- Widget 9 -->
<div class="pb-24 font-size-18 font-weight-300">
What are your top campaigns?
</div>
<div class="fuse-card mb-32">
<div class="p-16" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h1 pr-16">Top campaigns</div>
<div>
<button mat-icon-button [matMenuTriggerFor]="card20Menu" aria-label="more">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #card20Menu="matMenu">
<button fxLayout="row" fxLayoutAlign="start center" mat-menu-item>
<mat-icon color="accent">check_box</mat-icon>
<span>Show Clicks</span>
</button>
<button fxLayout="row" fxLayoutAlign="start center" mat-menu-item>
<mat-icon color="accent">check_box</mat-icon>
<span>Show Conversion</span>
</button>
<button fxLayout="row" fxLayoutAlign="start center" mat-menu-item>
<mat-icon>check_box_outline_blank</mat-icon>
<span>Show CPC</span>
</button>
</mat-menu>
</div>
</div>
<table class="simple clickable">
<thead>
<tr>
<th></th>
<th class="text-right">Clicks</th>
<th class="text-right">Conv</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of widgets.widget9.rows">
<td>{{row.title}}</td>
<td class="text-right">{{row.clicks}}</td>
<td class="text-right">{{row.conversion}}</td>
</tr>
</tbody>
</table>
<div class="card-divider full-width"></div>
<div class="p-8 pt-16" fxLayout="row" fxLayoutAlign="start center">
<button mat-button color="accent">GO TO CAMPAIGNS</button>
</div>
</div>
<!-- / widget 9 -->
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,64 @@
#dashboard-analytics {
/*#widget1 {
.line-series {
.line {
stroke-width: 2px;
}
}
.gridline-path {
&.gridline-path-horizontal {
stroke: rgba(255, 255, 255, 0.12);
}
&.gridline-path-vertical {
stroke-width: 0;
}
}
.tick {
text {
fill: rgba(255, 255, 255, 0.37)
}
}
.tooltip-anchor {
fill: rgba(255, 255, 255, 0.54);
}
}*/
.main-widget {
display: flex;
flex-direction: column;
}
.content {
display: flex;
flex: 1 0 auto;
padding: 32px;
.left {
display: flex;
flex-direction: column;
flex: 1 0 auto;
.widget {
flex: 1 0 auto;
}
}
.right {
display: flex;
flex: 0 0 auto;
width: 320px;
min-width: 320px;
max-width: 320px;
}
}
}

View File

@ -0,0 +1,91 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { AnalyticsDashboardService } from './analytics.service';
import { fuseAnimations } from '../../../../../core/animations';
@Component({
selector : 'fuse-analytics-dashboard',
templateUrl : './analytics.component.html',
styleUrls : ['./analytics.component.scss'],
encapsulation: ViewEncapsulation.None,
animations : fuseAnimations
})
export class FuseAnalyticsDashboardComponent
{
widgets: any;
widget1SelectedYear = '2016';
widget5SelectedDay = 'today';
constructor(
private analyticsDashboardService: AnalyticsDashboardService
)
{
// Get the widgets from the service
this.widgets = this.analyticsDashboardService.widgets;
// Register the custom chart.js plugin
this.registerCustomChartJSPlugin();
}
/**
* Register a custom plugin
*/
registerCustomChartJSPlugin()
{
(<any>window).Chart.plugins.register({
afterDatasetsDraw: function (chart, easing) {
// Only activate the plugin if it's made available
// in the options
if (
!chart.options.plugins.xLabelsOnTop ||
(chart.options.plugins.xLabelsOnTop && chart.options.plugins.xLabelsOnTop.active === false)
)
{
return;
}
// To only draw at the end of animation, check for easing === 1
const ctx = chart.ctx;
chart.data.datasets.forEach(function (dataset, i) {
const meta = chart.getDatasetMeta(i);
if ( !meta.hidden )
{
meta.data.forEach(function (element, index) {
// Draw the text in black, with the specified font
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
const fontSize = 13;
const fontStyle = 'normal';
const fontFamily = 'Roboto, Helvetica Neue, Arial';
ctx.font = (<any>window).Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
// Just naively convert to string for now
const dataString = dataset.data[index].toString() + 'k';
// Make sure alignment settings are correct
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const padding = 15;
const startY = 24;
const position = element.tooltipPosition();
ctx.fillText(dataString, position.x, startY);
ctx.save();
ctx.beginPath();
ctx.setLineDash([5, 3]);
ctx.moveTo(position.x, startY + padding);
ctx.lineTo(position.x, position.y - padding);
ctx.strokeStyle = 'rgba(255,255,255,0.12)';
ctx.stroke();
ctx.restore();
});
}
});
}
});
}
}

View File

@ -0,0 +1,41 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SharedModule } from '../../../../../core/modules/shared.module';
import { NgxChartsModule } from '@swimlane/ngx-charts';
import { AgmCoreModule } from '@agm/core';
import { FuseAnalyticsDashboardComponent } from './analytics.component';
import { AnalyticsDashboardService } from './analytics.service';
import { FuseWidgetModule } from '../../../../../core/components/widget/widget.module';
const routes: Routes = [
{
path : '**',
component: FuseAnalyticsDashboardComponent,
resolve : {
data: AnalyticsDashboardService
}
}
];
@NgModule({
imports : [
SharedModule,
RouterModule.forChild(routes),
FuseWidgetModule,
NgxChartsModule,
AgmCoreModule.forRoot({
apiKey: 'AIzaSyD81ecsCj4yYpcXSLFcYU97PvRsE_X8Bx8'
})
],
declarations: [
FuseAnalyticsDashboardComponent
],
providers : [
AnalyticsDashboardService
]
})
export class FuseAnalyticsDashboardModule
{
}

View File

@ -4,9 +4,8 @@ import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
@Injectable() @Injectable()
export class ProjectsDashboardService implements Resolve<any> export class AnalyticsDashboardService implements Resolve<any>
{ {
projects: any[];
widgets: any[]; widgets: any[];
constructor( constructor(
@ -23,11 +22,9 @@ export class ProjectsDashboardService implements Resolve<any>
*/ */
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Promise.all([ Promise.all([
this.getProjects(),
this.getWidgets() this.getWidgets()
]).then( ]).then(
() => { () => {
@ -38,21 +35,10 @@ export class ProjectsDashboardService implements Resolve<any>
}); });
} }
getProjects(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/projects-dashboard-projects')
.subscribe((response: any) => {
this.projects = response;
resolve(response);
}, reject);
});
}
getWidgets(): Promise<any> getWidgets(): Promise<any>
{ {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http.get('api/projects-dashboard-widgets') this.http.get('api/analytics-dashboard-widgets')
.subscribe((response: any) => { .subscribe((response: any) => {
this.widgets = response; this.widgets = response;
resolve(response); resolve(response);

View File

@ -26,6 +26,12 @@ export class FuseNavigationModel implements FuseNavigationModelInterface
'title': 'Project', 'title': 'Project',
'type' : 'item', 'type' : 'item',
'url' : '/apps/dashboards/project' 'url' : '/apps/dashboards/project'
},
{
'id' : 'analytics',
'title': 'Analytics',
'type' : 'item',
'url' : '/apps/dashboards/analytics'
} }
] ]
}, },