Its in there but its ugly and no charts yet
This commit is contained in:
parent
3e7cdb9fa2
commit
2186c75c6f
File diff suppressed because it is too large
Load Diff
|
@ -36,8 +36,11 @@
|
|||
"@ngx-formly/core": "^5.8.0",
|
||||
"@ngx-formly/material": "^5.8.0",
|
||||
"bwip-js": "^2.0.11",
|
||||
"chart.js": "^2.9.4",
|
||||
"chartjs-plugin-datalabels": "^0.7.0",
|
||||
"code-128-encoder": "^3.1.1",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"ng2-charts": "^2.4.2",
|
||||
"ngx-qrcode-svg": "^2.0.0",
|
||||
"rfdc": "^1.1.4",
|
||||
"rxjs": "~6.6.3",
|
||||
|
@ -46,7 +49,7 @@
|
|||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^0.1001.2",
|
||||
"@angular-devkit/build-angular": "^0.1101.1",
|
||||
"@angular/cli": "^10.1.2",
|
||||
"@angular/compiler-cli": "~11.0.0-next.2",
|
||||
"@angular/language-service": "~11.0.0-next.2",
|
||||
|
|
|
@ -6,6 +6,9 @@ import {PrintComponent} from './print/print.component';
|
|||
import {SampleComponent} from './sample/sample.component';
|
||||
import {SettingsComponent} from './settings/settings.component';
|
||||
import {MultipleLabelsComponent} from './multiple-labels/multiple-labels.component';
|
||||
import { DepositsComponent } from './deposits/deposits.component';
|
||||
import { GraphsComponent } from './graphs/graphs.component';
|
||||
import { ImportedFilesComponent} from './imported-files/imported-files.component'
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
|
@ -13,6 +16,21 @@ export const routes: Routes = [
|
|||
pathMatch: 'full',
|
||||
component: SampleComponent
|
||||
},
|
||||
{
|
||||
path: 'deposits',
|
||||
pathMatch: 'full',
|
||||
component: DepositsComponent
|
||||
},
|
||||
{
|
||||
path: 'dashboard',
|
||||
pathMatch: 'full',
|
||||
component: GraphsComponent
|
||||
},
|
||||
{
|
||||
path: 'imports',
|
||||
pathMatch: 'full',
|
||||
component: ImportedFilesComponent
|
||||
},
|
||||
{
|
||||
path: 'sample',
|
||||
pathMatch: 'full',
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {Component, Inject} from '@angular/core';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {AppEnvironment} from './models/appEnvironment.interface';
|
||||
import { Component, Inject, OnInit} from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { AppEnvironment } from './models/appEnvironment.interface';
|
||||
|
||||
declare var jQuery: any;
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
|
@ -8,7 +10,7 @@ import {AppEnvironment} from './models/appEnvironment.interface';
|
|||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
|
||||
export class AppComponent {
|
||||
export class AppComponent implements OnInit {
|
||||
loading: boolean;
|
||||
|
||||
constructor(
|
||||
|
@ -18,7 +20,13 @@ export class AppComponent {
|
|||
this.titleService.setTitle(this.environment.title);
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
(function ($) {
|
||||
$("#menu-toggle").click(function () {
|
||||
$("#wrapper").toggleClass("toggled");
|
||||
});
|
||||
})(jQuery);
|
||||
}
|
||||
reload() {
|
||||
this.loading = true;
|
||||
setTimeout(() => this.loading = false, 300);
|
||||
|
|
|
@ -32,11 +32,22 @@ import {PrintLayoutComponent} from './print-layout/print-layout.component';
|
|||
import {PrintComponent} from './print/print.component';
|
||||
import {SampleComponent} from './sample/sample.component';
|
||||
import {ApiService} from './services/api.service';
|
||||
import {GraphService} from './services/graph.service';
|
||||
import {CacheService} from './services/cache.service';
|
||||
import {SettingsService} from './services/settings.service';
|
||||
import {SettingsComponent} from './settings/settings.component';
|
||||
import {MultipleLabelsComponent} from './multiple-labels/multiple-labels.component';
|
||||
import {RectangleDatamatrixRectangularComponent} from './label-layout/formats/rectangle-datamatrix-rectangular/rectangle-datamatrix-rectangular.component';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { DepositsComponent } from './deposits/deposits.component';
|
||||
import { ImportedFilesComponent } from './imported-files/imported-files.component';
|
||||
import { GraphsComponent } from './graphs/graphs.component';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatNativeDateModule, DateAdapter } from '@angular/material/core';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import { CustomDateAdapter } from './custom-date-adapter';
|
||||
|
||||
|
||||
/**
|
||||
* This function is used internal to get a string instance of the `<base href="" />` value from `index.html`.
|
||||
|
@ -72,8 +83,15 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
|
|||
RectangleDatamatrixRectangularComponent,
|
||||
SampleComponent,
|
||||
SettingsComponent,
|
||||
DepositsComponent,
|
||||
GraphsComponent,
|
||||
ImportedFilesComponent
|
||||
],
|
||||
imports: [
|
||||
MatPaginatorModule,
|
||||
MatNativeDateModule,
|
||||
BrowserModule,
|
||||
ChartsModule,
|
||||
BrowserAnimationsModule,
|
||||
FlexLayoutModule,
|
||||
FormlyModule,
|
||||
|
@ -81,6 +99,7 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
|
|||
HttpClientModule,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatDatepickerModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
|
@ -94,8 +113,11 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
|
|||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
GraphService,
|
||||
CacheService,
|
||||
SettingsService,
|
||||
SettingsService,
|
||||
MatDatepickerModule,
|
||||
{provide: DateAdapter, useClass: CustomDateAdapter },
|
||||
{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment},
|
||||
{provide: APP_BASE_HREF, useFactory: getBaseHref, deps: [PlatformLocation]},
|
||||
{provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}},
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { NativeDateAdapter } from '@angular/material/core';
|
||||
|
||||
|
||||
/** Adapts the native JS Date for use with cdk-based components that work with dates. */
|
||||
export class CustomDateAdapter extends NativeDateAdapter {
|
||||
|
||||
getFirstDayOfWeek(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
<div class="container-fluid mt--7">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="mb-0">Inventory Deposits</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<h5 class="card-title">(Total Inventory - Total Prints) since first_deposit | safe: samples_since | safe Strips
|
||||
Remaining</h5>
|
||||
|
||||
<div class="justify-content-center">
|
||||
<div class="table-responsive">
|
||||
<!-- Projects table -->
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date Added</th>
|
||||
<th>Amount</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
<input matInput [(ngModel)]="date_temp" [value] = "date_temp" [matDatepicker]="picker">
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
|
||||
</td>
|
||||
<td><input [(ngModel)]="newDeposit.amount" name="amount"></td>
|
||||
<td><input [(ngModel)]="newDeposit.notes" name="notes">
|
||||
<button type="submit" (click)="addDeposit()">Submit</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngFor="let deposit of depositList">
|
||||
<td>{{deposit.date_added | date : 'full' }}</td>
|
||||
<td>{{deposit.amount}}</td>
|
||||
<td>{{deposit.notes}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<mat-paginator #paginator [length]="100000" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
|
||||
(page)="updatePage($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DepositsComponent } from './deposits.component';
|
||||
|
||||
describe('DepositsComponent', () => {
|
||||
let component: DepositsComponent;
|
||||
let fixture: ComponentFixture<DepositsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DepositsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DepositsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { PageEvent } from '@angular/material/paginator'
|
||||
import { ApiService } from '../services/api.service';
|
||||
import { InventoryDeposit } from "../models/deposit.interface";
|
||||
|
||||
@Component({
|
||||
selector: 'app-deposits',
|
||||
templateUrl: './deposits.component.html',
|
||||
styleUrls: ['./deposits.component.css']
|
||||
})
|
||||
export class DepositsComponent implements OnInit {
|
||||
depositList: InventoryDeposit[] = [];
|
||||
newDeposit: InventoryDeposit = {
|
||||
amount: 0,
|
||||
date_added: "",
|
||||
notes: ''
|
||||
};
|
||||
date_temp = new Date(Date.now());
|
||||
|
||||
constructor(
|
||||
private depositService: ApiService) {
|
||||
}
|
||||
|
||||
|
||||
addDeposit(): void {
|
||||
this.newDeposit.date_added = this.date_temp.toLocaleDateString();
|
||||
|
||||
this.depositService.addDeposit(this.newDeposit).subscribe(deposit => this.depositList.push(deposit))
|
||||
}
|
||||
|
||||
current_page: number = 0;
|
||||
|
||||
pageSize: number = 10;
|
||||
pageSizeOptions: number[] = [10,20,50,100];
|
||||
|
||||
updatePage(event: PageEvent) {
|
||||
this.current_page = event.pageIndex;
|
||||
this.depositService.getDeposits(this.current_page).subscribe(searchResult => this.depositList = searchResult);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.depositService.getDeposits(0).subscribe(searchResult => this.depositList = searchResult);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.card{
|
||||
display: flex;
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
<div class="container-fluid mt--7">
|
||||
<div class="row mb-4">
|
||||
<div class="col-xl-2">
|
||||
<div class="card card-stats mb-xl-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">Total Samples within<br> {{form.start_date | date}}
|
||||
-
|
||||
{{form.end_date | date}}</h5>
|
||||
<span id="card_1" class="h2 font-weight-bold">{{topBarData[0]}}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-2">
|
||||
<div class="card card-stats mb-xl-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">Total Samples within <br> {{start_date_1 | date}}
|
||||
-
|
||||
{{end_date_1 | date}}</h5>
|
||||
<span id="card_1" class="h2 font-weight-bold mb-0">{{topBarData[1]}}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-2">
|
||||
<div class="card card-stats mb-xl-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">Total Samples within <br> {{start_date_2 | date}}
|
||||
-
|
||||
{{end_date_2 | date}}</h5>
|
||||
<span id="card_1" class="h2 font-weight-bold mb-0">{{topBarData[2]}}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3">
|
||||
<div class="card card-stats mb-xl-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">Email Notifications within <br> {{form.start_date |
|
||||
date}} - {{form.end_date | date}}</h5>
|
||||
<span id="card_1" class="h2 font-weight-bold mb-0"><span
|
||||
style="color:green">{{topBarData[3]}}</span>/<span style="color:red">{{topBarData[4]}}</span></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 ">
|
||||
<div class="card card-stats mb-xl-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">Text Notifications within <br> {{form.start_date |
|
||||
date}} - {{form.end_date | date}}</h5>
|
||||
<span id="card_1" class="h2 font-weight-bold mb-0"><span
|
||||
style="color:green">{{topBarData[5]}}</span>/<span style="color:red">{{topBarData[6]}}</span></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col mb-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="mb-3 ">{{ChartName}}</h3>
|
||||
</div>
|
||||
<div style="display: block">
|
||||
<canvas baseChart [datasets]="dailyChartsData" [labels]="dailyChartLabels" [options]="barChartOptions"
|
||||
[legend]="true" chartType="bar" (chartClick)="chartClicked($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col mb-3">
|
||||
<div class="card ">
|
||||
<div class="card-header">
|
||||
|
||||
<div class=row>
|
||||
<h3 class="mb-3 col ">Search</h3>
|
||||
<button (click)="searchToday()" class="btn btn-sm btn-primary col mr-3">Today</button>
|
||||
<button (click)="searchAll()" class="btn btn-sm btn-primary col mr-3">All</button>
|
||||
<button (click)="updateGraphData()" class="btn btn-sm btn-primary col ">Run</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="form-group row">
|
||||
<label for="range" class="col mt-3 mr-3">Search Range</label>
|
||||
<mat-form-field>
|
||||
<mat-date-range-input [rangePicker]="picker">
|
||||
<input matStartDate [(ngModel)]="start_date">
|
||||
<input matEndDate [(ngModel)]="end_date">
|
||||
</mat-date-range-input>
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-date-range-picker #picker></mat-date-range-picker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="form-group row ">
|
||||
|
||||
<label for="location" class="col mt-3 mr-3">Location ID(s)</label>
|
||||
|
||||
<textarea class="form-control col mr-3 mt-3" id="location" [(ngModel)]="form.location" rows="2"></textarea>
|
||||
|
||||
</div>
|
||||
<div class="form-group row ">
|
||||
|
||||
<label for="student" class="col mt-3 mr-3">Student ID(s)</label>
|
||||
|
||||
<textarea class="form-control col mr-3 mt-3" id="student" [(ngModel)]="form.student_id" rows="2"></textarea>
|
||||
|
||||
</div>
|
||||
<div class="form-group row ">
|
||||
|
||||
<label for="compute" class="col mt-3 mr-3">Compute ID(s)</label>
|
||||
|
||||
<textarea class="form-control col mr-3 mt-3" id="compute" [(ngModel)]="form.compute_id" rows="2"></textarea>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
<div class="col mb-3 ">
|
||||
<div class="card ">
|
||||
<div class="card-header">
|
||||
<h3 class="mb-3 ">Average Count By Weekday For {{form.start_date | date}} - {{form.end_date | date}}</h3>
|
||||
</div>
|
||||
<div style="display: block">
|
||||
<canvas baseChart [datasets]="weekdayChartsData" [labels]="weekdayChartLabels" [options]="barChartOptions"
|
||||
[legend]=false chartType="horizontalBar">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col mb-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="mb-3">Average Count By Hour For {{form.start_date | date}} - {{form.end_date | date}}</h3>
|
||||
</div>
|
||||
<div style="display: block">
|
||||
<canvas baseChart [datasets]="hourlyChartsData" [labels]="hourlyChartLabels" [options]="barChartOptions"
|
||||
[legend]=false chartType="horizontalBar">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card row ">
|
||||
<div class="card-header">
|
||||
<h3 class="mb-0">Records to be processed </h3>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Barcode </th>
|
||||
<th>Date</th>
|
||||
<th>Location</th>
|
||||
<th>IDs</th>
|
||||
<th>Contacts</th>
|
||||
<th>Notifications</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let sample of searchResult">
|
||||
<td>{{sample.barcode}}</td>
|
||||
<td>{{sample.date| date :"medium"}}</td>
|
||||
<td>Location: {{sample.location}}<br>Station: {{sample.station}}</td>
|
||||
<td>Compute ID: {{sample.compute_id}}<br>Student ID: {{sample.student_id}}</td>
|
||||
<td>Phone: {{sample.phone}}<br>Email: {{sample.email}}</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<mat-paginator #paginator [length]="topBarData[0]" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
|
||||
(page)="updatePage($event)">
|
||||
</mat-paginator>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GraphsComponent } from './graphs.component';
|
||||
|
||||
describe('GraphsComponent', () => {
|
||||
let component: GraphsComponent;
|
||||
let fixture: ComponentFixture<GraphsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ GraphsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GraphsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,224 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
|
||||
|
||||
import { Label } from 'ng2-charts';
|
||||
import { GraphService } from '../services/graph.service'
|
||||
import { Sample } from '../models/sample.interface'
|
||||
import { SearchForm } from '../models/search_form'
|
||||
import {PageEvent} from '@angular/material/paginator'
|
||||
import * as pluginDataLabels from 'chartjs-plugin-datalabels';
|
||||
|
||||
@Component({
|
||||
selector: 'app-graphs',
|
||||
templateUrl: './graphs.component.html',
|
||||
styleUrls: ['./graphs.component.css']
|
||||
})
|
||||
export class GraphsComponent implements OnInit {
|
||||
|
||||
constructor(private graphService: GraphService) { }
|
||||
|
||||
topBarData: Array<number> = [0, 0, 0, 0, 0, 0, 0];
|
||||
ChartName: String = "Location Activity";
|
||||
dailyChartLabels: Label[] = [];
|
||||
weekdayChartLabels: Label[] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
||||
hourlyChartLabels: Label[] = ["1 AM", "2 AM", "3 AM", "4 AM", "5 AM", "6 AM", "7 AM", "8 AM", "9 AM", "10 AM", "11 AM", "12 AM", "1 PM", "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM", "9 PM", "10 PM", "11 PM", "12 PM"];
|
||||
dailyChartsData: ChartDataSets[] = [];
|
||||
hourlyChartsData: ChartDataSets[] = [];
|
||||
weekdayChartsData: ChartDataSets[] = [];
|
||||
|
||||
barChartPlugins = [pluginDataLabels];
|
||||
barChartOptions: ChartOptions = {
|
||||
responsive: true,
|
||||
// We use these empty structures as placeholders for dynamic theming.
|
||||
scales: {
|
||||
xAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
},
|
||||
stacked: true
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
},
|
||||
stacked: true
|
||||
}]
|
||||
},
|
||||
legend: {
|
||||
onClick: (e, i) => {
|
||||
this.form.location = String(i.text);
|
||||
this.updateGraphData();
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
datalabels: {
|
||||
|
||||
rotation: -45,
|
||||
anchor: 'end',
|
||||
align: 'end',
|
||||
formatter: (value: any, ctx: any) => {
|
||||
|
||||
let datasets = ctx.chart.data.datasets;
|
||||
if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
|
||||
let sum = 0;
|
||||
datasets.map((dataset: any) => {
|
||||
sum += dataset.data[ctx.dataIndex];
|
||||
});
|
||||
return sum;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}, layout: {
|
||||
padding: {
|
||||
left: 0,
|
||||
right: 20,
|
||||
top: 30,
|
||||
bottom: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
arrayOne(n: number): any[] {
|
||||
return Array(n);
|
||||
|
||||
}
|
||||
|
||||
tempData: JSON = <JSON>{};
|
||||
|
||||
searchResult: Sample[] = [];
|
||||
|
||||
start_date: Date = new Date();
|
||||
end_date: Date = new Date();
|
||||
|
||||
start_date_1: string = "";
|
||||
end_date_1: string = "";
|
||||
start_date_2: string = "";
|
||||
end_date_2: string = "";
|
||||
|
||||
current_page: number = 0;
|
||||
pageSize: number = 10;
|
||||
pageSizeOptions: number[] = [10,20,50,100];
|
||||
|
||||
updatePage(event: PageEvent) {
|
||||
this.current_page = event.pageIndex;
|
||||
this.graphService.getRawSearchData(this.form, this.current_page).subscribe(searchResult => this.searchResult = searchResult);
|
||||
}
|
||||
|
||||
form: SearchForm = {
|
||||
start_date: "",
|
||||
end_date: "",
|
||||
student_id: "",
|
||||
location: "",
|
||||
compute_id: "",
|
||||
include_tests: false
|
||||
};
|
||||
|
||||
|
||||
searchToday(): void {
|
||||
this.start_date = new Date();
|
||||
this.end_date = new Date();
|
||||
this.updateGraphData();
|
||||
}
|
||||
searchAll(): void {
|
||||
this.start_date = new Date(2020,9,5);
|
||||
this.end_date = new Date();
|
||||
this.updateGraphData();
|
||||
}
|
||||
|
||||
updateGraphData(): void {
|
||||
|
||||
if (this.form.location.trim().split(" ").length == 1) {
|
||||
this.ChartName = "Total Samples per Station @ Location " + this.form.location;
|
||||
} else {
|
||||
this.ChartName = "Total Samples per Location";
|
||||
}
|
||||
if (this.form.location.trim() == "") {
|
||||
this.ChartName = "Total Samples per Location";
|
||||
}
|
||||
|
||||
this.form.start_date = this.start_date.toLocaleDateString();
|
||||
this.form.end_date = this.end_date.toLocaleDateString();
|
||||
|
||||
var date = new Date();
|
||||
var date_2 = new Date();
|
||||
|
||||
date.setDate(this.start_date.getDate() - 7);
|
||||
this.start_date_1 = date.toLocaleDateString();
|
||||
|
||||
date_2.setDate(this.end_date.getDate() - 7);
|
||||
this.end_date_1 = date_2.toLocaleDateString();
|
||||
|
||||
date.setDate(date.getDate() - 7);
|
||||
this.start_date_2 = date.toLocaleDateString();
|
||||
|
||||
date_2.setDate(date_2.getDate() - 7);
|
||||
this.end_date_2 = date_2.toLocaleDateString();
|
||||
|
||||
var temp = new Date(this.start_date.getTime());
|
||||
this.dailyChartLabels = [];
|
||||
while (true) {
|
||||
this.dailyChartLabels.push(temp.toLocaleDateString());
|
||||
if (temp.toLocaleDateString() == this.end_date.toLocaleDateString()) {
|
||||
break;
|
||||
} else {
|
||||
temp.setDate(temp.getDate() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.graphService.getDayData(this.form).subscribe(tempData => {
|
||||
this.tempData = tempData;
|
||||
this.dailyChartsData = [];
|
||||
Object.entries(this.tempData).forEach(([loc_or_stat_name, totals]) => {
|
||||
this.dailyChartsData.push({ data: totals, label: loc_or_stat_name, stack: 'a' })
|
||||
});
|
||||
});
|
||||
|
||||
this.graphService.getWeekdayData(this.form).subscribe(tempData => {
|
||||
this.tempData = tempData;
|
||||
this.weekdayChartsData = [];
|
||||
Object.entries(this.tempData).forEach(([loc_or_stat_name, totals]) => {
|
||||
this.weekdayChartsData.push({ data: totals, label: loc_or_stat_name, stack: 'a' })
|
||||
});
|
||||
});
|
||||
|
||||
this.graphService.getHourData(this.form).subscribe(tempData => {
|
||||
this.tempData = tempData;
|
||||
this.hourlyChartsData = [];
|
||||
Object.entries(this.tempData).forEach(([loc_or_stat_name, totals]) => {
|
||||
this.hourlyChartsData.push({ data: totals, label: loc_or_stat_name, stack: 'c' })
|
||||
});
|
||||
});
|
||||
|
||||
this.graphService.getTopBarData(this.form).subscribe(tempData => {
|
||||
this.topBarData = tempData;
|
||||
});
|
||||
this.graphService.getRawSearchData(this.form, 0).subscribe(searchResult => this.searchResult = searchResult);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
var end_date = new Date();
|
||||
var start_date = new Date();
|
||||
this.form.start_date = start_date.toLocaleDateString();
|
||||
this.form.end_date = end_date.toLocaleDateString();
|
||||
this.updateGraphData();
|
||||
}
|
||||
|
||||
chartClicked(e: any): void {
|
||||
if (e.active.length > 0) {
|
||||
const chart = e.active[0]._chart;
|
||||
const activePoints = chart.getElementAtEvent(e.event);
|
||||
if (activePoints.length > 0) {
|
||||
// get the internal index of slice in pie chart
|
||||
const clickedElementIndex = activePoints[0]._index;
|
||||
const label = chart.data.labels[clickedElementIndex];
|
||||
// get value by index
|
||||
const value = chart.data.datasets[0].data[clickedElementIndex];
|
||||
console.log(clickedElementIndex, label, value)
|
||||
// this.updateGraphData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
<div class="container-fluid mt--7">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="mb-0">The following files were imported from IVY</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="justify-content-center">
|
||||
<div class="table-responsive">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead><tr><th>Date Added</th><th>File Name</th><th>Sample Count</th></tr></thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let file of fileList">
|
||||
<td>{{file.date_added | date : 'medium'}}</td>
|
||||
<td>{{file.file_name}}</td>
|
||||
<td>{{file.sample_count}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-paginator #paginator [length]="100000" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
|
||||
(page)="updatePage($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ImportedFilesComponent } from './imported-files.component';
|
||||
|
||||
describe('ImportedFilesComponent', () => {
|
||||
let component: ImportedFilesComponent;
|
||||
let fixture: ComponentFixture<ImportedFilesComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ImportedFilesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ImportedFilesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ApiService } from '../services/api.service';
|
||||
|
||||
import {PageEvent} from '@angular/material/paginator'
|
||||
import { IvyFile } from '../models/ivyfile.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-imported-files',
|
||||
templateUrl: './imported-files.component.html',
|
||||
styleUrls: ['./imported-files.component.css']
|
||||
})
|
||||
export class ImportedFilesComponent implements OnInit {
|
||||
fileList: IvyFile[] = [];
|
||||
constructor(private fileService: ApiService) { }
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fileService.getFiles(0).subscribe(fileList => this.fileList = fileList);
|
||||
}
|
||||
|
||||
current_page: number = 0;
|
||||
pageSize: number = 10;
|
||||
pageSizeOptions: number[] = [10,20,50,100];
|
||||
|
||||
updatePage(event: PageEvent) {
|
||||
this.current_page = event.pageIndex;
|
||||
|
||||
this.fileService.getFiles(this.current_page).subscribe(fileList => this.fileList = fileList);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export interface InventoryDeposit {
|
||||
amount: number;
|
||||
notes: string;
|
||||
date_added: string;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface IvyFile {
|
||||
sample_count: number;
|
||||
file_name: string;
|
||||
date_added: Date;
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
export interface Sample {
|
||||
barcode: string;
|
||||
date: Date;
|
||||
student_id: string;
|
||||
initials?: string;
|
||||
date: Date;
|
||||
location: string;
|
||||
station?: string;
|
||||
computing_id?: string;
|
||||
}
|
||||
phone?: string;
|
||||
email?: string;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export interface SearchForm{
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
student_id : string;
|
||||
location : string;
|
||||
compute_id : string;
|
||||
include_tests : boolean;
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {Observable, throwError} from 'rxjs';
|
||||
import {catchError, timeout} from 'rxjs/operators';
|
||||
import {ApiError} from '../models/apiError.interface';
|
||||
import {AppEnvironment} from '../models/appEnvironment.interface';
|
||||
import {Sample} from '../models/sample.interface';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError, timeout } from 'rxjs/operators';
|
||||
import { ApiError } from '../models/apiError.interface';
|
||||
import { AppEnvironment } from '../models/appEnvironment.interface';
|
||||
import { Sample } from '../models/sample.interface';
|
||||
import { InventoryDeposit } from '../models/deposit.interface';
|
||||
import { IvyFile } from '../models/ivyfile.interface';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
|
||||
|
||||
|
||||
@Injectable({
|
||||
|
@ -16,6 +19,8 @@ export class ApiService {
|
|||
apiRoot: string;
|
||||
endpoints = {
|
||||
sample: '/sample',
|
||||
deposit: '/deposit',
|
||||
ivy_file: '/ivy_file',
|
||||
};
|
||||
|
||||
constructor(
|
||||
|
@ -29,7 +34,38 @@ export class ApiService {
|
|||
/** Get the string value from a given URL */
|
||||
getStringFromUrl(url: string): Observable<string> {
|
||||
return this.httpClient
|
||||
.get(url, {responseType: 'text'})
|
||||
.get(url, { responseType: 'text' })
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
/** */
|
||||
getDeposits(page: Number): Observable<InventoryDeposit[]> {
|
||||
let param = new HttpParams().set("page", String(page));
|
||||
|
||||
const url = this.apiRoot + this.endpoints.deposit;
|
||||
return this.httpClient
|
||||
.get<InventoryDeposit[]>(url, { params: param })
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
/** */
|
||||
addDeposit(deposit: InventoryDeposit): Observable<InventoryDeposit> {
|
||||
const url = this.apiRoot + this.endpoints.deposit;
|
||||
return this.httpClient
|
||||
.post<InventoryDeposit>(url, deposit)
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
|
||||
/** */
|
||||
getFiles(page: Number): Observable<IvyFile[]> {
|
||||
let params = new HttpParams().set("page", String(page));
|
||||
const url = this.apiRoot + this.endpoints.ivy_file;
|
||||
return this.httpClient
|
||||
.get<IvyFile[]>(url, { params: params })
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GraphService } from './graph.service';
|
||||
|
||||
describe('GraphService', () => {
|
||||
let service: GraphService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(GraphService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
import { Injectable, Inject } from '@angular/core';
|
||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError, timeout } from 'rxjs/operators';
|
||||
import { ApiError } from '../models/apiError.interface';
|
||||
import { AppEnvironment } from '../models/appEnvironment.interface';
|
||||
import { HttpParams } from '@angular/common/http';
|
||||
import { Sample } from '../models/sample.interface';
|
||||
|
||||
import { SearchForm } from '../models/search_form';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class GraphService {
|
||||
apiRoot: string;
|
||||
constructor(@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
|
||||
@Inject(APP_BASE_HREF) public baseHref: string,
|
||||
private httpClient: HttpClient,
|
||||
) {
|
||||
this.apiRoot = environment.api;
|
||||
}
|
||||
getRawSearchData(form: SearchForm, page: Number): Observable<Sample[]> {
|
||||
var params = this.createParams(form);
|
||||
params = params.set("page", String(page));
|
||||
|
||||
return this.httpClient
|
||||
.get<Sample[]>(this.apiRoot + `/dashboard/search`, { params: params })
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
getTopBarData(form: SearchForm): Observable<number[]> {
|
||||
// let params = new HttpParams().set("start_date", form.start_date).set("end_date", form.end_date);
|
||||
return this.httpClient
|
||||
.get<number[]>(this.apiRoot + '/dashboard/tob_bar', { params: this.createParams(form) })
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
|
||||
}
|
||||
|
||||
getDayData(form: SearchForm): Observable<JSON> {
|
||||
return this.httpClient
|
||||
.get<JSON>(this.apiRoot + "/dashboard/day", { params: this.createParams(form) })
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
getWeekdayData(form: SearchForm): Observable<JSON> {
|
||||
return this.httpClient
|
||||
.get<JSON>(this.apiRoot + "/dashboard/weekday", { params: this.createParams(form) })
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
|
||||
}
|
||||
|
||||
getHourData(form: SearchForm): Observable<JSON> {
|
||||
return this.httpClient
|
||||
.get<JSON>(this.apiRoot + "/dashboard/hour", { params: this.createParams(form) })
|
||||
.pipe(timeout(1000), catchError(err => this._handleError(err)))
|
||||
.pipe(catchError(err => this._handleError(err)));
|
||||
}
|
||||
|
||||
createParams(form: SearchForm): HttpParams {
|
||||
let params = new HttpParams()
|
||||
.set("start_date", form.start_date)
|
||||
.set("end_date", form.end_date)
|
||||
.set("student_id", form.student_id)
|
||||
.set("compute_id", form.compute_id)
|
||||
.set("location", form.location)
|
||||
return params;
|
||||
}
|
||||
|
||||
private _handleError(error: ApiError): Observable<never> {
|
||||
return throwError(error.message || 'Could not complete your request; please try again later.');
|
||||
}
|
||||
/** Log a HeroService message with the MessageService */
|
||||
private log(message: string) {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import createClone from 'rfdc';
|
||||
//https://github.com/visjs/vis-network/issues/67
|
||||
import * as createClone from 'rfdc';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
import serializeJs from 'serialize-javascript';
|
||||
import {defaultOptions} from '../config/defaults';
|
||||
|
|
|
@ -8,8 +8,7 @@ import {AppDefaults} from '../models/appDefaults.interface';
|
|||
import {LabelLayout} from '../models/labelLayout.interface';
|
||||
import {Sample} from '../models/sample.interface';
|
||||
import {SettingsService} from '../services/settings.service';
|
||||
import createClone from 'rfdc';
|
||||
|
||||
import * as createClone from 'rfdc';
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: './settings.component.html',
|
||||
|
|
|
@ -23,4 +23,5 @@
|
|||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue