Fix a large amount of things in the ui

This commit is contained in:
Thomas Bloor 2019-09-09 19:52:38 +01:00
parent 52bce4dd0a
commit 7883dbb169
No known key found for this signature in database
GPG key ID: 4657C7EBE42CC5CC
7 changed files with 185 additions and 266 deletions

3
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/workspace.xml

View file

@ -9,6 +9,7 @@
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"start:dev": "ng serve --optimization=false --configuration=dev", "start:dev": "ng serve --optimization=false --configuration=dev",
"start:local": "ng serve --optimization=false --configuration=local",
"build": "ng build", "build": "ng build",
"test": "ng test", "test": "ng test",
"test:ci": "ng test --watch=false", "test:ci": "ng test --watch=false",

View file

@ -3,7 +3,25 @@
<div class="col-lg-12"> <div class="col-lg-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<strong>List of Transaction Types</strong> <h4>Filter</h4>
</div>
<div class="card-block">
<form class="form-inline">
<label class="mr-2" for="filter-from">From</label>
<input id="filter-from" class="form-control" type="date" [(ngModel)]="filterFrom" name="from">
<label class="mx-2" for="filter-to">To</label>
<input class="form-control" id="filter-to" type="date" [(ngModel)]="filterTo" name="to">
<button type="submit" class="btn btn-primary ml-2" (click)="loadData()">Filter</button>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<strong>Transaction Types</strong>
</div> </div>
<div *ngIf="metaTypeListAvailable" class="card-block"> <div *ngIf="metaTypeListAvailable" class="card-block">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
@ -24,14 +42,10 @@
</div> </div>
</div> </div>
</div> </div>
</div> <div class="col-lg-6">
</div>
<div class="animated fadeIn">
<div class="row">
<div class="col-lg-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<strong>List of Ward Transactions</strong> <strong>Ward Spending</strong>
</div> </div>
<div *ngIf="wardListAvailable" class="card-block"> <div *ngIf="wardListAvailable" class="card-block">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
@ -55,37 +69,12 @@
</div> </div>
</div> </div>
<div class="animated fadeIn"> <div class="animated fadeIn">
<div class="card">
<div class="row">
<div class="col-sm-8">
<h4 class="card-title mb-0">Blah</h4>
</div>
<div *ngIf="" class="card-block">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Ward</th>
<th>Amount of Transactions</th>
<th>Sum of Transactions</th>
</tr>
</thead>
<tbody>
<tr ward-result *ngFor="let ward of wardList"></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card"> <div class="card">
<div class="card-block"> <div class="card-block">
<div class="row"> <div class="row">
<div class="col-sm-8"> <div class="col-sm-8">
<h4 class="card-title mb-0">Supplier spend amount and number of purchases</h4> <h4 class="card-title mb-0">Supplier spend amount and number of purchases</h4>
</div> </div>
<div class="col-sm-4 hidden-sm-down" *ngIf="isBubbleChartLoaded">
<input type="date" [(ngModel)]="bubbleChartBegin" (change)="bubbleChartUpdate()">
<input type="date" [(ngModel)]="bubbleChartEnd" (change)="bubbleChartUpdate()">
</div>
</div> </div>
<small>vertical shows number of purchases, size of bubble shows the total spend amount, horizontal shows date</small> <small>vertical shows number of purchases, size of bubble shows the total spend amount, horizontal shows date</small>
<div class="col-sm-12" *ngIf="!isBubbleChartLoaded"> <div class="col-sm-12" *ngIf="!isBubbleChartLoaded">
@ -106,8 +95,8 @@
<div class="card-block"> <div class="card-block">
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<h4 class="card-title mb-0">Spend amount and number of organisations</h4> <h4 class="card-title mb-0">Spend & Number of Transactions</h4>
<small>horizontal axis shows date, vertical axis shows total number of orgs and amount spent</small> <small>Date against Value and Number of Transactions</small>
</div> </div>
</div> </div>
<div> <div>
@ -128,15 +117,15 @@
<h4 class="card-title mb-0">Supplier Spend History</h4> <h4 class="card-title mb-0">Supplier Spend History</h4>
</div> </div>
<div class="col-sm-6 hidden-sm-down"> <div class="col-sm-6 hidden-sm-down">
<button type="button" class="btn btn-danger" (click)="supplierChartPrevious()">Previous Suppliers</button> <button type="button" class="btn btn-danger" (click)="previousSupplierHistoryPage()">Previous Page</button>
<button type="button" class="btn btn-info" (click)="supplierChartNext()">Next Suppliers</button> <button type="button" class="btn btn-info" (click)="nextSupplierHistoryPage()">Next Page</button>
<span class="ml-2">Page {{ _supplierHistoryPage }} of {{ _supplierHistoryPages }}</span>
</div> </div>
</div> </div>
<div *ngIf="isSupplierChartLoaded"> <div *ngIf="isSupplierChartLoaded">
<canvas baseChart #supplierChart <canvas baseChart #supplierChart
[datasets]="supplierMonthChartData" [datasets]="supplierMonthChartData"
[options]="supplierMonthChartOptions" [options]="supplierMonthChartOptions"
[colors]="supplierMonthChartColours"
[labels]="supplierMonthChartLabels" [labels]="supplierMonthChartLabels"
[legend]="showLegend" [legend]="showLegend"
[chartType]="supplierMonthChartType"> [chartType]="supplierMonthChartType">

View file

@ -1,10 +1,9 @@
import {Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef} from '@angular/core'; import {Component, OnInit, Input, Output, EventEmitter, ViewChild} from '@angular/core';
import {ApiService} from '../providers/api-service'; import {ApiService} from '../providers/api-service';
import {BaseChartDirective, Color} from 'ng2-charts'; import {BaseChartDirective} from 'ng2-charts';
import {CurrencyPipe} from '@angular/common'; import {CurrencyPipe} from '@angular/common';
import {ChartType} from "chart.js"; import {ChartType} from "chart.js";
import * as moment from 'moment'; import * as moment from 'moment';
import { NgModel } from '@angular/forms';
@Component({ @Component({
templateUrl: 'more-graphs-and-tables.component.html', templateUrl: 'more-graphs-and-tables.component.html',
@ -12,14 +11,13 @@ import { NgModel } from '@angular/forms';
export class MoreStuffComponent implements OnInit { export class MoreStuffComponent implements OnInit {
@Output() public onClick = new EventEmitter(); @Output() public onClick = new EventEmitter();
@Input() public categories: any; @Input() public categories: any;
lineChartBegin: any;
lineChartEnd: any; // Global Filter Setup
bubbleChartBegin: any; filterFrom: any;
bubbleChartEnd: any; filterTo: any;
cached_graph_data: any;
isBubbleChartLoaded:boolean = false; isBubbleChartLoaded: boolean = false;
isLineChartLoaded:boolean = false; isSupplierChartLoaded: boolean = false;
isSupplierChartLoaded:boolean = false;
wardList: any; wardList: any;
wardListAvailable = false; wardListAvailable = false;
metaTypeList: any; metaTypeList: any;
@ -29,64 +27,34 @@ export class MoreStuffComponent implements OnInit {
private api: ApiService, private api: ApiService,
private currencyPipe: CurrencyPipe, private currencyPipe: CurrencyPipe,
) { ) {
let now = moment();
this.filterTo = now.format('YYYY-MM-DD');
now.subtract(1, 'months');
this.filterFrom = now.format('YYYY-MM-DD');
this.tableSummary(); this.tableSummary();
this.bubbleChartBegin = moment().format('YYYY-MM-DD');
this.bubbleChartEnd = moment().format('YYYY-MM-DD');
this.lineChartBegin = moment().format('YYYY-MM-DD');
this.lineChartEnd = moment().format('YYYY-MM-DD');
} }
ngOnInit(): void { ngOnInit(): void {
this.loadData();
}
private loadData() {
this.tableSummary();
this.loadYearSpend(); this.loadYearSpend();
this.loadSupplierBubble(false, ('0'), ('0')); this.loadSupplierBubble();
this.loadSupplierHistory(); this.loadSupplierHistory();
} }
public showLegend = true; public showLegend = true;
public sampleColours: Color[] = [
{
backgroundColor: [
'red',
'green',
'#52afed',
'purple',
'yellow',
'brown',
'magenta',
'cyan',
'orange',
'pink'
]
}
];
/* /*
* Supplier Bubble Chart Setup * Supplier Bubble Chart Setup
*/ */
private formatGraphData(passed_graph_data: any, useRange : boolean, start_range: string, end_range: string) : any[] { private formatGraphData(data: any): any[] {
let graph_data = []; let graph_data = [];
if (useRange == true) { data.data.map(item => {
passed_graph_data.data.map(item=> {
let is_item_in_range = (new Date(item.date.substring(0, 10)) >= new Date(start_range) && new Date(item.date.substring(0, 10)) <= new Date(end_range));
if (is_item_in_range) {
graph_data.push({
t: new Date(item.date.substring(0, 10)),
r: item.value > 1000000 ? (item.value / 200000) : (item.value / 100000) + 5,
supplier: item.seller,
y: item.count,
value: item.value,
count: item.count,
});
}
});
return graph_data;
} else {
passed_graph_data.data.map(item => {
graph_data.push({ graph_data.push({
t: item.date, t: item.date,
r: item.value > 1000000 ? (item.value / 200000) : (item.value / 100000) + 5, r: item.value > 1000000 ? (item.value / 200000) : (item.value / 100000) + 5,
@ -99,44 +67,19 @@ export class MoreStuffComponent implements OnInit {
return graph_data; return graph_data;
} }
}
private loadSupplierBubble(useRange: boolean, start_range : string, end_range : string) { private loadSupplierBubble() {
this.isBubbleChartLoaded = false; this.api.loadMiscUrl('organisation/external/supplier_count', {
// console.log("Fetching data for bubble chart... this will take a while. custom range = " + useRange); from: this.filterFrom,
to: this.filterTo,
var is_cached = false; }).subscribe(
try {
if (this.cached_graph_data) {
is_cached = true;
}
} catch {
// not cached
}
if (is_cached) {
// console.log("Using cached data of " + this.cached_graph_data.length + " items.");
this.supplierBubbleChartData[0].data = this.formatGraphData(this.cached_graph_data, useRange, start_range, end_range);
this.isBubbleChartLoaded = true;
// console.log("variable \"this.isBubbleChartLoaded\": " + this.isBubbleChartLoaded);
}
else {
// console.log("Not using cached data.");
this.api.loadMiscUrl('organisation/external/supplier_count').subscribe(
result => { result => {
this.cached_graph_data = result; this.supplierBubbleChartData[0].data = this.formatGraphData(result);
this.supplierBubbleChartData[0].data = this.formatGraphData(result, useRange, start_range, end_range);
// console.log("Graph fetched with " + this.supplierBubbleChartData[0].data.length + " items.");
this.isBubbleChartLoaded = true; this.isBubbleChartLoaded = true;
// console.log("variable \"this.isBubbleChartLoaded\": " + this.isBubbleChartLoaded);
} }
) )
} }
// console.log("variable \"this.isBubbleChartLoaded\": " + this.isBubbleChartLoaded);
}
public supplierBubbleChartType: ChartType = 'bubble'; public supplierBubbleChartType: ChartType = 'bubble';
public supplierBubbleChartData: any[] = [ public supplierBubbleChartData: any[] = [
{ {
@ -156,15 +99,15 @@ export class MoreStuffComponent implements OnInit {
time: { time: {
unit: 'month' unit: 'month'
}, },
scaleLabel:{ scaleLabel: {
display:true, display: true,
labelString:'Date' labelString: 'Date'
} }
}], }],
yAxes: [{ yAxes: [{
scaleLabel: { scaleLabel: {
display:true, display: true,
labelString:'Number of purchases' labelString: 'Number of purchases'
} }
}] }]
}, },
@ -184,7 +127,10 @@ export class MoreStuffComponent implements OnInit {
} }
private tableSummary() { private tableSummary() {
this.api.loadMiscUrl('organisation/external/lcc_tables').subscribe( this.api.loadMiscUrl('organisation/external/lcc_tables', {
from: this.filterFrom,
to: this.filterTo,
}).subscribe(
result => { result => {
this.wardList = result.wards; this.wardList = result.wards;
this.metaTypeList = Object.keys(result.types).map(key => result.types[key]); this.metaTypeList = Object.keys(result.types).map(key => result.types[key]);
@ -197,13 +143,16 @@ export class MoreStuffComponent implements OnInit {
}, },
error => { error => {
console.log('Retrieval Error'); console.log('Retrieval Error');
console.log( error._body ); console.log(error._body);
} }
) )
} }
private loadYearSpend() { private loadYearSpend() {
this.api.loadMiscUrl('organisation/external/year_spend').subscribe( this.api.loadMiscUrl('organisation/external/year_spend', {
from: this.filterFrom,
to: this.filterTo,
}).subscribe(
result => { result => {
let value_data = []; let value_data = [];
let count_data = []; let count_data = [];
@ -226,20 +175,6 @@ export class MoreStuffComponent implements OnInit {
) )
} }
bubbleChartUpdate() {
// console.log("start_range input box: " + this.bubbleChartBegin);
// console.log("end_range input box: " + this.bubbleChartEnd);
// this is called when daterange is changed
this.loadSupplierBubble(true, (this.bubbleChartBegin), (this.bubbleChartEnd));
// console.log("variable \"this.isBubbleChartLoaded\": " + this.isBubbleChartLoaded);
/*
bubbleChartBegin: any;
bubbleChartEnd: any;
*/
}
public yearSpendChartData: any[] = [ public yearSpendChartData: any[] = [
{ {
data: [], data: [],
@ -261,6 +196,7 @@ export class MoreStuffComponent implements OnInit {
}, },
]; ];
public yearSpendChartOptions: any = { public yearSpendChartOptions: any = {
elements: {line: {tension: 0}},
responsive: true, responsive: true,
scales: { scales: {
xAxes: [{ xAxes: [{
@ -269,24 +205,16 @@ export class MoreStuffComponent implements OnInit {
unit: 'month' unit: 'month'
}, },
scaleLabel: { scaleLabel: {
display:true, display: true,
labelString: 'Date' labelString: 'Date'
} }
}], }],
yAxes: [ yAxes: [
{id: 'y-value', position: 'left', beginAtZero: true}, {id: 'y-value', position: 'left', beginAtZero: true, type: 'linear'},
{id: 'y-count', position: 'right', beginAtZero: true}, {id: 'y-count', position: 'right', beginAtZero: true, type: 'linear'},
] ]
}, },
}; };
public yearSpendChartColors: Color[] = [
{
backgroundColor: [
'#ffa1b5',
'#52afed'
]
}
];
public yearSpendChartLabels: string[] = []; public yearSpendChartLabels: string[] = [];
public yearSpendChartType: ChartType = 'line'; public yearSpendChartType: ChartType = 'line';
@ -304,64 +232,60 @@ export class MoreStuffComponent implements OnInit {
private loadSupplierHistory() { private loadSupplierHistory() {
this.api.loadMiscUrl('organisation/external/supplier_history').subscribe( this.api.loadMiscUrl('organisation/external/supplier_history').subscribe(
result => { result => {
let labels = []; this._supplierHistoryData = result.data;
let year = []; this._supplierHistoryPage = 1;
let half = []; this._supplierHistoryPages = Math.ceil(this._supplierHistoryData.length / this._supplierHistoryPerPage);
let quarter = []; this.updateSupplierHistoryData();
result.data.map(item => {
labels.push(item.name);
year.push(item.year_total);
half.push(item.half_total);
quarter.push(item.quarter_total);
});
this.supplierMonthChartData[0].data = quarter.slice(0,15);
this.supplierMonthChartData[1].data = half.slice(0,15);
this.supplierMonthChartData[2].data = year.slice(0,15);
this.supplierMonthChartLabels = labels.slice(0,15);
}
)
this.isSupplierChartLoaded = true; this.isSupplierChartLoaded = true;
} }
);
}
private updateSupplierHistoryData() {
const lastResult = this._supplierHistoryPerPage * this._supplierHistoryPage;
console.log(this._supplierHistoryPage);
const firstResult = lastResult - this._supplierHistoryPerPage;
const pageData = this._supplierHistoryData.slice(firstResult, lastResult);
console.log(pageData);
private supplierChartNext() {
result => {
let labels = []; let labels = [];
let year = []; let year = [];
let half = []; let half = [];
let quarter = []; let quarter = [];
result.data.map(item => { pageData.map(item => {
labels.push(item.name); labels.push(item.name);
year.push(item.year_total); year.push(item.year_total);
half.push(item.half_total); half.push(item.half_total);
quarter.push(item.quarter_total); quarter.push(item.quarter_total);
}); });
this.supplierMonthChartData[0].data = quarter.slice(16,30);
this.supplierMonthChartData[1].data = half.slice(16,30); this.supplierMonthChartData[0].data = quarter;
this.supplierMonthChartData[2].data = year.slice(16,30); this.supplierMonthChartData[1].data = half;
this.supplierMonthChartLabels = labels.slice(16,30); this.supplierMonthChartData[2].data = year;
} this.supplierMonthChartLabels = labels;
this.isSupplierChartLoaded = true;
}
private supplierChartPrevious() {
result => {
let labels = [];
let year = [];
let half = [];
let quarter = [];
result.data.map(item => {
labels.push(item.name);
year.push(item.year_total);
half.push(item.half_total);
quarter.push(item.quarter_total);
});
this.supplierMonthChartData[0].data = quarter.slice(0,15);
this.supplierMonthChartData[1].data = half.slice(0,15);
this.supplierMonthChartData[2].data = year.slice(0,15);
this.supplierMonthChartLabels = labels.slice(0,15);
}
this.isSupplierChartLoaded = true;
} }
public nextSupplierHistoryPage() {
if (this._supplierHistoryPage < this._supplierHistoryPages) {
this._supplierHistoryPage++;
}
this.updateSupplierHistoryData();
}
public previousSupplierHistoryPage() {
if (this._supplierHistoryPage > 1) {
this._supplierHistoryPage--;
}
this.updateSupplierHistoryData();
}
private _supplierHistoryData: any[];
private _supplierHistoryPerPage: number = 15;
private _supplierHistoryPage: number = 1;
private _supplierHistoryPages: number = 1;
public supplierMonthChartData: any[] = [ public supplierMonthChartData: any[] = [
{ {
data: [], data: [],
@ -394,13 +318,13 @@ export class MoreStuffComponent implements OnInit {
scales: { scales: {
xAxes: [{ xAxes: [{
scaleLabel: { scaleLabel: {
display:true, display: true,
labelString: 'Spend amount £' labelString: 'Spend amount £'
} }
}], }],
yAxes: [{ yAxes: [{
scaleLabel: { scaleLabel: {
display:true, display: true,
labelString: 'Supplier Names' labelString: 'Supplier Names'
} }
}] }]

View file

@ -3,12 +3,15 @@
<div class="col-lg-12"> <div class="col-lg-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<strong>Search list of suppliers</strong> <h4>Search Suppliers</h4>
<small>Search by name or postcode of suppliers here</small>
</div> </div>
<div *ngIf="supplierListAvailable" class="card-block"> <div *ngIf="supplierListAvailable" class="card-block">
<div class="search-hero"> <div class="input-group">
<input class="form-control" type="text" name="search" [(ngModel)]="searchText" autocomplete="off" placeholder="Search for a supplier here"> <input class="form-control" type="text" name="search" [(ngModel)]="searchText" autocomplete="off"
placeholder="Search by Name or Postcode" (keydown.enter)="searchSuppliers()">
<div class="input-group-append">
<button class="btn btn-primary" (click)="searchSuppliers()">Search</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -20,8 +23,8 @@
<div class="col-lg-12"> <div class="col-lg-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<strong>List of Suppliers</strong> <h4>List of Suppliers</h4>
<small>This lists all suppliers that have been submitted.</small> <div class="small">Click on Column Headers to change Sort Order</div>
</div> </div>
<div *ngIf="supplierListAvailable" class="card-block"> <div *ngIf="supplierListAvailable" class="card-block">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
@ -42,7 +45,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr supplier-result *ngFor="let supplier of supplierList | paginate: paginateConfig" [supplier]="supplier"></tr> <tr supplier-result *ngFor="let supplier of supplierList | paginate: paginateConfig"
[supplier]="supplier"></tr>
</tbody> </tbody>
</table> </table>
<pagination-template #p="paginationApi" <pagination-template #p="paginationApi"
@ -65,13 +69,6 @@
</li> </li>
</ul> </ul>
</pagination-template> </pagination-template>
<select (change)="loadSuppliers(1)" [(ngModel)]="perPage">
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="200">200</option>
</select>
</div> </div>
<div *ngIf="!supplierListAvailable" class="card-block"> <div *ngIf="!supplierListAvailable" class="card-block">
No Suppliers available. No Suppliers available.

View file

@ -13,6 +13,8 @@ export class SuppliersComponent implements OnInit, AfterViewInit {
@Input() public categories: any; @Input() public categories: any;
public perPage: number = 10; public perPage: number = 10;
searchText: string;
supplierList: any; supplierList: any;
supplierListAvailable = false; supplierListAvailable = false;
sortBy = 'name'; sortBy = 'name';
@ -25,10 +27,6 @@ export class SuppliersComponent implements OnInit, AfterViewInit {
totalItems: 0 totalItems: 0
}; };
public recurClick(event: any): void {
this.onClick.emit( event );
}
constructor( constructor(
private api: ApiService, private api: ApiService,
) { } ) { }
@ -38,7 +36,7 @@ export class SuppliersComponent implements OnInit, AfterViewInit {
} }
loadSuppliers(logPage: number) { loadSuppliers(logPage: number) {
this.api.externalSuppliers(logPage, this.sortBy, this.sortDir, this.perPage).subscribe( this.api.externalSuppliers(logPage, this.sortBy, this.sortDir, this.perPage, this.searchText).subscribe(
result => { result => {
this.supplierList = result.suppliers; this.supplierList = result.suppliers;
if (this.supplierList) { if (this.supplierList) {
@ -65,6 +63,11 @@ export class SuppliersComponent implements OnInit, AfterViewInit {
this.loadSuppliers(1); this.loadSuppliers(1);
} }
searchSuppliers() {
// Go back to page 1 when searching
this.loadSuppliers(1);
}
ngAfterViewInit() { ngAfterViewInit() {
} }

View file

@ -144,26 +144,28 @@ export class ApiService {
); );
} }
public loadMiscUrl(extra_url) { public loadMiscUrl(extra_url, extraArgs = {}) {
const key = this.sessionKey; const key = this.sessionKey;
return this.http.post<any>( return this.http.post<any>(
this.apiUrl + '/v1/' + extra_url, this.apiUrl + '/v1/' + extra_url,
{ {
session_key : key, session_key : key,
...extraArgs,
} }
); );
} }
public externalSuppliers(data, sortBy, sortDir, perPage) { public externalSuppliers(page, sortBy, sortDir, perPage, search) {
const key = this.sessionKey; const key = this.sessionKey;
return this.http.post<any>( return this.http.post<any>(
this.apiUrl + '/v1/organisation/external/suppliers', this.apiUrl + '/v1/organisation/external/suppliers',
{ {
session_key : key, session_key : key,
page : data, page : page,
sort_by : sortBy, sort_by : sortBy,
sort_dir : sortDir, sort_dir : sortDir,
per_page : perPage per_page : perPage,
search : search,
} }
); );
} }