Updated Hero Points Page with latest work

This commit is contained in:
Unknown 2018-03-08 11:32:24 +00:00
parent e1bf569f37
commit ab0ba52b6f
12 changed files with 404 additions and 231 deletions

View file

@ -26,8 +26,8 @@ import { OrgSnippetsService } from './providers/org-snippets.service';
import { CustSnippetsService } from './providers/cust-snippets.service'; import { CustSnippetsService } from './providers/cust-snippets.service';
import { CustPiesService } from './providers/cust-pies.service'; import { CustPiesService } from './providers/cust-pies.service';
import { MedalsService } from './providers/medals.service'; import { MedalsService } from './providers/medals.service';
//import { HeroPointsSnippetsService } from './providers/hero-points-snippets.service'; import { HeroPointsSnippetsService } from './providers/hero-points-snippets.service';
//import { HeroPointsStatsService } from './providers/hero-points-stats.service'; import { HeroPointsGraphService } from './providers/hero-points-graph.service';
// Layouts // Layouts
import { FullLayoutComponent } from './layouts/full-layout.component'; import { FullLayoutComponent } from './layouts/full-layout.component';
@ -77,10 +77,10 @@ import { NgPipesModule } from 'ngx-pipes';
OrgSnippetsService, OrgSnippetsService,
CustGraphsService, CustGraphsService,
CustSnippetsService, CustSnippetsService,
//HeroPointsSnippetsService, HeroPointsSnippetsService,
//HeroPointsStatsService,
MedalsService, MedalsService,
CustPiesService, CustPiesService,
HeroPointsGraphService,
{ {
provide: LocationStrategy, provide: LocationStrategy,
useClass: HashLocationStrategy useClass: HashLocationStrategy

View file

@ -26,7 +26,8 @@ import { HeroPointsComponent } from './hero-points.component';
import { GraphWidget } from '../widgets/graph-widget.component'; import { GraphWidget } from '../widgets/graph-widget.component';
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component'; import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
import { CustBarSnippetComponent } from '../snippets/cust-snippet-bar.component'; import { CustBarSnippetComponent } from '../snippets/cust-snippet-bar.component';
//import { HeroPointsSnippetBarComponent } from '../snippets/hero-points-snippet-bar.component'; import { HeroPointsSnippetBarComponent } from '../snippets/hero-points-snippet-bar.component';
import { HeroPointsGraphWidget } from '../widgets/hero-points-graph-widget.component';
import { GraphPanel } from '../panels/graph-panel.component'; import { GraphPanel } from '../panels/graph-panel.component';
import { PiePanel } from '../panels/pie-panel.component'; import { PiePanel } from '../panels/pie-panel.component';
@ -80,7 +81,8 @@ import { NgPipesModule } from 'ngx-pipes';
GraphWidget, GraphWidget,
OrgBarSnippetComponent, OrgBarSnippetComponent,
CustBarSnippetComponent, CustBarSnippetComponent,
//HeroPointsSnippetBarComponent, HeroPointsSnippetBarComponent,
HeroPointsGraphWidget,
GraphPanel, GraphPanel,
PiePanel, PiePanel,
HeroPointsComponent, HeroPointsComponent,

View file

@ -1,14 +1,14 @@
<div class="animated fadeIn"> <div class="animated fadeIn">
<!--<snippet-bar-hero-points></snippet-bar-hero-points>--><!-- Snippet for hero points (four circles) --> <snippet-bar-hero-points></snippet-bar-hero-points><!-- Snippet for hero points (four circles) -->
<div class="row"> <!-- second row --> <div class="row"> <!-- second row -->
<div class="col-md-6 col-xl-3"> <!-- Hero points gain charts --> <div class="col-md-6 col-xl-3"> <!-- Hero points gain charts -->
<div *ngFor="let widget of widgetList"> <!-- Points gain line graph --> <div *ngFor="let widget of widgetList"> <!-- Points gain line graph -->
<!--<widget-graph *ngIf="widget.type == 'graph'" <hero-points-widget-graph *ngIf="widget.type == 'graph'"
[graphName]="widget.name" [graphName]="widget.name"
[graphTitle]="widget.title" [graphTitle]="widget.title"
[graphIcon]="widget.icon" [graphIcon]="widget.icon"
[dataType]="widget.dataType"> [dataType]="widget.dataType">
</widget-graph>--> </hero-points-widget-graph>
</div> <!-- let widget of widgetList --> </div> <!-- let widget of widgetList -->
<div class="card"> <!-- Points gain by week charts --> <div class="card"> <!-- Points gain by week charts -->
<div class="card-block"> <div class="card-block">
@ -21,41 +21,41 @@
<ul class="horizontal-bars type-2"> <ul class="horizontal-bars type-2">
<li> <!-- This Week --> <li> <!-- This Week -->
<span class="title">This Week</span> <span class="title">This Week</span>
<span class="value">{{ transactionListWeek | filterBy: ['weekId']: (transactionListWeek | pluck: 'weekId' | max) | pluck: 'pointsEarned' }}<span class="text-muted small"> ({{( transactionListWeek | filterBy: ['weekId']: (transactionListWeek | pluck: 'weekId' | max) | pluck: 'pointsEarned' ) / ( transactionListWeek | pluck: 'pointsEarned' | max ) | percent:'1.0-0' }})</span></span> <span class="value">{{ heroPointsStats.this_week }}<span class="text-muted small"> ({{ heroPointsStats.this_week / heroPointsStats.max | percent:'1.0-0' }})</span></span>
<div class="bars"> <div class="bars">
<div class="progress" style="height: 6px;"> <div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar" <div class="progress-bar bg-success" role="progressbar"
style="width: 39%" aria-valuemin="0" aria-valuemax="100"></div> [style.width]="heroPointsStats.this_week / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
</div> <!-- progress --> </div> <!-- progress -->
</div> <!-- bars --> </div> <!-- bars -->
</li> </li>
<li> <!-- Last Week --> <li> <!-- Last Week -->
<span class="title">Last Week</span> <span class="title">Last Week</span>
<span class="value">{{ transactionListWeek | filterBy: ['weekId']: (transactionListWeek | pluck: 'weekId' | max) - 1 | pluck: 'pointsEarned' }}<span class="text-muted small"> ({{( transactionListWeek | filterBy: ['weekId']: (transactionListWeek | pluck: 'weekId' | max) - 1 | pluck: 'pointsEarned' ) / ( transactionListWeek | pluck: 'pointsEarned' | max ) | percent:'1.0-0' }})</span></span> <span class="value">{{ heroPointsStats.last_week }}<span class="text-muted small"> ({{ heroPointsStats.last_week / heroPointsStats.max | percent:'1.0-0' }})</span></span>
<div class="bars"> <div class="bars">
<div class="progress" style="height: 6px;"> <div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar" <div class="progress-bar bg-success" role="progressbar"
style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div> [style.width]="heroPointsStats.last_week / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
</div> <!-- progress --> </div> <!-- progress -->
</div> <!-- bars --> </div> <!-- bars -->
</li> </li>
<li> <!-- Week Maximum --> <li> <!-- Week Maximum -->
<span class="title">Week Maximum</span> <span class="title">Week Maximum</span>
<span class="value">{{ transactionListWeek | pluck: 'pointsEarned' | max }}<span class="text-muted small"> (100%)</span></span> <span class="value">{{ heroPointsStats.max }}<span class="text-muted small"> ({{ heroPointsStats.max / heroPointsStats.max | percent:'1.0-0' }})</span></span>
<div class="bars"> <div class="bars">
<div class="progress" style="height: 6px;"> <div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar" <div class="progress-bar bg-success" role="progressbar"
style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div> [style.width]="heroPointsStats.max / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
</div> <!-- progress --> </div> <!-- progress -->
</div> <!-- bars --> </div> <!-- bars -->
</li> </li>
<li> <!-- Weekly Average --> <li> <!-- Weekly Average -->
<span class="title">Weekly Average</span> <span class="title">Weekly Average</span>
<span class="value">{{ ((transactionListWeek | pluck: 'pointsEarned' | sum) / (transactionListWeek.length)) | round }}<span class="text-muted small"> ({{ (((transactionListWeek | pluck: 'pointsEarned' | sum) / (transactionListWeek.length)) | round ) / ( transactionListWeek | pluck: 'pointsEarned' | max ) | percent:'1.0-0' }})</span></span> <span class="value">{{ heroPointsStats.avg }}<span class="text-muted small"> ({{ heroPointsStats.avg / heroPointsStats.max | percent:'1.0-0' }})</span></span>
<div class="bars"> <div class="bars">
<div class="progress" style="height: 6px;"> <div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar" <div class="progress-bar bg-success" role="progressbar"
style="width: 51%" aria-valuemin="0" aria-valuemax="100"></div> [style.width]="heroPointsStats.avg / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
</div> <!-- progess --> </div> <!-- progess -->
</div> <!-- bars --> </div> <!-- bars -->
</li> </li>
@ -106,7 +106,7 @@
</div> <!-- if noUserList --> </div> <!-- if noUserList -->
</div> </div>
<div class="col-md-6 col-xl-6"> <!-- Medal Table --> <div class="col-md-6 col-xl-6"> <!-- Medal Table -->
<div *ngIf="!noMedalList"> <div *ngIf="!noGlobalMedalList && !noOrganisationMedalList">
<table class="table header-fixed"> <table class="table header-fixed">
<thead> <thead>
<tr> <tr>
@ -115,110 +115,140 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<ng-container *ngFor="let groupNo of medalList | unique: 'medalGroupId'; let i = index"> <!-- ngFor each group of medals (global) --> <ng-container *ngFor="let medals of globalMedalList"> <!-- ngFor each group of medals (global) -->
<ng-container *ngIf="(medalList | filterBy: ['title']: groupNo.title: search: true).length > 1"> <!-- if the group is longer than 1 --> <ng-container *ngFor="let group of medals | values; let groupIndex = index">
<ng-container *ngIf="(group[0] | values).length - 1 > 1"> <!-- if the group is longer than 1 -->
<!-- *ngIf 'global' medal and is/isn't expanded (headers)--> <!-- *ngIf 'global' medal and is/isn't expanded (headers)-->
<tr *ngIf="groupNo.expanded && groupNo.medalType == 'global'" (click)="groupNo.expanded = false" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }"> <tr *ngIf="group.expanded" (click)="group.expanded = false" [ngStyle]="{'background-color':(group[0] | values | filterBy: ['awarded']: 'true' | pluck: 'awarded').length === (group[0] | values | filterBy: ['awarded']).length ? '#5FBC47' : '#20A8D8', 'color': '#ffffff' }">
<td class="col-3">{{groupNo.title}}</td> <td class="col-3">{{(medals | keys)[groupIndex]}}</td>
<td class="col-6">Click to collapse</td> <td class="col-6">Click to collapse</td>
<td class="col-1">-</td> <td class="col-1">-</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{ ((medalList | filterBy: ['medalType']: 'global' | filterBy: ['title']: groupNo.title: search: true) | filterBy: ['unlocked']: true).length }}/{{ (medalList | filterBy: ['medalType']: 'global'| filterBy: ['title']: groupNo.title: search: true).length }}</td> <td class="col-2" [ngStyle]="{'text-align': 'right'}">{{(group[0] | values | filterBy: ['awarded']: 'true' | pluck: 'awarded').length}}/{{(group[0] | values | filterBy: ['awarded']).length}}</td>
</tr> </tr>
<tr *ngIf="!groupNo.expanded && groupNo.medalType == 'global'" (click)="groupNo.expanded = true" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }"> <tr *ngIf="!group.expanded" (click)="group.expanded = true" [ngStyle]="{'background-color':(group[0] | values | filterBy: ['awarded']: 'true' | pluck: 'awarded').length === (group[0] | values | filterBy: ['awarded']).length ? '#5FBC47' : '#20A8D8', 'color': '#ffffff' }">
<td class="col-3">{{groupNo.title}}</td> <td class="col-3">{{(medals | keys)[groupIndex]}}</td>
<td class="col-6">Click to expand</td> <td class="col-6">Click to expand</td>
<td class="col-1">+</td> <td class="col-1">+</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}"> {{ ((medalList | filterBy: ['medalType']: 'global' | filterBy: ['title']: groupNo.title: search: true) | filterBy: ['unlocked']: true).length }}/{{ (medalList | filterBy: ['medalType']: 'global'| filterBy: ['title']: groupNo.title: search: true).length }}</td> <td class="col-2" [ngStyle]="{'text-align': 'right'}">{{(group[0] | values | filterBy: ['awarded']: 'true' | pluck: 'awarded').length}}/{{(group[0] | values | filterBy: ['awarded']).length}}</td>
</tr> </tr>
<!-- display actual medals --> <!-- display actual medals -->
<ng-container *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']"> <!-- list each medal in each group, ordered by rewards, ascending order --> <ng-container *ngFor="let threshold of group[0] | values | orderBy: '+threshold'; let thresholdIndex = index"> <!-- list each medal in each group, ordered by threshold, ascending order -->
<tr *ngIf="groupNo.expanded && medal.medalGroupId == i && groupNo.medalType == 'global'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }"> <ng-container *ngIf="threshold | isObject">
<td class="col-3">{{ medal.title }}</td> <ng-container *ngIf="threshold.awarded">
<td class="col-7">{{ medal.description }}</td> <tr *ngIf="group.expanded" [ngStyle]="{'background-color': '#5FBC47', 'color': '#ffffff' }">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td> <!-- progress --> <ng-container *ngIf="">
</ng-container>
<td class="col-3">{{(medals | keys)[groupIndex]}} {{thresholdIndex + 1}}</td>
<td class="col-7">DESCRIPTION</td>
<ng-container *ngIf="thresholdIndex + 1 != (group[0] | values | orderBy: '+threshold').length - 1">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{threshold.threshold}}/{{threshold.threshold}}</td> <!-- progress -->
</ng-container>
<ng-container *ngIf="thresholdIndex + 1 == (group[0] | values | orderBy: '+threshold').length - 1">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{group[0].total}}/{{threshold.threshold}}</td> <!-- progress -->
</ng-container>
</tr>
</ng-container>
<ng-container *ngIf="!threshold.awarded">
<tr *ngIf="group.expanded" [ngStyle]="{'background-color': '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{(medals | keys)[groupIndex]}} {{thresholdIndex + 1}}</td>
<td class="col-7">DESCRIPTION</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{group[0].total}}/{{threshold.threshold}}</td> <!-- progress -->
</tr> </tr>
</ng-container> </ng-container>
</ng-container> </ng-container>
<ng-container *ngIf="(medalList | filterBy: ['title']: groupNo.title: search: true).length == 1"> <!-- if there is only one medal in the group, just display medal, no header --> </ng-container>
<ng-container *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']"> <!-- Unnecessary loop because there is only one medal in each group --> </ng-container>
<tr *ngIf="medal.medalGroupId == i && groupNo.medalType == 'global'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }"> <ng-container *ngIf="(group[0] | values).length - 1 == 1"> <!-- if there is only one medal in the group, just display medal, no header -->
<td class="col-3">{{ medal.title }}</td> <!-- medal name --> <ng-container *ngFor="let threshold of group[0] | values | orderBy: '+threshold'; let thresholdIndex = index">
<td class="col-7">{{ medal.description }}</td> <!-- medal description --> <ng-container *ngIf="threshold | isObject">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td> <!-- progress --> <tr [ngStyle]="{'background-color':threshold.awarded === true ? '#5FBC47' : '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{(medals | keys)[groupIndex]}}</td>
<td class="col-7">DESCRIPTION</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{group[0].total}}/{{threshold.threshold}}</td> <!-- progress -->
</tr> </tr>
</ng-container> </ng-container>
</ng-container> </ng-container>
</ng-container>
</ng-container>
</ng-container> <!-- ngFor each group of medals (global) --> </ng-container> <!-- ngFor each group of medals (global) -->
<ng-container *ngFor="let organisation of organisationList | orderBy: '+name'; let orgI = index"> <!-- ngFor organisation of organisationList --> <ng-container *ngFor="let medals of organisationMedalList">
<!-- ngIf organisation is/isn't expanded display correct header --> <ng-container *ngFor="let orgs of medals | values">
<tr *ngIf="organisation.expanded" (click)="organisation.expanded = false" [ngStyle]="{'background-color': '#F39C12', 'color': '#ffffff' }"> <ng-container *ngFor="let org of orgs; let orgIndex = index">
<td class="col-3">{{organisation.name}}</td> <tr *ngIf="org.expanded" (click)="org.expanded = false" [ngStyle]="{'background-color': '#F39C12', 'color': '#ffffff' }">
<td class="col-3">{{org.name}}</td>
<td class="col-6">Click to collapse</td> <td class="col-6">Click to collapse</td>
<td class="col-1">-</td> <td class="col-1">-</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{ (medalList | filterBy: ['medalType']: 'organisation' | filterBy: ['unlocked']: true).length }}/{{ (medalList | filterBy: ['medalType']: 'organisation').length }}</td> <td class="col-2" [ngStyle]="{'text-align': 'right'}">{{org.count}}/{{org.threshold}}</td>
</tr> </tr>
<tr *ngIf="!organisation.expanded" (click)="organisation.expanded = true" [ngStyle]="{'background-color': '#F39C12', 'color': '#ffffff' }"> <tr *ngIf="!org.expanded" (click)="org.expanded = true" [ngStyle]="{'background-color': '#F39C12', 'color': '#ffffff' }">
<td class="col-3">{{organisation.name}}</td> <td class="col-3">{{org.name}}</td>
<td class="col-6">Click to expand</td> <td class="col-6">Click to expand</td>
<td class="col-1">+</td> <td class="col-1">+</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{ (medalList | filterBy: ['medalType']: 'organisation' | filterBy: ['unlocked']: true).length }}/{{ (medalList | filterBy: ['medalType']: 'organisation').length }}</td> <td class="col-2" [ngStyle]="{'text-align': 'right'}">{{org.count}}/{{org.threshold}}</td>
</tr> </tr>
<ng-container *ngFor="let groupNo of medalList | unique: 'medalGroupId'; let i = index"> <!-- ngFor each group of medals (organisation) --> <ng-container *ngFor="let group of org | values; let groupIndex = index">
<ng-container *ngIf="(medalList | filterBy: ['title']: groupNo.title: search: true).length > 1"> <!-- ngIf length of group is more than 1 --> <ng-container *ngIf="group | isObject">
<!-- ngIf organisation is/isn't expanded --> <ng-container *ngIf="(group[0] | keys).length - 1 > 1">
<tr *ngIf="organisation.expanded && groupNo.expanded && groupNo.medalType == 'organisation'" (click)="groupNo.expanded = false" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }"> <tr *ngIf="group.expanded && org.expanded" (click)="group.expanded = false" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }">
<td class="col-3">{{groupNo.title}}</td> <td class="col-3">{{(org | keys)[groupIndex]}}</td>
<td class="col-6">Click to collapse</td> <td class="col-6">Click to collapse</td>
<td class="col-1">-</td> <td class="col-1">-</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{ ((medalList | filterBy: ['medalType']: 'organisation' | filterBy: ['title']: groupNo.title: search: true) | filterBy: ['unlocked']: true).length }}/{{ (medalList | filterBy: ['medalType']: 'organisation'| filterBy: ['title']: groupNo.title: search: true).length }}</td> <td class="col-2" [ngStyle]="{'text-align': 'right'}">{{(group[0] | values | filterBy: ['awarded']: 'true' | pluck: 'awarded').length}}/{{(group[0] | values | filterBy: ['awarded']).length}}</td>
</tr> </tr>
<tr *ngIf="organisation.expanded && !groupNo.expanded && groupNo.medalType == 'organisation'" (click)="groupNo.expanded = true" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }"> <tr *ngIf="!group.expanded && org.expanded" (click)="group.expanded = true" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }">
<td class="col-3">{{groupNo.title}}</td> <td class="col-3">{{(org | keys)[groupIndex]}}</td>
<td class="col-6">Click to expand</td> <td class="col-6">Click to expand</td>
<td class="col-1">+</td> <td class="col-1">+</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{ ((medalList | filterBy: ['medalType']: 'organisation' | filterBy: ['title']: groupNo.title: search: true) | filterBy: ['unlocked']: true).length }}/{{ (medalList | filterBy: ['medalType']: 'organisation'| filterBy: ['title']: groupNo.title: search: true).length }}</td> <td class="col-2" [ngStyle]="{'text-align': 'right'}">{{(group[0] | values | filterBy: ['awarded']: 'true' | pluck: 'awarded').length}}/{{(group[0] | values | filterBy: ['awarded']).length}}</td>
</tr> </tr>
<ng-container *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']"> <ng-container *ngFor="let threshold of group[0] | values | orderBy: '+threshold'; let thresholdIndex = index">
<tr *ngIf="organisation.expanded && groupNo.expanded && medal.medalGroupId == i && groupNo.medalType == 'organisation'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }"> <ng-container *ngIf="threshold | isObject">
<td class="col-3">{{ medal.title }}</td> <ng-container *ngIf="threshold.awarded">
<td class="col-7">{{ medal.description }}</td> <tr *ngIf="group.expanded && org.expanded" [ngStyle]="{'background-color': '#5FBC47', 'color': '#ffffff' }">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td> <td class="col-3">{{(org | keys)[groupIndex]}} {{thresholdIndex + 1}}</td>
<td class="col-7">DESCRIPTION</td>
<ng-container *ngIf="thresholdIndex + 1 != (group[0] | values | orderBy: '+threshold').length - 1">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{threshold.threshold}}/{{threshold.threshold}}</td>
</ng-container>
<ng-container *ngIf="thresholdIndex + 1 == (group[0] | values | orderBy: '+threshold').length - 1">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{group[0].total}}/{{threshold.threshold}}</td>
</ng-container>
</tr>
</ng-container>
<ng-container *ngIf="!threshold.awarded">
<tr *ngIf="group.expanded && org.expanded" [ngStyle]="{'background-color': '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{(org | keys)[groupIndex]}} {{thresholdIndex + 1}}</td>
<td class="col-7">DESCRIPTION</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{group[0].total}}/{{threshold.threshold}}</td>
</tr> </tr>
</ng-container> </ng-container>
</ng-container> </ng-container>
<ng-container *ngIf="(medalList | filterBy: ['title']: groupNo.title: search: true).length <= 1"> <!-- ngIf length or group is <= 1 --> </ng-container>
<ng-container *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']"> </ng-container>
<tr *ngIf="organisation.expanded && medal.medalGroupId == i && groupNo.medalType == 'organisation'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }"> <ng-container *ngIf="(group[0] | keys).length - 1 == 1">
<td class="col-3">{{ medal.title }}</td> <ng-container *ngFor="let threshold of group[0] | values | orderBy: '+threshold'; let thresholdIndex = index">
<td class="col-7">{{ medal.description }}</td> <ng-container *ngIf="threshold | isObject">
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td> <ng-container *ngIf="org.expanded">
<tr [ngStyle]="{'background-color':threshold.awarded === true ? '#5FBC47' : '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{(org | keys)[groupIndex]}}</td>
<td class="col-7">DESCRIPTION</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{group[0].total}}/{{threshold.threshold}}</td>
</tr> </tr>
</ng-container> </ng-container>
</ng-container> </ng-container>
</ng-container> <!-- ngFor each group of medals (organisation) --> </ng-container>
</ng-container> <!-- ngFor organisation of organisationList --> </ng-container>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</tbody> </tbody>
</table> </table>
</div> <!-- if !noMedalList --> </div> <!-- if !noMedalList -->
<div *ngIf="noMedalList" class="card-block"> <div *ngIf="noGlobalMedalList && noOrganisationMedalList" class="card-block">
No Leaderboard available. No Leaderboard available.
</div> <!-- if noMedalList --> </div> <!-- if noMedalList -->
</div>
</div><!-- Row --> </div><!-- Row -->
<div *ngFor="let group of globalMedalList">
<div *ngFor="let threshold of group.group_name | pluck: 'threshold'">
</div>
</div>
<div *ngFor="let medals of testList">
<div *ngFor="let group of medals | values; let groupIndex = index">
<div>threshold length - total {{(group[0] | values).length - 1}}</div>
<div>group_title {{(medals | keys)[groupIndex]}}</div>
<div>All awarded values for group {{group[0] | values | filterBy: ['awarded']: 'true' | pluck: 'awarded' | json}}</div>
<div *ngFor="let thresholds of group[0] | values">
<div *ngIf="thresholds | isObject">
<div>9 {{thresholds | json}}</div>
</div>
</div>
</div>
</div> </div>
</div> </div>

View file

@ -13,53 +13,34 @@ import 'rxjs/add/operator/map';
// import Services // import Services
import { CustSnippetsService } from '../providers/cust-snippets.service'; import { CustSnippetsService } from '../providers/cust-snippets.service';
import { MedalsService } from '../providers/medals.service'; import { MedalsService } from '../providers/medals.service';
//import { HeroPointsStatsService } from '../providers/hero-points-stats.service'; import { HeroPointsSnippetsService } from '../providers/hero-points-snippets.service';
// Pipes
import { KeysPipe } from 'ngx-pipes';
import { FilterByPipe} from 'ngx-pipes';
import { ValuesPipe } from 'ngx-pipes';
import { PluckPipe } from 'ngx-pipes';
@Component({ @Component({
templateUrl: './hero-points.component.html', templateUrl: './hero-points.component.html',
//ngx Pipes
providers: [
KeysPipe,
FilterByPipe,
ValuesPipe,
PluckPipe,
HeroPointsSnippetsService,
]
}) })
export class HeroPointsComponent implements OnInit { export class HeroPointsComponent implements OnInit {
order: string = 'heroPoints'; order: string = 'heroPoints';
public testList = [ public globalMedalList = [{
{
group_name: [{
5: {
awarded: true,
awarded_at: null,
points: 0,
threshold: 5,
},
10: {
awarded: true,
awarded_at: null,
points: 0,
threshold: 10,
},
25: {
awarded: false,
awarded_at: null,
points: 0,
threshold: 25,
},
total: 11,
}],
group_name2: [{
10: {
awarded: false,
awarded_at: null,
points: 0,
threshold: 10,
},
total: 3,
}],
}
]
public globalMedalList = [
{
group_name: [{ group_name: [{
threshold: { threshold: {
awarded: false, awarded: false,
@ -69,8 +50,7 @@ export class HeroPointsComponent implements OnInit {
}, },
total: 0, total: 0,
}], }],
} }];
]
public organisationMedalList = [{ public organisationMedalList = [{
org_id: [{ org_id: [{
@ -84,45 +64,20 @@ export class HeroPointsComponent implements OnInit {
total: 0, total: 0,
}], }],
name: '', name: '',
count: null,
threshold: null,
}], }],
}] }];
public medals = {
global: {
group_name: {
threshold: {
awarded: false,
awarded_at: "2017-01-01-T00:00:00Z",
points: 0,
threshold: 0,
},
total: 0,
},
},
organisation: {
org_id: {
group_name: {
threshold: {
awarded: false,
awarded_at: "2017-01-01-T00:00:00Z",
multiplier: 0,
points: 0,
threshold: 0,
},
total: 0,
},
name: "Placeholder",
}
}
};
// Hero points stats // Hero points stats
public statsThisWeek = 0; public heroPointsStats = {
public statsLastWeek = 0; this_week: 0,
public statsMax = 0; last_week: 0,
public statsSum = 0; max: 0,
public statsCount = 0; sum: 0,
count: 0,
avg: 0,
};
// Gets list of transactions // Gets list of transactions
public paginateConfig: PaginationInstance = { public paginateConfig: PaginationInstance = {
@ -145,7 +100,11 @@ export class HeroPointsComponent implements OnInit {
constructor( constructor(
private api: ApiService, private api: ApiService,
private medalsService: MedalsService, private medalsService: MedalsService,
//private heroPointsStatsService: HeroPointsStatsService, private keysPipe: KeysPipe,
private filterByPipe: FilterByPipe,
private valuesPipe: ValuesPipe,
private pluckPipe: PluckPipe,
private heroPointsSnippetsService: HeroPointsSnippetsService,
) { ) {
this.api.customerStats().subscribe( this.api.customerStats().subscribe(
result => { result => {
@ -173,19 +132,19 @@ export class HeroPointsComponent implements OnInit {
this.setGlobalMedalList(result.global), this.setGlobalMedalList(result.global),
this.setOrganisationMedalList(result.organisation) this.setOrganisationMedalList(result.organisation)
} }
) );
/*
this.heroPointsStatsService.getHeroPointsStats() this.heroPointsSnippetsService.getHeroPointsSnippets()
.subscribe( .subscribe(
result => { result => {
this.statsThisWeek = result.points.stats.this_week; this.heroPointsStats.this_week = result.widget_progress.this_week;
this.statsLastWeek = result.points.stats.last_week; this.heroPointsStats.last_week = result.widget_progress.last_week;
this.statsMax = result.points.stats.max; this.heroPointsStats.max = result.widget_progress.max;
this.statsSum = result.points.stats.sum; this.heroPointsStats.sum = result.widget_progress.sum;
this.statsCount = result.points.stats.count; this.heroPointsStats.count = result.widget_progress.count;
this.heroPointsStats.avg = result.widget_progress.sum / result.widget_progress.count;
} }
) );
*/
}; };
public setGlobalMedalList( data:any ){ public setGlobalMedalList( data:any ){
@ -216,6 +175,8 @@ export class HeroPointsComponent implements OnInit {
total: data.org_id.group_name.total, total: data.org_id.group_name.total,
}], }],
name: data.org_id.name, name: data.org_id.name,
count: (this.pluckPipe.transform(this.filterByPipe.transform(this.valuesPipe.transform(data.org_id.group_name), ['awarded'], true), 'awarded')).length,
threshold: (this.filterByPipe.transform(this.valuesPipe.transform(data.org_id.group_name), ['awarded'], true)).length,
}], }],
}] }]
}; };

