mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-01-08 19:45:08 +00:00
(dashboards/finance) Added finance dashboard
(dashboards/crypto) Added crypto dashboard
This commit is contained in:
parent
569809aabb
commit
1581ea74cc
|
@ -79,6 +79,8 @@ export const appRoutes: Route[] = [
|
|||
{path: 'dashboards', children: [
|
||||
{path: 'project', loadChildren: () => import('app/modules/admin/dashboards/project/project.module').then(m => m.ProjectModule)},
|
||||
{path: 'analytics', loadChildren: () => import('app/modules/admin/dashboards/analytics/analytics.module').then(m => m.AnalyticsModule)},
|
||||
{path: 'finance', loadChildren: () => import('app/modules/admin/dashboards/finance/finance.module').then(m => m.FinanceModule)},
|
||||
{path: 'crypto', loadChildren: () => import('app/modules/admin/dashboards/crypto/crypto.module').then(m => m.CryptoModule)},
|
||||
]},
|
||||
|
||||
// Apps
|
||||
|
|
|
@ -22,6 +22,20 @@ export const defaultNavigation: FuseNavigationItem[] = [
|
|||
type : 'basic',
|
||||
icon : 'heroicons_outline:chart-pie',
|
||||
link : '/dashboards/analytics'
|
||||
},
|
||||
{
|
||||
id : 'dashboards.finance',
|
||||
title: 'Finance',
|
||||
type : 'basic',
|
||||
icon : 'heroicons_outline:cash',
|
||||
link : '/dashboards/finance'
|
||||
},
|
||||
{
|
||||
id : 'dashboards.crypto',
|
||||
title: 'Crypto',
|
||||
type : 'basic',
|
||||
icon : 'heroicons_outline:currency-dollar',
|
||||
link : '/dashboards/crypto'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
38
src/app/mock-api/dashboards/crypto/api.ts
Normal file
38
src/app/mock-api/dashboards/crypto/api.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FuseMockApiService } from '@fuse/lib/mock-api';
|
||||
import { crypto as cryptoData } from 'app/mock-api/dashboards/crypto/data';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CryptoMockApi
|
||||
{
|
||||
private _crypto: any = cryptoData;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(private _fuseMockApiService: FuseMockApiService)
|
||||
{
|
||||
// Register Mock API handlers
|
||||
this.registerHandlers();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Register Mock API handlers
|
||||
*/
|
||||
registerHandlers(): void
|
||||
{
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Crypto - GET
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
this._fuseMockApiService
|
||||
.onGet('api/dashboards/crypto')
|
||||
.reply(() => [200, cloneDeep(this._crypto)]);
|
||||
}
|
||||
}
|
1196
src/app/mock-api/dashboards/crypto/data.ts
Normal file
1196
src/app/mock-api/dashboards/crypto/data.ts
Normal file
File diff suppressed because it is too large
Load Diff
38
src/app/mock-api/dashboards/finance/api.ts
Normal file
38
src/app/mock-api/dashboards/finance/api.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FuseMockApiService } from '@fuse/lib/mock-api';
|
||||
import { finance as financeData } from 'app/mock-api/dashboards/finance/data';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FinanceMockApi
|
||||
{
|
||||
private _finance: any = financeData;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(private _fuseMockApiService: FuseMockApiService)
|
||||
{
|
||||
// Register Mock API handlers
|
||||
this.registerHandlers();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Register Mock API handlers
|
||||
*/
|
||||
registerHandlers(): void
|
||||
{
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Sales - GET
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
this._fuseMockApiService
|
||||
.onGet('api/dashboards/finance')
|
||||
.reply(() => [200, cloneDeep(this._finance)]);
|
||||
}
|
||||
}
|
1045
src/app/mock-api/dashboards/finance/data.ts
Normal file
1045
src/app/mock-api/dashboards/finance/data.ts
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -5,8 +5,10 @@ import { AuthMockApi } from 'app/mock-api/common/auth/api';
|
|||
import { CalendarMockApi } from 'app/mock-api/apps/calendar/api';
|
||||
import { ChatMockApi } from 'app/mock-api/apps/chat/api';
|
||||
import { ContactsMockApi } from 'app/mock-api/apps/contacts/api';
|
||||
import { CryptoMockApi } from 'app/mock-api/dashboards/crypto/api';
|
||||
import { ECommerceInventoryMockApi } from 'app/mock-api/apps/ecommerce/inventory/api';
|
||||
import { FileManagerMockApi } from 'app/mock-api/apps/file-manager/api';
|
||||
import { FinanceMockApi } from 'app/mock-api/dashboards/finance/api';
|
||||
import { HelpCenterMockApi } from 'app/mock-api/apps/help-center/api';
|
||||
import { IconsMockApi } from 'app/mock-api/ui/icons/api';
|
||||
import { MailboxMockApi } from 'app/mock-api/apps/mailbox/api';
|
||||
|
@ -29,8 +31,10 @@ export const mockApiServices = [
|
|||
CalendarMockApi,
|
||||
ChatMockApi,
|
||||
ContactsMockApi,
|
||||
CryptoMockApi,
|
||||
ECommerceInventoryMockApi,
|
||||
FileManagerMockApi,
|
||||
FinanceMockApi,
|
||||
HelpCenterMockApi,
|
||||
IconsMockApi,
|
||||
MailboxMockApi,
|
||||
|
|
277
src/app/modules/admin/dashboards/crypto/crypto.component.html
Normal file
277
src/app/modules/admin/dashboards/crypto/crypto.component.html
Normal file
|
@ -0,0 +1,277 @@
|
|||
<div class="absolute inset-0 flex flex-col min-w-0 overflow-hidden">
|
||||
|
||||
<mat-drawer-container class="flex-auto h-full">
|
||||
|
||||
<!-- Drawer -->
|
||||
<mat-drawer
|
||||
class="w-80"
|
||||
[autoFocus]="false"
|
||||
[mode]="drawerMode"
|
||||
[opened]="drawerOpened"
|
||||
#matDrawer>
|
||||
|
||||
<div class="flex flex-col flex-auto h-full dark:bg-default">
|
||||
|
||||
<!-- Watchlist -->
|
||||
<div class="flex flex-col flex-0">
|
||||
<div
|
||||
class="flex flex-0 items-center p-5 border-b"
|
||||
*ngFor="let item of data.watchlist">
|
||||
<div class="flex flex-col flex-auto pr-6">
|
||||
<div class="flex items-baseline">
|
||||
<div class="mr-1 font-medium text-md text-secondary">{{item.title}}</div>
|
||||
<div class="font-medium text-sm text-hint uppercase tracking-wider">({{item.iso}})</div>
|
||||
</div>
|
||||
<div class="flex items-end mt-2">
|
||||
<div class="min-w-20 font-mono text-2xl tracking-tighter leading-none">
|
||||
{{item.amount | currency:'USD':'symbol':'1.2-4'}}
|
||||
</div>
|
||||
<mat-icon
|
||||
class="text-green-500 icon-size-3.5 mx-0.5 mb-px"
|
||||
[ngClass]="{'text-green-500': item.trend.dir === 'up', 'text-red-500': item.trend.dir === 'down'}"
|
||||
[svgIcon]="item.trend.dir === 'up' ? 'heroicons_solid:arrow-narrow-up' : 'heroicons_solid:arrow-narrow-down'"></mat-icon>
|
||||
<div
|
||||
class="font-mono font-medium text-sm leading-none mb-px"
|
||||
[ngClass]="{'text-green-500': item.trend.dir === 'up', 'text-red-500': item.trend.dir === 'down'}">
|
||||
{{item.trend.amount}}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<apx-chart
|
||||
class="flex flex-auto items-center h-10 overflow-hidden"
|
||||
[chart]="watchlistChartOptions.chart"
|
||||
[colors]="item.trend.dir === 'up' ? ['#48BB78']: ['#F56565']"
|
||||
[series]="item.series"
|
||||
[stroke]="watchlistChartOptions.stroke"
|
||||
[tooltip]="watchlistChartOptions.tooltip"
|
||||
[xaxis]="watchlistChartOptions.xaxis"></apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Buy / Sell -->
|
||||
<div class="flex flex-col flex-auto flex-shrink-0 pt-6 bg-gray-50 dark:bg-transparent">
|
||||
|
||||
<!-- Action -->
|
||||
<div class="flex flex-col px-6 pb-2">
|
||||
<mat-form-field>
|
||||
<mat-label>Action</mat-label>
|
||||
<span
|
||||
class="flex items-center justify-center"
|
||||
matPrefix>
|
||||
<ng-container *ngIf="buySellSelect.value === 'buy'">
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:download'"></mat-icon>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="buySellSelect.value === 'sell'">
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:upload'"></mat-icon>
|
||||
</ng-container>
|
||||
</span>
|
||||
<mat-select
|
||||
[value]="'buy'"
|
||||
#buySellSelect="matSelect">
|
||||
<mat-option [value]="'buy'">Buy</mat-option>
|
||||
<mat-option [value]="'sell'">Sell</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Wallet -->
|
||||
<div class="flex flex-col px-6 pb-2">
|
||||
<mat-form-field class="w-full">
|
||||
<mat-label>Wallet</mat-label>
|
||||
<mat-select
|
||||
[value]="'btc'"
|
||||
#walletSelector="matSelect">
|
||||
<mat-select-trigger>
|
||||
<span class="flex items-center">
|
||||
<span>{{walletSelector.triggerValue}}</span>
|
||||
<span class="mx-1 text-hint">-</span>
|
||||
<span class="flex items-center font-mono">
|
||||
<span>{{data.wallets[walletSelector.value]}}</span>
|
||||
<span class="ml-1">{{walletSelector.value | uppercase}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</mat-select-trigger>
|
||||
<mat-option [value]="'btc'">Bitcoin</mat-option>
|
||||
<mat-option [value]="'eth'">Ethereum</mat-option>
|
||||
<mat-option [value]="'bch'">Bitcoin Cash</mat-option>
|
||||
<mat-option [value]="'xrp'">XRP</mat-option>
|
||||
</mat-select>
|
||||
<mat-hint class="flex items-center">
|
||||
<span class="mr-1">USD:</span>
|
||||
<span class="font-mono font-medium text-normal">
|
||||
{{data.wallets[walletSelector.value] * data.prices[walletSelector.value] | currency:'USD'}}
|
||||
</span>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Buy form -->
|
||||
<form
|
||||
class="flex flex-col px-6"
|
||||
*ngIf="buySellSelect.value === 'buy'">
|
||||
<mat-form-field class="w-full">
|
||||
<mat-label>Amount</mat-label>
|
||||
<input
|
||||
matInput
|
||||
autocomplete="off"
|
||||
#buyAmount>
|
||||
<mat-select
|
||||
[value]="'coin'"
|
||||
matSuffix
|
||||
#buyType="matSelect">
|
||||
<mat-option [value]="'coin'">{{walletSelector.value | uppercase}}</mat-option>
|
||||
<mat-option [value]="'usd'">USD</mat-option>
|
||||
</mat-select>
|
||||
<span
|
||||
matPrefix
|
||||
*ngIf="buyType.value === 'usd'">
|
||||
$
|
||||
</span>
|
||||
<mat-hint class="flex items-center">
|
||||
<ng-container *ngIf="buyType.value === 'coin'">
|
||||
<span class="mr-1">It will cost:</span>
|
||||
<span class="font-mono font-medium text-normal">
|
||||
{{buyAmount.value * data.prices[walletSelector.value] | currency:'USD':'symbol':'1.2-4'}}
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="buyType.value === 'usd'">
|
||||
<span class="mr-1">You will receive:</span>
|
||||
<span class="font-mono font-medium text-normal">
|
||||
{{buyAmount.value / data.prices[walletSelector.value] | number:'1.2-6'}} {{walletSelector.value | uppercase}}
|
||||
</span>
|
||||
</ng-container>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
<button
|
||||
class="mt-4 mb-8"
|
||||
mat-flat-button
|
||||
[color]="'primary'">
|
||||
BUY
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- Sell form -->
|
||||
<form
|
||||
class="flex flex-col px-6"
|
||||
*ngIf="buySellSelect.value === 'sell'">
|
||||
<mat-form-field class="w-full">
|
||||
<mat-label>Amount</mat-label>
|
||||
<input
|
||||
matInput
|
||||
autocomplete="off"
|
||||
#sellAmount>
|
||||
<mat-select
|
||||
[value]="'coin'"
|
||||
matSuffix
|
||||
#sellType="matSelect">
|
||||
<mat-option [value]="'coin'">{{walletSelector.value | uppercase}}</mat-option>
|
||||
<mat-option [value]="'usd'">USD</mat-option>
|
||||
</mat-select>
|
||||
<span
|
||||
matPrefix
|
||||
*ngIf="sellType.value === 'usd'">
|
||||
$
|
||||
</span>
|
||||
<mat-hint class="flex items-center">
|
||||
<ng-container *ngIf="sellType.value === 'coin'">
|
||||
<span class="mr-1">You will receive:</span>
|
||||
<span class="font-mono font-medium text-normal">
|
||||
{{sellAmount.value * data.prices[walletSelector.value] | currency:'USD':'symbol':'1.2-4'}}
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="sellType.value === 'usd'">
|
||||
<span class="mr-1">You will sell:</span>
|
||||
<span class="font-mono font-medium text-normal">
|
||||
{{sellAmount.value / data.prices[walletSelector.value] | number:'1.2-6'}} {{walletSelector.value | uppercase}}
|
||||
</span>
|
||||
</ng-container>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
<button
|
||||
class="mt-4 mb-8"
|
||||
mat-flat-button
|
||||
[color]="'primary'">
|
||||
SELL
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</mat-drawer>
|
||||
|
||||
<!-- Content -->
|
||||
<mat-drawer-content class="flex flex-col">
|
||||
|
||||
<!-- BTC Price -->
|
||||
<div class="flex flex-col flex-auto min-h-full bg-card dark:bg-default">
|
||||
<div class="flex flex-wrap items-center pl-4 pr-6 py-3 md:pl-6 border-b">
|
||||
<button
|
||||
class="mr-6 lg:hidden"
|
||||
mat-icon-button
|
||||
(click)="matDrawer.toggle()">
|
||||
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
|
||||
</button>
|
||||
<div class="flex flex-col flex-auto my-3 mr-6">
|
||||
<div class="flex items-center">
|
||||
<div class="font-medium text-2xl text-secondary mr-2">Bitcoin</div>
|
||||
<div class="font-medium text-lg text-hint tracking-wider">(BTC)</div>
|
||||
</div>
|
||||
<div class="flex items-end mt-1">
|
||||
<div class="mr-2 font-mono text-3xl leading-none tracking-tight">{{data.btc.amount | currency:'USD':'symbol':'1.2-2'}}</div>
|
||||
<mat-icon
|
||||
class="text-green-500 icon-size-5 mr-0.5 mb-px"
|
||||
[ngClass]="{'text-green-500': data.btc.trend.dir === 'up', 'text-red-500': data.btc.trend.dir === 'down'}"
|
||||
[svgIcon]="data.btc.trend.dir === 'up' ? 'heroicons_solid:arrow-narrow-up' : 'heroicons_solid:arrow-narrow-down'"></mat-icon>
|
||||
<div
|
||||
class="font-mono font-medium text-lg leading-none mb-px"
|
||||
[ngClass]="{'text-green-500': data.btc.trend.dir === 'up', 'text-red-500': data.btc.trend.dir === 'down'}">
|
||||
{{data.btc.trend.amount}}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden sm:flex items-center my-3">
|
||||
<div class="p-4 leading-none rounded-l-xl border border-r-0">
|
||||
<div class="text-sm font-medium text-secondary">Market Cap</div>
|
||||
<div class="mt-2 font-mono text-xl">{{(data.btc.marketCap / 1000000000) | number: '1.0-2' | currency}}B</div>
|
||||
</div>
|
||||
<div class="p-4 leading-none border border-r-0">
|
||||
<div class="text-sm font-medium text-secondary">Volume</div>
|
||||
<div class="mt-2 font-mono text-xl">{{(data.btc.volume / 1000000000) | number: '1.0-2' | currency}}B</div>
|
||||
</div>
|
||||
<div class="p-4 leading-none border border-r-0">
|
||||
<div class="text-sm font-medium text-secondary">Supply</div>
|
||||
<div class="mt-2 font-mono text-xl">{{(data.btc.supply / 1000000) | number: '1.0-2'}}M</div>
|
||||
</div>
|
||||
<div class="p-4 leading-none rounded-r-xl border">
|
||||
<div class="text-sm font-medium text-secondary">All Time High</div>
|
||||
<div class="mt-2 font-mono text-xl">{{data.btc.allTimeHigh | currency:'USD'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative flex flex-auto bg-gray-50 dark:bg-transparent">
|
||||
<apx-chart
|
||||
class="relative w-full h-160 md:absolute md:inset-0 md:h-auto overflow-hidden"
|
||||
[chart]="btcOptions.chart"
|
||||
[colors]="btcOptions.colors"
|
||||
[dataLabels]="btcOptions.dataLabels"
|
||||
[grid]="btcOptions.grid"
|
||||
[legend]="btcOptions.legend"
|
||||
[series]="btcOptions.series"
|
||||
[stroke]="btcOptions.stroke"
|
||||
[tooltip]="btcOptions.tooltip"
|
||||
[xaxis]="btcOptions.xaxis"
|
||||
[yaxis]="btcOptions.yaxis"
|
||||
#btcChartComponent></apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</mat-drawer-content>
|
||||
|
||||
</mat-drawer-container>
|
||||
|
||||
</div>
|
238
src/app/modules/admin/dashboards/crypto/crypto.component.ts
Normal file
238
src/app/modules/admin/dashboards/crypto/crypto.component.ts
Normal file
|
@ -0,0 +1,238 @@
|
|||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import * as moment from 'moment';
|
||||
import { ApexOptions, ChartComponent } from 'ng-apexcharts';
|
||||
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
|
||||
import { CryptoService } from 'app/modules/admin/dashboards/crypto/crypto.service';
|
||||
|
||||
@Component({
|
||||
selector : 'crypto',
|
||||
templateUrl : './crypto.component.html',
|
||||
encapsulation : ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class CryptoComponent implements OnInit, OnDestroy
|
||||
{
|
||||
@ViewChild('btcChartComponent') btcChartComponent: ChartComponent;
|
||||
appConfig: any;
|
||||
btcOptions: ApexOptions = {};
|
||||
data: any;
|
||||
drawerMode: 'over' | 'side' = 'side';
|
||||
drawerOpened: boolean = true;
|
||||
watchlistChartOptions: ApexOptions = {};
|
||||
private _unsubscribeAll: Subject<any> = new Subject<any>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(
|
||||
private _cryptoService: CryptoService,
|
||||
private _changeDetectorRef: ChangeDetectorRef,
|
||||
private _fuseMediaWatcherService: FuseMediaWatcherService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Lifecycle hooks
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* On init
|
||||
*/
|
||||
ngOnInit(): void
|
||||
{
|
||||
// Subscribe to media changes
|
||||
this._fuseMediaWatcherService.onMediaChange$
|
||||
.pipe(takeUntil(this._unsubscribeAll))
|
||||
.subscribe(({matchingAliases}) => {
|
||||
|
||||
// Set the drawerMode and drawerOpened if 'lg' breakpoint is active
|
||||
if ( matchingAliases.includes('lg') )
|
||||
{
|
||||
this.drawerMode = 'side';
|
||||
this.drawerOpened = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.drawerMode = 'over';
|
||||
this.drawerOpened = false;
|
||||
}
|
||||
|
||||
// Mark for check
|
||||
this._changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
// Get the data
|
||||
this._cryptoService.data$
|
||||
.pipe(takeUntil(this._unsubscribeAll))
|
||||
.subscribe((data) => {
|
||||
|
||||
// Store the data
|
||||
this.data = data;
|
||||
|
||||
// Prepare the chart data
|
||||
this._prepareChartData();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* On destroy
|
||||
*/
|
||||
ngOnDestroy(): void
|
||||
{
|
||||
// Unsubscribe from all subscriptions
|
||||
this._unsubscribeAll.next();
|
||||
this._unsubscribeAll.complete();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Private methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepare the chart data from the data
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private _prepareChartData(): void
|
||||
{
|
||||
// BTC
|
||||
this.btcOptions = {
|
||||
chart : {
|
||||
animations: {
|
||||
enabled: false
|
||||
},
|
||||
fontFamily: 'inherit',
|
||||
foreColor : 'inherit',
|
||||
width : '100%',
|
||||
height : '100%',
|
||||
type : 'line',
|
||||
toolbar : {
|
||||
show: false
|
||||
},
|
||||
zoom : {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
colors : ['#5A67D8'],
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
grid : {
|
||||
borderColor : 'var(--fuse-border)',
|
||||
position : 'back',
|
||||
show : true,
|
||||
strokeDashArray: 6,
|
||||
xaxis : {
|
||||
lines: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
yaxis : {
|
||||
lines: {
|
||||
show: true
|
||||
}
|
||||
}
|
||||
},
|
||||
legend : {
|
||||
show: false
|
||||
},
|
||||
series : this.data.btc.price.series,
|
||||
stroke : {
|
||||
width: 2,
|
||||
curve: 'straight'
|
||||
},
|
||||
tooltip : {
|
||||
shared: true,
|
||||
theme : 'dark',
|
||||
y : {
|
||||
formatter: (value: number): string => '$' + value.toFixed(2)
|
||||
}
|
||||
},
|
||||
xaxis : {
|
||||
type : 'numeric',
|
||||
crosshairs: {
|
||||
show : true,
|
||||
position: 'back',
|
||||
fill : {
|
||||
type : 'color',
|
||||
color: 'var(--fuse-border)'
|
||||
},
|
||||
width : 3,
|
||||
stroke : {
|
||||
dashArray: 0,
|
||||
width : 0
|
||||
},
|
||||
opacity : 0.9
|
||||
},
|
||||
tickAmount: 8,
|
||||
axisTicks : {
|
||||
show : true,
|
||||
color: 'var(--fuse-border)'
|
||||
},
|
||||
axisBorder: {
|
||||
show: false
|
||||
},
|
||||
tooltip : {
|
||||
enabled: false
|
||||
},
|
||||
labels : {
|
||||
show : true,
|
||||
trim : false,
|
||||
rotate : 0,
|
||||
minHeight : 40,
|
||||
hideOverlappingLabels: true,
|
||||
formatter : (value): string => moment().subtract(Math.abs(parseInt(value, 10)), 'minutes').format('HH:mm'),
|
||||
style : {
|
||||
colors: 'currentColor'
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis : {
|
||||
axisTicks : {
|
||||
show : true,
|
||||
color: 'var(--fuse-border)'
|
||||
},
|
||||
axisBorder : {
|
||||
show: false
|
||||
},
|
||||
forceNiceScale: true,
|
||||
labels : {
|
||||
minWidth : 40,
|
||||
formatter: (value: number): string => '$' + value.toFixed(0),
|
||||
style : {
|
||||
colors: 'currentColor'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Watchlist options
|
||||
this.watchlistChartOptions = {
|
||||
chart : {
|
||||
animations: {
|
||||
enabled: false
|
||||
},
|
||||
width : '100%',
|
||||
height : '100%',
|
||||
type : 'line',
|
||||
sparkline : {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
colors : ['#A0AEC0'],
|
||||
stroke : {
|
||||
width: 2,
|
||||
curve: 'smooth'
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
},
|
||||
xaxis : {
|
||||
type: 'category'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
42
src/app/modules/admin/dashboards/crypto/crypto.module.ts
Normal file
42
src/app/modules/admin/dashboards/crypto/crypto.module.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { NgApexchartsModule } from 'ng-apexcharts';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import { CryptoComponent } from 'app/modules/admin/dashboards/crypto/crypto.component';
|
||||
import { cryptoRoutes } from 'app/modules/admin/dashboards/crypto/crypto.routing';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CryptoComponent
|
||||
],
|
||||
imports : [
|
||||
RouterModule.forChild(cryptoRoutes),
|
||||
MatButtonModule,
|
||||
MatButtonToggleModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatMenuModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
MatTabsModule,
|
||||
NgApexchartsModule,
|
||||
SharedModule
|
||||
]
|
||||
})
|
||||
export class CryptoModule
|
||||
{
|
||||
}
|
32
src/app/modules/admin/dashboards/crypto/crypto.resolvers.ts
Normal file
32
src/app/modules/admin/dashboards/crypto/crypto.resolvers.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CryptoService } from 'app/modules/admin/dashboards/crypto/crypto.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CryptoResolver implements Resolve<any>
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(private _cryptoService: CryptoService)
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resolver
|
||||
*
|
||||
* @param route
|
||||
* @param state
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any>
|
||||
{
|
||||
return this._cryptoService.getData();
|
||||
}
|
||||
}
|
13
src/app/modules/admin/dashboards/crypto/crypto.routing.ts
Normal file
13
src/app/modules/admin/dashboards/crypto/crypto.routing.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { Route } from '@angular/router';
|
||||
import { CryptoComponent } from 'app/modules/admin/dashboards/crypto/crypto.component';
|
||||
import { CryptoResolver } from 'app/modules/admin/dashboards/crypto/crypto.resolvers';
|
||||
|
||||
export const cryptoRoutes: Route[] = [
|
||||
{
|
||||
path : '',
|
||||
component: CryptoComponent,
|
||||
resolve : {
|
||||
data: CryptoResolver
|
||||
}
|
||||
}
|
||||
];
|
47
src/app/modules/admin/dashboards/crypto/crypto.service.ts
Normal file
47
src/app/modules/admin/dashboards/crypto/crypto.service.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CryptoService
|
||||
{
|
||||
private _data: BehaviorSubject<any> = new BehaviorSubject(null);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(private _httpClient: HttpClient)
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Accessors
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Getter for data
|
||||
*/
|
||||
get data$(): Observable<any>
|
||||
{
|
||||
return this._data.asObservable();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get data
|
||||
*/
|
||||
getData(): Observable<any>
|
||||
{
|
||||
return this._httpClient.get('api/dashboards/crypto').pipe(
|
||||
tap((response: any) => {
|
||||
this._data.next(response);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
526
src/app/modules/admin/dashboards/finance/finance.component.html
Normal file
526
src/app/modules/admin/dashboards/finance/finance.component.html
Normal file
|
@ -0,0 +1,526 @@
|
|||
<div class="flex flex-col flex-auto w-full">
|
||||
|
||||
<div class="flex flex-wrap w-full max-w-screen-xl mx-auto p-6 md:p-8">
|
||||
|
||||
<!-- Title and action buttons -->
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div>
|
||||
<h2 class="text-3xl font-semibold tracking-tight leading-8">Finance dashboard</h2>
|
||||
<div class="font-medium tracking-tight text-secondary">Keep track of your financial status</div>
|
||||
</div>
|
||||
<div class="flex items-center ml-6">
|
||||
<button
|
||||
class="hidden sm:inline-flex"
|
||||
mat-stroked-button>
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:document-report'"></mat-icon>
|
||||
<span class="ml-2">Reports</span>
|
||||
</button>
|
||||
<button
|
||||
class="hidden sm:inline-flex ml-3"
|
||||
mat-stroked-button>
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:cog'"></mat-icon>
|
||||
<span class="ml-2">Settings</span>
|
||||
</button>
|
||||
<button
|
||||
class="hidden sm:inline-flex ml-3"
|
||||
mat-flat-button
|
||||
[color]="'primary'">
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:save'"></mat-icon>
|
||||
<span class="ml-2">Export</span>
|
||||
</button>
|
||||
|
||||
<!-- Actions menu (visible on xs) -->
|
||||
<div class="sm:hidden">
|
||||
<button
|
||||
[matMenuTriggerFor]="actionsMenu"
|
||||
mat-icon-button>
|
||||
<mat-icon [svgIcon]="'heroicons_outline:dots-vertical'"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #actionsMenu="matMenu">
|
||||
<button mat-menu-item>Export</button>
|
||||
<button mat-menu-item>Reports</button>
|
||||
<button mat-menu-item>Settings</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-2 gap-8 w-full mt-8">
|
||||
<div class="grid gap-8 sm:grid-flow-col xl:grid-flow-row">
|
||||
<!-- Previous statement -->
|
||||
<div class="relative flex flex-col flex-auto p-6 pr-3 pb-3 bg-card rounded-2xl shadow overflow-hidden">
|
||||
<div class="absolute bottom-0 right-0 w-24 h-24 -m-6">
|
||||
<mat-icon
|
||||
class="icon-size-24 opacity-25 text-green-500 dark:text-green-400"
|
||||
[svgIcon]="'heroicons_outline:check-circle'"></mat-icon>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-lg font-medium tracking-tight leading-6 truncate">Previous Statement</div>
|
||||
<div class="text-green-600 font-medium text-sm">
|
||||
Paid on {{data.previousStatement.date}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-auto -mt-2">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="previousStatementMenu">
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:dots-vertical'"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #previousStatementMenu="matMenu">
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:credit-card'"></mat-icon>
|
||||
<span>View statement</span>
|
||||
</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:cash'"></mat-icon>
|
||||
<span>Spending breakdown</span>
|
||||
</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:receipt-tax'"></mat-icon>
|
||||
<span>Tax breakdown</span>
|
||||
</span>
|
||||
</button>
|
||||
<mat-divider class="my-2"></mat-divider>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:printer'"></mat-icon>
|
||||
<span>Print statement</span>
|
||||
</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:mail'"></mat-icon>
|
||||
<span>Email statement</span>
|
||||
</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row flex-wrap mt-4 -mx-6">
|
||||
<div class="flex flex-col mx-6 my-3">
|
||||
<div class="text-sm font-medium leading-none text-secondary">Card Limit</div>
|
||||
<div class="mt-2 font-medium text-3xl leading-none">{{data.previousStatement.limit | currency:'USD'}}</div>
|
||||
</div>
|
||||
<div class="flex flex-col mx-6 my-3">
|
||||
<div class="text-sm font-medium leading-none text-secondary">Spent</div>
|
||||
<div class="mt-2 font-medium text-3xl leading-none">{{data.previousStatement.spent | currency:'USD'}}</div>
|
||||
</div>
|
||||
<div class="flex flex-col mx-6 my-3">
|
||||
<div class="text-sm font-medium leading-none text-secondary">Minimum</div>
|
||||
<div class="mt-2 font-medium text-3xl leading-none">{{data.previousStatement.minimum | currency:'USD'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Current statement -->
|
||||
<div class="relative flex flex-col flex-auto p-6 pr-3 pb-3 bg-card rounded-2xl shadow overflow-hidden">
|
||||
<div class="absolute bottom-0 right-0 w-24 h-24 -m-6">
|
||||
<mat-icon
|
||||
class="icon-size-24 opacity-25 text-red-500 dark:text-red-400"
|
||||
[svgIcon]="'heroicons_outline:exclamation-circle'"></mat-icon>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-lg font-medium tracking-tight leading-6 truncate">Current Statement</div>
|
||||
<div class="text-red-600 font-medium text-sm">
|
||||
Must be paid before {{data.currentStatement.date}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-auto -mt-2">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="currentStatementMenu">
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:dots-vertical'"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #currentStatementMenu="matMenu">
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:credit-card'"></mat-icon>
|
||||
<span>View statement</span>
|
||||
</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:cash'"></mat-icon>
|
||||
<span>Spending breakdown</span>
|
||||
</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:receipt-tax'"></mat-icon>
|
||||
<span>Tax breakdown</span>
|
||||
</span>
|
||||
</button>
|
||||
<mat-divider class="my-2"></mat-divider>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:printer'"></mat-icon>
|
||||
<span>Print statement</span>
|
||||
</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:mail'"></mat-icon>
|
||||
<span>Email statement</span>
|
||||
</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row flex-wrap mt-4 -mx-6">
|
||||
<div class="flex flex-col mx-6 my-3">
|
||||
<div class="text-sm font-medium leading-none text-secondary">Card Limit</div>
|
||||
<div class="mt-2 font-medium text-3xl leading-none">{{data.currentStatement.limit | currency:'USD'}}</div>
|
||||
</div>
|
||||
<div class="flex flex-col mx-6 my-3">
|
||||
<div class="text-sm font-medium leading-none text-secondary">Spent</div>
|
||||
<div class="mt-2 font-medium text-3xl leading-none">{{data.currentStatement.spent | currency:'USD'}}</div>
|
||||
</div>
|
||||
<div class="flex flex-col mx-6 my-3">
|
||||
<div class="text-sm font-medium leading-none text-secondary">Minimum</div>
|
||||
<div class="mt-2 font-medium text-3xl leading-none">{{data.currentStatement.minimum | currency:'USD'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Account balance -->
|
||||
<div class="flex flex-col flex-auto bg-card shadow rounded-2xl overflow-hidden">
|
||||
<div class="flex flex-col p-6 pb-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="mr-4 text-lg font-medium tracking-tight leading-6 truncate">Account Balance</div>
|
||||
<div class="text-secondary font-medium">Monthly balance growth and avg. monthly income</div>
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<button
|
||||
class="h-6 min-h-6 px-2 rounded-full bg-hover"
|
||||
mat-button
|
||||
[matMenuTriggerFor]="accountBalanceMenu">
|
||||
<span class="font-medium text-sm text-secondary">12 months</span>
|
||||
</button>
|
||||
<mat-menu #accountBalanceMenu="matMenu">
|
||||
<button mat-menu-item>3 months</button>
|
||||
<button mat-menu-item>6 months</button>
|
||||
<button mat-menu-item>9 months</button>
|
||||
<button mat-menu-item>12 months</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start mt-6 mr-2">
|
||||
<div class="flex flex-col">
|
||||
<div class="font-semibold text-3xl md:text-5xl tracking-tighter">{{data.accountBalance.growRate}}%</div>
|
||||
<div class="font-medium text-sm text-secondary leading-none">Average Monthly Growth</div>
|
||||
</div>
|
||||
<div class="flex flex-col ml-8 md:ml-16">
|
||||
<div class="font-semibold text-3xl md:text-5xl tracking-tighter">{{data.accountBalance.ami | currency:'USD'}}</div>
|
||||
<div class="font-medium text-sm text-secondary leading-none">Average Monthly Income</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col flex-auto">
|
||||
<apx-chart
|
||||
class="flex-auto w-full h-full"
|
||||
[chart]="accountBalanceOptions.chart"
|
||||
[colors]="accountBalanceOptions.colors"
|
||||
[fill]="accountBalanceOptions.fill"
|
||||
[series]="accountBalanceOptions.series"
|
||||
[stroke]="accountBalanceOptions.stroke"
|
||||
[tooltip]="accountBalanceOptions.tooltip"
|
||||
[xaxis]="accountBalanceOptions.xaxis"></apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-3 gap-8 w-full mt-8">
|
||||
<!-- Recent transactions table -->
|
||||
<div class="xl:col-span-2 flex flex-col flex-auto bg-card shadow rounded-2xl overflow-hidden">
|
||||
<div class="p-6">
|
||||
<div class="mr-4 text-lg font-medium tracking-tight leading-6 truncate">Recent transactions</div>
|
||||
<div class="text-secondary font-medium">1 pending, 4 completed</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto mx-6">
|
||||
<table
|
||||
class="w-full bg-transparent"
|
||||
mat-table
|
||||
matSort
|
||||
[dataSource]="recentTransactionsDataSource"
|
||||
[trackBy]="trackByFn"
|
||||
#recentTransactionsTable>
|
||||
|
||||
<!-- Transaction ID -->
|
||||
<ng-container matColumnDef="transactionId">
|
||||
<th
|
||||
mat-header-cell
|
||||
mat-sort-header
|
||||
*matHeaderCellDef>
|
||||
Transaction ID
|
||||
</th>
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let transaction">
|
||||
<span class="pr-6 font-medium text-sm text-secondary whitespace-nowrap">
|
||||
{{transaction.transactionId}}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Date -->
|
||||
<ng-container matColumnDef="date">
|
||||
<th
|
||||
mat-header-cell
|
||||
mat-sort-header
|
||||
*matHeaderCellDef>
|
||||
Date
|
||||
</th>
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let transaction">
|
||||
<span class="pr-6 whitespace-nowrap">
|
||||
{{transaction.date | date:'MMM dd, y'}}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th
|
||||
mat-header-cell
|
||||
mat-sort-header
|
||||
*matHeaderCellDef>
|
||||
Name
|
||||
</th>
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let transaction">
|
||||
<span class="pr-6 whitespace-nowrap">
|
||||
{{transaction.name}}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Amount -->
|
||||
<ng-container matColumnDef="amount">
|
||||
<th
|
||||
mat-header-cell
|
||||
mat-sort-header
|
||||
*matHeaderCellDef>
|
||||
Amount
|
||||
</th>
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let transaction">
|
||||
<span class="pr-6 font-medium whitespace-nowrap">
|
||||
{{transaction.amount | currency:'USD'}}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Status -->
|
||||
<ng-container matColumnDef="status">
|
||||
<th
|
||||
mat-header-cell
|
||||
mat-sort-header
|
||||
*matHeaderCellDef>
|
||||
Status
|
||||
</th>
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let transaction">
|
||||
<span
|
||||
class="inline-flex items-center font-bold text-xs px-2.5 py-0.5 rounded-full tracking-wide uppercase"
|
||||
[ngClass]="{'bg-red-200 text-red-800 dark:bg-red-600 dark:text-red-50': transaction.status === 'pending',
|
||||
'bg-green-200 text-green-800 dark:bg-green-600 dark:text-green-50': transaction.status === 'completed'}">
|
||||
<span class="leading-relaxed whitespace-nowrap">{{transaction.status}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Footer -->
|
||||
<ng-container matColumnDef="recentOrdersTableFooter">
|
||||
<td
|
||||
class="py-6 px-0 border-0"
|
||||
mat-footer-cell
|
||||
*matFooterCellDef
|
||||
colspan="6">
|
||||
<button mat-stroked-button>See all transactions</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr
|
||||
mat-header-row
|
||||
*matHeaderRowDef="recentTransactionsTableColumns"></tr>
|
||||
<tr
|
||||
class="order-row h-16"
|
||||
mat-row
|
||||
*matRowDef="let row; columns: recentTransactionsTableColumns;"></tr>
|
||||
<tr
|
||||
class="h-16 border-0"
|
||||
mat-footer-row
|
||||
*matFooterRowDef="['recentOrdersTableFooter']"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Budget -->
|
||||
<div class="flex flex-col flex-auto p-6 bg-card rounded-2xl shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="flex flex-col">
|
||||
<div class="mr-4 text-lg font-medium tracking-tight leading-6 truncate">Budget</div>
|
||||
<div class="text-secondary font-medium">Monthly budget summary</div>
|
||||
</div>
|
||||
<div class="ml-auto -mt-2 -mr-2">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="budgetMenu">
|
||||
<mat-icon
|
||||
class="icon-size-5"
|
||||
[svgIcon]="'heroicons_solid:dots-vertical'"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #budgetMenu="matMenu">
|
||||
<button mat-menu-item>Expenses breakdown</button>
|
||||
<button mat-menu-item>Savings breakdown</button>
|
||||
<button mat-menu-item>Bills breakdown</button>
|
||||
<mat-divider class="my-2"></mat-divider>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:printer'"></mat-icon>
|
||||
<span>Print budget summary</span>
|
||||
</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<span class="flex items-center">
|
||||
<mat-icon
|
||||
class="icon-size-5 mr-3"
|
||||
[svgIcon]="'heroicons_solid:mail'"></mat-icon>
|
||||
<span>Email budget summary</span>
|
||||
</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
Last month; you had <strong>223</strong> expense transactions, <strong>12</strong> savings entries and <strong>4</strong> bills.
|
||||
</div>
|
||||
<div class="my-8 space-y-8">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center justify-center w-14 h-14 rounded bg-red-100 text-red-800 dark:bg-red-600 dark:text-red-50">
|
||||
<mat-icon
|
||||
class="text-current"
|
||||
[svgIcon]="'heroicons_outline:credit-card'"></mat-icon>
|
||||
</div>
|
||||
<div class="flex-auto ml-4 leading-none">
|
||||
<div class="text-sm font-medium text-secondary">Expenses</div>
|
||||
<div class="mt-2 font-medium text-2xl">{{data.budget.expenses | currency:'USD'}}</div>
|
||||
<mat-progress-bar
|
||||
class="mt-3 rounded-full"
|
||||
[color]="'warn'"
|
||||
[mode]="'determinate'"
|
||||
[value]="(data.budget.expenses * 100) / data.budget.expensesLimit"></mat-progress-bar>
|
||||
</div>
|
||||
<div class="flex items-end justify-end min-w-18 mt-auto ml-6">
|
||||
<div class="text-lg leading-none">2.6%</div>
|
||||
<mat-icon
|
||||
class="text-green-600 icon-size-4 ml-1"
|
||||
[svgIcon]="'heroicons_solid:arrow-narrow-down'"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center justify-center w-14 h-14 rounded bg-indigo-100 text-indigo-800 dark:bg-indigo-600 dark:text-indigo-50">
|
||||
<mat-icon
|
||||
class="text-current"
|
||||
[svgIcon]="'heroicons_outline:cash'"></mat-icon>
|
||||
</div>
|
||||
<div class="flex-auto ml-4 leading-none">
|
||||
<div class="text-sm font-medium text-secondary">Savings</div>
|
||||
<div class="mt-2 font-medium text-2xl">{{data.budget.savings | currency:'USD'}}</div>
|
||||
<mat-progress-bar
|
||||
class="mt-3 rounded-full"
|
||||
[mode]="'determinate'"
|
||||
[value]="(data.budget.savings * 100) / data.budget.savingsGoal"></mat-progress-bar>
|
||||
</div>
|
||||
<div class="flex items-end justify-end min-w-18 mt-auto ml-6">
|
||||
<div class="text-lg leading-none">12.7%</div>
|
||||
<mat-icon
|
||||
class="text-red-600 icon-size-4 ml-1"
|
||||
[svgIcon]="'heroicons_solid:arrow-narrow-up'"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center justify-center w-14 h-14 rounded bg-teal-100 text-teal-800 dark:bg-teal-600 dark:text-teal-50">
|
||||
<mat-icon
|
||||
class="text-current"
|
||||
[svgIcon]="'heroicons_outline:light-bulb'"></mat-icon>
|
||||
</div>
|
||||
<div class="flex-auto ml-4 leading-none">
|
||||
<div class="text-sm font-medium text-secondary">Bills</div>
|
||||
<div class="mt-2 font-medium text-2xl">{{data.budget.bills | currency:'USD'}}</div>
|
||||
<mat-progress-bar
|
||||
class="mt-3 rounded-full"
|
||||
[mode]="'determinate'"
|
||||
[value]="(data.budget.bills * 100) / data.budget.billsLimit"></mat-progress-bar>
|
||||
</div>
|
||||
<div class="flex items-end justify-end min-w-18 mt-auto ml-6">
|
||||
<div class="text-lg leading-none">105.7%</div>
|
||||
<mat-icon
|
||||
class="text-red-600 icon-size-4 ml-1"
|
||||
[svgIcon]="'heroicons_solid:arrow-narrow-up'"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-md text-secondary">Exceeded your personal limit! Be careful next month.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center mt-auto">
|
||||
<button
|
||||
class="mt-2"
|
||||
mat-stroked-button>
|
||||
Download Summary
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
146
src/app/modules/admin/dashboards/finance/finance.component.ts
Normal file
146
src/app/modules/admin/dashboards/finance/finance.component.ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { ApexOptions } from 'ng-apexcharts';
|
||||
import { FinanceService } from 'app/modules/admin/dashboards/finance/finance.service';
|
||||
|
||||
@Component({
|
||||
selector : 'finance',
|
||||
templateUrl : './finance.component.html',
|
||||
encapsulation : ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FinanceComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
{
|
||||
@ViewChild('recentTransactionsTable', {read: MatSort}) recentTransactionsTableMatSort: MatSort;
|
||||
|
||||
data: any;
|
||||
accountBalanceOptions: ApexOptions;
|
||||
recentTransactionsDataSource: MatTableDataSource<any> = new MatTableDataSource();
|
||||
recentTransactionsTableColumns: string[] = ['transactionId', 'date', 'name', 'amount', 'status'];
|
||||
private _unsubscribeAll: Subject<any> = new Subject<any>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(private _financeService: FinanceService)
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Lifecycle hooks
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* On init
|
||||
*/
|
||||
ngOnInit(): void
|
||||
{
|
||||
// Get the data
|
||||
this._financeService.data$
|
||||
.pipe(takeUntil(this._unsubscribeAll))
|
||||
.subscribe((data) => {
|
||||
|
||||
// Store the data
|
||||
this.data = data;
|
||||
|
||||
// Store the table data
|
||||
this.recentTransactionsDataSource.data = data.recentTransactions;
|
||||
|
||||
// Prepare the chart data
|
||||
this._prepareChartData();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* After view init
|
||||
*/
|
||||
ngAfterViewInit(): void
|
||||
{
|
||||
// Make the data source sortable
|
||||
this.recentTransactionsDataSource.sort = this.recentTransactionsTableMatSort;
|
||||
}
|
||||
|
||||
/**
|
||||
* On destroy
|
||||
*/
|
||||
ngOnDestroy(): void
|
||||
{
|
||||
// Unsubscribe from all subscriptions
|
||||
this._unsubscribeAll.next();
|
||||
this._unsubscribeAll.complete();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Track by function for ngFor loops
|
||||
*
|
||||
* @param index
|
||||
* @param item
|
||||
*/
|
||||
trackByFn(index: number, item: any): any
|
||||
{
|
||||
return item.id || index;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Private methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepare the chart data from the data
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private _prepareChartData(): void
|
||||
{
|
||||
// Account balance
|
||||
this.accountBalanceOptions = {
|
||||
chart : {
|
||||
animations: {
|
||||
speed : 400,
|
||||
animateGradually: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
fontFamily: 'inherit',
|
||||
foreColor : 'inherit',
|
||||
width : '100%',
|
||||
height : '100%',
|
||||
type : 'area',
|
||||
sparkline : {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
colors : ['#A3BFFA', '#667EEA'],
|
||||
fill : {
|
||||
colors : ['#CED9FB', '#AECDFD'],
|
||||
opacity: 0.5,
|
||||
type : 'solid'
|
||||
},
|
||||
series : this.data.accountBalance.series,
|
||||
stroke : {
|
||||
curve: 'straight',
|
||||
width: 2
|
||||
},
|
||||
tooltip: {
|
||||
followCursor: true,
|
||||
theme : 'dark',
|
||||
x : {
|
||||
format: 'MMM dd, yyyy'
|
||||
},
|
||||
y : {
|
||||
formatter: (value): string => value + '%'
|
||||
}
|
||||
},
|
||||
xaxis : {
|
||||
type: 'datetime'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
34
src/app/modules/admin/dashboards/finance/finance.module.ts
Normal file
34
src/app/modules/admin/dashboards/finance/finance.module.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { NgApexchartsModule } from 'ng-apexcharts';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import { FinanceComponent } from 'app/modules/admin/dashboards/finance/finance.component';
|
||||
import { financeRoutes } from 'app/modules/admin/dashboards/finance/finance.routing';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
FinanceComponent
|
||||
],
|
||||
imports : [
|
||||
RouterModule.forChild(financeRoutes),
|
||||
MatButtonModule,
|
||||
MatDividerModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatProgressBarModule,
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
NgApexchartsModule,
|
||||
SharedModule
|
||||
]
|
||||
})
|
||||
export class FinanceModule
|
||||
{
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FinanceService } from 'app/modules/admin/dashboards/finance/finance.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FinanceResolver implements Resolve<any>
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(private _financeService: FinanceService)
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resolver
|
||||
*
|
||||
* @param route
|
||||
* @param state
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any>
|
||||
{
|
||||
return this._financeService.getData();
|
||||
}
|
||||
}
|
13
src/app/modules/admin/dashboards/finance/finance.routing.ts
Normal file
13
src/app/modules/admin/dashboards/finance/finance.routing.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { Route } from '@angular/router';
|
||||
import { FinanceComponent } from 'app/modules/admin/dashboards/finance/finance.component';
|
||||
import { FinanceResolver } from 'app/modules/admin/dashboards/finance/finance.resolvers';
|
||||
|
||||
export const financeRoutes: Route[] = [
|
||||
{
|
||||
path : '',
|
||||
component: FinanceComponent,
|
||||
resolve : {
|
||||
data: FinanceResolver
|
||||
}
|
||||
}
|
||||
];
|
47
src/app/modules/admin/dashboards/finance/finance.service.ts
Normal file
47
src/app/modules/admin/dashboards/finance/finance.service.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FinanceService
|
||||
{
|
||||
private _data: BehaviorSubject<any> = new BehaviorSubject(null);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(private _httpClient: HttpClient)
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Accessors
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Getter for data
|
||||
*/
|
||||
get data$(): Observable<any>
|
||||
{
|
||||
return this._data.asObservable();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get data
|
||||
*/
|
||||
getData(): Observable<any>
|
||||
{
|
||||
return this._httpClient.get('api/dashboards/finance').pipe(
|
||||
tap((response: any) => {
|
||||
this._data.next(response);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user