Compare commits
5 commits
developmen
...
james/hero
Author | SHA1 | Date | |
---|---|---|---|
|
ab0ba52b6f | ||
|
e1bf569f37 | ||
|
c7efe060eb | ||
|
e378f57a8c | ||
|
cf1151872d |
21 changed files with 4705 additions and 206 deletions
2161
package-lock.json
generated
2161
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -29,14 +29,19 @@
|
|||
"@angular/router": "5.2.0",
|
||||
"@angular/upgrade": "5.2.0",
|
||||
"@types/moment": "2.13.0",
|
||||
"angular-scrollable-table": "^1.1.2",
|
||||
"chart.js": "2.7.1",
|
||||
"core-js": "2.5.1",
|
||||
"js-marker-clusterer": "1.0.0",
|
||||
"moment": "^2.19.2",
|
||||
"ng2-charts": "1.6.0",
|
||||
"ng2-expanding-table": "^1.5.2",
|
||||
"ng2-validation-manager": "0.5.3",
|
||||
"ngx-bootstrap": "2.0.0-beta.8",
|
||||
"ngx-order-pipe": "^1.1.2",
|
||||
"ngx-pagination": "3.0.3",
|
||||
"ngx-pipes": "^2.1.0",
|
||||
"node": "^9.4.0",
|
||||
"rxjs": "5.5.6",
|
||||
"ts-helpers": "1.1.2",
|
||||
"webpack": "3.8.1",
|
||||
|
@ -44,7 +49,8 @@
|
|||
"zone.js": "0.8.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.6.4",
|
||||
"@angular-devkit/schematics": "0.0.48",
|
||||
"@angular/cli": "^1.6.6",
|
||||
"@angular/compiler-cli": "5.2.0",
|
||||
"@types/jasmine": "2.8.2",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
|
@ -61,6 +67,6 @@
|
|||
"protractor": "5.2.0",
|
||||
"ts-node": "3.3.0",
|
||||
"tslint": "5.8.0",
|
||||
"typescript": "2.6.x"
|
||||
"typescript": "^2.5.3"
|
||||
}
|
||||
}
|
||||
|
|
4
src/app/_guards/_interfaces/chart-data.ts
Normal file
4
src/app/_guards/_interfaces/chart-data.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export interface ChartData {
|
||||
data: Array<number>;
|
||||
label: string;
|
||||
}
|
|
@ -25,6 +25,9 @@ import { CustGraphsService } from './providers/cust-graphs.service';
|
|||
import { OrgSnippetsService } from './providers/org-snippets.service';
|
||||
import { CustSnippetsService } from './providers/cust-snippets.service';
|
||||
import { CustPiesService } from './providers/cust-pies.service';
|
||||
import { MedalsService } from './providers/medals.service';
|
||||
import { HeroPointsSnippetsService } from './providers/hero-points-snippets.service';
|
||||
import { HeroPointsGraphService } from './providers/hero-points-graph.service';
|
||||
|
||||
// Layouts
|
||||
import { FullLayoutComponent } from './layouts/full-layout.component';
|
||||
|
@ -38,11 +41,15 @@ import { P500Component } from './pages/500.component';
|
|||
import { AuthModule } from './auth/auth.module';
|
||||
import { DashboardModule } from './dashboard/dashboard.module';
|
||||
|
||||
// Pipes
|
||||
import { NgPipesModule } from 'ngx-pipes';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HttpClientModule,
|
||||
NgxPaginationModule,
|
||||
NgPipesModule,
|
||||
BsDropdownModule.forRoot(),
|
||||
TabsModule.forRoot(),
|
||||
AuthModule,
|
||||
|
@ -70,12 +77,18 @@ import { DashboardModule } from './dashboard/dashboard.module';
|
|||
OrgSnippetsService,
|
||||
CustGraphsService,
|
||||
CustSnippetsService,
|
||||
HeroPointsSnippetsService,
|
||||
MedalsService,
|
||||
CustPiesService,
|
||||
HeroPointsGraphService,
|
||||
{
|
||||
provide: LocationStrategy,
|
||||
useClass: HashLocationStrategy
|
||||
}
|
||||
],
|
||||
exports: [
|
||||
NgPipesModule,
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<div class="row">
|
||||
<div *ngFor="let widget of widgetList" class="col-sm-6 col-lg-3">
|
||||
<widget-graph *ngIf="widget.type == 'graph'"
|
||||
[graphName]="widget.name"
|
||||
[graphTitle]="widget.title"
|
||||
[graphIcon]="widget.icon"
|
||||
[dataType]="widget.dataType">
|
||||
[graphName]="widget.name"
|
||||
[graphTitle]="widget.title"
|
||||
[graphIcon]="widget.icon"
|
||||
[dataType]="widget.dataType">
|
||||
</widget-graph>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
|
|
|
@ -21,10 +21,13 @@ import { PayrollLogComponent } from './payroll-log.component';
|
|||
import { LeaderboardComponent } from './leaderboard.component';
|
||||
import { MapComponent } from './map.component';
|
||||
import { TrailMapComponent } from './trail-map.component';
|
||||
import { HeroPointsComponent } from './hero-points.component';
|
||||
|
||||
import { GraphWidget } from '../widgets/graph-widget.component';
|
||||
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
|
||||
import { CustBarSnippetComponent } from '../snippets/cust-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 { PiePanel } from '../panels/pie-panel.component';
|
||||
|
||||
|
@ -38,6 +41,9 @@ import { LeaderboardResultComponent } from '../shared/leaderboard-result.compone
|
|||
// API key env variable import
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
// Pipes
|
||||
import { NgPipesModule } from 'ngx-pipes';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
// Angular imports
|
||||
|
@ -53,6 +59,7 @@ import { environment } from '../../environments/environment';
|
|||
NgxPaginationModule,
|
||||
DashboardRoutingModule,
|
||||
ModalModule.forRoot(),
|
||||
NgPipesModule,
|
||||
],
|
||||
declarations: [
|
||||
DashboardComponent,
|
||||
|
@ -74,8 +81,11 @@ import { environment } from '../../environments/environment';
|
|||
GraphWidget,
|
||||
OrgBarSnippetComponent,
|
||||
CustBarSnippetComponent,
|
||||
HeroPointsSnippetBarComponent,
|
||||
HeroPointsGraphWidget,
|
||||
GraphPanel,
|
||||
PiePanel,
|
||||
HeroPointsComponent,
|
||||
],
|
||||
providers: [
|
||||
CurrencyPipe,
|
||||
|
|
|
@ -17,6 +17,7 @@ import { PayrollLogComponent } from './payroll-log.component';
|
|||
import { LeaderboardComponent } from './leaderboard.component';
|
||||
import { MapComponent } from './map.component';
|
||||
import { TrailMapComponent } from './trail-map.component';
|
||||
import { HeroPointsComponent } from './hero-points.component';
|
||||
|
||||
// Using child path to allow for FullLayout theming
|
||||
const routes: Routes = [
|
||||
|
@ -84,6 +85,11 @@ const routes: Routes = [
|
|||
path: 'feedback',
|
||||
component: FeedbackComponent,
|
||||
data: { title: 'Give Feedback' },
|
||||
},
|
||||
{
|
||||
path: 'hero-points',
|
||||
component: HeroPointsComponent,
|
||||
data: { title: 'Hero Points' }
|
||||
}
|
||||
],
|
||||
}
|
||||
|
|
254
src/app/dashboard/hero-points.component.html
Normal file
254
src/app/dashboard/hero-points.component.html
Normal file
|
@ -0,0 +1,254 @@
|
|||
<div class="animated fadeIn">
|
||||
<snippet-bar-hero-points></snippet-bar-hero-points><!-- Snippet for hero points (four circles) -->
|
||||
<div class="row"> <!-- second row -->
|
||||
<div class="col-md-6 col-xl-3"> <!-- Hero points gain charts -->
|
||||
<div *ngFor="let widget of widgetList"> <!-- Points gain line graph -->
|
||||
<hero-points-widget-graph *ngIf="widget.type == 'graph'"
|
||||
[graphName]="widget.name"
|
||||
[graphTitle]="widget.title"
|
||||
[graphIcon]="widget.icon"
|
||||
[dataType]="widget.dataType">
|
||||
</hero-points-widget-graph>
|
||||
</div> <!-- let widget of widgetList -->
|
||||
<div class="card"> <!-- Points gain by week charts -->
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h5 class="card-title float-left mb-0">Points Gain by Week</h5>
|
||||
</div> <!--/.col-->
|
||||
</div> <!--/.row-->
|
||||
<div class="chart-wrapper">
|
||||
<ul class="horizontal-bars type-2">
|
||||
<li> <!-- This Week -->
|
||||
<span class="title">This Week</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="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="heroPointsStats.this_week / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div> <!-- progress -->
|
||||
</div> <!-- bars -->
|
||||
</li>
|
||||
<li> <!-- Last Week -->
|
||||
<span class="title">Last Week</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="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="heroPointsStats.last_week / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div> <!-- progress -->
|
||||
</div> <!-- bars -->
|
||||
</li>
|
||||
<li> <!-- Week Maximum -->
|
||||
<span class="title">Week Maximum</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="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="heroPointsStats.max / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div> <!-- progress -->
|
||||
</div> <!-- bars -->
|
||||
</li>
|
||||
<li> <!-- Weekly Average -->
|
||||
<span class="title">Weekly Average</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="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="heroPointsStats.avg / heroPointsStats.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div> <!-- progess -->
|
||||
</div> <!-- bars -->
|
||||
</li>
|
||||
</ul> <!-- horizontal-bars -->
|
||||
</div> <!-- chart-wrapper -->
|
||||
</div> <!-- card-block -->
|
||||
</div> <!-- card -->
|
||||
</div> <!-- col -->
|
||||
<div class="col-md-6 col-xl-3"> <!-- Leaderboard -->
|
||||
<div *ngIf="!noUserList">
|
||||
<table class="table table-striped table-hover"> <!-- Leaderboard Table -->
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Name</th>
|
||||
<th>Hero Points</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let user of userList | orderBy: '-heroPoints' | paginate: paginateLeadConfig; let i = index" [ngStyle]="{'background-color': user.userId === 1 ? '#F39C12': 'auto', 'color': user.userId === 1 ? '#ffffff': 'auto'}">
|
||||
<td>{{ i + 1 + (paginateLeadConfig.itemsPerPage * paginateLeadConfig.currentPage) - paginateLeadConfig.itemsPerPage }}</td> <!-- uses page number and number of pages to calculate the rankings -->
|
||||
<td>{{ user.displayName }}</td> <!-- Display Name -->
|
||||
<td>{{ user.heroPoints }}</td> <!-- Hero Points -->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<pagination-template #p="paginationApi" [id]="paginateLeadConfig.id" (pageChange)="paginateLeadConfig.currentPage = $event"> <!-- Leaderboard pagination -->
|
||||
<ul class="pagination">
|
||||
<li class="page-item" [class.disabled]="p.isFirstPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isFirstPage()" (click)="p.previous()">Prev</a>
|
||||
</li>
|
||||
<li *ngFor="let page of p.pages" class="page-item" [class.active]="p.getCurrent() === page.value">
|
||||
<a class="page-link clickable" (click)="p.setCurrent(page.value)" *ngIf="p.getCurrent() !== page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</a>
|
||||
<div class="page-link" *ngIf="p.getCurrent() === page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="page-item" [class.disabled]="p.isLastPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isLastPage()" (click)="p.next()">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</pagination-template>
|
||||
</div> <!-- if !noUserList -->
|
||||
<div *ngIf="noUserList" class="card-block">
|
||||
No Leaderboard available.
|
||||
</div> <!-- if noUserList -->
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-6"> <!-- Medal Table -->
|
||||
<div *ngIf="!noGlobalMedalList && !noOrganisationMedalList">
|
||||
<table class="table header-fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-3">Medal</th>
|
||||
<th class="col-9">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let medals of globalMedalList"> <!-- ngFor each group of medals (global) -->
|
||||
<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)-->
|
||||
<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">{{(medals | keys)[groupIndex]}}</td>
|
||||
<td class="col-6">Click to collapse</td>
|
||||
<td class="col-1">-</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 *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">{{(medals | keys)[groupIndex]}}</td>
|
||||
<td class="col-6">Click to expand</td>
|
||||
<td class="col-1">+</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>
|
||||
<!-- display actual medals -->
|
||||
<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 -->
|
||||
<ng-container *ngIf="threshold | isObject">
|
||||
<ng-container *ngIf="threshold.awarded">
|
||||
<tr *ngIf="group.expanded" [ngStyle]="{'background-color': '#5FBC47', 'color': '#ffffff' }">
|
||||
<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>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="(group[0] | values).length - 1 == 1"> <!-- if there is only one medal in the group, just display medal, no header -->
|
||||
<ng-container *ngFor="let threshold of group[0] | values | orderBy: '+threshold'; let thresholdIndex = index">
|
||||
<ng-container *ngIf="threshold | isObject">
|
||||
<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>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container> <!-- ngFor each group of medals (global) -->
|
||||
<ng-container *ngFor="let medals of organisationMedalList">
|
||||
<ng-container *ngFor="let orgs of medals | values">
|
||||
<ng-container *ngFor="let org of orgs; let orgIndex = index">
|
||||
<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-1">-</td>
|
||||
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{org.count}}/{{org.threshold}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!org.expanded" (click)="org.expanded = true" [ngStyle]="{'background-color': '#F39C12', 'color': '#ffffff' }">
|
||||
<td class="col-3">{{org.name}}</td>
|
||||
<td class="col-6">Click to expand</td>
|
||||
<td class="col-1">+</td>
|
||||
<td class="col-2" [ngStyle]="{'text-align': 'right'}">{{org.count}}/{{org.threshold}}</td>
|
||||
</tr>
|
||||
<ng-container *ngFor="let group of org | values; let groupIndex = index">
|
||||
<ng-container *ngIf="group | isObject">
|
||||
<ng-container *ngIf="(group[0] | keys).length - 1 > 1">
|
||||
<tr *ngIf="group.expanded && org.expanded" (click)="group.expanded = false" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }">
|
||||
<td class="col-3">{{(org | keys)[groupIndex]}}</td>
|
||||
<td class="col-6">Click to collapse</td>
|
||||
<td class="col-1">-</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 *ngIf="!group.expanded && org.expanded" (click)="group.expanded = true" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }">
|
||||
<td class="col-3">{{(org | keys)[groupIndex]}}</td>
|
||||
<td class="col-6">Click to expand</td>
|
||||
<td class="col-1">+</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>
|
||||
<ng-container *ngFor="let threshold of group[0] | values | orderBy: '+threshold'; let thresholdIndex = index">
|
||||
<ng-container *ngIf="threshold | isObject">
|
||||
<ng-container *ngIf="threshold.awarded">
|
||||
<tr *ngIf="group.expanded && org.expanded" [ngStyle]="{'background-color': '#5FBC47', 'color': '#ffffff' }">
|
||||
<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>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="(group[0] | keys).length - 1 == 1">
|
||||
<ng-container *ngFor="let threshold of group[0] | values | orderBy: '+threshold'; let thresholdIndex = index">
|
||||
<ng-container *ngIf="threshold | isObject">
|
||||
<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>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</div> <!-- if !noMedalList -->
|
||||
<div *ngIf="noGlobalMedalList && noOrganisationMedalList" class="card-block">
|
||||
No Leaderboard available.
|
||||
</div> <!-- if noMedalList -->
|
||||
</div><!-- Row -->
|
||||
</div>
|
||||
</div>
|
2042
src/app/dashboard/hero-points.component.ts
Normal file
2042
src/app/dashboard/hero-points.component.ts
Normal file
File diff suppressed because it is too large
Load diff
|
@ -97,6 +97,14 @@
|
|||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li *ngIf="accountType == 'customer'" class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/hero-points']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-trophy"></i></div>
|
||||
<div class="col-10">Hero Points</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@ export class CustGraphsService {
|
|||
|
||||
public getGraph(name: string, data: any = {}) {
|
||||
data.graph = name;
|
||||
//console.log(this.api.post(this.custGraphUrl, data));
|
||||
return this.api.post(this.custGraphUrl, data);
|
||||
}
|
||||
}
|
||||
|
|
15
src/app/providers/hero-points-graph.service.ts
Normal file
15
src/app/providers/hero-points-graph.service.ts
Normal 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);
|
||||
}
|
||||
}
|
8
src/app/providers/hero-points-leaderboard.service.ts
Normal file
8
src/app/providers/hero-points-leaderboard.service.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class HeroPointsLeaderboardService {
|
||||
|
||||
constructor() { }
|
||||
|
||||
}
|
15
src/app/providers/hero-points-snippets.service.ts
Normal file
15
src/app/providers/hero-points-snippets.service.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ApiService } from './api-service';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
@Injectable()
|
||||
export class HeroPointsSnippetsService {
|
||||
private heroPointsSnippetsUrl = '/v1/user/points';
|
||||
|
||||
constructor(private api: ApiService) { }
|
||||
|
||||
// This endpoint should mimic basicStats
|
||||
public getHeroPointsSnippets(): Observable<any> {
|
||||
return this.api.post(this.heroPointsSnippetsUrl);
|
||||
}
|
||||
}
|
16
src/app/providers/medals.service.ts
Normal file
16
src/app/providers/medals.service.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ApiService } from './api-service';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
@Injectable()
|
||||
export class MedalsService {
|
||||
private medalsUrl = '/v1/user/medals';
|
||||
|
||||
constructor(private api: ApiService) { }
|
||||
|
||||
public getMedals(): Observable<any> {
|
||||
return this.api.post(this.medalsUrl);
|
||||
//return Observable.of()
|
||||
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import { CustSnippetsService } from '../providers/cust-snippets.service';
|
|||
selector: 'snippet-bar-cust',
|
||||
templateUrl: 'cust-snippet-bar.component.html',
|
||||
})
|
||||
|
||||
export class CustBarSnippetComponent implements OnInit {
|
||||
|
||||
public userSum = 0;
|
||||
|
|
50
src/app/snippets/hero-points-snippet-bar.component.html
Normal file
50
src/app/snippets/hero-points-snippet-bar.component.html
Normal file
|
@ -0,0 +1,50 @@
|
|||
<div class="row">
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-orange">My Hero Points</h5></div>
|
||||
<div class="number-circle-hero mx-auto"><strong>{{ heroPointsSnippets.points_total | number:'1.0-0' }}</strong></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-dark-green">Last Transaction Points</h5></div>
|
||||
<div class="number-circle mx-auto"><strong>{{ heroPointsSnippets.point_last | number:'1.0-0' }}</strong></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-dark-green">Total Transactions</h5></div>
|
||||
<div class="number-circle mx-auto"><strong>{{ heroPointsSnippets.trans_count | number:'1.0-0' }}</strong></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-dark-green">Average Multiplier</h5></div>
|
||||
<div class="number-circle mx-auto"><strong>{{ heroPointsSnippets.avg_multi | number:'1.0-0' }}</strong></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
34
src/app/snippets/hero-points-snippet-bar.component.ts
Normal file
34
src/app/snippets/hero-points-snippet-bar.component.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { HeroPointsSnippetsService } from '../providers/hero-points-snippets.service';
|
||||
|
||||
@Component({
|
||||
selector: 'snippet-bar-hero-points',
|
||||
templateUrl: './hero-points-snippet-bar.component.html',
|
||||
})
|
||||
|
||||
export class HeroPointsSnippetBarComponent implements OnInit {
|
||||
|
||||
// Hero Points snippets
|
||||
public heroPointsSnippets = {
|
||||
avg_multi: 0,
|
||||
point_last: 0,
|
||||
points_total: 0,
|
||||
trans_count: 0,
|
||||
};
|
||||
|
||||
constructor(
|
||||
private heroPointsSnippetsService: HeroPointsSnippetsService,
|
||||
) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.heroPointsSnippetsService.getHeroPointsSnippets()
|
||||
.subscribe(
|
||||
result => {
|
||||
this.heroPointsSnippets.avg_multi = result.snippets.avg_multi;
|
||||
this.heroPointsSnippets.point_last = result.snippets.point_last;
|
||||
this.heroPointsSnippets.points_total = result.snippets.points_total;
|
||||
this.heroPointsSnippets.trans_count = result.snippets.trans_count;
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
21
src/app/widgets/hero-points-graph-widget.component.html
Normal file
21
src/app/widgets/hero-points-graph-widget.component.html
Normal 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>
|
154
src/app/widgets/hero-points-graph-widget.component.ts
Normal file
154
src/app/widgets/hero-points-graph-widget.component.ts
Normal 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';
|
||||
}
|
||||
}
|
|
@ -6,8 +6,14 @@
|
|||
.table-striped tbody tr:nth-of-type(odd) {
|
||||
background-color: #d2eef7;
|
||||
}
|
||||
|
||||
.table-striped tbody tr:nth-of-type(even) {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.table-hover tbody tr:hover td {
|
||||
background-color: $table-bg-hover;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.table thead tr {
|
||||
|
@ -15,6 +21,63 @@
|
|||
color: #e8ebed;
|
||||
}
|
||||
|
||||
.header-fixed {
|
||||
width: 100%;
|
||||
table-layout:fixed;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.header-fixed > thead,
|
||||
.header-fixed > tbody,
|
||||
.header-fixed > thead > tr,
|
||||
.header-fixed > tbody > tr,
|
||||
.header-fixed > thead > tr > th,
|
||||
.header-fixed > tbody > tr > td {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header-fixed > tbody > tr:after,
|
||||
.header-fixed > thead > tr:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.header-fixed {
|
||||
|
||||
tbody {
|
||||
overflow-y: auto;
|
||||
height: 370px;
|
||||
}
|
||||
}
|
||||
|
||||
tr.collapse.in {
|
||||
display:table-row;
|
||||
}
|
||||
|
||||
.header-fixed > tbody > tr > td,
|
||||
.header-fixed > thead > tr > th,
|
||||
{
|
||||
//width: 33.33%;
|
||||
float: left;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
.col-1 {width: 8.33%;}
|
||||
.col-2 {width: 16.66%;}
|
||||
.col-3 {width: 25%;}
|
||||
.col-4 {width: 33.33%;}
|
||||
.col-5 {width: 41.66%;}
|
||||
.col-6 {width: 50%;}
|
||||
.col-7 {width: 58.33%;}
|
||||
.col-8 {width: 66.66%;}
|
||||
.col-9 {width: 75%;}
|
||||
.col-10 {width: 83.33%;}
|
||||
.col-11 {width: 91.66%;}
|
||||
.col-12 {width: 100%;}
|
||||
}
|
||||
|
||||
|
||||
// Map styling
|
||||
agm-map {
|
||||
height: 75vh;
|
||||
|
@ -43,6 +106,23 @@ agm-map {
|
|||
color: #10602c;
|
||||
}
|
||||
|
||||
.number-circle-hero {
|
||||
height: 10rem;
|
||||
border-radius:50%;
|
||||
text-align:center;
|
||||
width: 10rem;
|
||||
padding: 5rem 5%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
background: #F39C12;
|
||||
color: white;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.text-orange {
|
||||
color: #D35400;
|
||||
}
|
||||
|
||||
// white title font variant on type-2 as defined in _widgets.css
|
||||
.horizontal-bars {
|
||||
padding: 0;
|
||||
|
|
Reference in a new issue