View file

@ -0,0 +1,15 @@
import { Injectable } from '@angular/core';
import { ApiService } from './api-service';
import { Observable } from 'rxjs/Rx';
@Injectable()
export class HeroPointsGraphService {
private heroPointsGraphUrl = '/v1/user/points';
constructor(private api: ApiService) { }
// This endpoint should mimic basicStats
public getHeroPointsGraph(): Observable<any> {
return this.api.post(this.heroPointsGraphUrl);
}
}

View file

@ -9,7 +9,7 @@ export class HeroPointsSnippetsService {
constructor(private api: ApiService) { } constructor(private api: ApiService) { }
// This endpoint should mimic basicStats // This endpoint should mimic basicStats
public getPointsData(): Observable<any> { public getHeroPointsSnippets(): Observable<any> {
return this.api.post(this.heroPointsSnippetsUrl); return this.api.post(this.heroPointsSnippetsUrl);
} }
} }

View file

@ -1,14 +0,0 @@
import { Injectable } from '@angular/core';
import { ApiService } from './api-service';
import { Observable } from 'rxjs/Rx';
@Injectable()
export class HeroPointsStatsService {
private heroPointsStatsUrl = '/v1/user/points';
constructor(private api: ApiService) { }
public getHeroPointsStats(): Observable<any> {
return this.api.post(this.heroPointsStatsUrl);
}
}

