Updated Hero Points Page

Added everything
This commit is contained in:
Unknown 2018-02-07 11:52:41 +00:00
parent 08a8f464f3
commit e378f57a8c
23 changed files with 6634 additions and 1017 deletions

View file

@ -2,6 +2,11 @@
# Next Release # Next Release
# v0.1.1
* Redid layout on circle customer view
* Renamed customer dashboard headers
# v0.1.0 # v0.1.0
* Changed Story Trail choosing to modals * Changed Story Trail choosing to modals

5001
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "localloop-web", "name": "localloop-web",
"version": "0.1.0", "version": "0.1.1",
"description": "LocalLoop Web - Web interface for LocalLoop app", "description": "LocalLoop Web - Web interface for LocalLoop app",
"author": "", "author": "",
"url": "http://www.peartrade.org", "url": "http://www.peartrade.org",
@ -20,32 +20,38 @@
"dependencies": { "dependencies": {
"@agm/core": "1.0.0-beta.2", "@agm/core": "1.0.0-beta.2",
"@agm/js-marker-clusterer": "1.0.0-beta.2", "@agm/js-marker-clusterer": "1.0.0-beta.2",
"@angular/common": "5.0.1", "@angular/common": "5.2.0",
"@angular/compiler": "5.0.1", "@angular/compiler": "5.2.0",
"@angular/core": "5.0.1", "@angular/core": "5.2.0",
"@angular/forms": "5.0.1", "@angular/forms": "5.2.0",
"@angular/platform-browser": "5.0.1", "@angular/platform-browser": "5.2.0",
"@angular/platform-browser-dynamic": "5.0.1", "@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.0.1", "@angular/router": "5.2.0",
"@angular/upgrade": "5.0.1", "@angular/upgrade": "5.2.0",
"@types/moment": "2.13.0", "@types/moment": "2.13.0",
"angular-scrollable-table": "^1.1.2",
"chart.js": "2.7.1", "chart.js": "2.7.1",
"core-js": "2.5.1", "core-js": "2.5.1",
"js-marker-clusterer": "1.0.0", "js-marker-clusterer": "1.0.0",
"moment": "^2.19.2", "moment": "^2.19.2",
"ng2-charts": "1.6.0", "ng2-charts": "1.6.0",
"ng2-expanding-table": "^1.5.2",
"ng2-validation-manager": "0.5.3", "ng2-validation-manager": "0.5.3",
"ngx-bootstrap": "2.0.0-beta.8", "ngx-bootstrap": "2.0.0-beta.8",
"ngx-order-pipe": "^1.1.2",
"ngx-pagination": "3.0.3", "ngx-pagination": "3.0.3",
"rxjs": "5.5.2", "ngx-pipes": "^2.1.0",
"node": "^9.4.0",
"rxjs": "5.5.6",
"ts-helpers": "1.1.2", "ts-helpers": "1.1.2",
"webpack": "3.8.1", "webpack": "3.8.1",
"webpack-dev-server": "2.9.4", "webpack-dev-server": "2.9.4",
"zone.js": "0.8.18" "zone.js": "0.8.18"
}, },
"devDependencies": { "devDependencies": {
"@angular/cli": "1.5.0", "@angular-devkit/schematics": "0.0.48",
"@angular/compiler-cli": "5.0.1", "@angular/cli": "^1.6.6",
"@angular/compiler-cli": "5.2.0",
"@types/jasmine": "2.8.2", "@types/jasmine": "2.8.2",
"@types/jasminewd2": "2.0.3", "@types/jasminewd2": "2.0.3",
"@types/node": "8.0.52", "@types/node": "8.0.52",
@ -61,6 +67,6 @@
"protractor": "5.2.0", "protractor": "5.2.0",
"ts-node": "3.3.0", "ts-node": "3.3.0",
"tslint": "5.8.0", "tslint": "5.8.0",
"typescript": "2.4.2" "typescript": "^2.5.3"
} }
} }

View file

@ -0,0 +1,4 @@
export interface ChartData {
data: Array<number>;
label: string;
}

View file

@ -25,6 +25,9 @@ import { CustGraphsService } from './providers/cust-graphs.service';
import { OrgSnippetsService } from './providers/org-snippets.service'; 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 { HeroPointsSnippetsService } from './providers/hero-points-snippets.service';
//import { HeroPointsStatsService } from './providers/hero-points-stats.service';
// Layouts // Layouts
import { FullLayoutComponent } from './layouts/full-layout.component'; import { FullLayoutComponent } from './layouts/full-layout.component';
@ -38,11 +41,15 @@ import { P500Component } from './pages/500.component';
import { AuthModule } from './auth/auth.module'; import { AuthModule } from './auth/auth.module';
import { DashboardModule } from './dashboard/dashboard.module'; import { DashboardModule } from './dashboard/dashboard.module';
// Pipes
import { NgPipesModule } from 'ngx-pipes';
@NgModule({ @NgModule({
imports: [ imports: [
BrowserModule, BrowserModule,
HttpClientModule, HttpClientModule,
NgxPaginationModule, NgxPaginationModule,
NgPipesModule,
BsDropdownModule.forRoot(), BsDropdownModule.forRoot(),
TabsModule.forRoot(), TabsModule.forRoot(),
AuthModule, AuthModule,
@ -70,12 +77,18 @@ import { DashboardModule } from './dashboard/dashboard.module';
OrgSnippetsService, OrgSnippetsService,
CustGraphsService, CustGraphsService,
CustSnippetsService, CustSnippetsService,
//HeroPointsSnippetsService,
//HeroPointsStatsService,
MedalsService,
CustPiesService, CustPiesService,
{ {
provide: LocationStrategy, provide: LocationStrategy,
useClass: HashLocationStrategy useClass: HashLocationStrategy
} }
], ],
exports: [
NgPipesModule,
],
bootstrap: [ AppComponent ] bootstrap: [ AppComponent ]
}) })
export class AppModule { } export class AppModule { }

View file

@ -24,6 +24,20 @@
<span class="help-block">Enter the amount spent, such as 5.35 for £5.35.</span> <span class="help-block">Enter the amount spent, such as 5.35 for £5.35.</span>
</div> </div>
</div> </div>
<div class="form-group row">
<label class="col-md-3 form-control-label" for="text-input">Category</label>
<div class="col-md-9">
<div class="input-group">
<select type="text" class="form-control" [(ngModel)]="categoryId">
<option value="">Uncategorised</option>
<option *ngFor="let category of categoryIdList, let i=index" [ngValue]="category" >
{{ categoryNameList[i] }}
</option>
</select>
</div>
<span class="help-block"><strong>Optional:</strong> Choose the relevant Category for the purchase.</span>
</div>
</div>
<div class="form-group row"> <div class="form-group row">
<label class="col-md-3 form-control-label" for="text-input"><strong>Organisation Name</strong></label> <label class="col-md-3 form-control-label" for="text-input"><strong>Organisation Name</strong></label>
<div class="col-md-9"> <div class="col-md-9">

View file

@ -30,6 +30,8 @@ export class AddDataComponent implements OnInit {
organisationTown: string; organisationTown: string;
organisationPostcode: string; organisationPostcode: string;
amount: number; amount: number;
// Assumes Groceries is 1st category
categoryId: number = 1;
transactionAdditionType = 1; transactionAdditionType = 1;
storeList = []; storeList = [];
showAddStore = false; showAddStore = false;
@ -37,6 +39,8 @@ export class AddDataComponent implements OnInit {
transactionFormInvalid = true; transactionFormInvalid = true;
myDate: any; myDate: any;
minDate: any; minDate: any;
categoryIdList: number[] = [];
categoryNameList: string[] = [];
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
@ -64,6 +68,15 @@ export class AddDataComponent implements OnInit {
}); });
this.myDate = moment().format('YYYY-MM-DD[T]HH:mm'); this.myDate = moment().format('YYYY-MM-DD[T]HH:mm');
// this.myDate = new Date().toISOString().slice(0, 16); // this.myDate = new Date().toISOString().slice(0, 16);
this.api.categoryList().subscribe(
result => {
this.setCategoryList(result.categories);
},
error => {
console.log('Retrieval Error');
console.log( error._body );
}
);
} }
ngOnInit(): void { ngOnInit(): void {
@ -71,6 +84,11 @@ export class AddDataComponent implements OnInit {
this.accountType = localStorage.getItem('usertype'); this.accountType = localStorage.getItem('usertype');
} }
private setCategoryList(data: any) {
this.categoryIdList = Object.keys(data.ids).map(key => data.ids[key]);
this.categoryNameList = Object.keys(data.names).map(key => data.names[key]);
}
getMinDate() { getMinDate() {
// gets the April 1st date of the current year // gets the April 1st date of the current year
const aprilDate = moment().month(3).date(1); const aprilDate = moment().month(3).date(1);
@ -170,6 +188,7 @@ export class AddDataComponent implements OnInit {
transaction_value : this.amount, transaction_value : this.amount,
purchase_time : purchaseTime, purchase_time : purchaseTime,
organisation_id : this.organisationId, organisation_id : this.organisationId,
category : this.categoryId,
}; };
break; break;
case 2: case 2:
@ -178,6 +197,7 @@ export class AddDataComponent implements OnInit {
transaction_value : this.amount, transaction_value : this.amount,
purchase_time : purchaseTime, purchase_time : purchaseTime,
organisation_id : this.organisationId, organisation_id : this.organisationId,
category : this.categoryId,
}; };
break; break;
case 3: case 3:
@ -189,6 +209,7 @@ export class AddDataComponent implements OnInit {
street_name : this.submitOrg.street_name, street_name : this.submitOrg.street_name,
town : this.submitOrg.town, town : this.submitOrg.town,
postcode : this.submitOrg.postcode, postcode : this.submitOrg.postcode,
category : this.categoryId,
}; };
break; break;
} }

View file

@ -3,10 +3,10 @@
<div class="row"> <div class="row">
<div *ngFor="let widget of widgetList" class="col-sm-6 col-lg-3"> <div *ngFor="let widget of widgetList" class="col-sm-6 col-lg-3">
<widget-graph *ngIf="widget.type == 'graph'" <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> </widget-graph>
</div><!--/.col--> </div><!--/.col-->
</div><!--/.row--> </div><!--/.row-->

View file

@ -20,10 +20,12 @@ import { PayrollLogComponent } from './payroll-log.component';
import { LeaderboardComponent } from './leaderboard.component'; import { LeaderboardComponent } from './leaderboard.component';
import { MapComponent } from './map.component'; import { MapComponent } from './map.component';
import { TrailMapComponent } from './trail-map.component'; import { TrailMapComponent } from './trail-map.component';
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 { 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';
@ -37,6 +39,9 @@ import { LeaderboardResultComponent } from '../shared/leaderboard-result.compone
// API key env variable import // API key env variable import
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
// Pipes
import { NgPipesModule } from 'ngx-pipes';
@NgModule({ @NgModule({
imports: [ imports: [
// Angular imports // Angular imports
@ -52,6 +57,7 @@ import { environment } from '../../environments/environment';
NgxPaginationModule, NgxPaginationModule,
DashboardRoutingModule, DashboardRoutingModule,
ModalModule.forRoot(), ModalModule.forRoot(),
NgPipesModule,
], ],
declarations: [ declarations: [
DashboardComponent, DashboardComponent,
@ -72,8 +78,10 @@ import { environment } from '../../environments/environment';
GraphWidget, GraphWidget,
OrgBarSnippetComponent, OrgBarSnippetComponent,
CustBarSnippetComponent, CustBarSnippetComponent,
//HeroPointsSnippetBarComponent,
GraphPanel, GraphPanel,
PiePanel, PiePanel,
HeroPointsComponent,
], ],
providers: [ providers: [
CurrencyPipe, CurrencyPipe,

View file

@ -16,6 +16,7 @@ import { PayrollLogComponent } from './payroll-log.component';
import { LeaderboardComponent } from './leaderboard.component'; import { LeaderboardComponent } from './leaderboard.component';
import { MapComponent } from './map.component'; import { MapComponent } from './map.component';
import { TrailMapComponent } from './trail-map.component'; import { TrailMapComponent } from './trail-map.component';
import { HeroPointsComponent } from './hero-points.component';
// Using child path to allow for FullLayout theming // Using child path to allow for FullLayout theming
const routes: Routes = [ const routes: Routes = [
@ -78,6 +79,11 @@ const routes: Routes = [
path: 'feedback', path: 'feedback',
component: FeedbackComponent, component: FeedbackComponent,
data: { title: 'Give Feedback' }, data: { title: 'Give Feedback' },
},
{
path: 'hero-points',
component: HeroPointsComponent,
data: { title: 'Hero Points' }
} }
], ],
} }

View file

@ -0,0 +1,224 @@
<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 -->
<!--<widget-graph *ngIf="widget.type == 'graph'"
[graphName]="widget.name"
[graphTitle]="widget.title"
[graphIcon]="widget.icon"
[dataType]="widget.dataType">
</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">{{ 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>
<div class="bars">
<div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: 39%" aria-valuemin="0" aria-valuemax="100"></div>
</div> <!-- progress -->
</div> <!-- bars -->
</li>
<li> <!-- Last Week -->
<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>
<div class="bars">
<div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div>
</div> <!-- progress -->
</div> <!-- bars -->
</li>
<li> <!-- Week Maximum -->
<span class="title">Week Maximum</span>
<span class="value">{{ transactionListWeek | pluck: 'pointsEarned' | max }}<span class="text-muted small"> (100%)</span></span>
<div class="bars">
<div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div>
</div> <!-- progress -->
</div> <!-- bars -->
</li>
<li> <!-- Weekly Average -->
<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>
<div class="bars">
<div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: 51%" 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="!noMedalList">
<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 groupNo of medalList | unique: 'medalGroupId'; let i = index"> <!-- 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 -->
<!-- *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' }">
<td class="col-3">{{groupNo.title}}</td>
<td class="col-6">Click to collapse</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>
</tr>
<tr *ngIf="!groupNo.expanded && groupNo.medalType == 'global'" (click)="groupNo.expanded = true" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }">
<td class="col-3">{{groupNo.title}}</td>
<td class="col-6">Click to expand</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>
</tr>
<!-- display actual medals -->
<ng-container *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']"> <!-- list each medal in each group, ordered by rewards, ascending order -->
<tr *ngIf="groupNo.expanded && medal.medalGroupId == i && groupNo.medalType == 'global'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{ medal.title }}</td>
<td class="col-7">{{ medal.description }}</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td> <!-- progress -->
</tr>
</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 *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']"> <!-- Unnecessary loop because there is only one medal in each group -->
<tr *ngIf="medal.medalGroupId == i && groupNo.medalType == 'global'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{ medal.title }}</td> <!-- medal name -->
<td class="col-7">{{ medal.description }}</td> <!-- medal description -->
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td> <!-- progress -->
</tr>
</ng-container>
</ng-container>
</ng-container> <!-- ngFor each group of medals (global) -->
<ng-container *ngFor="let organisation of organisationList | orderBy: '+name'; let orgI = index"> <!-- ngFor organisation of organisationList -->
<!-- ngIf organisation is/isn't expanded display correct header -->
<tr *ngIf="organisation.expanded" (click)="organisation.expanded = false" [ngStyle]="{'background-color': '#F39C12', 'color': '#ffffff' }">
<td class="col-3">{{organisation.name}}</td>
<td class="col-6">Click to collapse</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>
</tr>
<tr *ngIf="!organisation.expanded" (click)="organisation.expanded = true" [ngStyle]="{'background-color': '#F39C12', 'color': '#ffffff' }">
<td class="col-3">{{organisation.name}}</td>
<td class="col-6">Click to expand</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>
</tr>
<ng-container *ngFor="let groupNo of medalList | unique: 'medalGroupId'; let i = index"> <!-- ngFor each group of medals (organisation) -->
<ng-container *ngIf="(medalList | filterBy: ['title']: groupNo.title: search: true).length > 1"> <!-- ngIf length of group is more than 1 -->
<!-- ngIf organisation is/isn't expanded -->
<tr *ngIf="organisation.expanded && groupNo.expanded && groupNo.medalType == 'organisation'" (click)="groupNo.expanded = false" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }">
<td class="col-3">{{groupNo.title}}</td>
<td class="col-6">Click to collapse</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>
</tr>
<tr *ngIf="organisation.expanded && !groupNo.expanded && groupNo.medalType == 'organisation'" (click)="groupNo.expanded = true" [ngStyle]="{'background-color': '#20A8D8', 'color': '#ffffff' }">
<td class="col-3">{{groupNo.title}}</td>
<td class="col-6">Click to expand</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>
</tr>
<ng-container *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']">
<tr *ngIf="organisation.expanded && groupNo.expanded && medal.medalGroupId == i && groupNo.medalType == 'organisation'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{ medal.title }}</td>
<td class="col-7">{{ medal.description }}</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td>
</tr>
</ng-container>
</ng-container>
<ng-container *ngIf="(medalList | filterBy: ['title']: groupNo.title: search: true).length <= 1"> <!-- ngIf length or group is <= 1 -->
<ng-container *ngFor="let medal of medalList | orderBy: ['+medalGroupId', '+reward']">
<tr *ngIf="organisation.expanded && medal.medalGroupId == i && groupNo.medalType == 'organisation'" [ngStyle]="{'background-color':medal.unlocked === true ? '#89DD73' : '#F25F5F', 'color': '#ffffff' }">
<td class="col-3">{{ medal.title }}</td>
<td class="col-7">{{ medal.description }}</td>
<td class="col-2" [ngStyle]="{'text-align': 'right'}">0/{{ medal.valueRequired }}</td>
</tr>
</ng-container>
</ng-container>
</ng-container> <!-- ngFor each group of medals (organisation) -->
</ng-container> <!-- ngFor organisation of organisationList -->
</tbody>
</table>
</div> <!-- if !noMedalList -->
<div *ngIf="noMedalList" class="card-block">
No Leaderboard available.
</div> <!-- if noMedalList -->
</div>
</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>

File diff suppressed because it is too large Load diff

View file

@ -89,6 +89,14 @@
</div> </div>
</a> </a>
</li> </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> </ul>
</nav> </nav>
</div> </div>

View file

@ -116,6 +116,17 @@ export class ApiService {
); );
} }
// Basic Customer User stats API
public categoryList() {
const key = this.sessionKey;
return this.http.post<any>(
this.apiUrl + '/search/category',
{
session_key : key,
}
);
}
// Searches organisations used for transaction submission // Searches organisations used for transaction submission
public search(data) { public search(data) {

View file

@ -9,6 +9,7 @@ export class CustGraphsService {
public getGraph(name: string, data: any = {}) { public getGraph(name: string, data: any = {}) {
data.graph = name; data.graph = name;
//console.log(this.api.post(this.custGraphUrl, data));
return this.api.post(this.custGraphUrl, data); return this.api.post(this.custGraphUrl, data);
} }
} }

View file

@ -0,0 +1,8 @@
import { Injectable } from '@angular/core';
@Injectable()
export class HeroPointsLeaderboardService {
constructor() { }
}

View 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 getPointsData(): Observable<any> {
return this.api.post(this.heroPointsSnippetsUrl);
}
}

View file

@ -0,0 +1,14 @@
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

@ -0,0 +1,15 @@
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);
}
}

View file

@ -5,6 +5,7 @@ import { CustSnippetsService } from '../providers/cust-snippets.service';
selector: 'snippet-bar-cust', selector: 'snippet-bar-cust',
templateUrl: 'cust-snippet-bar.component.html', templateUrl: 'cust-snippet-bar.component.html',
}) })
export class CustBarSnippetComponent implements OnInit { export class CustBarSnippetComponent implements OnInit {
public userSum = 0; public userSum = 0;

View 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>{{ pointTotal | 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>{{pointLast | 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>{{ transCount | 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>{{ avgMulti | number:'1.0-0' }}</strong></div>
</li>
</ul>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,31 @@
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 {
public pointTotal = 0;
public pointLast = 0;
public transCount = 0;
public avgMulti = 0;
constructor(
private snippetsService: HeroPointsSnippetsService,
) { }
public ngOnInit(): void {
this.snippetsService.getPointsData()
.subscribe(
result => {
this.pointTotal = result.snippets.point_total;
this.pointLast = result.snippets.point_last;
this.transCount = result.snippets.trans_count;
this.avgMulti = result.snippets.avg_multi;
}
);
}
}

View file

@ -6,8 +6,14 @@
.table-striped tbody tr:nth-of-type(odd) { .table-striped tbody tr:nth-of-type(odd) {
background-color: #d2eef7; background-color: #d2eef7;
} }
.table-striped tbody tr:nth-of-type(even) {
background-color: #ffffff;
}
.table-hover tbody tr:hover td { .table-hover tbody tr:hover td {
background-color: $table-bg-hover; background-color: $table-bg-hover;
color: #000000;
} }
.table thead tr { .table thead tr {
@ -15,6 +21,63 @@
color: #e8ebed; 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 // Map styling
agm-map { agm-map {
height: 75vh; height: 75vh;
@ -43,6 +106,23 @@ agm-map {
color: #10602c; 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 // white title font variant on type-2 as defined in _widgets.css
.horizontal-bars { .horizontal-bars {
padding: 0; padding: 0;