2021-05-21 11:48:42 +03:00

435 lines
13 KiB
TypeScript

import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ApexOptions } from 'ng-apexcharts';
import { ProjectService } from 'app/modules/admin/dashboards/project/project.service';
@Component({
selector : 'project',
templateUrl : './project.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectComponent implements OnInit, OnDestroy
{
chartGithubIssues: ApexOptions = {};
chartTaskDistribution: ApexOptions = {};
chartBudgetDistribution: ApexOptions = {};
chartWeeklyExpenses: ApexOptions = {};
chartMonthlyExpenses: ApexOptions = {};
chartYearlyExpenses: ApexOptions = {};
data: any;
selectedProject: string = 'ACME Corp. Backend App';
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _projectService: ProjectService,
private _router: Router
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the data
this._projectService.data$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((data) => {
// Store the data
this.data = data;
// Prepare the chart data
this._prepareChartData();
});
// Attach SVG fill fixer to all ApexCharts
window['Apex'] = {
chart: {
events: {
mounted: (chart: any, options?: any): void => {
this._fixSvgFill(chart.el);
},
updated: (chart: any, options?: any): void => {
this._fixSvgFill(chart.el);
}
}
}
};
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Fix the SVG fill references. This fix must be applied to all ApexCharts
* charts in order to fix 'black color on gradient fills on certain browsers'
* issue caused by the '<base>' tag.
*
* Fix based on https://gist.github.com/Kamshak/c84cdc175209d1a30f711abd6a81d472
*
* @param element
* @private
*/
private _fixSvgFill(element: Element): void
{
// Current URL
const currentURL = this._router.url;
// 1. Find all elements with 'fill' attribute within the element
// 2. Filter out the ones that doesn't have cross reference so we only left with the ones that use the 'url(#id)' syntax
// 3. Insert the 'currentURL' at the front of the 'fill' attribute value
Array.from(element.querySelectorAll('*[fill]'))
.filter(el => el.getAttribute('fill').indexOf('url(') !== -1)
.forEach((el) => {
const attrVal = el.getAttribute('fill');
el.setAttribute('fill', `url(${currentURL}${attrVal.slice(attrVal.indexOf('#'))}`);
});
}
/**
* Prepare the chart data from the data
*
* @private
*/
private _prepareChartData(): void
{
// Github issues
this.chartGithubIssues = {
chart : {
fontFamily: 'inherit',
foreColor : 'inherit',
height : '100%',
type : 'line',
toolbar : {
show: false
},
zoom : {
enabled: false
}
},
colors : ['#64748B', '#94A3B8'],
dataLabels : {
enabled : true,
enabledOnSeries: [0],
background : {
borderWidth: 0
}
},
grid : {
borderColor: 'var(--fuse-border)'
},
labels : this.data.githubIssues.labels,
legend : {
show: false
},
plotOptions: {
bar: {
columnWidth: '50%'
}
},
series : this.data.githubIssues.series,
states : {
hover: {
filter: {
type : 'darken',
value: 0.75
}
}
},
stroke : {
width: [3, 0]
},
tooltip : {
followCursor: true,
theme : 'dark'
},
xaxis : {
axisBorder: {
show: false
},
axisTicks : {
color: 'var(--fuse-border)'
},
labels : {
style: {
colors: 'var(--fuse-text-secondary)'
}
},
tooltip : {
enabled: false
}
},
yaxis : {
labels: {
offsetX: -16,
style : {
colors: 'var(--fuse-text-secondary)'
}
}
}
};
// Task distribution
this.chartTaskDistribution = {
chart : {
fontFamily: 'inherit',
foreColor : 'inherit',
height : '100%',
type : 'polarArea',
toolbar : {
show: false
},
zoom : {
enabled: false
}
},
labels : this.data.taskDistribution.labels,
legend : {
position: 'bottom'
},
plotOptions: {
polarArea: {
spokes: {
connectorColors: 'var(--fuse-border)'
},
rings : {
strokeColor: 'var(--fuse-border)'
}
}
},
series : this.data.taskDistribution.series,
states : {
hover: {
filter: {
type : 'darken',
value: 0.75
}
}
},
stroke : {
width: 2
},
theme : {
monochrome: {
enabled : true,
color : '#93C5FD',
shadeIntensity: 0.75,
shadeTo : 'dark'
}
},
tooltip : {
followCursor: true,
theme : 'dark'
},
yaxis : {
labels: {
style: {
colors: 'var(--fuse-text-secondary)'
}
}
}
};
// Budget distribution
this.chartBudgetDistribution = {
chart : {
fontFamily: 'inherit',
foreColor : 'inherit',
height : '100%',
type : 'radar',
sparkline : {
enabled: true
}
},
colors : ['#818CF8'],
dataLabels : {
enabled : true,
formatter : (val: number): string | number => `${val}%`,
textAnchor: 'start',
style : {
fontSize : '13px',
fontWeight: 500
},
background: {
borderWidth: 0,
padding : 4
},
offsetY : -15
},
markers : {
strokeColors: '#818CF8',
strokeWidth : 4
},
plotOptions: {
radar: {
polygons: {
strokeColors : 'var(--fuse-border)',
connectorColors: 'var(--fuse-border)'
}
}
},
series : this.data.budgetDistribution.series,
stroke : {
width: 2
},
tooltip : {
theme: 'dark',
y : {
formatter: (val: number): string => `${val}%`
}
},
xaxis : {
labels : {
show : true,
style: {
fontSize : '12px',
fontWeight: '500'
}
},
categories: this.data.budgetDistribution.categories
},
yaxis : {
max : (max: number): number => parseInt((max + 10).toFixed(0), 10),
tickAmount: 7
}
};
// Weekly expenses
this.chartWeeklyExpenses = {
chart : {
animations: {
enabled: false
},
fontFamily: 'inherit',
foreColor : 'inherit',
height : '100%',
type : 'line',
sparkline : {
enabled: true
}
},
colors : ['#22D3EE'],
series : this.data.weeklyExpenses.series,
stroke : {
curve: 'smooth'
},
tooltip: {
theme: 'dark'
},
xaxis : {
type : 'category',
categories: this.data.weeklyExpenses.labels
},
yaxis : {
labels: {
formatter: (val): string => `$${val}`
}
}
};
// Monthly expenses
this.chartMonthlyExpenses = {
chart : {
animations: {
enabled: false
},
fontFamily: 'inherit',
foreColor : 'inherit',
height : '100%',
type : 'line',
sparkline : {
enabled: true
}
},
colors : ['#4ADE80'],
series : this.data.monthlyExpenses.series,
stroke : {
curve: 'smooth'
},
tooltip: {
theme: 'dark'
},
xaxis : {
type : 'category',
categories: this.data.monthlyExpenses.labels
},
yaxis : {
labels: {
formatter: (val): string => `$${val}`
}
}
};
// Yearly expenses
this.chartYearlyExpenses = {
chart : {
animations: {
enabled: false
},
fontFamily: 'inherit',
foreColor : 'inherit',
height : '100%',
type : 'line',
sparkline : {
enabled: true
}
},
colors : ['#FB7185'],
series : this.data.yearlyExpenses.series,
stroke : {
curve: 'smooth'
},
tooltip: {
theme: 'dark'
},
xaxis : {
type : 'category',
categories: this.data.yearlyExpenses.labels
},
yaxis : {
labels: {
formatter: (val): string => `$${val}`
}
}
};
}
}