View file

@ -10,6 +10,7 @@ export class MedalsService {
public getMedals(): Observable<any> { public getMedals(): Observable<any> {
return this.api.post(this.medalsUrl); return this.api.post(this.medalsUrl);
//return Observable.of()
} }
} }

View file

@ -5,7 +5,7 @@
<ul style="list-style: none; padding-left: 0;"> <ul style="list-style: none; padding-left: 0;">
<li class="hidden-sm-down"> <li class="hidden-sm-down">
<div><h5 class="text-orange">My Hero Points</h5></div> <div><h5 class="text-orange">My Hero Points</h5></div>
<div class="number-circle-hero mx-auto"><strong>{{ pointTotal | number:'1.0-0' }}</strong></div> <div class="number-circle-hero mx-auto"><strong>{{ heroPointsSnippets.points_total | number:'1.0-0' }}</strong></div>
</li> </li>
</ul> </ul>
</div> </div>
@ -17,7 +17,7 @@
<ul style="list-style: none; padding-left: 0;"> <ul style="list-style: none; padding-left: 0;">
<li class="hidden-sm-down"> <li class="hidden-sm-down">
<div><h5 class="text-dark-green">Last Transaction Points</h5></div> <div><h5 class="text-dark-green">Last Transaction Points</h5></div>
<div class="number-circle mx-auto"><strong>{{pointLast | number:'1.0-0' }}</strong></div> <div class="number-circle mx-auto"><strong>{{ heroPointsSnippets.point_last | number:'1.0-0' }}</strong></div>
</li> </li>
</ul> </ul>
</div> </div>
@ -29,7 +29,7 @@
<ul style="list-style: none; padding-left: 0;"> <ul style="list-style: none; padding-left: 0;">
<li class="hidden-sm-down"> <li class="hidden-sm-down">
<div><h5 class="text-dark-green">Total Transactions</h5></div> <div><h5 class="text-dark-green">Total Transactions</h5></div>
<div class="number-circle mx-auto"><strong>{{ transCount | number:'1.0-0' }}</strong></div> <div class="number-circle mx-auto"><strong>{{ heroPointsSnippets.trans_count | number:'1.0-0' }}</strong></div>
</li> </li>
</ul> </ul>
</div> </div>
@ -41,7 +41,7 @@
<ul style="list-style: none; padding-left: 0;"> <ul style="list-style: none; padding-left: 0;">
<li class="hidden-sm-down"> <li class="hidden-sm-down">
<div><h5 class="text-dark-green">Average Multiplier</h5></div> <div><h5 class="text-dark-green">Average Multiplier</h5></div>
<div class="number-circle mx-auto"><strong>{{ avgMulti | number:'1.0-0' }}</strong></div> <div class="number-circle mx-auto"><strong>{{ heroPointsSnippets.avg_multi | number:'1.0-0' }}</strong></div>
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -8,24 +8,27 @@ import { HeroPointsSnippetsService } from '../providers/hero-points-snippets.ser
export class HeroPointsSnippetBarComponent implements OnInit { export class HeroPointsSnippetBarComponent implements OnInit {
public pointTotal = 0; // Hero Points snippets
public pointLast = 0; public heroPointsSnippets = {
public transCount = 0; avg_multi: 0,
public avgMulti = 0; point_last: 0,
points_total: 0,
trans_count: 0,
};
constructor( constructor(
private snippetsService: HeroPointsSnippetsService, private heroPointsSnippetsService: HeroPointsSnippetsService,
) { } ) { }
public ngOnInit(): void { public ngOnInit(): void {
this.snippetsService.getPointsData() this.heroPointsSnippetsService.getHeroPointsSnippets()
.subscribe( .subscribe(
result => { result => {
this.pointTotal = result.snippets.point_total; this.heroPointsSnippets.avg_multi = result.snippets.avg_multi;
this.pointLast = result.snippets.point_last; this.heroPointsSnippets.point_last = result.snippets.point_last;
this.transCount = result.snippets.trans_count; this.heroPointsSnippets.points_total = result.snippets.points_total;
this.avgMulti = result.snippets.avg_multi; this.heroPointsSnippets.trans_count = result.snippets.trans_count;
} }
); )
} }
} }

