mirror of
https://github.com/akveo/ngx-admin.git
synced 2025-12-16 15:40:11 +01:00
feat(dashboard): add new E-commerce dashboard (#1754)
This commit is contained in:
parent
3482404b88
commit
56e4709a55
106 changed files with 6333 additions and 19 deletions
|
|
@ -37,6 +37,7 @@
|
|||
"node_modules/nebular-icons/scss/nebular-icons.scss",
|
||||
"node_modules/angular-tree-component/dist/angular-tree-component.css",
|
||||
"node_modules/pace-js/templates/pace-theme-flash.tmpl.css",
|
||||
"node_modules/leaflet/dist/leaflet.css",
|
||||
"src/app/@theme/styles/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
|
|
|
|||
|
|
@ -6,6 +6,15 @@ import { ElectricityService } from './electricity.service';
|
|||
import { StateService } from './state.service';
|
||||
import { SmartTableService } from './smart-table.service';
|
||||
import { PlayerService } from './player.service';
|
||||
import { UserActivityService } from './user-activity.service';
|
||||
import { OrdersChartService } from './orders-chart.service';
|
||||
import { ProfitChartService } from './profit-chart.service';
|
||||
import { TrafficListService } from './traffic-list.service';
|
||||
import { PeriodsService } from './periods.service';
|
||||
import { EarningService } from './earning.service';
|
||||
import { OrdersProfitChartService } from './orders-profit-chart.service';
|
||||
import { TrafficBarService } from './traffic-bar.service';
|
||||
import { ProfitBarAnimationChartService } from './profit-bar-animation-chart.service';
|
||||
|
||||
const SERVICES = [
|
||||
UserService,
|
||||
|
|
@ -13,6 +22,15 @@ const SERVICES = [
|
|||
StateService,
|
||||
SmartTableService,
|
||||
PlayerService,
|
||||
UserActivityService,
|
||||
OrdersChartService,
|
||||
ProfitChartService,
|
||||
TrafficListService,
|
||||
PeriodsService,
|
||||
EarningService,
|
||||
OrdersProfitChartService,
|
||||
TrafficBarService,
|
||||
ProfitBarAnimationChartService,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
|||
116
src/app/@core/data/earning.service.ts
Normal file
116
src/app/@core/data/earning.service.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { of as observableOf, Observable } from 'rxjs';
|
||||
|
||||
export class LiveUpdateChart {
|
||||
liveChart: { value: [string, number] }[];
|
||||
delta: {
|
||||
up: boolean;
|
||||
value: number;
|
||||
};
|
||||
dailyIncome: number;
|
||||
}
|
||||
|
||||
export class PieChart {
|
||||
value: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class EarningService {
|
||||
|
||||
private currentDate: Date = new Date();
|
||||
private currentValue = Math.random() * 1000;
|
||||
private ONE_DAY = 24 * 3600 * 1000;
|
||||
|
||||
private pieChartData = [
|
||||
{
|
||||
value: 50,
|
||||
name: 'Bitcoin',
|
||||
},
|
||||
{
|
||||
value: 25,
|
||||
name: 'Tether',
|
||||
},
|
||||
{
|
||||
value: 25,
|
||||
name: 'Ethereum',
|
||||
},
|
||||
];
|
||||
|
||||
private liveUpdateChartData = {
|
||||
bitcoin: {
|
||||
liveChart: [],
|
||||
delta: {
|
||||
up: true,
|
||||
value: 4,
|
||||
},
|
||||
dailyIncome: 45895,
|
||||
},
|
||||
tether: {
|
||||
liveChart: [],
|
||||
delta: {
|
||||
up: false,
|
||||
value: 9,
|
||||
},
|
||||
dailyIncome: 5862,
|
||||
},
|
||||
ethereum: {
|
||||
liveChart: [],
|
||||
delta: {
|
||||
up: false,
|
||||
value: 21,
|
||||
},
|
||||
dailyIncome: 584,
|
||||
},
|
||||
};
|
||||
|
||||
getDefaultLiveChartData(elementsNumber: number) {
|
||||
this.currentDate = new Date();
|
||||
this.currentValue = Math.random() * 1000;
|
||||
|
||||
return Array.from(Array(elementsNumber))
|
||||
.map(item => this.generateRandomLiveChartData());
|
||||
}
|
||||
|
||||
generateRandomLiveChartData() {
|
||||
this.currentDate = new Date(+this.currentDate + this.ONE_DAY);
|
||||
this.currentValue = this.currentValue + Math.random() * 20 - 11;
|
||||
|
||||
if (this.currentValue < 0) {
|
||||
this.currentValue = Math.random() * 100;
|
||||
}
|
||||
|
||||
return {
|
||||
value: [
|
||||
[
|
||||
this.currentDate.getFullYear(),
|
||||
this.currentDate.getMonth(),
|
||||
this.currentDate.getDate(),
|
||||
].join('/'),
|
||||
Math.round(this.currentValue),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
generateRandomEarningData(currency) {
|
||||
const data = this.liveUpdateChartData[currency.toLowerCase()];
|
||||
const newValue = this.generateRandomLiveChartData();
|
||||
|
||||
data.liveChart.shift();
|
||||
data.liveChart.push(newValue);
|
||||
|
||||
return observableOf(data.liveChart);
|
||||
}
|
||||
|
||||
getEarningLiveUpdateCardData(currency: string) {
|
||||
const data = this.liveUpdateChartData[currency.toLowerCase()];
|
||||
|
||||
data.liveChart = this.getDefaultLiveChartData(150);
|
||||
|
||||
return observableOf(data);
|
||||
}
|
||||
|
||||
getEarningPieChartData(): Observable<PieChart[]> {
|
||||
return observableOf(this.pieChartData);
|
||||
}
|
||||
}
|
||||
158
src/app/@core/data/orders-chart.service.ts
Normal file
158
src/app/@core/data/orders-chart.service.ts
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { PeriodsService } from './periods.service';
|
||||
|
||||
export class OrdersChart {
|
||||
chartLabel: string[];
|
||||
linesData: number[][];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class OrdersChartService {
|
||||
|
||||
private year = [
|
||||
'2012',
|
||||
'2013',
|
||||
'2014',
|
||||
'2015',
|
||||
'2016',
|
||||
'2017',
|
||||
'2018',
|
||||
];
|
||||
|
||||
private data = { };
|
||||
|
||||
constructor(private period: PeriodsService) {
|
||||
this.data = {
|
||||
week: this.getDataForWeekPeriod(),
|
||||
month: this.getDataForMonthPeriod(),
|
||||
year: this.getDataForYearPeriod(),
|
||||
};
|
||||
}
|
||||
|
||||
private getDataForWeekPeriod(): OrdersChart {
|
||||
return {
|
||||
chartLabel: this.getDataLabels(42, this.period.getWeeks()),
|
||||
linesData: [
|
||||
[
|
||||
184, 267, 326, 366, 389, 399,
|
||||
392, 371, 340, 304, 265, 227,
|
||||
191, 158, 130, 108, 95, 91, 97,
|
||||
109, 125, 144, 166, 189, 212,
|
||||
236, 259, 280, 300, 316, 329,
|
||||
338, 342, 339, 329, 312, 288,
|
||||
258, 221, 178, 128, 71,
|
||||
],
|
||||
[
|
||||
158, 178, 193, 205, 212, 213,
|
||||
204, 190, 180, 173, 168, 164,
|
||||
162, 160, 159, 158, 159, 166,
|
||||
179, 195, 215, 236, 257, 276,
|
||||
292, 301, 304, 303, 300, 293,
|
||||
284, 273, 262, 251, 241, 234,
|
||||
232, 232, 232, 232, 232, 232,
|
||||
],
|
||||
[
|
||||
58, 137, 202, 251, 288, 312,
|
||||
323, 324, 311, 288, 257, 222,
|
||||
187, 154, 124, 100, 81, 68, 61,
|
||||
58, 61, 69, 80, 96, 115, 137,
|
||||
161, 186, 210, 233, 254, 271,
|
||||
284, 293, 297, 297, 297, 297,
|
||||
297, 297, 297, 297, 297,
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private getDataForMonthPeriod(): OrdersChart {
|
||||
return {
|
||||
chartLabel: this.getDataLabels(47, this.period.getMonths()),
|
||||
linesData: [
|
||||
[
|
||||
5, 63, 113, 156, 194, 225,
|
||||
250, 270, 283, 289, 290,
|
||||
286, 277, 264, 244, 220,
|
||||
194, 171, 157, 151, 150,
|
||||
152, 155, 160, 166, 170,
|
||||
167, 153, 135, 115, 97,
|
||||
82, 71, 64, 63, 62, 61,
|
||||
62, 65, 73, 84, 102,
|
||||
127, 159, 203, 259, 333,
|
||||
],
|
||||
[
|
||||
6, 83, 148, 200, 240,
|
||||
265, 273, 259, 211,
|
||||
122, 55, 30, 28, 36,
|
||||
50, 68, 88, 109, 129,
|
||||
146, 158, 163, 165,
|
||||
173, 187, 208, 236,
|
||||
271, 310, 346, 375,
|
||||
393, 400, 398, 387,
|
||||
368, 341, 309, 275,
|
||||
243, 220, 206, 202,
|
||||
207, 222, 247, 286, 348,
|
||||
],
|
||||
[
|
||||
398, 348, 315, 292, 274,
|
||||
261, 251, 243, 237, 231,
|
||||
222, 209, 192, 172, 152,
|
||||
132, 116, 102, 90, 80, 71,
|
||||
64, 58, 53, 49, 48, 54, 66,
|
||||
84, 104, 125, 142, 156, 166,
|
||||
172, 174, 172, 167, 159, 149,
|
||||
136, 121, 105, 86, 67, 45, 22,
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private getDataForYearPeriod(): OrdersChart {
|
||||
return {
|
||||
chartLabel: this.getDataLabels(42, this.year),
|
||||
linesData: [
|
||||
[
|
||||
190, 269, 327, 366, 389, 398,
|
||||
396, 387, 375, 359, 343, 327,
|
||||
312, 298, 286, 276, 270, 268,
|
||||
265, 258, 247, 234, 220, 204,
|
||||
188, 172, 157, 142, 128, 116,
|
||||
106, 99, 95, 94, 92, 89, 84,
|
||||
77, 69, 60, 49, 36, 22,
|
||||
],
|
||||
[
|
||||
265, 307, 337, 359, 375, 386,
|
||||
393, 397, 399, 397, 390, 379,
|
||||
365, 347, 326, 305, 282, 261,
|
||||
241, 223, 208, 197, 190, 187,
|
||||
185, 181, 172, 160, 145, 126,
|
||||
105, 82, 60, 40, 26, 19, 22,
|
||||
43, 82, 141, 220, 321,
|
||||
],
|
||||
[
|
||||
9, 165, 236, 258, 244, 206,
|
||||
186, 189, 209, 239, 273, 307,
|
||||
339, 365, 385, 396, 398, 385,
|
||||
351, 300, 255, 221, 197, 181,
|
||||
170, 164, 162, 161, 159, 154,
|
||||
146, 135, 122, 108, 96, 87,
|
||||
83, 82, 82, 82, 82, 82, 82,
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
getDataLabels(nPoints: number, labelsArray: string[]): string[] {
|
||||
const labelsArrayLength = labelsArray.length;
|
||||
const step = Math.round(nPoints / labelsArrayLength);
|
||||
|
||||
return Array.from(Array(nPoints)).map((item, index) => {
|
||||
const dataIndex = Math.round(index / step);
|
||||
|
||||
return index % step === 0 ? labelsArray[dataIndex] : '';
|
||||
});
|
||||
}
|
||||
|
||||
getOrdersChartData(period: string): OrdersChart {
|
||||
return this.data[period];
|
||||
}
|
||||
}
|
||||
48
src/app/@core/data/orders-profit-chart.service.ts
Normal file
48
src/app/@core/data/orders-profit-chart.service.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { of as observableOf, Observable } from 'rxjs';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OrdersChart, OrdersChartService } from './orders-chart.service';
|
||||
import { ProfitChart, ProfitChartService } from './profit-chart.service';
|
||||
|
||||
export class OrderProfitChartSummary {
|
||||
title: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class OrdersProfitChartService {
|
||||
|
||||
private summary = [
|
||||
{
|
||||
title: 'Marketplace',
|
||||
value: 3654,
|
||||
},
|
||||
{
|
||||
title: 'Last Month',
|
||||
value: 946,
|
||||
},
|
||||
{
|
||||
title: 'Last Week',
|
||||
value: 654,
|
||||
},
|
||||
{
|
||||
title: 'Today',
|
||||
value: 230,
|
||||
},
|
||||
];
|
||||
|
||||
constructor(private ordersChartService: OrdersChartService,
|
||||
private profitChartService: ProfitChartService) {
|
||||
}
|
||||
|
||||
getOrderProfitChartSummary(): Observable<OrderProfitChartSummary[]> {
|
||||
return observableOf(this.summary);
|
||||
}
|
||||
|
||||
getOrdersChartData(period: string): Observable<OrdersChart> {
|
||||
return observableOf(this.ordersChartService.getOrdersChartData(period));
|
||||
}
|
||||
|
||||
getProfitChartData(period: string): Observable<ProfitChart> {
|
||||
return observableOf(this.profitChartService.getProfitChartData(period));
|
||||
}
|
||||
}
|
||||
33
src/app/@core/data/periods.service.ts
Normal file
33
src/app/@core/data/periods.service.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class PeriodsService {
|
||||
getYears() {
|
||||
return [
|
||||
'2010', '2011', '2012',
|
||||
'2013', '2014', '2015',
|
||||
'2016', '2017', '2018',
|
||||
];
|
||||
}
|
||||
|
||||
getMonths() {
|
||||
return [
|
||||
'Jan', 'Feb', 'Mar',
|
||||
'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep',
|
||||
'Oct', 'Nov', 'Dec',
|
||||
];
|
||||
}
|
||||
|
||||
getWeeks() {
|
||||
return [
|
||||
'Mon',
|
||||
'Tue',
|
||||
'Wed',
|
||||
'Thu',
|
||||
'Fri',
|
||||
'Sat',
|
||||
'Sun',
|
||||
];
|
||||
}
|
||||
}
|
||||
41
src/app/@core/data/profit-bar-animation-chart.service.ts
Normal file
41
src/app/@core/data/profit-bar-animation-chart.service.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { of as observableOf, Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class ProfitBarAnimationChartService {
|
||||
|
||||
private data: any;
|
||||
|
||||
constructor() {
|
||||
this.data = {
|
||||
firstLine: this.getDataForFirstLine(),
|
||||
secondLine: this.getDataForSecondLine(),
|
||||
};
|
||||
}
|
||||
|
||||
getDataForFirstLine(): number[] {
|
||||
return this.createEmptyArray(100)
|
||||
.map((_, index) => {
|
||||
const oneFifth = index / 5;
|
||||
|
||||
return (Math.sin(oneFifth) * (oneFifth - 10) + index / 6) * 5;
|
||||
});
|
||||
}
|
||||
|
||||
getDataForSecondLine(): number[] {
|
||||
return this.createEmptyArray(100)
|
||||
.map((_, index) => {
|
||||
const oneFifth = index / 5;
|
||||
|
||||
return (Math.cos(oneFifth) * (oneFifth - 10) + index / 6) * 5;
|
||||
});
|
||||
}
|
||||
|
||||
createEmptyArray(nPoints: number) {
|
||||
return Array.from(Array(nPoints));
|
||||
}
|
||||
|
||||
getChartData(): Observable<{ firstLine: number[]; secondLine: number[] }> {
|
||||
return observableOf(this.data);
|
||||
}
|
||||
}
|
||||
80
src/app/@core/data/profit-chart.service.ts
Normal file
80
src/app/@core/data/profit-chart.service.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { PeriodsService } from './periods.service';
|
||||
|
||||
export class ProfitChart {
|
||||
chartLabel: string[];
|
||||
data: number[][];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ProfitChartService {
|
||||
|
||||
private year = [
|
||||
'2012',
|
||||
'2013',
|
||||
'2014',
|
||||
'2015',
|
||||
'2016',
|
||||
'2017',
|
||||
'2018',
|
||||
];
|
||||
|
||||
private data = { };
|
||||
|
||||
constructor(private period: PeriodsService) {
|
||||
this.data = {
|
||||
week: this.getDataForWeekPeriod(),
|
||||
month: this.getDataForMonthPeriod(),
|
||||
year: this.getDataForYearPeriod(),
|
||||
};
|
||||
}
|
||||
|
||||
private getDataForWeekPeriod(): ProfitChart {
|
||||
const nPoint = this.period.getWeeks().length;
|
||||
|
||||
return {
|
||||
chartLabel: this.period.getWeeks(),
|
||||
data: [
|
||||
this.getRandomData(nPoint),
|
||||
this.getRandomData(nPoint),
|
||||
this.getRandomData(nPoint),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private getDataForMonthPeriod(): ProfitChart {
|
||||
const nPoint = this.period.getMonths().length;
|
||||
|
||||
return {
|
||||
chartLabel: this.period.getMonths(),
|
||||
data: [
|
||||
this.getRandomData(nPoint),
|
||||
this.getRandomData(nPoint),
|
||||
this.getRandomData(nPoint),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private getDataForYearPeriod(): ProfitChart {
|
||||
const nPoint = this.year.length;
|
||||
|
||||
return {
|
||||
chartLabel: this.year,
|
||||
data: [
|
||||
this.getRandomData(nPoint),
|
||||
this.getRandomData(nPoint),
|
||||
this.getRandomData(nPoint),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private getRandomData(nPoints: number): number[] {
|
||||
return Array.from(Array(nPoints)).map(() => {
|
||||
return Math.round(Math.random() * 500);
|
||||
});
|
||||
}
|
||||
|
||||
getProfitChartData(period: string): ProfitChart {
|
||||
return this.data[period];
|
||||
}
|
||||
}
|
||||
51
src/app/@core/data/traffic-bar.service.ts
Normal file
51
src/app/@core/data/traffic-bar.service.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { of as observableOf, Observable } from 'rxjs';
|
||||
import { PeriodsService } from './periods.service';
|
||||
|
||||
export class TrafficBar {
|
||||
data: number[];
|
||||
labels: string[];
|
||||
formatter: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TrafficBarService {
|
||||
|
||||
private data = { };
|
||||
|
||||
constructor(private period: PeriodsService) {
|
||||
this.data = {
|
||||
week: this.getDataForWeekPeriod(),
|
||||
month: this.getDataForMonthPeriod(),
|
||||
year: this.getDataForYearPeriod(),
|
||||
};
|
||||
}
|
||||
|
||||
getDataForWeekPeriod(): TrafficBar {
|
||||
return {
|
||||
data: [10, 15, 19, 7, 20, 13, 15],
|
||||
labels: this.period.getWeeks(),
|
||||
formatter: '{c0} MB',
|
||||
};
|
||||
}
|
||||
|
||||
getDataForMonthPeriod(): TrafficBar {
|
||||
return {
|
||||
data: [0.5, 0.3, 0.8, 0.2, 0.3, 0.7, 0.8, 1, 0.7, 0.8, 0.6, 0.7],
|
||||
labels: this.period.getMonths(),
|
||||
formatter: '{c0} GB',
|
||||
};
|
||||
}
|
||||
|
||||
getDataForYearPeriod(): TrafficBar {
|
||||
return {
|
||||
data: [10, 15, 19, 7, 20, 13, 15, 19, 11],
|
||||
labels: this.period.getYears(),
|
||||
formatter: '{c0} GB',
|
||||
};
|
||||
}
|
||||
|
||||
getTrafficBarData(period: string): Observable<TrafficBar> {
|
||||
return observableOf(this.data[period]);
|
||||
}
|
||||
}
|
||||
100
src/app/@core/data/traffic-list.service.ts
Normal file
100
src/app/@core/data/traffic-list.service.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { of as observableOf, Observable } from 'rxjs';
|
||||
import { PeriodsService } from './periods.service';
|
||||
|
||||
export class TrafficList {
|
||||
date: string;
|
||||
value: number;
|
||||
delta: {
|
||||
up: boolean;
|
||||
value: number;
|
||||
};
|
||||
comparison: {
|
||||
prevDate: string;
|
||||
prevValue: number;
|
||||
nextDate: string;
|
||||
nextValue: number;
|
||||
};
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TrafficListService {
|
||||
|
||||
private getRandom = (roundTo: number) => Math.round(Math.random() * roundTo);
|
||||
private data = {};
|
||||
|
||||
constructor(private period: PeriodsService) {
|
||||
this.data = {
|
||||
week: this.getDataWeek(),
|
||||
month: this.getDataMonth(),
|
||||
year: this.getDataYear(),
|
||||
};
|
||||
}
|
||||
|
||||
private getDataWeek(): TrafficList[] {
|
||||
const getFirstDateInPeriod = () => {
|
||||
const weeks = this.period.getWeeks();
|
||||
|
||||
return weeks[weeks.length - 1];
|
||||
};
|
||||
|
||||
return this.reduceData(this.period.getWeeks(), getFirstDateInPeriod);
|
||||
}
|
||||
|
||||
private getDataMonth(): TrafficList[] {
|
||||
const getFirstDateInPeriod = () => {
|
||||
const months = this.period.getMonths();
|
||||
const date = new Date();
|
||||
const prevYear = date.getFullYear() - 1;
|
||||
|
||||
return `${months[months.length - 1]}, ${prevYear}`;
|
||||
};
|
||||
|
||||
return this.reduceData(this.period.getMonths(), getFirstDateInPeriod);
|
||||
}
|
||||
|
||||
private getDataYear(): TrafficList[] {
|
||||
const getFirstDateInPeriod = () => {
|
||||
const years = this.period.getYears();
|
||||
|
||||
return `${parseInt(years[0], 10) - 1}`;
|
||||
};
|
||||
|
||||
return this.reduceData(this.period.getYears(), getFirstDateInPeriod);
|
||||
}
|
||||
|
||||
private reduceData(timePeriods: string[], getFirstDateInPeriod: () => string): TrafficList[] {
|
||||
return timePeriods.reduce((result, timePeriod, index) => {
|
||||
const hasResult = result[index - 1];
|
||||
const prevDate = hasResult ?
|
||||
result[index - 1].comparison.nextDate :
|
||||
getFirstDateInPeriod();
|
||||
const prevValue = hasResult ?
|
||||
result[index - 1].comparison.nextValue :
|
||||
this.getRandom(100);
|
||||
const nextValue = this.getRandom(100);
|
||||
const deltaValue = prevValue - nextValue;
|
||||
|
||||
const item = {
|
||||
date: timePeriod,
|
||||
value: this.getRandom(1000),
|
||||
delta: {
|
||||
up: deltaValue <= 0,
|
||||
value: Math.abs(deltaValue),
|
||||
},
|
||||
comparison: {
|
||||
prevDate,
|
||||
prevValue,
|
||||
nextDate: timePeriod,
|
||||
nextValue,
|
||||
},
|
||||
};
|
||||
|
||||
return [...result, item];
|
||||
}, []);
|
||||
}
|
||||
|
||||
getTrafficListData(period: string): Observable<TrafficList> {
|
||||
return observableOf(this.data[period]);
|
||||
}
|
||||
}
|
||||
67
src/app/@core/data/user-activity.service.ts
Normal file
67
src/app/@core/data/user-activity.service.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { of as observableOf, Observable } from 'rxjs';
|
||||
import { PeriodsService } from './periods.service';
|
||||
|
||||
export class UserActive {
|
||||
date: string;
|
||||
pagesVisitCount: number;
|
||||
deltaUp: boolean;
|
||||
newVisits: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UserActivityService {
|
||||
|
||||
private getRandom = (roundTo: number) => Math.round(Math.random() * roundTo);
|
||||
|
||||
data = {};
|
||||
|
||||
constructor(private periods: PeriodsService) {
|
||||
this.data = {
|
||||
week: this.getDataWeek(),
|
||||
month: this.getDataMonth(),
|
||||
year: this.getDataYear(),
|
||||
};
|
||||
}
|
||||
|
||||
private getDataWeek(): UserActive[] {
|
||||
return this.periods.getWeeks().map((week) => {
|
||||
return {
|
||||
date: week,
|
||||
pagesVisitCount: this.getRandom(1000),
|
||||
deltaUp: this.getRandom(1) % 2 === 0,
|
||||
newVisits: this.getRandom(100),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private getDataMonth(): UserActive[] {
|
||||
const date = new Date();
|
||||
const days = date.getDate();
|
||||
const month = this.periods.getMonths()[date.getMonth()];
|
||||
|
||||
return Array.from(Array(days)).map((_, index) => {
|
||||
return {
|
||||
date: `${index + 1} ${month}`,
|
||||
pagesVisitCount: this.getRandom(1000),
|
||||
deltaUp: this.getRandom(1) % 2 === 0,
|
||||
newVisits: this.getRandom(100),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private getDataYear(): UserActive[] {
|
||||
return this.periods.getYears().map((year) => {
|
||||
return {
|
||||
date: year,
|
||||
pagesVisitCount: this.getRandom(1000),
|
||||
deltaUp: this.getRandom(1) % 2 === 0,
|
||||
newVisits: this.getRandom(100),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getUserActivityData(period: string): Observable<UserActive[]> {
|
||||
return observableOf(this.data[period]);
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +105,28 @@
|
|||
}
|
||||
}
|
||||
|
||||
/deep/ nb-menu {
|
||||
& > .menu-items {
|
||||
& > .menu-item:first-child {
|
||||
.menu-title {
|
||||
&::after {
|
||||
content: 'new';
|
||||
color: nb-theme(color-white);
|
||||
margin-left: 1rem;
|
||||
background: nb-theme(color-danger);
|
||||
padding: 0 0.5rem;
|
||||
border-radius: nb-theme(radius);
|
||||
font-size: nb-theme(font-size-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nb-e-commerce {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.compacted {
|
||||
|
||||
/deep/ nb-sidebar-header {
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ export * from './capitalize.pipe';
|
|||
export * from './plural.pipe';
|
||||
export * from './round.pipe';
|
||||
export * from './timing.pipe';
|
||||
export * from './number-with-commas.pipe';
|
||||
|
|
|
|||
9
src/app/@theme/pipes/number-with-commas.pipe.ts
Normal file
9
src/app/@theme/pipes/number-with-commas.pipe.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({ name: 'ngxNumberWithCommas' })
|
||||
export class NumberWithCommasPipe implements PipeTransform {
|
||||
|
||||
transform(input: number): string {
|
||||
return new Intl.NumberFormat().format(input);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,14 +18,14 @@ export const CORPORATE_THEME = {
|
|||
},
|
||||
|
||||
traffic: {
|
||||
colorBlack: '#000000',
|
||||
colorBlack: '#ffffff',
|
||||
tooltipBg: '#eef2f5',
|
||||
tooltipBorderColor: '#eef2f5',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: '400',
|
||||
|
||||
lineBg: '#c0c8d1',
|
||||
lineBg: '#cae6f3',
|
||||
lineShadowBlur: '0',
|
||||
itemColor: '#bcc3cc',
|
||||
itemBorderColor: '#bcc3cc',
|
||||
|
|
@ -68,6 +68,62 @@ export const CORPORATE_THEME = {
|
|||
areaBorderColor: '#ebeef2',
|
||||
},
|
||||
|
||||
profitBarAnimationEchart: {
|
||||
textColor: '#b2bac2',
|
||||
|
||||
firstAnimationBarColor: '#719efc',
|
||||
secondAnimationBarColor: '#5dcfe3',
|
||||
|
||||
splitLineStyleOpacity: '0.06',
|
||||
splitLineStyleWidth: '1',
|
||||
splitLineStyleColor: '#000000',
|
||||
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: '400',
|
||||
tooltipFontSize: '16',
|
||||
tooltipBg: '#eef2f5',
|
||||
tooltipBorderColor: '#eef2f5',
|
||||
tooltipBorderWidth: '3',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
|
||||
},
|
||||
|
||||
trafficBarEchart: {
|
||||
gradientFrom: '#ff8ea0',
|
||||
gradientTo: '#ffa36b',
|
||||
shadow: 'rgba(0, 0, 0, 0)',
|
||||
shadowBlur: '0',
|
||||
|
||||
axisTextColor: '#b2bac2',
|
||||
axisFontSize: '12',
|
||||
|
||||
tooltipBg: '#edf0f4',
|
||||
tooltipBorderColor: '#ebeef2',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
},
|
||||
|
||||
countryOrders: {
|
||||
countryBorderColor: 'rgba(255, 255, 255, 1)',
|
||||
countryFillColor: 'rgba(236, 242, 245, 1)',
|
||||
countryBorderWidth: '1',
|
||||
hoveredCountryBorderColor: 'rgba(113, 158, 252, 1)',
|
||||
hoveredCountryFillColor: 'rgba(199, 216, 247, 1)',
|
||||
hoveredCountryBorderWidth: '3',
|
||||
|
||||
chartAxisLineColor: 'rgba(0, 0, 0, 0)',
|
||||
chartAxisTextColor: '#b2bac2',
|
||||
chartAxisFontSize: '16',
|
||||
chartGradientTo: 'rgba(113, 158, 252, 1)',
|
||||
chartGradientFrom: 'rgba(113, 158, 252, 1)',
|
||||
chartAxisSplitLine: '#ebeef2',
|
||||
chartShadowLineColor: '#2f296b',
|
||||
|
||||
chartLineBottomShadowColor: 'rgba(113, 158, 252, 1)',
|
||||
|
||||
chartInnerLineColor: '#eceff4',
|
||||
},
|
||||
|
||||
echarts: {
|
||||
bg: '#ffffff',
|
||||
textColor: '#484848',
|
||||
|
|
@ -82,5 +138,167 @@ export const CORPORATE_THEME = {
|
|||
axisLineColor: '#cccccc',
|
||||
textColor: '#484848',
|
||||
},
|
||||
|
||||
orders: {
|
||||
tooltipBg: '#ffffff',
|
||||
tooltipLineColor: 'rgba(0, 0, 0, 0)',
|
||||
tooltipLineWidth: '0',
|
||||
tooltipBorderColor: '#ebeef2',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
tooltipFontSize: '20',
|
||||
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#b2bac2',
|
||||
yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
|
||||
|
||||
itemBorderColor: '#73a1ff',
|
||||
lineStyle: 'solid',
|
||||
lineWidth: '4',
|
||||
|
||||
// first line
|
||||
firstAreaGradFrom: 'rgba(227, 236, 254, 0.7)',
|
||||
firstAreaGradTo: 'rgba(227, 236, 254, 0.7)',
|
||||
firstShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
// second line
|
||||
secondLineGradFrom: 'rgba(93, 207, 227, 1)',
|
||||
secondLineGradTo: 'rgba(93, 207, 227, 1)',
|
||||
|
||||
secondAreaGradFrom: 'rgba(0, 0, 0, 0)',
|
||||
secondAreaGradTo: 'rgba(0, 0, 0, 0)',
|
||||
secondShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
// third line
|
||||
thirdLineGradFrom: 'rgba(113, 158, 252, 1)',
|
||||
thirdLineGradTo: 'rgba(113, 158, 252, 1)',
|
||||
|
||||
thirdAreaGradFrom: 'rgba(0, 0, 0, 0)',
|
||||
thirdAreaGradTo: 'rgba(0, 0, 0, 0)',
|
||||
thirdShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
|
||||
profit: {
|
||||
bg: '#ffffff',
|
||||
textColor: '#ffffff',
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
splitLineColor: 'rgba(161, 161 ,229, 0.2)',
|
||||
areaOpacity: '1',
|
||||
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#b2bac2',
|
||||
|
||||
// first bar
|
||||
firstLineGradFrom: '#719efc',
|
||||
firstLineGradTo: '#719efc',
|
||||
firstLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
|
||||
// second bar
|
||||
secondLineGradFrom: '#5dcfe3',
|
||||
secondLineGradTo: '#5dcfe3',
|
||||
secondLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
|
||||
// third bar
|
||||
thirdLineGradFrom: '#e3ecfe',
|
||||
thirdLineGradTo: '#e3ecfe',
|
||||
thirdLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
},
|
||||
|
||||
orderProfitLegend: {
|
||||
firstItem: '#719efc',
|
||||
secondItem: '#5dcfe3',
|
||||
thirdItem: '#e3ecfe',
|
||||
},
|
||||
|
||||
visitors: {
|
||||
tooltipBg: '#ffffff',
|
||||
tooltipLineColor: 'rgba(0, 0, 0, 0)',
|
||||
tooltipLineWidth: '0',
|
||||
tooltipBorderColor: '#ebeef2',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
tooltipFontSize: '20',
|
||||
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#b2bac2',
|
||||
yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
|
||||
|
||||
itemBorderColor: '#73a1ff',
|
||||
lineStyle: 'dotted',
|
||||
lineWidth: '6',
|
||||
lineGradFrom: '#73a1ff',
|
||||
lineGradTo: '#73a1ff',
|
||||
lineShadow: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
areaGradFrom: 'rgba(146, 181, 252, 1)',
|
||||
areaGradTo: 'rgba(146, 181, 252, 1)',
|
||||
shadowLineDarkBg: '#a695ff',
|
||||
|
||||
innerLineStyle: 'solid',
|
||||
innerLineWidth: '1',
|
||||
|
||||
innerAreaGradFrom: 'rgba(227, 236, 254, 1)',
|
||||
innerAreaGradTo: 'rgba(227, 236, 254, 1)',
|
||||
},
|
||||
|
||||
visitorsLegend: {
|
||||
firstIcon: '#e3ecfe',
|
||||
secondIcon: '#719efc',
|
||||
},
|
||||
|
||||
visitorsPie: {
|
||||
firstPieGradientLeft: '#94e2ed',
|
||||
firstPieGradientRight: '#94e2ed',
|
||||
firstPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
firstPieRadius: ['65%', '90%'],
|
||||
|
||||
secondPieGradientLeft: '#719efc',
|
||||
secondPieGradientRight: '#719efc',
|
||||
secondPieShadowColor: '#b2cafb',
|
||||
secondPieRadius: ['63%', '92%'],
|
||||
shadowOffsetX: '-4',
|
||||
shadowOffsetY: '-4',
|
||||
},
|
||||
|
||||
visitorsPieLegend: {
|
||||
firstSection: '#719efc',
|
||||
secondSection: '#99e5ee',
|
||||
},
|
||||
|
||||
earningPie: {
|
||||
radius: ['65%', '100%'],
|
||||
center: ['50%', '50%'],
|
||||
|
||||
fontSize: '22',
|
||||
|
||||
firstPieGradientLeft: '#719efc',
|
||||
firstPieGradientRight: '#719efc',
|
||||
firstPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
secondPieGradientLeft: '#ff9f6f',
|
||||
secondPieGradientRight: '#ff9f6f',
|
||||
secondPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
thirdPieGradientLeft: '#ff5e83',
|
||||
thirdPieGradientRight: '#ff5e83',
|
||||
thirdPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
|
||||
earningLine: {
|
||||
gradFrom: '#e3ecfe',
|
||||
gradTo: '#e3ecfe',
|
||||
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: '400',
|
||||
tooltipFontSize: '16',
|
||||
tooltipBg: '#eef2f5',
|
||||
tooltipBorderColor: '#eef2f5',
|
||||
tooltipBorderWidth: '3',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -69,6 +69,62 @@ export const COSMIC_THEME = {
|
|||
areaBorderColor: '#654ddb',
|
||||
},
|
||||
|
||||
profitBarAnimationEchart: {
|
||||
textColor: '#ffffff',
|
||||
|
||||
firstAnimationBarColor: '#0088ff',
|
||||
secondAnimationBarColor: '#7659ff',
|
||||
|
||||
splitLineStyleOpacity: '0.06',
|
||||
splitLineStyleWidth: '1',
|
||||
splitLineStyleColor: '#000000',
|
||||
|
||||
tooltipTextColor: '#ffffff',
|
||||
tooltipFontWeight: 'normal',
|
||||
tooltipFontSize: '16',
|
||||
tooltipBg: 'rgba(0, 255, 170, 0.35)',
|
||||
tooltipBorderColor: '#00d977',
|
||||
tooltipBorderWidth: '3',
|
||||
tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;',
|
||||
},
|
||||
|
||||
trafficBarEchart: {
|
||||
gradientFrom: '#fc0',
|
||||
gradientTo: '#ffa100',
|
||||
shadow: '#ffb600',
|
||||
shadowBlur: '5',
|
||||
|
||||
axisTextColor: '#a1a1e5',
|
||||
axisFontSize: '12',
|
||||
|
||||
tooltipBg: 'rgba(0, 255, 170, 0.35)',
|
||||
tooltipBorderColor: '#00d977',
|
||||
tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;',
|
||||
tooltipTextColor: '#ffffff',
|
||||
tooltipFontWeight: 'normal',
|
||||
},
|
||||
|
||||
countryOrders: {
|
||||
countryBorderColor: '#525dbd',
|
||||
countryFillColor: '#4f41a6',
|
||||
countryBorderWidth: '2',
|
||||
hoveredCountryBorderColor: '#00f9a6',
|
||||
hoveredCountryFillColor: '#377aa7',
|
||||
hoveredCountryBorderWidth: '3',
|
||||
|
||||
chartAxisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
chartAxisTextColor: '#a1a1e5',
|
||||
chartAxisFontSize: '16',
|
||||
chartGradientTo: '#00c7c7',
|
||||
chartGradientFrom: '#00d977',
|
||||
chartAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
|
||||
chartShadowBarColor: '#2f296b',
|
||||
|
||||
chartLineBottomShadowColor: '#00977e',
|
||||
|
||||
chartInnerLineColor: '#2f296b',
|
||||
},
|
||||
|
||||
echarts: {
|
||||
bg: '#3d3780',
|
||||
textColor: '#ffffff',
|
||||
|
|
@ -83,5 +139,166 @@ export const COSMIC_THEME = {
|
|||
axisLineColor: '#a1a1e5',
|
||||
textColor: '#ffffff',
|
||||
},
|
||||
|
||||
orders: {
|
||||
tooltipBg: 'rgba(0, 255, 170, 0.35)',
|
||||
tooltipLineColor: 'rgba(255, 255, 255, 0.1)',
|
||||
tooltipLineWidth: '1',
|
||||
tooltipBorderColor: '#00d977',
|
||||
tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: '#ffffff',
|
||||
tooltipFontWeight: 'normal',
|
||||
tooltipFontSize: '20',
|
||||
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#a1a1e5',
|
||||
yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
|
||||
|
||||
itemBorderColor: '#ffffff',
|
||||
lineStyle: 'solid',
|
||||
lineWidth: '4',
|
||||
|
||||
// first line
|
||||
firstAreaGradFrom: 'rgba(78, 64, 164, 1)',
|
||||
firstAreaGradTo: 'rgba(78, 64, 164, 1)',
|
||||
firstShadowLineDarkBg: '#018dff',
|
||||
|
||||
// second line
|
||||
secondLineGradFrom: '#00bece',
|
||||
secondLineGradTo: '#00da78',
|
||||
|
||||
secondAreaGradFrom: 'rgba(38, 139, 145, 0.8)',
|
||||
secondAreaGradTo: 'rgba(38, 139, 145, 0.5)',
|
||||
secondShadowLineDarkBg: '#2c5a85',
|
||||
|
||||
// third line
|
||||
thirdLineGradFrom: '#8069ff',
|
||||
thirdLineGradTo: '#8357ff',
|
||||
|
||||
thirdAreaGradFrom: 'rgba(118, 73, 208, 0.7)',
|
||||
thirdAreaGradTo: 'rgba(188, 92, 255, 0.4)',
|
||||
thirdShadowLineDarkBg: '#a695ff',
|
||||
},
|
||||
|
||||
profit: {
|
||||
bg: '#3d3780',
|
||||
textColor: '#ffffff',
|
||||
axisLineColor: '#a1a1e5',
|
||||
splitLineColor: '#342e73',
|
||||
areaOpacity: '1',
|
||||
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#a1a1e5',
|
||||
|
||||
// first bar
|
||||
firstLineGradFrom: '#00bece',
|
||||
firstLineGradTo: '#00da78',
|
||||
firstLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
|
||||
// second bar
|
||||
secondLineGradFrom: '#8069ff',
|
||||
secondLineGradTo: '#8357ff',
|
||||
secondLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
|
||||
// third bar
|
||||
thirdLineGradFrom: '#4e40a4',
|
||||
thirdLineGradTo: '#4e40a4',
|
||||
thirdLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
},
|
||||
|
||||
orderProfitLegend: {
|
||||
firstItem: 'linear-gradient(90deg, #00c7c7 0%, #00d977 100%)',
|
||||
secondItem: 'linear-gradient(90deg, #a454ff 0%, #7659ff 100%)',
|
||||
thirdItem: '#4e40a4',
|
||||
},
|
||||
|
||||
visitors: {
|
||||
tooltipBg: 'rgba(0, 255, 170, 0.35)',
|
||||
tooltipLineColor: 'rgba(255, 255, 255, 0.1)',
|
||||
tooltipLineWidth: '1',
|
||||
tooltipBorderColor: '#00d977',
|
||||
tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: '#ffffff',
|
||||
tooltipFontWeight: 'normal',
|
||||
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#a1a1e5',
|
||||
yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
|
||||
|
||||
itemBorderColor: '#ffffff',
|
||||
lineStyle: 'dotted',
|
||||
lineWidth: '6',
|
||||
lineGradFrom: '#ffffff',
|
||||
lineGradTo: '#ffffff',
|
||||
lineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
|
||||
areaGradFrom: 'rgba(188, 92, 255, 1)',
|
||||
areaGradTo: 'rgba(188, 92, 255, 0.5)',
|
||||
shadowLineDarkBg: '#a695ff',
|
||||
|
||||
innerLineStyle: 'solid',
|
||||
innerLineWidth: '1',
|
||||
|
||||
innerAreaGradFrom: 'rgba(59, 165, 243, 1)',
|
||||
innerAreaGradTo: 'rgba(4, 133, 243 , 1)',
|
||||
},
|
||||
|
||||
visitorsLegend: {
|
||||
firstIcon: 'linear-gradient(90deg, #0088ff 0%, #3dafff 100%)',
|
||||
secondIcon: 'linear-gradient(90deg, #a454ff 0%, #7659ff 100%)',
|
||||
},
|
||||
|
||||
visitorsPie: {
|
||||
firstPieGradientLeft: '#7bff24',
|
||||
firstPieGradientRight: '#2ec7fe',
|
||||
firstPieShadowColor: '#19977E',
|
||||
firstPieRadius: ['70%', '90%'],
|
||||
|
||||
secondPieGradientLeft: '#ff894a',
|
||||
secondPieGradientRight: '#ffcc10',
|
||||
secondPieShadowColor: '#cf7c1c',
|
||||
secondPieRadius: ['60%', '95%'],
|
||||
shadowOffsetX: '0',
|
||||
shadowOffsetY: '3',
|
||||
},
|
||||
|
||||
visitorsPieLegend: {
|
||||
firstSection: 'linear-gradient(90deg, #ffcb17 0%, #ff874c 100%)',
|
||||
secondSection: 'linear-gradient(90deg, #00c7c7 0%, #00d977 100%)',
|
||||
},
|
||||
|
||||
earningPie: {
|
||||
radius: ['65%', '100%'],
|
||||
center: ['50%', '50%'],
|
||||
|
||||
fontSize: '22',
|
||||
|
||||
firstPieGradientLeft: '#00d77f',
|
||||
firstPieGradientRight: '#00d77f',
|
||||
firstPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
secondPieGradientLeft: '#7756f7',
|
||||
secondPieGradientRight: '#7756f7',
|
||||
secondPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
thirdPieGradientLeft: '#ffca00',
|
||||
thirdPieGradientRight: '#ffca00',
|
||||
thirdPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
|
||||
earningLine: {
|
||||
gradFrom: 'rgba(118, 89, 255, 0.4)',
|
||||
gradTo: 'rgba(164, 84, 255, 0.5)',
|
||||
|
||||
tooltipTextColor: '#ffffff',
|
||||
tooltipFontWeight: 'normal',
|
||||
tooltipFontSize: '16',
|
||||
tooltipBg: 'rgba(0, 255, 170, 0.35)',
|
||||
tooltipBorderColor: '#00d977',
|
||||
tooltipBorderWidth: '3',
|
||||
tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -70,6 +70,62 @@ export const DEFAULT_THEME = {
|
|||
areaBorderColor: '#ebeef2',
|
||||
},
|
||||
|
||||
profitBarAnimationEchart: {
|
||||
textColor: '#484848',
|
||||
|
||||
firstAnimationBarColor: '#3edd81',
|
||||
secondAnimationBarColor: '#8d7fff',
|
||||
|
||||
splitLineStyleOpacity: '0.06',
|
||||
splitLineStyleWidth: '1',
|
||||
splitLineStyleColor: '#000000',
|
||||
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
tooltipFontSize: '16',
|
||||
tooltipBg: '#ffffff',
|
||||
tooltipBorderColor: '#c0c8d1',
|
||||
tooltipBorderWidth: '3',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
|
||||
},
|
||||
|
||||
trafficBarEchart: {
|
||||
gradientFrom: '#fc0',
|
||||
gradientTo: '#ffa100',
|
||||
shadow: '#ffb600',
|
||||
shadowBlur: '0',
|
||||
|
||||
axisTextColor: '#b2bac2',
|
||||
axisFontSize: '12',
|
||||
|
||||
tooltipBg: '#ffffff',
|
||||
tooltipBorderColor: '#c0c8d1',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
},
|
||||
|
||||
countryOrders: {
|
||||
countryBorderColor: 'rgba(255, 255, 255, 1)',
|
||||
countryFillColor: 'rgba(236, 242, 245, 1)',
|
||||
countryBorderWidth: '1',
|
||||
hoveredCountryBorderColor: '#40dc7e',
|
||||
hoveredCountryFillColor: '#c7f4d9',
|
||||
hoveredCountryBorderWidth: '3',
|
||||
|
||||
chartAxisLineColor: 'rgba(0, 0, 0, 0)',
|
||||
chartAxisTextColor: '#b2bac2',
|
||||
chartAxisFontSize: '16',
|
||||
chartGradientTo: '#3edd81',
|
||||
chartGradientFrom: '#3bddaf',
|
||||
chartAxisSplitLine: '#ebeef2',
|
||||
chartShadowLineColor: '#2f296b',
|
||||
|
||||
chartLineBottomShadowColor: '#eceff4',
|
||||
|
||||
chartInnerLineColor: '#eceff4',
|
||||
},
|
||||
|
||||
echarts: {
|
||||
bg: '#ffffff',
|
||||
textColor: '#484848',
|
||||
|
|
@ -84,5 +140,168 @@ export const DEFAULT_THEME = {
|
|||
axisLineColor: '#cccccc',
|
||||
textColor: '#484848',
|
||||
},
|
||||
|
||||
orders: {
|
||||
tooltipBg: '#ffffff',
|
||||
tooltipLineColor: 'rgba(0, 0, 0, 0)',
|
||||
tooltipLineWidth: '0',
|
||||
tooltipBorderColor: '#ebeef2',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
tooltipFontSize: '20',
|
||||
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#b2bac2',
|
||||
yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
|
||||
|
||||
itemBorderColor: '#42db7d',
|
||||
lineStyle: 'solid',
|
||||
lineWidth: '4',
|
||||
|
||||
// first line
|
||||
firstAreaGradFrom: 'rgba(236, 242, 245, 0.8)',
|
||||
firstAreaGradTo: 'rgba(236, 242, 245, 0.8)',
|
||||
firstShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
// second line
|
||||
secondLineGradFrom: 'rgba(164, 123, 255, 1)',
|
||||
secondLineGradTo: 'rgba(164, 123, 255, 1)',
|
||||
|
||||
secondAreaGradFrom: 'rgba(188, 92, 255, 0.2)',
|
||||
secondAreaGradTo: 'rgba(188, 92, 255, 0)',
|
||||
secondShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
// third line
|
||||
thirdLineGradFrom: 'rgba(55, 220, 136, 1)',
|
||||
thirdLineGradTo: 'rgba(55, 220, 136, 1)',
|
||||
|
||||
thirdAreaGradFrom: 'rgba(31 ,106, 124, 0.2)',
|
||||
thirdAreaGradTo: 'rgba(4, 126, 230, 0)',
|
||||
thirdShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
|
||||
// TODO: need design for default theme
|
||||
profit: {
|
||||
bg: '#ffffff',
|
||||
textColor: '#ffffff',
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
splitLineColor: 'rgba(161, 161 ,229, 0.2)',
|
||||
areaOpacity: '1',
|
||||
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#b2bac2',
|
||||
|
||||
// first bar
|
||||
firstLineGradFrom: '#00bece',
|
||||
firstLineGradTo: '#00da78',
|
||||
firstLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
|
||||
// second bar
|
||||
secondLineGradFrom: '#8069ff',
|
||||
secondLineGradTo: '#8357ff',
|
||||
secondLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
|
||||
// third bar
|
||||
thirdLineGradFrom: 'rgba(236, 242, 245, 0.8)',
|
||||
thirdLineGradTo: 'rgba(236, 242, 245, 0.8)',
|
||||
thirdLineShadow: 'rgba(14, 16, 48, 0.4)',
|
||||
},
|
||||
|
||||
orderProfitLegend: {
|
||||
firstItem: 'linear-gradient(90deg, #3edd81 0%, #3bddad 100%)',
|
||||
secondItem: 'linear-gradient(90deg, #8d7fff 0%, #b17fff 100%)',
|
||||
thirdItem: 'rgba(236, 242, 245, 0.8)',
|
||||
},
|
||||
|
||||
visitors: {
|
||||
tooltipBg: '#ffffff',
|
||||
tooltipLineColor: 'rgba(0, 0, 0, 0)',
|
||||
tooltipLineWidth: '0',
|
||||
tooltipBorderColor: '#ebeef2',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
tooltipFontSize: '20',
|
||||
|
||||
axisLineColor: 'rgba(161, 161 ,229, 0.3)',
|
||||
axisFontSize: '16',
|
||||
axisTextColor: '#b2bac2',
|
||||
yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
|
||||
|
||||
itemBorderColor: '#42db7d',
|
||||
lineStyle: 'dotted',
|
||||
lineWidth: '6',
|
||||
lineGradFrom: '#ffffff',
|
||||
lineGradTo: '#ffffff',
|
||||
lineShadow: 'rgba(14, 16, 48, 0)',
|
||||
|
||||
areaGradFrom: 'rgba(188, 92, 255, 1)',
|
||||
areaGradTo: 'rgba(188, 92, 255, 0.5)',
|
||||
shadowLineDarkBg: '#a695ff',
|
||||
|
||||
innerLineStyle: 'solid',
|
||||
innerLineWidth: '1',
|
||||
|
||||
innerAreaGradFrom: 'rgba(60, 221, 156, 1)',
|
||||
innerAreaGradTo: 'rgba(60, 221, 156, 1)',
|
||||
},
|
||||
|
||||
visitorsLegend: {
|
||||
firstIcon: 'linear-gradient(90deg, #3edd81 0%, #3bddad 100%)',
|
||||
secondIcon: 'linear-gradient(90deg, #8d7fff 0%, #b17fff 100%)',
|
||||
},
|
||||
|
||||
visitorsPie: {
|
||||
firstPieGradientLeft: '#8defbb',
|
||||
firstPieGradientRight: '#8defbb',
|
||||
firstPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
firstPieRadius: ['70%', '90%'],
|
||||
|
||||
secondPieGradientLeft: '#ff894a',
|
||||
secondPieGradientRight: '#ffcc10',
|
||||
secondPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
secondPieRadius: ['60%', '97%'],
|
||||
shadowOffsetX: '0',
|
||||
shadowOffsetY: '0',
|
||||
},
|
||||
|
||||
visitorsPieLegend: {
|
||||
firstSection: 'linear-gradient(90deg, #ffcb17 0%, #ff874c 100%)',
|
||||
secondSection: '#8defbb',
|
||||
},
|
||||
|
||||
earningPie: {
|
||||
radius: ['65%', '100%'],
|
||||
center: ['50%', '50%'],
|
||||
|
||||
fontSize: '22',
|
||||
|
||||
firstPieGradientLeft: '#00d77f',
|
||||
firstPieGradientRight: '#00d77f',
|
||||
firstPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
secondPieGradientLeft: '#7756f7',
|
||||
secondPieGradientRight: '#7756f7',
|
||||
secondPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
thirdPieGradientLeft: '#ffca00',
|
||||
thirdPieGradientRight: '#ffca00',
|
||||
thirdPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
|
||||
earningLine: {
|
||||
gradFrom: 'rgba(188, 92, 255, 0.5)',
|
||||
gradTo: 'rgba(188, 92, 255, 0.5)',
|
||||
|
||||
tooltipTextColor: '#2a2a2a',
|
||||
tooltipFontWeight: 'bolder',
|
||||
tooltipFontSize: '16',
|
||||
tooltipBg: '#ffffff',
|
||||
tooltipBorderColor: '#c0c8d1',
|
||||
tooltipBorderWidth: '3',
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,22 @@ $nb-themes: nb-register-theme((
|
|||
switcher-background: #ebeff5,
|
||||
switcher-background-percentage: 50%,
|
||||
drops-icon-line-gadient: -webkit-linear-gradient(#01dbb5, #0bbb79),
|
||||
|
||||
list-item-border-width: 1px,
|
||||
|
||||
slide-out-container-width: 30%,
|
||||
slide-out-background: linear-gradient(270deg, #f7fafb 0%, #ecf2f5 100%),
|
||||
slide-out-shadow-color: 0 4px 14px 0 #a2d2c8,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #a2d2c8,
|
||||
|
||||
chart-panel-summary-box-shadow: none,
|
||||
chart-panel-summary-background-color: #ecf2f5,
|
||||
chart-panel-summary-border-color: #ebeff1,
|
||||
chart-panel-summary-border-width: 1px,
|
||||
|
||||
ecommerce-card-border-width: 1px,
|
||||
|
||||
progress-bar-background: linear-gradient(90deg, #3edd81 0%, #3bddaf 100%),
|
||||
), default, default);
|
||||
|
||||
$nb-themes: nb-register-theme((
|
||||
|
|
@ -32,6 +48,22 @@ $nb-themes: nb-register-theme((
|
|||
switcher-background: #4e41a5,
|
||||
switcher-background-percentage: 14%,
|
||||
drops-icon-line-gadient: -webkit-linear-gradient(#a258fe, #7958fa),
|
||||
|
||||
list-item-border-width: 1px,
|
||||
|
||||
slide-out-container-width: 30%,
|
||||
slide-out-background: radial-gradient(circle, #302c6e 0%, #423f8c 100%),
|
||||
slide-out-shadow-color: 2px 0 3px rgba(19, 19, 94, 0.9),
|
||||
slide-out-shadow-color-rtl: -2px 0 3px rgba(19, 19, 94, 0.9),
|
||||
|
||||
chart-panel-summary-box-shadow: none,
|
||||
chart-panel-summary-background-color: rgba(0, 0, 0, 0.1),
|
||||
chart-panel-summary-border-color: #332e73,
|
||||
chart-panel-summary-border-width: 1px,
|
||||
|
||||
ecommerce-card-border-width: 1px,
|
||||
|
||||
progress-bar-background: linear-gradient(90deg, #00c7c7 0%, #00d977 100%),
|
||||
), cosmic, cosmic);
|
||||
|
||||
$nb-themes: nb-register-theme((
|
||||
|
|
@ -46,4 +78,20 @@ $nb-themes: nb-register-theme((
|
|||
switcher-background: #2b2d34,
|
||||
switcher-background-percentage: 14%,
|
||||
drops-icon-line-gadient: -webkit-linear-gradient(#e9e8eb, #a7a2be),
|
||||
|
||||
list-item-border-width: 1px,
|
||||
|
||||
slide-out-container-width: 30%,
|
||||
slide-out-background: linear-gradient(270deg, #f7fafb 0%, #ecf2f5 100%),
|
||||
slide-out-shadow-color: 0 4px 14px 0 #a2d2c8,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #a2d2c8,
|
||||
|
||||
chart-panel-summary-box-shadow: none,
|
||||
chart-panel-summary-background-color: #f7fafb,
|
||||
chart-panel-summary-border-color: #ebeff1,
|
||||
chart-panel-summary-border-width: 1px,
|
||||
|
||||
ecommerce-card-border-width: 1px,
|
||||
|
||||
progress-bar-background: linear-gradient(90deg, #ff9f6f 0%, #ff8b97 100%),
|
||||
), corporate, corporate);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
NbCheckboxModule,
|
||||
NbPopoverModule,
|
||||
NbContextMenuModule,
|
||||
NbProgressBarModule,
|
||||
} from '@nebular/theme';
|
||||
|
||||
import { NbSecurityModule } from '@nebular/security';
|
||||
|
|
@ -32,7 +33,13 @@ import {
|
|||
TinyMCEComponent,
|
||||
ThemeSwitcherListComponent,
|
||||
} from './components';
|
||||
import { CapitalizePipe, PluralPipe, RoundPipe, TimingPipe } from './pipes';
|
||||
import {
|
||||
CapitalizePipe,
|
||||
PluralPipe,
|
||||
RoundPipe,
|
||||
TimingPipe,
|
||||
NumberWithCommasPipe,
|
||||
} from './pipes';
|
||||
import {
|
||||
OneColumnLayoutComponent,
|
||||
SampleLayoutComponent,
|
||||
|
|
@ -59,7 +66,8 @@ const NB_MODULES = [
|
|||
NbPopoverModule,
|
||||
NbContextMenuModule,
|
||||
NgbModule,
|
||||
NbSecurityModule, // *nbIsGranted directive
|
||||
NbSecurityModule, // *nbIsGranted directive,
|
||||
NbProgressBarModule,
|
||||
];
|
||||
|
||||
const COMPONENTS = [
|
||||
|
|
@ -87,6 +95,7 @@ const PIPES = [
|
|||
PluralPipe,
|
||||
RoundPipe,
|
||||
TimingPipe,
|
||||
NumberWithCommasPipe,
|
||||
];
|
||||
|
||||
const NB_THEME_PROVIDERS = [
|
||||
|
|
|
|||
|
|
@ -76,23 +76,17 @@ export class EchartsBarAnimationComponent implements AfterViewInit, OnDestroy {
|
|||
name: 'bar',
|
||||
type: 'bar',
|
||||
data: data1,
|
||||
animationDelay: function(idx) {
|
||||
return idx * 10;
|
||||
},
|
||||
animationDelay: idx => idx * 10,
|
||||
},
|
||||
{
|
||||
name: 'bar2',
|
||||
type: 'bar',
|
||||
data: data2,
|
||||
animationDelay: function(idx) {
|
||||
return idx * 10 + 100;
|
||||
},
|
||||
animationDelay: idx => idx * 10 + 100,
|
||||
},
|
||||
],
|
||||
animationEasing: 'elasticOut',
|
||||
animationDelayUpdate: function(idx) {
|
||||
return idx * 5;
|
||||
},
|
||||
animationDelayUpdate: idx => idx * 5,
|
||||
};
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@
|
|||
<li class="dropdown-item" *ngFor="let t of types" (click)="type = t">{{ t }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<ngx-electricity-chart></ngx-electricity-chart>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.675rem 0.5rem 0.5rem 1.25rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
nb-card-body {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<div class="chart-header">
|
||||
<ngx-legend-chart [legendItems]="chartLegend"></ngx-legend-chart>
|
||||
|
||||
<div class="dropdown"
|
||||
[ngClass]="{ 'ghost-dropdown': currentTheme === 'corporate' }"
|
||||
ngbDropdown>
|
||||
<button type="button" ngbDropdownToggle class="btn"
|
||||
[ngClass]="{
|
||||
'btn-outline-success': currentTheme === 'default',
|
||||
'btn-primary': currentTheme !== 'default',
|
||||
'btn-sm': breakpoint.width <= breakpoints.is}">
|
||||
{{ type }}
|
||||
</button>
|
||||
<ul class="dropdown-menu" ngbDropdownMenu>
|
||||
<li class="dropdown-item" *ngFor="let period of types" (click)="changePeriod(period)">
|
||||
{{ period }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 2.125rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
min-width: 8.125rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(is) {
|
||||
.chart-header {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
ngx-legend-chart {
|
||||
align-self: flex-start;
|
||||
|
||||
/deep/ .legends {
|
||||
padding-left: 0;
|
||||
font-size: nb-theme(font-size-sm);
|
||||
}
|
||||
|
||||
/deep/ .legend {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
||||
import { NbMediaBreakpoint, NbMediaBreakpointsService, NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-chart-panel-header',
|
||||
styleUrls: ['./chart-panel-header.component.scss'],
|
||||
templateUrl: './chart-panel-header.component.html',
|
||||
})
|
||||
export class ChartPanelHeaderComponent implements OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
@Output() periodChange = new EventEmitter<string>();
|
||||
|
||||
@Input() type: string = 'week';
|
||||
|
||||
types: string[] = ['week', 'month', 'year'];
|
||||
chartLegend: {iconColor: string; title: string}[];
|
||||
breakpoint: NbMediaBreakpoint = { name: '', width: 0 };
|
||||
breakpoints: any;
|
||||
currentTheme: string;
|
||||
|
||||
constructor(private themeService: NbThemeService,
|
||||
private breakpointService: NbMediaBreakpointsService) {
|
||||
this.themeService.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(theme => {
|
||||
const orderProfitLegend = theme.variables.orderProfitLegend;
|
||||
|
||||
this.currentTheme = theme.name;
|
||||
this.setLegendItems(orderProfitLegend);
|
||||
});
|
||||
|
||||
this.breakpoints = this.breakpointService.getBreakpointsMap();
|
||||
this.themeService.onMediaQueryChange()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(([oldValue, newValue]) => {
|
||||
this.breakpoint = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
setLegendItems(orderProfitLegend) {
|
||||
this.chartLegend = [
|
||||
{
|
||||
iconColor: orderProfitLegend.firstItem,
|
||||
title: 'Payment',
|
||||
},
|
||||
{
|
||||
iconColor: orderProfitLegend.secondItem,
|
||||
title: 'Canceled',
|
||||
},
|
||||
{
|
||||
iconColor: orderProfitLegend.thirdItem,
|
||||
title: 'All orders',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
changePeriod(period: string): void {
|
||||
this.type = period;
|
||||
this.periodChange.emit(period);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
.summary-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
background-color: nb-theme(chart-panel-summary-background-color);
|
||||
box-shadow: nb-theme(chart-panel-summary-box-shadow);
|
||||
justify-content: space-between;
|
||||
padding: 1.5rem 4rem 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border:
|
||||
nb-theme(chart-panel-summary-border-width)
|
||||
solid
|
||||
nb-theme(chart-panel-summary-border-color);
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 2rem;
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.value, .title {
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: nb-theme(font-size-sm);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: nb-theme(font-size-xlg);
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(is) {
|
||||
.summary-container {
|
||||
padding-left: nb-theme(padding);
|
||||
padding-right: nb-theme(padding);
|
||||
}
|
||||
|
||||
.value {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-chart-panel-summary',
|
||||
styleUrls: ['./chart-panel-summary.component.scss'],
|
||||
template: `
|
||||
<div class="summary-container">
|
||||
<div class="summory" *ngFor="let item of summary">
|
||||
<div class="title">{{ item.title }}</div>
|
||||
<div class="value">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class ChartPanelSummaryComponent {
|
||||
@Input() summary: {title: string; value: number}[];
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<nb-card size="large">
|
||||
<nb-tabset fullWidth (changeTab)="changeTab($event)">
|
||||
<nb-tab tabTitle="Orders">
|
||||
<div class="chart-container">
|
||||
<ngx-chart-panel-summary [summary]="chartPanelSummary"></ngx-chart-panel-summary>
|
||||
<ngx-chart-panel-header [type]="period"
|
||||
(periodChange)="setPeriodAndGetChartData($event)">
|
||||
</ngx-chart-panel-header>
|
||||
<ngx-orders-chart #ordersChart [ordersChartData]="ordersChartData"></ngx-orders-chart>
|
||||
</div>
|
||||
</nb-tab>
|
||||
<nb-tab tabTitle="Profit" [lazyLoad]="true">
|
||||
<div class="chart-container">
|
||||
<ngx-chart-panel-summary [summary]="chartPanelSummary"></ngx-chart-panel-summary>
|
||||
<ngx-chart-panel-header [type]="period"
|
||||
(periodChange)="setPeriodAndGetChartData($event)">
|
||||
</ngx-chart-panel-header>
|
||||
<ngx-profit-chart #profitChart [profitChartData]="profitChartData"></ngx-profit-chart>
|
||||
</div>
|
||||
</nb-tab>
|
||||
</nb-tabset>
|
||||
</nb-card>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
|
||||
$legend-all-orders-color: #00977e;
|
||||
$legend-payment-color: #6935ca;
|
||||
$legend-canceled-color: #3f4fda;
|
||||
|
||||
@include nb-install-component() {
|
||||
|
||||
/deep/ nb-tabset {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
ul {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
nb-tab {
|
||||
flex: 1;
|
||||
padding-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
ngx-chart-panel-header, ngx-profit-chart, ngx-orders-chart {
|
||||
padding: 0 1.25rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import { Component, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
import { OrdersChartComponent } from './charts/orders-chart.component';
|
||||
import { ProfitChartComponent } from './charts/profit-chart.component';
|
||||
import { OrdersChart } from '../../../@core/data/orders-chart.service';
|
||||
import { ProfitChart } from '../../../@core/data/profit-chart.service';
|
||||
import { OrdersProfitChartService, OrderProfitChartSummary } from '../../../@core/data/orders-profit-chart.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-ecommerce-charts',
|
||||
styleUrls: ['./charts-panel.component.scss'],
|
||||
templateUrl: './charts-panel.component.html',
|
||||
})
|
||||
export class ECommerceChartsPanelComponent implements OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
chartPanelSummary: OrderProfitChartSummary[];
|
||||
period: string = 'week';
|
||||
ordersChartData: OrdersChart;
|
||||
profitChartData: ProfitChart;
|
||||
|
||||
@ViewChild('ordersChart') ordersChart: OrdersChartComponent;
|
||||
@ViewChild('profitChart') profitChart: ProfitChartComponent;
|
||||
|
||||
constructor(private ordersProfitChartService: OrdersProfitChartService) {
|
||||
this.ordersProfitChartService.getOrderProfitChartSummary()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe((summary) => {
|
||||
this.chartPanelSummary = summary;
|
||||
});
|
||||
|
||||
this.getOrdersChartData(this.period);
|
||||
this.getProfitChartData(this.period);
|
||||
}
|
||||
|
||||
setPeriodAndGetChartData(value: string): void {
|
||||
if (this.period !== value) {
|
||||
this.period = value;
|
||||
}
|
||||
|
||||
this.getOrdersChartData(value);
|
||||
this.getProfitChartData(value);
|
||||
}
|
||||
|
||||
changeTab(selectedTab) {
|
||||
if (selectedTab.tabTitle === 'Profit') {
|
||||
this.profitChart.resizeChart();
|
||||
} else {
|
||||
this.ordersChart.resizeChart();
|
||||
}
|
||||
}
|
||||
|
||||
getOrdersChartData(period: string) {
|
||||
this.ordersProfitChartService.getOrdersChartData(period)
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(ordersChartData => {
|
||||
this.ordersChartData = ordersChartData;
|
||||
});
|
||||
}
|
||||
|
||||
getProfitChartData(period: string) {
|
||||
this.ordersProfitChartService.getProfitChartData(period)
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(profitChartData => {
|
||||
this.profitChartData = profitChartData;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.echart {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
import { AfterViewInit, Component, Input, OnChanges, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { delay, takeWhile } from 'rxjs/operators';
|
||||
|
||||
import { OrdersChart } from '../../../../@core/data/orders-chart.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-orders-chart',
|
||||
styleUrls: ['./charts-common.component.scss'],
|
||||
template: `
|
||||
<div echarts [options]="option" class="echart" (chartInit)="onChartInit($event)"></div>
|
||||
`,
|
||||
})
|
||||
export class OrdersChartComponent implements AfterViewInit, OnDestroy, OnChanges {
|
||||
|
||||
@Input()
|
||||
ordersChartData: OrdersChart;
|
||||
|
||||
private alive = true;
|
||||
|
||||
echartsIntance: any;
|
||||
option: any;
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.option) {
|
||||
this.updateOrdersChartOptions(this.ordersChartData);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(
|
||||
takeWhile(() => this.alive),
|
||||
delay(1),
|
||||
)
|
||||
.subscribe(config => {
|
||||
const eTheme: any = config.variables.orders;
|
||||
|
||||
this.setOptions(eTheme);
|
||||
this.updateOrdersChartOptions(this.ordersChartData);
|
||||
});
|
||||
}
|
||||
|
||||
setOptions(eTheme) {
|
||||
this.option = {
|
||||
grid: {
|
||||
left: 40,
|
||||
top: 20,
|
||||
right: 0,
|
||||
bottom: 40,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
color: eTheme.tooltipLineColor,
|
||||
width: eTheme.tooltipLineWidth,
|
||||
},
|
||||
},
|
||||
textStyle: {
|
||||
color: eTheme.tooltipTextColor,
|
||||
fontSize: eTheme.tooltipFontSize,
|
||||
fontWeight: eTheme.tooltipFontWeight,
|
||||
},
|
||||
position: 'top',
|
||||
backgroundColor: eTheme.tooltipBg,
|
||||
borderColor: eTheme.tooltipBorderColor,
|
||||
borderWidth: 3,
|
||||
formatter: (params) => {
|
||||
return Math.round(parseInt(params.value, 10));
|
||||
},
|
||||
extraCssText: eTheme.tooltipExtraCss,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
offset: 5,
|
||||
data: [],
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: eTheme.axisTextColor,
|
||||
fontSize: eTheme.axisFontSize,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: eTheme.axisLineColor,
|
||||
width: '2',
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: eTheme.axisLineColor,
|
||||
width: '1',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: eTheme.axisTextColor,
|
||||
fontSize: eTheme.axisFontSize,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
|
||||
lineStyle: {
|
||||
color: eTheme.yAxisSplitLine,
|
||||
width: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
this.getFirstLine(eTheme),
|
||||
this.getSecondLine(eTheme),
|
||||
this.getThirdLine(eTheme),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
getFirstLine(eTheme) {
|
||||
return {
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbolSize: 20,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0,
|
||||
},
|
||||
emphasis: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 0,
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.firstAreaGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.firstAreaGradTo,
|
||||
}]),
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
getSecondLine(eTheme) {
|
||||
return {
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbolSize: 20,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0,
|
||||
},
|
||||
emphasis: {
|
||||
color: '#ffffff',
|
||||
borderColor: eTheme.itemBorderColor,
|
||||
borderWidth: 2,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: eTheme.lineWidth,
|
||||
type: eTheme.lineStyle,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.secondLineGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.secondLineGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.secondAreaGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.secondAreaGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
getThirdLine(eTheme) {
|
||||
return {
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbolSize: 20,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0,
|
||||
},
|
||||
emphasis: {
|
||||
color: '#ffffff',
|
||||
borderColor: eTheme.itemBorderColor,
|
||||
borderWidth: 2,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: eTheme.lineWidth,
|
||||
type: eTheme.lineStyle,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.thirdLineGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.thirdLineGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.thirdAreaGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.thirdAreaGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
updateOrdersChartOptions(ordersChartData: OrdersChart) {
|
||||
const options = this.option;
|
||||
const series = this.getNewSeries(options.series, ordersChartData.linesData);
|
||||
const xAxis = this.getNewXAxis(options.xAxis, ordersChartData.chartLabel);
|
||||
|
||||
this.option = {
|
||||
...options,
|
||||
xAxis,
|
||||
series,
|
||||
};
|
||||
}
|
||||
|
||||
getNewSeries(series, linesData: number[][]) {
|
||||
return series.map((line, index) => {
|
||||
return {
|
||||
...line,
|
||||
data: linesData[index],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getNewXAxis(xAxis, chartLabel: string[]) {
|
||||
return {
|
||||
...xAxis,
|
||||
data: chartLabel,
|
||||
};
|
||||
}
|
||||
|
||||
onChartInit(echarts) {
|
||||
this.echartsIntance = echarts;
|
||||
}
|
||||
|
||||
resizeChart() {
|
||||
if (this.echartsIntance) {
|
||||
// Fix recalculation chart size
|
||||
// TODO: investigate more deeply
|
||||
setTimeout(() => {
|
||||
this.echartsIntance.resize();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
import { AfterViewInit, Component, Input, OnChanges, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
import { ProfitChart } from '../../../../@core/data/profit-chart.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-profit-chart',
|
||||
styleUrls: ['./charts-common.component.scss'],
|
||||
template: `
|
||||
<div echarts [options]="options" class="echart" (chartInit)="onChartInit($event)"></div>
|
||||
`,
|
||||
})
|
||||
export class ProfitChartComponent implements AfterViewInit, OnDestroy, OnChanges {
|
||||
|
||||
@Input()
|
||||
profitChartData: ProfitChart;
|
||||
|
||||
private alive = true;
|
||||
|
||||
echartsIntance: any;
|
||||
options: any = {};
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.echartsIntance) {
|
||||
this.updateProfitChartOptions(this.profitChartData);
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(config => {
|
||||
const eTheme: any = config.variables.profit;
|
||||
|
||||
this.setOptions(eTheme);
|
||||
});
|
||||
}
|
||||
|
||||
setOptions(eTheme) {
|
||||
this.options = {
|
||||
backgroundColor: eTheme.bg,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
shadowStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: this.profitChartData.chartLabel,
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: eTheme.axisLineColor,
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: eTheme.axisTextColor,
|
||||
fontSize: eTheme.axisFontSize,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: eTheme.axisLineColor,
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: eTheme.splitLineColor,
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: eTheme.axisTextColor,
|
||||
fontSize: eTheme.axisFontSize,
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Canceled',
|
||||
type: 'bar',
|
||||
barGap: 0,
|
||||
barWidth: '20%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.firstLineGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.firstLineGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
data: this.profitChartData.data[0],
|
||||
},
|
||||
{
|
||||
name: 'Payment',
|
||||
type: 'bar',
|
||||
barWidth: '20%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.secondLineGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.secondLineGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
data: this.profitChartData.data[1],
|
||||
},
|
||||
{
|
||||
name: 'All orders',
|
||||
type: 'bar',
|
||||
barWidth: '20%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.thirdLineGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.thirdLineGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
data: this.profitChartData.data[2],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
updateProfitChartOptions(profitChartData: ProfitChart) {
|
||||
const options = this.options;
|
||||
const series = this.getNewSeries(options.series, profitChartData.data);
|
||||
|
||||
this.echartsIntance.setOption({
|
||||
series: series,
|
||||
xAxis: {
|
||||
data: this.profitChartData.chartLabel,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getNewSeries(series, data: number[][]) {
|
||||
return series.map((line, index) => {
|
||||
return {
|
||||
...line,
|
||||
data: data[index],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onChartInit(echarts) {
|
||||
this.echartsIntance = echarts;
|
||||
}
|
||||
|
||||
resizeChart() {
|
||||
if (this.echartsIntance) {
|
||||
// Fix recalculation chart size
|
||||
// TODO: investigate more deeply
|
||||
setTimeout(() => {
|
||||
this.echartsIntance.resize();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
padding: nb-theme(card-padding);
|
||||
border-left:
|
||||
nb-theme(card-header-border-width)
|
||||
nb-theme(card-header-border-type)
|
||||
nb-theme(card-header-border-color);
|
||||
|
||||
.header {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: nb-theme(card-header-font-family);
|
||||
color: nb-theme(color-fg);
|
||||
}
|
||||
|
||||
.echart {
|
||||
height: 85%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
height: 50%;
|
||||
border-top:
|
||||
nb-theme(card-border-width)
|
||||
nb-theme(card-header-border-type)
|
||||
nb-theme(card-header-border-color);
|
||||
|
||||
.echart {
|
||||
height: 75%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
import { AfterViewInit, Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-country-orders-chart',
|
||||
styleUrls: ['./country-orders-chart.component.scss'],
|
||||
template: `
|
||||
<div class="header">
|
||||
<span class="title">Selected Country</span>
|
||||
<h2>{{countryName}}</h2>
|
||||
</div>
|
||||
<div echarts [options]="option" class="echart" (chartInit)="onChartInit($event)"></div>
|
||||
`,
|
||||
})
|
||||
export class CountryOrdersChartComponent implements AfterViewInit, OnDestroy, OnChanges {
|
||||
|
||||
@Input() countryName: string;
|
||||
@Input() data: number[];
|
||||
@Input() maxValue: number;
|
||||
@Input() labels: string[];
|
||||
|
||||
private alive = true;
|
||||
|
||||
option: any = {};
|
||||
echartsInstance;
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.data && !changes.data.isFirstChange()) {
|
||||
this.echartsInstance.setOption({
|
||||
series: [
|
||||
{
|
||||
data: this.data.map(v => this.maxValue),
|
||||
},
|
||||
{
|
||||
data: this.data,
|
||||
},
|
||||
{
|
||||
data: this.data,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(config => {
|
||||
const countriesTheme: any = config.variables.countryOrders;
|
||||
|
||||
this.option = Object.assign({}, {
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '3%',
|
||||
bottom: '3%',
|
||||
top: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
axisLabel: {
|
||||
color: countriesTheme.chartAxisTextColor,
|
||||
fontSize: countriesTheme.chartAxisFontSize,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: countriesTheme.chartAxisLineColor,
|
||||
width: '2',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: countriesTheme.chartAxisSplitLine,
|
||||
width: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
data: this.labels,
|
||||
axisLabel: {
|
||||
color: countriesTheme.chartAxisTextColor,
|
||||
fontSize: countriesTheme.chartAxisFontSize,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: countriesTheme.chartAxisLineColor,
|
||||
width: '2',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{ // For shadow
|
||||
type: 'bar',
|
||||
data: this.data.map(v => this.maxValue),
|
||||
cursor: 'default',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: countriesTheme.chartInnerLineColor,
|
||||
},
|
||||
opacity: 1,
|
||||
},
|
||||
barWidth: '40%',
|
||||
barGap: '-100%',
|
||||
barCategoryGap: '30%',
|
||||
animation: false,
|
||||
z: 1,
|
||||
},
|
||||
{ // For bottom line
|
||||
type: 'bar',
|
||||
data: this.data,
|
||||
cursor: 'default',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: countriesTheme.chartLineBottomShadowColor,
|
||||
},
|
||||
opacity: 1,
|
||||
},
|
||||
barWidth: '40%',
|
||||
barGap: '-100%',
|
||||
barCategoryGap: '30%',
|
||||
z: 2,
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: '35%',
|
||||
data: this.data,
|
||||
cursor: 'default',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [{
|
||||
offset: 0,
|
||||
color: countriesTheme.chartGradientFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: countriesTheme.chartGradientTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
z: 3,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.echartsInstance = ec;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
|
||||
nb-card-body {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
nb-card-body {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { NbMediaBreakpoint, NbMediaBreakpointsService, NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-country-orders',
|
||||
styleUrls: ['./country-orders.component.scss'],
|
||||
template: `
|
||||
<nb-card [size]="breakpoint.width >= breakpoints.md ? 'medium' : 'xxlarge'">
|
||||
<nb-card-header>Country Orders Statistics</nb-card-header>
|
||||
<nb-card-body>
|
||||
<ngx-country-orders-map (select)="selectCountryById($event)"
|
||||
countryId="USA">
|
||||
</ngx-country-orders-map>
|
||||
<ngx-country-orders-chart [countryName]="countryName"
|
||||
[data]="countryData"
|
||||
[labels]="countriesCategories"
|
||||
maxValue="20">
|
||||
</ngx-country-orders-chart>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
`,
|
||||
})
|
||||
export class CountryOrdersComponent implements OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
private getRandomData(nPoints: number): number[] {
|
||||
return Array.from(Array(nPoints)).map(() => {
|
||||
return Math.round(Math.random() * 20);
|
||||
});
|
||||
}
|
||||
|
||||
countryName = '';
|
||||
countryData = [];
|
||||
countriesCategories = ['Sofas', 'Furniture', 'Lighting', 'Tables', 'Textiles'];
|
||||
breakpoint: NbMediaBreakpoint = { name: '', width: 0 };
|
||||
breakpoints: any;
|
||||
|
||||
constructor(private themeService: NbThemeService,
|
||||
private breakpointService: NbMediaBreakpointsService) {
|
||||
this.breakpoints = this.breakpointService.getBreakpointsMap();
|
||||
this.themeService.onMediaQueryChange()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(([oldValue, newValue]) => {
|
||||
this.breakpoint = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
selectCountryById(countryName: string) {
|
||||
const nPoint = this.countriesCategories.length;
|
||||
|
||||
this.countryName = countryName;
|
||||
this.countryData = this.getRandomData(nPoint);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 60%;
|
||||
|
||||
.leaflet-container {
|
||||
height: 100%;
|
||||
background-color: nb-theme(layout-bg);
|
||||
|
||||
@include nb-for-theme(default) {
|
||||
background-color: nb-theme(color-white);
|
||||
}
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
background-color: nb-theme(color-white);
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/deep/ .leaflet-control-zoom {
|
||||
border: none;
|
||||
|
||||
a {
|
||||
background-color: nb-theme(color-success);
|
||||
color: nb-theme(color-white);
|
||||
border-bottom: none;
|
||||
height: 2.5rem;
|
||||
|
||||
@include nb-for-theme(cosmic) {
|
||||
background-color: nb-theme(color-primary);
|
||||
}
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
background-color: nb-theme(color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.leaflet-control-zoom-in {
|
||||
border-top-left-radius: nb-theme(btn-border-radius);
|
||||
border-top-right-radius: nb-theme(btn-border-radius);
|
||||
}
|
||||
|
||||
.leaflet-control-zoom-out {
|
||||
margin-top: 1px;
|
||||
border-bottom-left-radius: nb-theme(btn-border-radius);
|
||||
border-bottom-right-radius: nb-theme(btn-border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .leaflet-control-attribution {
|
||||
background: transparent;
|
||||
|
||||
a {
|
||||
color: nb-theme(color-fg);
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
||||
|
||||
import * as L from 'leaflet';
|
||||
|
||||
import { CountryOrdersMapService } from './country-orders-map.service';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-country-orders-map',
|
||||
styleUrls: ['./country-orders-map.component.scss'],
|
||||
template: `
|
||||
<div leaflet [leafletOptions]="options" [leafletLayers]="layers" (leafletMapReady)="mapReady($event)"></div>
|
||||
`,
|
||||
})
|
||||
export class CountryOrdersMapComponent implements OnDestroy {
|
||||
|
||||
@Input() countryId: string;
|
||||
|
||||
@Output() select: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
layers = [];
|
||||
currentTheme: any;
|
||||
alive = true;
|
||||
selectedCountry;
|
||||
|
||||
options = {
|
||||
zoom: 2,
|
||||
minZoom: 2,
|
||||
maxZoom: 6,
|
||||
zoomControl: false,
|
||||
center: L.latLng({lat: 38.991709, lng: -76.886109}),
|
||||
maxBounds: new L.LatLngBounds(
|
||||
new L.LatLng(-89.98155760646617, -180),
|
||||
new L.LatLng(89.99346179538875, 180),
|
||||
),
|
||||
maxBoundsViscosity: 1.0,
|
||||
};
|
||||
|
||||
constructor(private ecMapService: CountryOrdersMapService,
|
||||
private theme: NbThemeService) {
|
||||
|
||||
combineLatest([
|
||||
this.ecMapService.getCords(),
|
||||
this.theme.getJsTheme(),
|
||||
])
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(([cords, config]: [any, any]) => {
|
||||
this.currentTheme = config.variables.countryOrders;
|
||||
this.layers = [this.createGeoJsonLayer(cords)];
|
||||
this.selectFeature(this.findFeatureLayerByCountryId(this.countryId));
|
||||
});
|
||||
}
|
||||
|
||||
mapReady(map: L.Map) {
|
||||
map.addControl(L.control.zoom({position: 'bottomright'}));
|
||||
|
||||
// fix the map fully displaying, existing leaflet bag
|
||||
setTimeout(() => {
|
||||
map.invalidateSize();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
private createGeoJsonLayer(cords) {
|
||||
return L.geoJSON(
|
||||
cords as any,
|
||||
{
|
||||
style: () => ({
|
||||
weight: this.currentTheme.countryBorderWidth,
|
||||
fillColor: this.currentTheme.countryFillColor,
|
||||
fillOpacity: 1,
|
||||
color: this.currentTheme.countryBorderColor,
|
||||
opacity: 1,
|
||||
}),
|
||||
onEachFeature: (f, l) => {
|
||||
this.onEachFeature(f, l);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private onEachFeature(feature, layer) {
|
||||
layer.on({
|
||||
mouseover: (e) => this.highlightFeature(e.target),
|
||||
mouseout: (e) => this.moveout(e.target),
|
||||
click: (e) => this.selectFeature(e.target),
|
||||
});
|
||||
}
|
||||
|
||||
private highlightFeature(featureLayer) {
|
||||
if (featureLayer) {
|
||||
featureLayer.setStyle({
|
||||
weight: this.currentTheme.hoveredCountryBorderWidth,
|
||||
fillColor: this.currentTheme.hoveredCountryFillColor,
|
||||
color: this.currentTheme.hoveredCountryBorderColor,
|
||||
});
|
||||
|
||||
if (!L.Browser.ie && !L.Browser.opera12 && !L.Browser.edge) {
|
||||
featureLayer.bringToFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private moveout(featureLayer) {
|
||||
if (featureLayer !== this.selectedCountry) {
|
||||
this.resetHighlight(featureLayer);
|
||||
|
||||
// When countries have common border we should highlight selected country once again
|
||||
this.highlightFeature(this.selectedCountry);
|
||||
}
|
||||
}
|
||||
|
||||
private resetHighlight(featureLayer) {
|
||||
if (featureLayer) {
|
||||
const geoJsonLayer = this.layers[0];
|
||||
|
||||
geoJsonLayer.resetStyle(featureLayer);
|
||||
}
|
||||
}
|
||||
|
||||
private selectFeature(featureLayer) {
|
||||
if (featureLayer !== this.selectedCountry) {
|
||||
this.resetHighlight(this.selectedCountry);
|
||||
this.highlightFeature(featureLayer);
|
||||
this.selectedCountry = featureLayer;
|
||||
this.select.emit(featureLayer.feature.properties.name);
|
||||
}
|
||||
}
|
||||
|
||||
private findFeatureLayerByCountryId(id) {
|
||||
const layers = this.layers[0].getLayers();
|
||||
const featureLayer = layers.find(item => {
|
||||
return item.feature.id === id;
|
||||
});
|
||||
|
||||
return featureLayer ? featureLayer : null;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.alive = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class CountryOrdersMapService {
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getCords(): Observable<any> {
|
||||
return this.http.get('./assets/leaflet-countries/countries.geo.json');
|
||||
}
|
||||
|
||||
}
|
||||
38
src/app/pages/e-commerce/e-commerce.component.html
Normal file
38
src/app/pages/e-commerce/e-commerce.component.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<div class="row">
|
||||
<div class="col-xxl-5">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<ngx-profit-card></ngx-profit-card>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<ngx-earning-card></ngx-earning-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ngx-traffic-reveal-card></ngx-traffic-reveal-card>
|
||||
</div>
|
||||
|
||||
<div class="col-xxl-7">
|
||||
<ngx-ecommerce-charts></ngx-ecommerce-charts>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xxl-9">
|
||||
<ngx-country-orders></ngx-country-orders>
|
||||
</div>
|
||||
|
||||
<div class="col-xxl-3">
|
||||
<ngx-progress-section></ngx-progress-section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xxl-9">
|
||||
<ngx-ecommerce-visitors-analytics></ngx-ecommerce-visitors-analytics>
|
||||
</div>
|
||||
|
||||
<div class="col-xxl-3">
|
||||
<ngx-user-activity></ngx-user-activity>
|
||||
</div>
|
||||
</div>
|
||||
8
src/app/pages/e-commerce/e-commerce.component.ts
Normal file
8
src/app/pages/e-commerce/e-commerce.component.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-ecommerce',
|
||||
templateUrl: './e-commerce.component.html',
|
||||
})
|
||||
export class ECommerceComponent {
|
||||
}
|
||||
95
src/app/pages/e-commerce/e-commerce.module.ts
Normal file
95
src/app/pages/e-commerce/e-commerce.module.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { NgxEchartsModule } from 'ngx-echarts';
|
||||
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||
|
||||
import { ThemeModule } from '../../@theme/theme.module';
|
||||
import { ECommerceComponent } from './e-commerce.component';
|
||||
import { ProfitCardComponent } from './profit-card/profit-card.component';
|
||||
import { ECommerceChartsPanelComponent } from './charts-panel/charts-panel.component';
|
||||
import { OrdersChartComponent } from './charts-panel/charts/orders-chart.component';
|
||||
import { ProfitChartComponent } from './charts-panel/charts/profit-chart.component';
|
||||
import { ChartPanelHeaderComponent } from './charts-panel/chart-panel-header/chart-panel-header.component';
|
||||
import { ChartPanelSummaryComponent } from './charts-panel/chart-panel-summary/chart-panel-summary.component';
|
||||
import { ChartModule } from 'angular2-chartjs';
|
||||
import { StatsCardBackComponent } from './profit-card/back-side/stats-card-back.component';
|
||||
import { StatsAreaChartComponent } from './profit-card/back-side/stats-area-chart.component';
|
||||
import { StatsBarAnimationChartComponent } from './profit-card/front-side/stats-bar-animation-chart.component';
|
||||
import { StatsCardFrontComponent } from './profit-card/front-side/stats-card-front.component';
|
||||
import { TrafficRevealCardComponent } from './traffic-reveal-card/traffic-reveal-card.component';
|
||||
import { TrafficBarComponent } from './traffic-reveal-card/front-side/traffic-bar/traffic-bar.component';
|
||||
import { TrafficFrontCardComponent } from './traffic-reveal-card/front-side/traffic-front-card.component';
|
||||
import { TrafficCardsHeaderComponent } from './traffic-reveal-card/traffic-cards-header/traffic-cards-header.component';
|
||||
import { TrafficBackCardComponent } from './traffic-reveal-card/back-side/traffic-back-card.component';
|
||||
import { TrafficBarChartComponent } from './traffic-reveal-card/back-side/traffic-bar-chart.component';
|
||||
import {
|
||||
ECommerceVisitorsAnalyticsComponent,
|
||||
} from './visitors-analytics/visitors-analytics.component';
|
||||
import {
|
||||
ECommerceVisitorsAnalyticsChartComponent,
|
||||
} from './visitors-analytics/visitors-analytics-chart/visitors-analytics-chart.component';
|
||||
import {
|
||||
ECommerceVisitorsStatisticsComponent,
|
||||
} from './visitors-analytics/visitors-statistics/visitors-statistics.component';
|
||||
import { ECommerceLegendChartComponent } from './legend-chart/legend-chart.component';
|
||||
import { ECommerceUserActivityComponent } from './user-activity/user-activity.component';
|
||||
import { ECommerceProgressSectionComponent } from './progress-section/progress-section.component';
|
||||
import { SlideOutComponent } from './slide-out/slide-out.component';
|
||||
|
||||
import { CountryOrdersComponent } from './country-orders/country-orders.component';
|
||||
import { CountryOrdersMapComponent } from './country-orders/map/country-orders-map.component';
|
||||
import { CountryOrdersMapService } from './country-orders/map/country-orders-map.service';
|
||||
import { LeafletModule } from '@asymmetrik/ngx-leaflet';
|
||||
import { CountryOrdersChartComponent } from './country-orders/chart/country-orders-chart.component';
|
||||
import { EarningCardComponent } from './earning-card/earning-card.component';
|
||||
import { EarningCardBackComponent } from './earning-card/back-side/earning-card-back.component';
|
||||
import { EarningPieChartComponent } from './earning-card/back-side/earning-pie-chart.component';
|
||||
import { EarningCardFrontComponent } from './earning-card/front-side/earning-card-front.component';
|
||||
import { EarningLiveUpdateChartComponent } from './earning-card/front-side/earning-live-update-chart.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
ThemeModule,
|
||||
ChartModule,
|
||||
NgxEchartsModule,
|
||||
NgxChartsModule,
|
||||
LeafletModule,
|
||||
],
|
||||
declarations: [
|
||||
ECommerceComponent,
|
||||
StatsCardFrontComponent,
|
||||
StatsAreaChartComponent,
|
||||
StatsBarAnimationChartComponent,
|
||||
ProfitCardComponent,
|
||||
ECommerceChartsPanelComponent,
|
||||
ChartPanelHeaderComponent,
|
||||
ChartPanelSummaryComponent,
|
||||
OrdersChartComponent,
|
||||
ProfitChartComponent,
|
||||
StatsCardBackComponent,
|
||||
TrafficRevealCardComponent,
|
||||
TrafficBarChartComponent,
|
||||
TrafficFrontCardComponent,
|
||||
TrafficBackCardComponent,
|
||||
TrafficBarComponent,
|
||||
TrafficCardsHeaderComponent,
|
||||
CountryOrdersComponent,
|
||||
CountryOrdersMapComponent,
|
||||
CountryOrdersChartComponent,
|
||||
ECommerceVisitorsAnalyticsComponent,
|
||||
ECommerceVisitorsAnalyticsChartComponent,
|
||||
ECommerceVisitorsStatisticsComponent,
|
||||
ECommerceLegendChartComponent,
|
||||
ECommerceUserActivityComponent,
|
||||
ECommerceProgressSectionComponent,
|
||||
SlideOutComponent,
|
||||
EarningCardComponent,
|
||||
EarningCardFrontComponent,
|
||||
EarningCardBackComponent,
|
||||
EarningPieChartComponent,
|
||||
EarningLiveUpdateChartComponent,
|
||||
],
|
||||
providers: [
|
||||
CountryOrdersMapService,
|
||||
],
|
||||
})
|
||||
export class ECommerceModule { }
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<nb-card-header>
|
||||
Earnings
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="chart-info">
|
||||
<div class="title" [style.color]="color">{{ name }}</div>
|
||||
<div class="time-period">Last week</div>
|
||||
<div class="value">{{ value }}%</div>
|
||||
</div>
|
||||
<ngx-earning-pie-chart [values]="earningPieChartData"
|
||||
(selectPie)="changeChartInfo($event)"
|
||||
[defaultSelectedCurrency]="defaultSelectedCurrency">
|
||||
</ngx-earning-pie-chart>
|
||||
</nb-card-body>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
nb-card-header {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
ngx-earning-pie-chart, .chart-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.chart-info {
|
||||
padding-top: 0.7rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: nb-theme(font-size-xlg);
|
||||
}
|
||||
|
||||
.time-period {
|
||||
margin-top: 1.5rem;
|
||||
color: nb-theme(color-fg);
|
||||
}
|
||||
|
||||
.value {
|
||||
margin-top: 0.2rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
|
||||
.echart {
|
||||
position: absolute;
|
||||
width: calc(50% - #{nb-theme(card-padding)});
|
||||
height: calc(100% - 2 * #{nb-theme(card-padding)});
|
||||
}
|
||||
|
||||
@include media-breakpoint-between(xl, xl) {
|
||||
ngx-earning-pie-chart {
|
||||
flex: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-between(sm, sm) {
|
||||
ngx-earning-pie-chart {
|
||||
flex: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { EarningService, PieChart } from '../../../../@core/data/earning.service';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-earning-card-back',
|
||||
styleUrls: ['./earning-card-back.component.scss'],
|
||||
templateUrl: './earning-card-back.component.html',
|
||||
})
|
||||
export class EarningCardBackComponent implements OnDestroy {
|
||||
private alive = true;
|
||||
|
||||
earningPieChartData: PieChart[];
|
||||
name: string;
|
||||
color: string;
|
||||
value: number;
|
||||
defaultSelectedCurrency: string = 'Bitcoin';
|
||||
|
||||
constructor(private earningService: EarningService ) {
|
||||
this.earningService.getEarningPieChartData()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe((earningPieChartData) => {
|
||||
this.earningPieChartData = earningPieChartData;
|
||||
});
|
||||
}
|
||||
|
||||
changeChartInfo(pieData: {value: number; name: string; color: any}) {
|
||||
this.value = pieData.value;
|
||||
this.name = pieData.name;
|
||||
this.color = pieData.color;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
import { AfterViewInit, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { delay, takeWhile } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-earning-pie-chart',
|
||||
styleUrls: ['./earning-card-back.component.scss'],
|
||||
template: `
|
||||
<div echarts
|
||||
class="echart"
|
||||
[options]="options"
|
||||
(chartInit)="onChartInit($event)"
|
||||
(chartClick)="onChartClick($event)">
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class EarningPieChartComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
@Output() selectPie = new EventEmitter<{value: number; name: string; color: string}>();
|
||||
@Input() values: {value: number; name: string; }[];
|
||||
@Input() defaultSelectedCurrency: string;
|
||||
|
||||
private alive = true;
|
||||
|
||||
options: any = {};
|
||||
echartsInstance;
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.echartsInstance = ec;
|
||||
}
|
||||
|
||||
onChartClick(event) {
|
||||
const pieData = {
|
||||
value: event.value,
|
||||
name: event.name,
|
||||
color: event.color.colorStops[0].color,
|
||||
};
|
||||
|
||||
this.emitSelectPie(pieData);
|
||||
}
|
||||
|
||||
emitSelectPie(pieData: {value: number; name: string; color: any}) {
|
||||
this.selectPie.emit(pieData);
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(
|
||||
takeWhile(() => this.alive),
|
||||
delay(1),
|
||||
)
|
||||
.subscribe(config => {
|
||||
const variables = config.variables;
|
||||
|
||||
this.options = this.getOptions(variables);
|
||||
const defaultSelectedData =
|
||||
this.options.series[0].data.find((item) => item.name === this.defaultSelectedCurrency);
|
||||
const color = defaultSelectedData.itemStyle.normal.color.colorStops[0].color;
|
||||
const pieData = {
|
||||
value: defaultSelectedData.value,
|
||||
name: defaultSelectedData.name,
|
||||
color,
|
||||
};
|
||||
|
||||
this.emitSelectPie(pieData);
|
||||
});
|
||||
}
|
||||
|
||||
getOptions(variables) {
|
||||
const earningPie: any = variables.earningPie;
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: ' ',
|
||||
clockWise: true,
|
||||
hoverAnimation: false,
|
||||
type: 'pie',
|
||||
center: earningPie.center,
|
||||
radius: earningPie.radius,
|
||||
data: [
|
||||
{
|
||||
value: this.values[0].value,
|
||||
name: this.values[0].name,
|
||||
label: {
|
||||
normal: {
|
||||
position: 'center',
|
||||
formatter: '',
|
||||
textStyle: {
|
||||
fontSize: '22',
|
||||
fontFamily: variables.fontSecondary,
|
||||
fontWeight: '600',
|
||||
color: variables.fgHeading,
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: false,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: earningPie.firstPieGradientLeft,
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: earningPie.firstPieGradientRight,
|
||||
},
|
||||
]),
|
||||
shadowColor: earningPie.firstPieShadowColor,
|
||||
shadowBlur: 0,
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: this.values[1].value,
|
||||
name: this.values[1].name,
|
||||
label: {
|
||||
normal: {
|
||||
position: 'center',
|
||||
formatter: '',
|
||||
textStyle: {
|
||||
fontSize: '22',
|
||||
fontFamily: variables.fontSecondary,
|
||||
fontWeight: '600',
|
||||
color: variables.fgHeading,
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: false,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: earningPie.secondPieGradientLeft,
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: earningPie.secondPieGradientRight,
|
||||
},
|
||||
]),
|
||||
shadowColor: earningPie.secondPieShadowColor,
|
||||
shadowBlur: 0,
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: this.values[2].value,
|
||||
name: this.values[2].name,
|
||||
label: {
|
||||
normal: {
|
||||
position: 'center',
|
||||
formatter: '',
|
||||
textStyle: {
|
||||
fontSize: '22',
|
||||
fontFamily: variables.fontSecondary,
|
||||
fontWeight: '600',
|
||||
color: variables.fgHeading,
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: false,
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: earningPie.thirdPieGradientLeft,
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: earningPie.thirdPieGradientRight,
|
||||
},
|
||||
]),
|
||||
shadowColor: earningPie.thirdPieShadowColor,
|
||||
shadowBlur: 0,
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<nb-flip-card [showToggleButton]="false" [flipped]="flipped">
|
||||
<nb-card-front>
|
||||
<nb-card size="xsmall">
|
||||
<ngx-earning-card-front></ngx-earning-card-front>
|
||||
<i class="nb-arrow-right" (click)="toggleFlipView()"></i>
|
||||
</nb-card>
|
||||
</nb-card-front>
|
||||
<nb-card-back>
|
||||
<nb-card size="xsmall">
|
||||
<ngx-earning-card-back></ngx-earning-card-back>
|
||||
<i class="nb-arrow-right" (click)="toggleFlipView()"></i>
|
||||
</nb-card>
|
||||
</nb-card-back>
|
||||
</nb-flip-card>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
nb-card {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nb-arrow-right {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@include nb-rtl(right, auto);
|
||||
@include nb-rtl(left, 0);
|
||||
padding: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/deep/ .flipped {
|
||||
.back-container {
|
||||
.nb-arrow-right {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.front-container {
|
||||
.nb-arrow-right {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngx-earning-card-back, ngx-earning-card-front {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/deep/ nb-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@include nb-rtl(flex-direction, row-reverse);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
/deep/ nb-card-body {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-earning-card',
|
||||
styleUrls: ['./earning-card.component.scss'],
|
||||
templateUrl: './earning-card.component.html',
|
||||
})
|
||||
export class EarningCardComponent {
|
||||
|
||||
flipped = false;
|
||||
|
||||
toggleFlipView() {
|
||||
this.flipped = !this.flipped;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<nb-card-header>
|
||||
<div class="dropdown ghost-dropdown" ngbDropdown>
|
||||
<button type="button" ngbDropdownToggle class="btn"
|
||||
[ngClass]="{
|
||||
'btn-success': currentTheme === 'default',
|
||||
'btn-primary': currentTheme !== 'default'}">
|
||||
{{ selectedCurrency }}
|
||||
</button>
|
||||
<ul class="dropdown-menu" ngbDropdownMenu>
|
||||
<li class="dropdown-item" *ngFor="let currency of currencies" (click)="changeCurrency(currency)">{{ currency }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="chart-info">
|
||||
<div class="title">Daily Income</div>
|
||||
<div class="value">{{ earningLiveUpdateCardData.dailyIncome | ngxNumberWithCommas }}</div>
|
||||
<div class="delta"
|
||||
[class.up]="earningLiveUpdateCardData.delta.up"
|
||||
[class.down]="!earningLiveUpdateCardData.delta.up">
|
||||
{{ earningLiveUpdateCardData.delta.value }}%
|
||||
</div>
|
||||
</div>
|
||||
<ngx-earning-live-update-chart
|
||||
[liveUpdateChartData]="liveUpdateChartData">
|
||||
</ngx-earning-live-update-chart>
|
||||
</nb-card-body>
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
@import '~@nebular/theme/styles/global/typography/typography';
|
||||
|
||||
@include nb-install-component() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
nb-card-header {
|
||||
flex-direction: row;
|
||||
padding-top: 0.45rem;
|
||||
padding-bottom: 0.45rem;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
min-width: 8.125rem;
|
||||
}
|
||||
|
||||
nb-card-body {
|
||||
padding: 1rem 0 0;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.chart-info {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 nb-theme(card-padding);
|
||||
}
|
||||
|
||||
.title {
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
|
||||
.value {
|
||||
color: nb-theme(color-success);
|
||||
font-size: 1.5rem;
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
color: nb-theme(color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.delta {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
color: nb-theme(color-fg-heading);
|
||||
padding-left: 0.5rem;
|
||||
font-size: 1rem;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@include nb-rtl(left, 1.25rem);
|
||||
@include nb-rtl(right, inherit);
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
right: 100%;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
}
|
||||
|
||||
&.down {
|
||||
&::before {
|
||||
bottom: 4px;
|
||||
border-top: 7px solid text-danger();
|
||||
}
|
||||
}
|
||||
|
||||
&.up {
|
||||
&::before {
|
||||
top: 4px;
|
||||
border-bottom: 7px solid text-success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngx-earning-live-update-chart {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/deep/ canvas {
|
||||
border-bottom-left-radius: nb-theme(card-border-radius);
|
||||
border-bottom-right-radius: nb-theme(card-border-radius);
|
||||
}
|
||||
|
||||
.echart {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
.delta {
|
||||
&.down {
|
||||
color: text-danger();
|
||||
|
||||
&::before {
|
||||
bottom: 4px;
|
||||
border-top: 7px solid text-danger();
|
||||
}
|
||||
}
|
||||
|
||||
&.up {
|
||||
color: text-primary();
|
||||
|
||||
&::before {
|
||||
top: 4px;
|
||||
border-bottom: 7px solid text-primary();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { interval } from 'rxjs';
|
||||
import { switchMap, takeWhile } from 'rxjs/operators';
|
||||
import { EarningService, LiveUpdateChart } from '../../../../@core/data/earning.service';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-earning-card-front',
|
||||
styleUrls: ['./earning-card-front.component.scss'],
|
||||
templateUrl: './earning-card-front.component.html',
|
||||
})
|
||||
export class EarningCardFrontComponent implements OnDestroy, OnInit {
|
||||
private alive = true;
|
||||
|
||||
@Input() selectedCurrency: string = 'Bitcoin';
|
||||
|
||||
intervalSubscription: Subscription;
|
||||
currencies: string[] = ['Bitcoin', 'Tether', 'Ethereum'];
|
||||
currentTheme: string;
|
||||
earningLiveUpdateCardData: LiveUpdateChart;
|
||||
liveUpdateChartData: { value: [string, number] }[];
|
||||
|
||||
constructor(private themeService: NbThemeService,
|
||||
private earningService: EarningService) {
|
||||
this.themeService.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(theme => {
|
||||
this.currentTheme = theme.name;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getEarningCardData(this.selectedCurrency);
|
||||
}
|
||||
|
||||
changeCurrency(currency) {
|
||||
if (this.selectedCurrency !== currency) {
|
||||
this.selectedCurrency = currency;
|
||||
|
||||
this.getEarningCardData(this.selectedCurrency);
|
||||
}
|
||||
}
|
||||
|
||||
private getEarningCardData(currency) {
|
||||
this.earningService.getEarningLiveUpdateCardData(currency)
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe((earningLiveUpdateCardData) => {
|
||||
this.earningLiveUpdateCardData = earningLiveUpdateCardData;
|
||||
this.liveUpdateChartData = earningLiveUpdateCardData.liveChart;
|
||||
|
||||
this.startReceivingLiveData(currency);
|
||||
});
|
||||
}
|
||||
|
||||
startReceivingLiveData(currency) {
|
||||
if (this.intervalSubscription) {
|
||||
this.intervalSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
this.intervalSubscription = interval(200)
|
||||
.pipe(
|
||||
takeWhile(() => this.alive),
|
||||
switchMap(() => this.earningService.generateRandomEarningData(currency)),
|
||||
)
|
||||
.subscribe((liveUpdateChartData) => {
|
||||
this.liveUpdateChartData = [...liveUpdateChartData];
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
import { delay, takeWhile } from 'rxjs/operators';
|
||||
import { AfterViewInit, Component, Input, OnChanges, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-earning-live-update-chart',
|
||||
styleUrls: ['earning-card-front.component.scss'],
|
||||
template: `
|
||||
<div echarts
|
||||
class="echart"
|
||||
[options]="option"
|
||||
(chartInit)="onChartInit($event)"></div>
|
||||
`,
|
||||
})
|
||||
export class EarningLiveUpdateChartComponent implements AfterViewInit, OnDestroy, OnChanges {
|
||||
private alive = true;
|
||||
|
||||
@Input() liveUpdateChartData: { value: [string, number] }[];
|
||||
|
||||
option: any;
|
||||
echartsInstance;
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.option) {
|
||||
this.updateChartOptions(this.liveUpdateChartData);
|
||||
}
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.echartsInstance = ec;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(
|
||||
delay(1),
|
||||
takeWhile(() => this.alive),
|
||||
)
|
||||
.subscribe(config => {
|
||||
const earningLineTheme: any = config.variables.earningLine;
|
||||
|
||||
this.setChartOption(earningLineTheme);
|
||||
});
|
||||
}
|
||||
|
||||
setChartOption(earningLineTheme) {
|
||||
this.option = {
|
||||
grid: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
boundaryGap: [0, '5%'],
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
textStyle: {
|
||||
color: earningLineTheme.tooltipTextColor,
|
||||
fontWeight: earningLineTheme.tooltipFontWeight,
|
||||
fontSize: earningLineTheme.tooltipFontSize,
|
||||
},
|
||||
position: 'top',
|
||||
backgroundColor: earningLineTheme.tooltipBg,
|
||||
borderColor: earningLineTheme.tooltipBorderColor,
|
||||
borderWidth: earningLineTheme.tooltipBorderWidth,
|
||||
formatter: params => `$ ${Math.round(parseInt(params.value[1], 10))}`,
|
||||
extraCssText: earningLineTheme.tooltipExtraCss,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
symbol: 'circle',
|
||||
sampling: 'average',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0,
|
||||
},
|
||||
emphasis: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 0,
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: earningLineTheme.gradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: earningLineTheme.gradTo,
|
||||
}]),
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
data: this.liveUpdateChartData,
|
||||
},
|
||||
],
|
||||
animation: true,
|
||||
};
|
||||
}
|
||||
|
||||
updateChartOptions(chartData: { value: [string, number] }[]) {
|
||||
this.echartsInstance.setOption({
|
||||
series: [{
|
||||
data: chartData,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export enum NgxLegendItemColor {
|
||||
GREEN = 'green',
|
||||
PURPLE = 'purple',
|
||||
LIGHT_PURPLE = 'light-purple',
|
||||
BLUE = 'blue',
|
||||
YELLOW = 'yellow',
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<div class="legends">
|
||||
<div *ngFor="let legend of legendItems" class="legend">
|
||||
<div class="legend-item-color"
|
||||
[style.background]="legend.iconColor"></div>
|
||||
<div class="legend-title">{{ legend.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
.legends {
|
||||
display: flex;
|
||||
@include nb-rtl(flex-direction, row-reverse);
|
||||
color: nb-theme(color-fg);
|
||||
padding: 0 0 0 2.85rem;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-left: 4rem;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.legend-item-color {
|
||||
min-width: 15px;
|
||||
min-height: 15px;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.legend-title {
|
||||
padding: 0 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
.legend {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { NgxLegendItemColor } from './enum.legend-item-color';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-legend-chart',
|
||||
styleUrls: ['./legend-chart.component.scss'],
|
||||
templateUrl: './legend-chart.component.html',
|
||||
})
|
||||
export class ECommerceLegendChartComponent {
|
||||
|
||||
/**
|
||||
* Take an array of legend items
|
||||
* Available iconColor: 'green', 'purple', 'light-purple', 'blue', 'yellow'
|
||||
* @type {{iconColor: string; title: string}[]}
|
||||
*/
|
||||
@Input()
|
||||
legendItems: { iconColor: NgxLegendItemColor; title: string }[] = [];
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import { delay, takeWhile } from 'rxjs/operators';
|
||||
import { AfterViewInit, Component, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
|
||||
const points = [300, 520, 435, 530, 730, 620, 660, 860];
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-stats-ares-chart',
|
||||
styleUrls: ['stats-card-back.component.scss'],
|
||||
template: `
|
||||
<div echarts [options]="option" class="echart"></div>
|
||||
`,
|
||||
})
|
||||
export class StatsAreaChartComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
option: any = {};
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(
|
||||
delay(1),
|
||||
takeWhile(() => this.alive),
|
||||
)
|
||||
.subscribe(config => {
|
||||
const trafficTheme: any = config.variables.traffic;
|
||||
|
||||
this.option = Object.assign({}, {
|
||||
grid: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: points,
|
||||
},
|
||||
yAxis: {
|
||||
boundaryGap: [0, '5%'],
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: trafficTheme.colorBlack,
|
||||
opacity: 0.06,
|
||||
width: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
textStyle: {
|
||||
color: trafficTheme.tooltipTextColor,
|
||||
fontWeight: trafficTheme.tooltipFontWeight,
|
||||
fontSize: 16,
|
||||
},
|
||||
position: 'top',
|
||||
backgroundColor: trafficTheme.tooltipBg,
|
||||
borderColor: trafficTheme.tooltipBorderColor,
|
||||
borderWidth: 3,
|
||||
formatter: '$ {c0}',
|
||||
extraCssText: trafficTheme.tooltipExtraCss,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
sampling: 'average',
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: trafficTheme.shadowLineDarkBg,
|
||||
},
|
||||
emphasis: {
|
||||
color: 'rgba(0,0,0,0)',
|
||||
borderColor: 'rgba(0,0,0,0)',
|
||||
borderWidth: 0,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 2,
|
||||
color: trafficTheme.shadowLineDarkBg,
|
||||
},
|
||||
},
|
||||
data: points.map(p => p - 15),
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
sampling: 'average',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: trafficTheme.itemColor,
|
||||
borderColor: trafficTheme.itemBorderColor,
|
||||
borderWidth: 2,
|
||||
},
|
||||
emphasis: {
|
||||
color: 'white',
|
||||
borderColor: trafficTheme.itemEmphasisBorderColor,
|
||||
borderWidth: 2,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 2,
|
||||
color: trafficTheme.lineBg,
|
||||
shadowColor: trafficTheme.lineBg,
|
||||
shadowBlur: trafficTheme.lineShadowBlur,
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: trafficTheme.gradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: trafficTheme.gradTo,
|
||||
}]),
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
data: points,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<nb-card-header>
|
||||
<div class="header-container">
|
||||
<div class="icon">
|
||||
<i class="ion-social-usd"></i>
|
||||
</div>
|
||||
<span class="title">Profit</span>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body class="p-0">
|
||||
<div class="info">
|
||||
<div class="period">
|
||||
<span class="time-interval">Jun 1 - Jun 30</span>
|
||||
<div class="value">
|
||||
<span class="currency">$</span>
|
||||
300
|
||||
</div>
|
||||
</div>
|
||||
<div class="period latest">
|
||||
<span class="time-interval">Jul 1 - Jul 31</span>
|
||||
<div class="value">
|
||||
<span class="currency">$</span>
|
||||
860
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ngx-stats-ares-chart></ngx-stats-ares-chart>
|
||||
</nb-card-body>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
/deep/ nb-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
@include nb-rtl(flex-direction, row-reverse);
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ nb-card-body {
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.info {
|
||||
padding: 0.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
display: flex;
|
||||
@include nb-rtl(flex-direction, row-reverse);
|
||||
}
|
||||
|
||||
.period {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
&.latest {
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.time-interval {
|
||||
color: nb-theme(color-fg);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-family: nb-theme(font-secondary), serif;
|
||||
font-size: 1.25rem;
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
color: nb-theme(card-fg-heading);
|
||||
|
||||
@include nb-for-theme(default) {
|
||||
color: nb-theme(color-success);
|
||||
}
|
||||
|
||||
.currency {
|
||||
color: nb-theme(color-success);
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
color: nb-theme(color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngx-stats-ares-chart {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/deep/ canvas {
|
||||
border-bottom-left-radius: nb-theme(card-border-radius);
|
||||
border-bottom-right-radius: nb-theme(card-border-radius);
|
||||
}
|
||||
|
||||
.echart {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-stats-card-back',
|
||||
styleUrls: ['./stats-card-back.component.scss'],
|
||||
templateUrl: './stats-card-back.component.html',
|
||||
})
|
||||
export class StatsCardBackComponent {
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
import { AfterViewInit, Component, Input, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-stats-bar-animation-chart',
|
||||
template: `
|
||||
<div echarts [options]="options" class="echart"></div>
|
||||
`,
|
||||
})
|
||||
export class StatsBarAnimationChartComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
@Input() linesData: { firstLine: number[]; secondLine: number[] } = {
|
||||
firstLine: [],
|
||||
secondLine: [],
|
||||
};
|
||||
|
||||
options: any = {};
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(config => {
|
||||
const profitBarAnimationEchart: any = config.variables.profitBarAnimationEchart;
|
||||
|
||||
this.setChartOption(profitBarAnimationEchart);
|
||||
});
|
||||
}
|
||||
|
||||
setChartOption(chartVariables) {
|
||||
this.options = {
|
||||
color: [
|
||||
chartVariables.firstAnimationBarColor,
|
||||
chartVariables.secondAnimationBarColor,
|
||||
],
|
||||
grid: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
legend: {
|
||||
data: ['transactions', 'orders'],
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
itemWidth: 15,
|
||||
itemHeight: 15,
|
||||
textStyle: {
|
||||
color: chartVariables.textColor,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
textStyle: {
|
||||
color: chartVariables.tooltipTextColor,
|
||||
fontWeight: chartVariables.tooltipFontWeight,
|
||||
fontSize: chartVariables.tooltipFontSize,
|
||||
},
|
||||
position: 'top',
|
||||
backgroundColor: chartVariables.tooltipBg,
|
||||
borderColor: chartVariables.tooltipBorderColor,
|
||||
borderWidth: chartVariables.tooltipBorderWidth,
|
||||
formatter: params => `$ ${Math.round(parseInt(params.value, 10))}`,
|
||||
extraCssText: chartVariables.tooltipExtraCss,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
data: this.linesData.firstLine.map((_, index) => index),
|
||||
silent: false,
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: chartVariables.splitLineStyleColor,
|
||||
opacity: chartVariables.splitLineStyleOpacity,
|
||||
width: chartVariables.splitLineStyleWidth,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'transactions',
|
||||
type: 'bar',
|
||||
data: this.linesData.firstLine,
|
||||
animationDelay: idx => idx * 10,
|
||||
},
|
||||
{
|
||||
name: 'orders',
|
||||
type: 'bar',
|
||||
data: this.linesData.secondLine,
|
||||
animationDelay: idx => idx * 10 + 100,
|
||||
},
|
||||
],
|
||||
animationEasing: 'elasticOut',
|
||||
animationDelayUpdate: idx => idx * 5,
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<nb-card-header>
|
||||
<div class="header-container">
|
||||
<div class="icon">
|
||||
<i class="ion-social-usd"></i>
|
||||
</div>
|
||||
<span class="title">Profit</span>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<ngx-stats-bar-animation-chart [linesData]="linesData"></ngx-stats-bar-animation-chart>
|
||||
</nb-card-body>
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
/deep/ nb-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
@include nb-rtl(flex-direction, row-reverse);
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ nb-card-body {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.period {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.time-interval {
|
||||
font-size: nb-theme(font-size-sm);
|
||||
color: nb-theme(color-fg);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-family: nb-theme(font-secondary), serif;
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
color: nb-theme(card-fg-heading);
|
||||
|
||||
.currency {
|
||||
color: nb-theme(color-success);
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
color: nb-theme(color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.start-period {
|
||||
.time-interval, .value {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.end-period {
|
||||
.time-interval, .value {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
ngx-stats-bar-animation-chart {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.echart {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { ProfitBarAnimationChartService } from '../../../../@core/data/profit-bar-animation-chart.service';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-stats-card-front',
|
||||
styleUrls: ['./stats-card-front.component.scss'],
|
||||
templateUrl: './stats-card-front.component.html',
|
||||
})
|
||||
export class StatsCardFrontComponent {
|
||||
|
||||
private alive = true;
|
||||
|
||||
linesData: { firstLine: number[]; secondLine: number[] };
|
||||
|
||||
constructor(private profitBarAnimationChartService: ProfitBarAnimationChartService) {
|
||||
this.profitBarAnimationChartService.getChartData()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe((linesData) => {
|
||||
this.linesData = linesData;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<nb-flip-card [showToggleButton]="false" [flipped]="flipped">
|
||||
<nb-card-front>
|
||||
<nb-card size="xsmall">
|
||||
<ngx-stats-card-front></ngx-stats-card-front>
|
||||
<i class="nb-arrow-right" (click)="toggleView()"></i>
|
||||
</nb-card>
|
||||
</nb-card-front>
|
||||
<nb-card-back>
|
||||
<nb-card size="xsmall">
|
||||
<ngx-stats-card-back></ngx-stats-card-back>
|
||||
<i class="nb-arrow-right" (click)="toggleView()"></i>
|
||||
</nb-card>
|
||||
</nb-card-back>
|
||||
</nb-flip-card>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
.nb-arrow-right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
@include nb-rtl(right, auto);
|
||||
@include nb-rtl(left, 0);
|
||||
padding: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::ng-deep .flipped {
|
||||
.back-container {
|
||||
.nb-arrow-right {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.front-container {
|
||||
.nb-arrow-right {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-profit-card',
|
||||
styleUrls: ['./profit-card.component.scss'],
|
||||
templateUrl: './profit-card.component.html',
|
||||
})
|
||||
export class ProfitCardComponent {
|
||||
|
||||
flipped = false;
|
||||
|
||||
toggleView() {
|
||||
this.flipped = !this.flipped;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<nb-card size="medium">
|
||||
<nb-card-body>
|
||||
<div class="progress-info" *ngFor="let item of progressInfoData">
|
||||
<div class="title">{{ item.title }}</div>
|
||||
<div class="value">{{ item.value | ngxNumberWithCommas }}</div>
|
||||
<nb-progress-bar [value]="item.activeProgress"></nb-progress-bar>
|
||||
<div class="description">
|
||||
<bdi>{{ item.description }}</bdi>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
|
||||
$shadow-green: #00977e;
|
||||
|
||||
@include nb-install-component() {
|
||||
.progress-info {
|
||||
color: nb-theme(color-fg-heading);
|
||||
margin-top: 2.5rem;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: nb-theme(font-secondary);
|
||||
font-size: nb-theme(font-size-lg);
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 3rem;
|
||||
font-weight: nb-theme(font-weight-light);
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: nb-theme(color-fg);
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/deep/ nb-progress-bar {
|
||||
margin-top: 0.2rem;
|
||||
|
||||
.progress-container {
|
||||
height: 0.8rem;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.progress-value {
|
||||
height: 0.6rem;
|
||||
background: nb-theme(progress-bar-background);
|
||||
|
||||
@include nb-for-theme(cosmic) {
|
||||
box-shadow: 0 0.2rem $shadow-green;
|
||||
}
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-progress-section',
|
||||
styleUrls: ['./progress-section.component.scss'],
|
||||
templateUrl: './progress-section.component.html',
|
||||
})
|
||||
export class ECommerceProgressSectionComponent {
|
||||
progressInfoData = [
|
||||
{
|
||||
title: 'Today’s Profit',
|
||||
value: 572900,
|
||||
activeProgress: 70,
|
||||
description: 'Better than last week (70%)',
|
||||
},
|
||||
{
|
||||
title: 'New Orders',
|
||||
value: 6378,
|
||||
activeProgress: 30,
|
||||
description: 'Better than last week (30%)',
|
||||
},
|
||||
{
|
||||
title: 'New Comments',
|
||||
value: 200,
|
||||
activeProgress: 55,
|
||||
description: 'Better than last week (55%)',
|
||||
},
|
||||
];
|
||||
}
|
||||
11
src/app/pages/e-commerce/slide-out/slide-out.component.html
Normal file
11
src/app/pages/e-commerce/slide-out/slide-out.component.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<i class="show-hide-toggle"
|
||||
[class.nb-arrow-thin-left]="!showVisitorsStatistics"
|
||||
[class.nb-arrow-thin-right]="showVisitorsStatistics"
|
||||
(click)="toggleStatistics()"></i>
|
||||
<div class="slide-out-container"
|
||||
[class.expanded]="showVisitorsStatistics"
|
||||
[class.collapsed]="!showVisitorsStatistics">
|
||||
<div class="content-wrapper">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
113
src/app/pages/e-commerce/slide-out/slide-out.component.scss
Normal file
113
src/app/pages/e-commerce/slide-out/slide-out.component.scss
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
$slide-out-container-width: nb-theme(slide-out-container-width);
|
||||
|
||||
// toggle button
|
||||
.show-hide-toggle {
|
||||
position: absolute;
|
||||
display: block;
|
||||
font-size: 2rem;
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
top: 1.5rem;
|
||||
right: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: nb-theme(color-fg);
|
||||
background-color: transparent;
|
||||
z-index: 2;
|
||||
@include nb-rtl(right, auto);
|
||||
@include nb-rtl(left, 1.5rem);
|
||||
}
|
||||
// toggle button
|
||||
|
||||
.slide-out-container {
|
||||
position: absolute;
|
||||
padding: 1.5rem;
|
||||
width: $slide-out-container-width;
|
||||
}
|
||||
|
||||
.slide-out-container,
|
||||
.slide-out-container::before {
|
||||
display: block;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
.slide-out-container::before {
|
||||
content: '';
|
||||
right: 0;
|
||||
@include nb-rtl(right, auto);
|
||||
@include nb-rtl(left, 0);
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
background: nb-theme(slide-out-background);
|
||||
box-shadow: nb-theme(slide-out-shadow-color);
|
||||
@include nb-rtl(box-shadow, nb-theme(slide-out-shadow-color-rtl));
|
||||
filter: blur(3px);
|
||||
opacity: 0.9;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.slide-out-container.collapsed {
|
||||
left: calc(100% - 6rem);
|
||||
@include nb-rtl(left, auto);
|
||||
@include nb-rtl(right, calc(100% - 6rem));
|
||||
}
|
||||
|
||||
.slide-out-container.expanded {
|
||||
left: calc(100% - #{$slide-out-container-width});
|
||||
@include nb-rtl(left, auto);
|
||||
@include nb-rtl(right, calc(100% - #{$slide-out-container-width}));
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0 6rem;
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
.expanded .content-wrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
$slide-out-container-width: 50%;
|
||||
|
||||
.slide-out-container {
|
||||
width: $slide-out-container-width;
|
||||
}
|
||||
|
||||
.slide-out-container.expanded {
|
||||
left: calc(100% - #{$slide-out-container-width});
|
||||
@include nb-rtl(right, calc(100% - #{$slide-out-container-width}));
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(is) {
|
||||
$slide-out-container-width: 100%;
|
||||
|
||||
.show-hide-toggle {
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.slide-out-container {
|
||||
width: $slide-out-container-width;
|
||||
}
|
||||
|
||||
.slide-out-container.collapsed {
|
||||
left: calc(100% - 3rem);
|
||||
@include nb-rtl(right, calc(100% - 3rem));
|
||||
}
|
||||
|
||||
.slide-out-container.expanded {
|
||||
left: calc(100% - #{$slide-out-container-width});
|
||||
@include nb-rtl(right, calc(100% - #{$slide-out-container-width}));
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/app/pages/e-commerce/slide-out/slide-out.component.ts
Normal file
15
src/app/pages/e-commerce/slide-out/slide-out.component.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-slide-out',
|
||||
styleUrls: ['./slide-out.component.scss'],
|
||||
templateUrl: './slide-out.component.html',
|
||||
})
|
||||
export class SlideOutComponent {
|
||||
|
||||
@Input() showVisitorsStatistics: boolean = false;
|
||||
|
||||
toggleStatistics() {
|
||||
this.showVisitorsStatistics = !this.showVisitorsStatistics;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<nb-card-body>
|
||||
<ngx-traffic-bar-chart [data]="trafficBarData.data"
|
||||
[labels]="trafficBarData.labels"
|
||||
[formatter]="trafficBarData.formatter">
|
||||
</ngx-traffic-bar-chart>
|
||||
</nb-card-body>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
nb-card-body {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ngx-traffic-bar-chart {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/deep/ canvas {
|
||||
border-bottom-left-radius: nb-theme(card-border-radius);
|
||||
border-bottom-right-radius: nb-theme(card-border-radius);
|
||||
}
|
||||
|
||||
.echart {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import { Component, Input, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-traffic-back-card',
|
||||
styleUrls: ['./traffic-back-card.component.scss'],
|
||||
templateUrl: './traffic-back-card.component.html',
|
||||
})
|
||||
export class TrafficBackCardComponent implements OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
@Input() trafficBarData: any;
|
||||
|
||||
currentTheme: string;
|
||||
|
||||
constructor(private themeService: NbThemeService) {
|
||||
this.themeService.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(theme => {
|
||||
this.currentTheme = theme.name;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
import { AfterViewInit, Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
declare const echarts: any;
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-traffic-bar-chart',
|
||||
styleUrls: ['traffic-back-card.component.scss'],
|
||||
template: `
|
||||
<div echarts [options]="option" class="echart" (chartInit)="onChartInit($event)"></div>
|
||||
`,
|
||||
})
|
||||
export class TrafficBarChartComponent implements AfterViewInit, OnDestroy, OnChanges {
|
||||
|
||||
@Input() data: number[];
|
||||
@Input() labels: string[];
|
||||
@Input() formatter: string;
|
||||
|
||||
private alive = true;
|
||||
|
||||
option: any = {};
|
||||
echartsInstance;
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.echartsInstance = ec;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (!changes.data.isFirstChange() && !changes.labels.isFirstChange()) {
|
||||
this.echartsInstance.setOption({
|
||||
series: [{
|
||||
data: this.data,
|
||||
}],
|
||||
xAxis: {
|
||||
data: this.labels,
|
||||
},
|
||||
tooltip: {
|
||||
formatter: this.formatter,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(config => {
|
||||
const trafficTheme: any = config.variables.trafficBarEchart;
|
||||
|
||||
this.option = Object.assign({}, {
|
||||
grid: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type : 'category',
|
||||
data : this.labels,
|
||||
axisLabel: {
|
||||
color: trafficTheme.axisTextColor,
|
||||
fontSize: trafficTheme.axisFontSize,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
show: false,
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
boundaryGap: [0, '5%'],
|
||||
},
|
||||
tooltip: {
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
textStyle: {
|
||||
color: trafficTheme.tooltipTextColor,
|
||||
fontWeight: trafficTheme.tooltipFontWeight,
|
||||
fontSize: 16,
|
||||
},
|
||||
position: 'top',
|
||||
backgroundColor: trafficTheme.tooltipBg,
|
||||
borderColor: trafficTheme.tooltipBorderColor,
|
||||
borderWidth: 3,
|
||||
formatter: this.formatter,
|
||||
extraCssText: trafficTheme.tooltipExtraCss,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: '40%',
|
||||
data: this.data,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: trafficTheme.gradientFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: trafficTheme.gradientTo,
|
||||
}]),
|
||||
opacity: 1,
|
||||
shadowColor: trafficTheme.gradientFrom,
|
||||
shadowBlur: trafficTheme.shadowBlur,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<div class="traffic-bar">
|
||||
<div class="period-from">{{ barData.prevDate }}</div>
|
||||
<div class="vertical-progress-bar">
|
||||
<div class="progress-line"
|
||||
[style.height.%]="barData.prevValue">
|
||||
</div>
|
||||
</div>
|
||||
<div class="vertical-progress-bar">
|
||||
<div class="progress-line"
|
||||
[style.height.%]="barData.nextValue"
|
||||
[class.success]="successDelta"
|
||||
[class.failure]="!successDelta">
|
||||
</div>
|
||||
</div>
|
||||
<div class="period-to">{{ barData.nextDate }}</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
@import '../../../../../@theme/styles/themes';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/hero-buttons';
|
||||
@import '~@nebular/theme/styles/global/typography/typography';
|
||||
|
||||
$traffic-bar-background-color: #d0d8e3;
|
||||
|
||||
@include nb-install-component() {
|
||||
height: 100%;
|
||||
|
||||
.traffic-bar {
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
> * {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.period-from, .period-to {
|
||||
align-self: flex-end;
|
||||
font-size: 1rem;
|
||||
line-height: 0.8;
|
||||
}
|
||||
|
||||
.period-from {
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
@include nb-rtl(right, inherit);
|
||||
@include nb-rtl(left, 100%);
|
||||
bottom: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.vertical-progress-bar {
|
||||
height: 100%;
|
||||
width: 0.7rem;
|
||||
transform: rotate(180deg);
|
||||
|
||||
.progress-line {
|
||||
width: 100%;
|
||||
background-color: $traffic-bar-background-color;
|
||||
|
||||
&.success {
|
||||
background-color: text-success();
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
background-color: text-primary();
|
||||
}
|
||||
}
|
||||
|
||||
&.failure {
|
||||
background-color: text-danger();
|
||||
}
|
||||
}
|
||||
|
||||
@include nb-for-theme(cosmic) {
|
||||
.progress-line {
|
||||
background: linear-gradient(180deg, #a054fe 0%, #7a58ff 100%);
|
||||
|
||||
&.success {
|
||||
background: linear-gradient(0deg, #00caba 0%, #00d77f 100%);
|
||||
}
|
||||
|
||||
&.failure {
|
||||
background: linear-gradient(180deg, #ff38ab 0%, #ff386c 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-traffic-bar',
|
||||
styleUrls: ['./traffic-bar.component.scss'],
|
||||
templateUrl: './traffic-bar.component.html',
|
||||
})
|
||||
export class TrafficBarComponent {
|
||||
|
||||
@Input() barData: { prevDate: string; prevValue: number; nextDate: string; nextValue: number };
|
||||
@Input() successDelta: boolean;
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<nb-card-body>
|
||||
<ul class="traffic-list">
|
||||
<li *ngFor="let item of frontCardData; trackBy: trackByDate">
|
||||
<div class="date">{{ item.date }}</div>
|
||||
<div class="value">{{ item.value }}</div>
|
||||
<div class="delta"
|
||||
[class.up]="item.delta.up"
|
||||
[class.down]="!item.delta.up">
|
||||
{{ item.delta.value }}%
|
||||
</div>
|
||||
<ngx-traffic-bar [barData]="item.comparison"
|
||||
[successDelta]="item.delta.up">
|
||||
</ngx-traffic-bar>
|
||||
</li>
|
||||
</ul>
|
||||
</nb-card-body>
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
@import '~@nebular/theme/components/card/card.component.theme';
|
||||
@import '~@nebular/theme/styles/global/typography/typography';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
overflow: auto;
|
||||
|
||||
nb-card-body {
|
||||
padding: 0;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.traffic-list {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 4.5rem;
|
||||
font-size: 1.25rem;
|
||||
position: relative;
|
||||
color: nb-theme(color-fg);
|
||||
padding: nb-theme(card-padding);
|
||||
border-bottom:
|
||||
nb-theme(list-item-border-width)
|
||||
nb-theme(card-header-border-type)
|
||||
nb-theme(separator);
|
||||
|
||||
&:hover {
|
||||
background-color: nb-theme(layout-bg);
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 100%;
|
||||
width: 6px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: nb-theme(color-success);
|
||||
border-radius: nb-theme(radius);
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
ngx-traffic-bar {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.date {
|
||||
@include nb-for-theme(corporate) {
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
|
||||
.delta {
|
||||
display: flex;
|
||||
padding-left: 0.5rem;
|
||||
font-size: 1rem;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
right: 100%;
|
||||
margin-right: 0.7rem;
|
||||
@include nb-rtl(margin-left, 0.7rem);
|
||||
@include nb-rtl(margin-right, 0);
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
}
|
||||
|
||||
&.down {
|
||||
color: text-danger();
|
||||
|
||||
&::before {
|
||||
bottom: 5px;
|
||||
border-top: 6px solid text-danger();
|
||||
}
|
||||
}
|
||||
|
||||
&.up {
|
||||
color: text-success();
|
||||
|
||||
&::before {
|
||||
top: 5px;
|
||||
border-bottom: 6px solid text-success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include nb-for-theme(cosmic) {
|
||||
.traffic-list li {
|
||||
&:hover {
|
||||
&::before {
|
||||
$color-top: nb-theme(btn-success-bg);
|
||||
$color-bottom: btn-hero-success-left-color();
|
||||
|
||||
background-image: linear-gradient(to top, $color-top, $color-bottom);
|
||||
box-shadow: 0 0 16px -2px btn-hero-success-middle-color();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
.traffic-list li {
|
||||
border-color: nb-theme(tabs-separator);
|
||||
|
||||
&:first-child {
|
||||
border-top:
|
||||
nb-theme(list-item-border-width)
|
||||
nb-theme(card-header-border-type)
|
||||
nb-theme(separator);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
background-color: nb-theme(color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date {
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
|
||||
.delta {
|
||||
&.up {
|
||||
color: text-primary();
|
||||
|
||||
&::before {
|
||||
border-bottom-color: text-primary();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(is) {
|
||||
ngx-traffic-bar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { Component, Input, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
import { TrafficList } from '../../../../@core/data/traffic-list.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-traffic-front-card',
|
||||
styleUrls: ['./traffic-front-card.component.scss'],
|
||||
templateUrl: './traffic-front-card.component.html',
|
||||
})
|
||||
export class TrafficFrontCardComponent implements OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
@Input() frontCardData: TrafficList;
|
||||
|
||||
currentTheme: string;
|
||||
|
||||
constructor(private themeService: NbThemeService) {
|
||||
this.themeService.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(theme => {
|
||||
this.currentTheme = theme.name;
|
||||
});
|
||||
}
|
||||
|
||||
trackByDate(_, item) {
|
||||
return item.date;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<nb-card-header>
|
||||
<span>Traffic</span>
|
||||
<div class="dropdown ghost-dropdown" ngbDropdown>
|
||||
<button type="button" class="btn btn-sm" ngbDropdownToggle
|
||||
[ngClass]="{
|
||||
'btn-success': currentTheme === 'default',
|
||||
'btn-primary': currentTheme !== 'default'}">
|
||||
{{ type }}
|
||||
</button>
|
||||
<ul ngbDropdownMenu class="dropdown-menu">
|
||||
<li class="dropdown-item" *ngFor="let period of types" (click)="changePeriod(period)">
|
||||
{{ period }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
nb-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.675rem 4rem 0.5rem 1.25rem;
|
||||
@include nb-rtl(padding-right, 1.25rem);
|
||||
@include nb-rtl(padding-left, 4rem);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-traffic-cards-header',
|
||||
styleUrls: ['./traffic-cards-header.component.scss'],
|
||||
templateUrl: './traffic-cards-header.component.html',
|
||||
})
|
||||
export class TrafficCardsHeaderComponent implements OnDestroy {
|
||||
private alive = true;
|
||||
|
||||
@Output() periodChange = new EventEmitter<string>();
|
||||
|
||||
@Input() type: string = 'week';
|
||||
|
||||
types: string[] = ['week', 'month', 'year'];
|
||||
currentTheme: string;
|
||||
|
||||
constructor(private themeService: NbThemeService) {
|
||||
this.themeService.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(theme => {
|
||||
this.currentTheme = theme.name;
|
||||
});
|
||||
}
|
||||
|
||||
changePeriod(period: string): void {
|
||||
this.type = period;
|
||||
this.periodChange.emit(period);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<nb-reveal-card [showToggleButton]="false" [revealed]="revealed">
|
||||
<nb-card-front>
|
||||
<nb-card size="small">
|
||||
<ngx-traffic-cards-header (periodChange)="setPeriod($event)"></ngx-traffic-cards-header>
|
||||
<ngx-traffic-front-card [frontCardData]="trafficListData"></ngx-traffic-front-card>
|
||||
<i class="nb-arrow-up" (click)="toggleView()"></i>
|
||||
</nb-card>
|
||||
</nb-card-front>
|
||||
<nb-card-back>
|
||||
<nb-card size="small">
|
||||
<ngx-traffic-cards-header (periodChange)="setPeriod($event)"></ngx-traffic-cards-header>
|
||||
<ngx-traffic-back-card [trafficBarData]="trafficBarData"></ngx-traffic-back-card>
|
||||
<i class="nb-arrow-down" (click)="toggleView()"></i>
|
||||
</nb-card>>
|
||||
</nb-card-back>
|
||||
</nb-reveal-card>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
.nb-arrow-up, .nb-arrow-down {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@include nb-rtl(right, auto);
|
||||
@include nb-rtl(left, 0);
|
||||
padding: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nb-card-back {
|
||||
/deep/ nb-card-header {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { TrafficList, TrafficListService } from '../../../@core/data/traffic-list.service';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
import { TrafficBar, TrafficBarService } from '../../../@core/data/traffic-bar.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-traffic-reveal-card',
|
||||
styleUrls: ['./traffic-reveal-card.component.scss'],
|
||||
templateUrl: './traffic-reveal-card.component.html',
|
||||
})
|
||||
export class TrafficRevealCardComponent implements OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
|
||||
trafficBarData: TrafficBar;
|
||||
trafficListData: TrafficList;
|
||||
revealed = false;
|
||||
period: string = 'week';
|
||||
|
||||
constructor(private trafficListService: TrafficListService,
|
||||
private trafficBarService: TrafficBarService) {
|
||||
this.getTrafficFrontCardData(this.period);
|
||||
this.getTrafficBackCardData(this.period);
|
||||
}
|
||||
|
||||
toggleView() {
|
||||
this.revealed = !this.revealed;
|
||||
}
|
||||
|
||||
setPeriod(value: string): void {
|
||||
this.getTrafficFrontCardData(value);
|
||||
this.getTrafficBackCardData(value);
|
||||
}
|
||||
|
||||
getTrafficBackCardData(period: string) {
|
||||
this.trafficBarService.getTrafficBarData(period)
|
||||
.pipe(takeWhile(() => this.alive ))
|
||||
.subscribe(trafficBarData => {
|
||||
this.trafficBarData = trafficBarData;
|
||||
});
|
||||
}
|
||||
|
||||
getTrafficFrontCardData(period: string) {
|
||||
this.trafficListService.getTrafficListData(period)
|
||||
.pipe(takeWhile(() => this.alive ))
|
||||
.subscribe(trafficListData => {
|
||||
this.trafficListData = trafficListData;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
@import '~@nebular/theme/styles/global/typography/typography';
|
||||
|
||||
@include nb-install-component() {
|
||||
nb-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: none;
|
||||
}
|
||||
|
||||
nb-card-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.user-activity-list {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-activity-list li {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
position: relative;
|
||||
color: nb-theme(color-fg);
|
||||
padding: nb-theme(card-padding);
|
||||
border-bottom:
|
||||
nb-theme(list-item-border-width)
|
||||
nb-theme(card-header-border-type)
|
||||
nb-theme(separator);
|
||||
|
||||
&:first-child {
|
||||
border-top:
|
||||
nb-theme(list-item-border-width)
|
||||
nb-theme(card-header-border-type)
|
||||
nb-theme(separator);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: nb-theme(layout-bg);
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 100%;
|
||||
width: 6px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: nb-theme(color-success);
|
||||
border-radius: nb-theme(radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.visited-date,
|
||||
.value,
|
||||
.title {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.visited-date {
|
||||
color: nb-theme(color-fg-heading);
|
||||
|
||||
@include nb-for-theme(cosmic) {
|
||||
color: nb-theme(color-fg);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.value {
|
||||
margin-top: 0.5rem;
|
||||
color: nb-theme(color-success);
|
||||
}
|
||||
|
||||
.delta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
right: 100%;
|
||||
margin-right: 0.7rem;
|
||||
@include nb-rtl(margin-right, 0);
|
||||
@include nb-rtl(margin-left, 0.7rem);
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
}
|
||||
|
||||
&.down {
|
||||
color: text-danger();
|
||||
|
||||
&::before {
|
||||
border-top: 6px solid text-danger();
|
||||
}
|
||||
}
|
||||
|
||||
&.up {
|
||||
color: text-success();
|
||||
|
||||
&::before {
|
||||
border-bottom: 6px solid text-success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include nb-for-theme(cosmic) {
|
||||
.user-activity-list li {
|
||||
&:hover {
|
||||
&::before {
|
||||
$color-top: nb-theme(btn-success-bg);
|
||||
$color-bottom: btn-hero-success-left-color();
|
||||
|
||||
background-image: linear-gradient(to top, $color-top, $color-bottom);
|
||||
box-shadow: 0 0 16px -2px btn-hero-success-middle-color();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delta {
|
||||
&.down, &.up {
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include nb-for-theme(corporate) {
|
||||
.user-activity-list li {
|
||||
border-color: nb-theme(tabs-separator);
|
||||
|
||||
&:first-child {
|
||||
border-color: nb-theme(tabs-separator);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
background-color: nb-theme(color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.visited-pages-count {
|
||||
.value {
|
||||
color: nb-theme(color-fg-heading);
|
||||
}
|
||||
}
|
||||
|
||||
.delta {
|
||||
&.up {
|
||||
color: text-primary();
|
||||
|
||||
&::before {
|
||||
border-bottom-color: text-primary();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
|
||||
import { UserActivityService, UserActive } from '../../../@core/data/user-activity.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-user-activity',
|
||||
styleUrls: ['./user-activity.component.scss'],
|
||||
template: `
|
||||
<nb-card size="medium">
|
||||
<nb-card-header>
|
||||
<span>User Activity</span>
|
||||
<div class="dropdown ghost-dropdown" ngbDropdown>
|
||||
<button type="button" class="btn btn-sm" ngbDropdownToggle
|
||||
[ngClass]="{ 'btn-success': currentTheme == 'default', 'btn-primary': currentTheme != 'default'}">
|
||||
{{ type }}
|
||||
</button>
|
||||
<ul ngbDropdownMenu class="dropdown-menu">
|
||||
<li class="dropdown-item"
|
||||
*ngFor="let t of types"
|
||||
(click)="getUserActivity(t); type = t">
|
||||
{{ t }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<ul class="user-activity-list">
|
||||
<li *ngFor="let item of userActivity">
|
||||
<div class="visited-date">
|
||||
{{ item.date }}
|
||||
</div>
|
||||
<div class="visited-pages-count">
|
||||
<div class="title">Pages Visit</div>
|
||||
<div class="value">{{ item.pagesVisitCount }}</div>
|
||||
</div>
|
||||
<div class="visited-percentages">
|
||||
<div class="title">New visits, %</div>
|
||||
<div class="delta value"
|
||||
[class.up]="item.deltaUp"
|
||||
[class.down]="!item.deltaUp">
|
||||
{{ item.newVisits }}%
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
`,
|
||||
})
|
||||
export class ECommerceUserActivityComponent implements OnDestroy, OnInit {
|
||||
|
||||
private alive = true;
|
||||
|
||||
userActivity: UserActive[] = [];
|
||||
type = 'month';
|
||||
types = ['week', 'month', 'year'];
|
||||
currentTheme: string;
|
||||
|
||||
constructor(private themeService: NbThemeService,
|
||||
private userActivityService: UserActivityService) {
|
||||
this.themeService.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(theme => {
|
||||
this.currentTheme = theme.name;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getUserActivity(this.type);
|
||||
}
|
||||
|
||||
getUserActivity(period: string) {
|
||||
this.userActivityService.getUserActivityData(period)
|
||||
.subscribe(userActivityData => {
|
||||
this.userActivity = userActivityData;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
display: block;
|
||||
height: 290px;
|
||||
width: 100%;
|
||||
|
||||
.echart {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
import { delay, takeWhile } from 'rxjs/operators';
|
||||
import { AfterViewInit, Component, OnDestroy } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-visitors-analytics-chart',
|
||||
styleUrls: ['./visitors-analytics-chart.component.scss'],
|
||||
template: `
|
||||
<div echarts [options]="option" class="echart"></div>
|
||||
`,
|
||||
})
|
||||
export class ECommerceVisitorsAnalyticsChartComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
private alive = true;
|
||||
private innerLinePoints: number[] = [
|
||||
94, 188, 225, 244, 253, 254, 249, 235, 208,
|
||||
173, 141, 118, 105, 97, 94, 96, 104, 121, 147,
|
||||
183, 224, 265, 302, 333, 358, 375, 388, 395,
|
||||
400, 400, 397, 390, 377, 360, 338, 310, 278,
|
||||
241, 204, 166, 130, 98, 71, 49, 32, 20, 13, 9,
|
||||
];
|
||||
private outerLinePoints: number[] = [
|
||||
85, 71, 59, 50, 45, 42, 41, 44 , 58, 88,
|
||||
136 , 199, 267, 326, 367, 391, 400, 397,
|
||||
376, 319, 200, 104, 60, 41, 36, 37, 44,
|
||||
55, 74, 100 , 131, 159, 180, 193, 199, 200,
|
||||
195, 184, 164, 135, 103, 73, 50, 33, 22, 15, 11,
|
||||
];
|
||||
private months: string[] = [
|
||||
'Jan', 'Feb', 'Mar',
|
||||
'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep',
|
||||
'Oct', 'Nov', 'Dec',
|
||||
];
|
||||
|
||||
option: any;
|
||||
data: Array<any>;
|
||||
themeSubscription: any;
|
||||
|
||||
constructor(private theme: NbThemeService) {
|
||||
const outerLinePointsLength = this.outerLinePoints.length;
|
||||
const monthsLength = this.months.length;
|
||||
|
||||
this.data = this.outerLinePoints.map((p, index) => {
|
||||
const monthIndex = Math.round(index / 4);
|
||||
const label = (index % Math.round(outerLinePointsLength / monthsLength) === 0)
|
||||
? this.months[monthIndex]
|
||||
: '';
|
||||
|
||||
return {
|
||||
label,
|
||||
value: p,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.theme.getJsTheme()
|
||||
.pipe(
|
||||
delay(1),
|
||||
takeWhile(() => this.alive),
|
||||
)
|
||||
.subscribe(config => {
|
||||
const eTheme: any = config.variables.visitors;
|
||||
|
||||
this.setOptions(eTheme);
|
||||
});
|
||||
}
|
||||
|
||||
setOptions(eTheme) {
|
||||
this.option = {
|
||||
grid: {
|
||||
left: 40,
|
||||
top: 20,
|
||||
right: 0,
|
||||
bottom: 60,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
color: eTheme.tooltipLineColor,
|
||||
width: eTheme.tooltipLineWidth,
|
||||
},
|
||||
},
|
||||
textStyle: {
|
||||
color: eTheme.tooltipTextColor,
|
||||
fontSize: 20,
|
||||
fontWeight: eTheme.tooltipFontWeight,
|
||||
},
|
||||
position: 'top',
|
||||
backgroundColor: eTheme.tooltipBg,
|
||||
borderColor: eTheme.tooltipBorderColor,
|
||||
borderWidth: 3,
|
||||
formatter: (params) => {
|
||||
return Math.round(parseInt(params[0].value, 10));
|
||||
},
|
||||
extraCssText: eTheme.tooltipExtraCss,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
offset: 25,
|
||||
data: this.data.map(i => i.label),
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: eTheme.axisTextColor,
|
||||
fontSize: eTheme.axisFontSize,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: eTheme.axisLineColor,
|
||||
width: '2',
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: eTheme.axisLineColor,
|
||||
width: '1',
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: eTheme.axisTextColor,
|
||||
fontSize: eTheme.axisFontSize,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
|
||||
lineStyle: {
|
||||
color: eTheme.yAxisSplitLine,
|
||||
width: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
this.getInnerLine(eTheme),
|
||||
this.getOuterLine(eTheme),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
getOuterLine(eTheme) {
|
||||
return {
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbolSize: 20,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0,
|
||||
},
|
||||
emphasis: {
|
||||
color: '#ffffff',
|
||||
borderColor: eTheme.itemBorderColor,
|
||||
borderWidth: 2,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: eTheme.lineWidth,
|
||||
type: eTheme.lineStyle,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.lineGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.lineGradTo,
|
||||
}]),
|
||||
shadowColor: eTheme.lineShadow,
|
||||
shadowBlur: 6,
|
||||
shadowOffsetY: 12,
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.areaGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.areaGradTo,
|
||||
}]),
|
||||
},
|
||||
},
|
||||
data: this.data.map(i => i.value),
|
||||
};
|
||||
}
|
||||
|
||||
getInnerLine(eTheme) {
|
||||
return {
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbolSize: 20,
|
||||
tooltip: {
|
||||
show: false,
|
||||
extraCssText: '',
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 0,
|
||||
},
|
||||
emphasis: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: eTheme.innerLineWidth,
|
||||
type: eTheme.innerLineStyle,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1),
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: eTheme.innerAreaGradFrom,
|
||||
}, {
|
||||
offset: 1,
|
||||
color: eTheme.innerAreaGradTo,
|
||||
}]),
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
data: this.innerLinePoints,
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<nb-card size="medium">
|
||||
<nb-card-header>
|
||||
<div class="title">Visitors Analytics</div>
|
||||
<div class="sub-title">Consumption</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<ngx-legend-chart [legendItems]="chartLegend"></ngx-legend-chart>
|
||||
</div>
|
||||
<ngx-visitors-analytics-chart></ngx-visitors-analytics-chart>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
|
||||
<ngx-slide-out [showVisitorsStatistics]="true">
|
||||
<ngx-visitors-statistics></ngx-visitors-statistics>
|
||||
</ngx-slide-out>
|
||||
</nb-card>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
|
||||
|
||||
@include nb-install-component() {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
nb-card {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
nb-card-header {
|
||||
border-bottom: none;
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
color: nb-theme(color-fg);
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 2.125rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(is) {
|
||||
ngx-legend-chart {
|
||||
/deep/ .legends {
|
||||
padding-left: 0;
|
||||
font-size: nb-theme(font-size-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-ecommerce-visitors-analytics',
|
||||
styleUrls: ['./visitors-analytics.component.scss'],
|
||||
templateUrl: './visitors-analytics.component.html',
|
||||
})
|
||||
export class ECommerceVisitorsAnalyticsComponent implements OnDestroy {
|
||||
private alive = true;
|
||||
|
||||
chartLegend: {iconColor: string; title: string}[];
|
||||
|
||||
constructor(private themeService: NbThemeService) {
|
||||
this.themeService.getJsTheme()
|
||||
.pipe(takeWhile(() => this.alive))
|
||||
.subscribe(theme => {
|
||||
this.setLegendItems(theme.variables.visitorsLegend);
|
||||
});
|
||||
}
|
||||
|
||||
setLegendItems(visitorsLegend): void {
|
||||
this.chartLegend = [
|
||||
{
|
||||
iconColor: visitorsLegend.firstIcon,
|
||||
title: 'Unique Visitors',
|
||||
},
|
||||
{
|
||||
iconColor: visitorsLegend.secondIcon,
|
||||
title: 'Page Views',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<div class="visitors-statistics">
|
||||
<div class="statistics-header">
|
||||
<div class="visitors-value">1,100</div>
|
||||
<div class="visitors-title">New Visitors</div>
|
||||
</div>
|
||||
<div class="statistics-chart">
|
||||
<div echarts [options]="option" class="echart"></div>
|
||||
</div>
|
||||
<div class="statistics-footer">
|
||||
<div class="chart-values">
|
||||
<span class="chart-value">25%</span>
|
||||
<span class="chart-value">75%</span>
|
||||
</div>
|
||||
<ngx-legend-chart class="visitors-statistics-legend" [legendItems]="chartLegend"></ngx-legend-chart>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
@import '../../../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
|
||||
.visitors-value {
|
||||
font-size: 3rem;
|
||||
font-weight: nb-theme(font-weight-light);
|
||||
color: nb-theme(color-fg-heading);
|
||||
line-height: 0.8;
|
||||
}
|
||||
|
||||
.visitors-title {
|
||||
margin-top: 1rem;
|
||||
font-size: 1.25rem;
|
||||
color: nb-theme(color-fg);
|
||||
}
|
||||
|
||||
.visitors-statistics {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.statistics-chart {
|
||||
margin-top: 3.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
width: 100%;
|
||||
|
||||
.echart {
|
||||
display: block;
|
||||
height: 190px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-values {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chart-value {
|
||||
color: nb-theme(color-fg-heading);
|
||||
font-size: 2rem;
|
||||
font-weight: nb-theme(font-weight-bold);
|
||||
margin-bottom: 1rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
// legend start
|
||||
.visitors-statistics-legend {
|
||||
/deep/ .legends {
|
||||
padding: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/deep/ .legend {
|
||||
flex: 1;
|
||||
margin-left: 0;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
// legend end
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue