This commit is contained in:
2023-08-03 08:58:53 +03:00
parent c82f8e3537
commit ad31d3ffce
30 changed files with 697 additions and 24 deletions
+1 -1
View File
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="src\app\layout\app-layout\" />
<Folder Include="src\app\shared\" />
<Folder Include="src\app\shared\components\date-period\" />
</ItemGroup>
<ItemGroup>
<TypeScriptConfiguration Remove="dist\**" />
+2
View File
@@ -37,4 +37,6 @@ export class ApiRoutes {
static Items = '/api/items';
static Dashboard = '/api/dashboard';
static DashboardIncome = '/api/dashboard/income';
static DashboardOutcome = '/api/dashboard/outcome';
}
@@ -44,7 +44,7 @@ export class ForgotPasswordComponent implements OnInit {
if (this.authForm.invalid) {
return;
} else {
this.router.navigate(['/dashboard/main']);
this.router.navigate(['/dashboard']);
}
}
}
@@ -51,7 +51,7 @@ implements OnInit {
this.authService.isAuthenticated$.subscribe(isAuthenticated => {
if (isAuthenticated) {
this.router.navigate([this.getRedirect() || '/dashboard/dashboard']);
this.router.navigate([this.getRedirect() || '/dashboard']);
}
});
}
@@ -46,7 +46,7 @@ export class SignupComponent implements OnInit {
if (this.authForm.invalid) {
return;
}
//this.router.navigate(['/admin/dashboard/main']);
this.authService.register({
username: this.authForm.get('email')!.value!,
password: this.authForm.get('password')!.value!,
@@ -85,7 +85,7 @@ export class AuthService {
this.setCurrentUserValue(userProfile.id, userProfile);
if (authState.action === AuthenticatedActionEnum.loggedIn) {
this.router.navigate([redirectUrl || '/dashboard/dashboard']);
this.router.navigate([redirectUrl || '/dashboard']);
}
});
}
@@ -1,7 +1,12 @@
// angular
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// app
import { Page404Component } from '../authentication/page404/page404.component';
import { Dashboard2Component } from './dashboard2/dashboard2.component';
import { DashboardIncomeComponent } from './income/dashboard.component';
import { DashboardOutcomeComponent } from './outcome/dashboard.component';
const routes: Routes = [
{
@@ -10,9 +15,17 @@ const routes: Routes = [
pathMatch: 'full',
},
{
path: 'dashboard',
path: 'rests',
component: Dashboard2Component,
},
{
path: 'income',
component: DashboardIncomeComponent,
},
{
path: 'outcome',
component: DashboardOutcomeComponent,
},
{ path: '**', component: Page404Component },
];
@@ -15,15 +15,22 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatTooltipModule } from '@angular/material/tooltip';
import { NgApexchartsModule } from 'ng-apexcharts';
import { TranslateModule } from '@ngx-translate/core';
import { MAT_DATE_LOCALE } from '@angular/material/core';
// app
import { DashboardRoutingModule } from './dashboard-routing.module';
import { Dashboard2Component } from './dashboard2/dashboard2.component';
import { DashboardIncomeComponent } from './income/dashboard.component';
import { DashboardOutcomeComponent } from './outcome/dashboard.component';
import { ComponentsModule } from 'src/app/shared/components/components.module';
import { SharedModule } from '../shared/shared.module';
@NgModule({
declarations: [Dashboard2Component],
declarations: [
Dashboard2Component,
DashboardIncomeComponent,
DashboardOutcomeComponent,
],
imports: [
CommonModule,
DashboardRoutingModule,
@@ -42,6 +49,7 @@ import { SharedModule } from '../shared/shared.module';
TranslateModule,
],
providers: [
{ provide: MAT_DATE_LOCALE, useValue: 'en-GB' },
DecimalPipe,
]
})
@@ -0,0 +1,85 @@
<section class="content">
<div class="content-block">
<div class="block-header">
<!-- breadcrumb -->
<app-breadcrumb [title]="'MENUITEMS.DASHBOARD.LIST.INCOME'" [items]="['HOME']" [active_item]="'MENUITEMS.DASHBOARD.LIST.INCOME'"></app-breadcrumb>
</div>
<!--
<div class="row">
<div class="col-4 col-sm-4 col-md-4">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>{{'BALANCE' | translate}}</h5>
</div>
<h3 class="text-danger">{{dashboardModel?.balance | number: '0.2-2'}}</h3>
</div>
</div>
</div>
</div>
<div class="col-4 col-sm-4 col-md-4">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>{{'DEBIT.BALANCE' | translate}}</h5>
<p class="text-muted"></p>
</div>
<h3 class="text-success">{{dashboardModel?.balanceDebit | number: '0.2-2'}}</h3>
</div>
</div>
</div>
</div>
<div class="col-4 col-sm-4 col-md-4">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>{{'CREDIT.BALANCE' | translate}}</h5>
</div>
<h3 class="text-danger">{{dashboardModel?.balanceCredit | number: '0.2-2'}}</h3>
</div>
</div>
</div>
</div>
</div>
-->
<div class="row clearfix">
<!-- Bar chart with line -->
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12">
<div class="card">
<div class="header">
<h2>{{'INCOME' | translate}}</h2>
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Action</button>
<button mat-menu-item>Another action</button>
<button mat-menu-item>Something else here</button>
</mat-menu>
</div>
<div class="body">
<div id="chart">
<apx-chart #chart *ngIf="chartOptions" class="apex-pie-center"
[series]="chartOptions.series!"
[chart]="chartOptions.chart!"
[dataLabels]="chartOptions.dataLabels!"
[plotOptions]="chartOptions.plotOptions!"
[title]="chartOptions.title!"
[legend]="chartOptions.legend!"
>
</apx-chart>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
@@ -0,0 +1,27 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Dashboard2Component } from './dashboard2.component';
describe('Dashboard2Component',
() => {
let component: Dashboard2Component;
let fixture: ComponentFixture<Dashboard2Component>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [Dashboard2Component]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(Dashboard2Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create',
() => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,104 @@
// angular
import { Component } from '@angular/core';
import { OnInit } from '@angular/core';
import { ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpParams } from '@angular/common/http';
import { DecimalPipe } from '@angular/common';
// libs
import {
ChartComponent,
ApexAxisChartSeries,
ApexTitleSubtitle,
ApexDataLabels,
ApexChart,
ApexPlotOptions,
ApexLegend
} from "ng-apexcharts";
// app
import { ApiRoutes } from '../../api-routes';
import { DashboardInOutModel } from '../../model/dashboard.model';
export type ChartOptions = {
series: ApexAxisChartSeries;
chart: ApexChart;
dataLabels: ApexDataLabels;
title: ApexTitleSubtitle;
plotOptions: ApexPlotOptions;
legend: ApexLegend;
};
@Component({
selector: 'app-dashboard-income',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
})
export class DashboardIncomeComponent implements OnInit {
@ViewChild("chart") chart!: ChartComponent;
public chartOptions!: Partial<ChartOptions>;
dashboardModel?: DashboardInOutModel;
colors: string[] = ["#fd7f6f", "#7eb0d5", "#b2e061", "#bd7ebe", "#ffb55a", "#ffee65", "#beb9db", "#fdcce5", "#8bd3c7"];
constructor(
private httpClient: HttpClient,
private _decimalPipe: DecimalPipe
) {
}
ngOnInit() {
let params = new HttpParams()
.set('from', '2023-07-01')
.set('to', '2023-08-01');
this.httpClient
.get<DashboardInOutModel>(ApiRoutes.DashboardIncome, { params: params })
.subscribe(response => {
var series = response.data.map(x => ({
x: x.name + '(' + this._decimalPipe.transform(x.value, '1.2-2') || "" + ')',
y: x.value
}));
var min = Math.min(...response.data.map(x => x.value));
var max = Math.max(...response.data.map(x => x.value));
var step = max / 10;
var ranges = [];
for (var i = 0; i < this.colors.length; i++) {
ranges.push({
from: i === 0 ? min : step * i,
to: i === this.colors.length - 1 ? max + 10 : step * (i + 1),
color: this.colors[this.colors.length - i - 1],
});
}
this.setChartOptions(series, ranges);
});
}
private setChartOptions(data: any[], ranges: any[]) {
var self = this;
this.chartOptions = {
series: [{
data: data
}],
chart: {
type: 'treemap',
width: 600,
height: 600,
},
plotOptions: {
treemap: {
enableShades: true,
shadeIntensity: 0.5,
reverseNegativeShade: true,
colorScale: {
ranges: ranges
}
}
},
};
}
}
@@ -0,0 +1,80 @@
<section class="content">
<div class="content-block">
<div class="block-header">
<!-- breadcrumb -->
<app-breadcrumb [title]="'MENUITEMS.DASHBOARD.LIST.OUTCOME'" [items]="['HOME']" [active_item]="'MENUITEMS.DASHBOARD.LIST.OUTCOME'"></app-breadcrumb>
</div>
<div class="row">
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12">
<div class="card">
<div class="header">
<h2>{{'PERIOD' | translate}}</h2>
</div>
<div class="body">
<div class="example-container">
<form [formGroup]="form!" (ngSubmit)="onSubmitClick()">
<div class="row">
<div class="col-md-6">
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>From</mat-label>
<input matInput [matDatepicker]="pickerFrom" (focus)="pickerFrom.open()" formControlName="dateFrom" required>
<mat-hint>YYYY/MM/DD</mat-hint>
<mat-datepicker-toggle tabindex="-1" matSuffix [for]="pickerFrom"></mat-datepicker-toggle>
<mat-datepicker #pickerFrom></mat-datepicker>
<mat-error *ngIf="form.controls?.['dateFrom']?.hasError('required')">
Please enter rate date
</mat-error>
</mat-form-field>
</div>
<div class="col-md-6">
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>To</mat-label>
<input matInput [matDatepicker]="pickerTo" (focus)="pickerTo.open()" formControlName="dateTo" required>
<mat-hint>YYYY/MM/DD</mat-hint>
<mat-datepicker-toggle tabindex="-1" matSuffix [for]="pickerTo"></mat-datepicker-toggle>
<mat-datepicker #pickerTo></mat-datepicker>
<mat-error *ngIf="form.controls?.['dateTo']?.hasError('required')">
Please enter rate date
</mat-error>
</mat-form-field>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<!-- Bar chart with line -->
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12">
<div class="card">
<div class="header">
<h2>{{'OUTCOME' | translate}}</h2>
<!--
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Action</button>
<button mat-menu-item>Another action</button>
<button mat-menu-item>Something else here</button>
</mat-menu>
-->
</div>
<div class="body">
<apx-chart #chart *ngIf="chartOptions" class="apex-pie-center"
[series]="chartOptions.series!"
[chart]="chartOptions.chart!"
[dataLabels]="chartOptions.dataLabels!"
[plotOptions]="chartOptions.plotOptions!"
[title]="chartOptions.title!"
[legend]="chartOptions.legend!">
</apx-chart>
</div>
</div>
</div>
</div>
</div>
</section>
@@ -0,0 +1,27 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Dashboard2Component } from './dashboard2.component';
describe('Dashboard2Component',
() => {
let component: Dashboard2Component;
let fixture: ComponentFixture<Dashboard2Component>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [Dashboard2Component]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(Dashboard2Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create',
() => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,148 @@
// angular
import { Component } from '@angular/core';
import { OnInit } from '@angular/core';
import { ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpParams } from '@angular/common/http';
import { DecimalPipe } from '@angular/common';
import { UntypedFormBuilder } from '@angular/forms';
import { UntypedFormGroup } from '@angular/forms';
import { Validators } from '@angular/forms';
import { Inject } from '@angular/core';
// libs
import {
ChartComponent,
ApexAxisChartSeries,
ApexTitleSubtitle,
ApexDataLabels,
ApexChart,
ApexPlotOptions,
ApexLegend
} from "ng-apexcharts";
import * as moment from 'moment'
import { MAT_DATE_FORMATS } from '@angular/material/core';
// app
import { ApiRoutes } from '../../api-routes';
import { DashboardInOutModel } from '../../model/dashboard.model';
export type ChartOptions = {
series: ApexAxisChartSeries;
chart: ApexChart;
dataLabels: ApexDataLabels;
title: ApexTitleSubtitle;
plotOptions: ApexPlotOptions;
legend: ApexLegend;
};
@Component({
selector: 'app-dashboard-outcome',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
})
export class DashboardOutcomeComponent implements OnInit {
@ViewChild("chart") chart!: ChartComponent;
public chartOptions!: Partial<ChartOptions>;
dashboardModel?: DashboardInOutModel;
public form!: UntypedFormGroup;
public dateFrom: Date = moment(new Date).add(-30, 'days').toDate();
public dateTo: Date = moment(new Date).add(0, 'days').toDate();
colors: string[] = ["#fd7f6f", "#7eb0d5", "#b2e061", "#bd7ebe", "#ffb55a", "#ffee65", "#beb9db", "#fdcce5", "#8bd3c7"];
constructor(
private fb: UntypedFormBuilder,
private httpClient: HttpClient,
private _decimalPipe: DecimalPipe
) {
}
ngOnInit() {
this.form = this.fb.group({
dateFrom: [
this.dateFrom,
[Validators.required],
],
dateTo: [
this.dateTo,
[Validators.required],
],
});
this.loadData();
}
onSubmitClick() {
}
private loadData(update?: boolean, category?: string) {
let params = new HttpParams()
.set('from', '2023-07-01')
.set('to', '2023-08-01')
.set('category', category || '')
;
this.httpClient
.get<DashboardInOutModel>(ApiRoutes.DashboardOutcome, { params: params })
.subscribe(response => {
this.dashboardModel = response;
var series = response.data.map(x => ({
x: x.name + ' (' + (this._decimalPipe.transform(x.value, '1.2-2') || '') + ')',
y: x.value
}));
var min = Math.min(...response.data.map(x => x.value));
var max = Math.max(...response.data.map(x => x.value));
var step = max / 10;
var ranges = [];
for (var i = 0; i < this.colors.length; i++) {
ranges.push({
from: i === 0 ? min : step * i,
to: i === this.colors.length - 1 ? max + 10 : step * (i + 1),
color: this.colors[this.colors.length - i - 1],
});
}
if (update) {
this.chart.updateSeries([{
data: series
}]);
} else {
this.setChartOptions(series, ranges);
}
});
}
private setChartOptions(data: any[], ranges: any[]) {
var self = this;
this.chartOptions = {
series: [{
data: data
}],
chart: {
type: 'treemap',
width: 600,
height: 600,
events: {
click: function (event, chartContext, config) {
var data = self.dashboardModel!.data[config.dataPointIndex];
self.loadData(true, data.id);
}
}
},
plotOptions: {
treemap: {
enableShades: true,
shadeIntensity: 0.5,
reverseNegativeShade: true,
colorScale: {
ranges: ranges
}
}
},
};
}
}
@@ -23,7 +23,7 @@ export const ROUTES: RouteInfo[] = [
badgeClass: '',
submenu: [
{
path: 'dashboard/dashboard',
path: 'dashboard/rests',
title: 'MENUITEMS.DASHBOARD.LIST.DASHBOARD',
iconType: '',
icon: '',
@@ -33,6 +33,28 @@ export const ROUTES: RouteInfo[] = [
badgeClass: '',
submenu: [],
},
{
path: 'dashboard/income',
title: 'MENUITEMS.DASHBOARD.LIST.INCOME',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
{
path: 'dashboard/outcome',
title: 'MENUITEMS.DASHBOARD.LIST.OUTCOME',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
],
},
@@ -23,3 +23,13 @@ export interface DashboardRestModel {
currencyQuantity: number;
balanceAtRate: number;
}
export interface DashboardInOutModel {
data: DashboardInOutItemModel[];
}
export interface DashboardInOutItemModel {
id: string;
name: string;
value: number;
}
+1 -1
View File
@@ -3,7 +3,6 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';
import { MAT_DATE_LOCALE } from '@angular/material/core';
// libs
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -22,6 +21,7 @@ import { MatExpansionModule } from '@angular/material/expansion';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatMenuModule } from '@angular/material/menu';
import { MAT_DATE_LOCALE } from '@angular/material/core';
// app
import { PagesRoutingModule } from './pages-routing.module';
+3 -1
View File
@@ -14,7 +14,9 @@
"DASHBOARD": {
"TEXT": "Dashboard",
"LIST": {
"DASHBOARD": "Dashboard"
"DASHBOARD": "Rests",
"INCOME": "Incomes",
"OUTCOME": "Outcomes"
}
},
"ACCOUNTS": {
+6 -2
View File
@@ -14,7 +14,9 @@
"DASHBOARD": {
"TEXT": "Панелі",
"LIST": {
"DASHBOARD": "Панель"
"DASHBOARD": "Залишки",
"INCOME": "Надходження",
"OUTCOME": "Витрати"
}
},
"ACCOUNTS": {
@@ -99,5 +101,7 @@
"CURRENT.BALANCE": "Поточний баланс",
"HOME": "Головна",
"OTHER": "Інші",
"TOTAL": "Всього"
"TOTAL": "Всього",
"INCOME": "Надходження",
"OUTCOME": "Витрати"
}