View file

@ -0,0 +1,21 @@
<div class="card card-inverse card-primary">
<div class="card-block pb-0">
<button type="button" class="btn btn-transparent p-0 float-right">
<i [ngClass]="graphIcon"></i>
</button>
<h4 *ngIf="dataType == availableDataTypes.number" class="mb-0">{{ graphSum }}</h4>
<p>{{ graphTitle }}</p>
</div>
<div class="chart-wrapper px-3" style="height:70px;">
<canvas baseChart
class="chart"
[datasets]="lineChartData"
[labels]="lineChartLabels"
[options]="lineChartOptions"
[colors]="lineChartColours"
[legend]="lineChartLegend"
[chartType]="lineChartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>

View file

@ -0,0 +1,154 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { OrgGraphsService } from '../providers/org-graphs.service';
import { DataType } from '../shared/data-types.enum';
interface ChartData {
data: Array<number>;
label: string;
}
@Component({
selector: 'hero-points-widget-graph',
templateUrl: 'hero-points-graph-widget.component.html',
})
export class HeroPointsGraphWidget implements OnInit {
@Input() public graphName: string;
@Input() public graphTitle = 'Graph';
@Input() public graphIcon = 'icon-graph';
@Input() public dataType: DataType = DataType.number;
@Output() public graphHover = new EventEmitter();
@Output() public graphClick = new EventEmitter();
public graphSum: Number = 0;
public availableDataTypes = DataType;
public lineChartData: Array<ChartData> = [
{
data: [],
label: 'Series A'
}
];
public lineChartLabels: Array<string>;
public lineChartOptions: any = {
maintainAspectRatio: false,
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'day',
displayFormats: {
day: 'MMM D',
},
tooltipFormat: 'MMM D',
},
gridLines: {
color: 'transparent',
zeroLineColor: 'transparent'
},
ticks: {
fontSize: 2,
source: 'data',
fontColor: 'transparent',
}
}],
yAxes: [{
display: false,
ticks: {
display: false,
}
}],
},
elements: {
line: {
borderWidth: 1
},
point: {
radius: 4,
hitRadius: 10,
hoverRadius: 4,
},
},
legend: {
display: false
},
tooltips: {
callbacks: {
label: (tooltip, data) => {
return this.tooltipLabelCallback(tooltip, data);
},
},
},
};
public lineChartColours: Array<any> = [
{
backgroundColor: '#20a8d8',
borderColor: 'rgba(255,255,255,.55)'
}
];
public lineChartLegend = false;
public lineChartType = 'line';
constructor(
private graphService: OrgGraphsService,
) { }
ngOnInit(): void {
if ( this.graphName == null ) {
throw new Error('Attribute \'graphName\' is required on component \'widget-graph\'');
}
if ( this.dataType === undefined ) {
// Need to do this as it may be passed in a loop with an undefined value
this.dataType = DataType.number;
}
if ( !( this.dataType in DataType ) ) {
console.warn('Unknown DataType for graph \'' + this.graphName + '\' - defaulting to number');
}
this.graphService.getGraph(this.graphName)
.subscribe( result => this.setData(result.graph) );
}
private setData(data: any) {
this.setChartData(data.data);
this.setChartLabels(data.labels);
this.setChartBounds(data.bounds);
}
private setChartBounds(data) {
this.lineChartOptions.scales.xAxes[0].time.max = data.max;
this.lineChartOptions.scales.xAxes[0].time.min = data.min;
}
private setChartData(data: Array<number>) {
this.lineChartData[0].data = data;
this.graphSum = data.reduce((a, b) => a + b, 0);
// Set point size based on data
if ( data.length < 15 ) {
this.lineChartOptions.elements.point.radius = 4;
this.lineChartOptions.elements.line.borderWidth = 1;
} else {
this.lineChartOptions.elements.point.radius = 2;
this.lineChartOptions.elements.line.borderWidth = 2;
}
}
private setChartLabels(data: Array<string>) {
this.lineChartLabels = data;
}
// events
public chartClicked(e: any): void {
console.log(e);
}
public chartHovered(e: any): void {
console.log(e);
}
private tooltipLabelCallback(tooltipItem: any, data: any) {
const value = tooltipItem.yLabel;
return value || '0';
}
}