fix
This commit is contained in:
@@ -1,7 +1,30 @@
|
||||
export enum ApiRoutes {
|
||||
UserRegister = '/api/user/register',
|
||||
UserLogin = '/api/user/login',
|
||||
UserProfile = '/api/user/profile',
|
||||
UserAttach = '/api/user/attach',
|
||||
UserDeattach = '/api/user/deattach',
|
||||
export class ApiRoutes {
|
||||
static UserRegister = '/api/user/register';
|
||||
static UserLogin = '/api/user/login';
|
||||
static UserProfile = '/api/user/profile';
|
||||
static UserAttach = '/api/user/attach';
|
||||
static UserDeattach = '/api/user/deattach';
|
||||
|
||||
static GeneralCurrencies = '/api/general/currencies';
|
||||
static SettingsCurrencies = '/api/settings/currencies';
|
||||
static SettingsCurrenciesRate = '/api/settings/currencies/:id/rate';
|
||||
|
||||
static SettingsAccountCategories = '/api/settings/account-categories';
|
||||
static SettingsAccountCategory = '/api/settings/account-categories/:id';
|
||||
|
||||
static SettingsAccounts = '/api/settings/accounts';
|
||||
static SettingsAccount = '/api/settings/accounts/:id';
|
||||
static SettingsAccountAccountCategory = '/api/settings/accounts/:id/category/:categoryId';
|
||||
|
||||
static SettingsMotionCategories = '/api/settings/motion-categories';
|
||||
static SettingsMotionCategory = '/api/settings/motion-categories/:id';
|
||||
|
||||
static SettingsMotions = '/api/settings/motions';
|
||||
static SettingsMotion = '/api/settings/motions/:id';
|
||||
|
||||
static Accounts = '/api/accounts';
|
||||
static Account = '/api/accounts/:id';
|
||||
|
||||
static Motions = '/api/accounts/:id/motions';
|
||||
static Motion = '/api/accounts/:id/motions/:motionId';
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import { AuthLayoutComponent } from './layout/app-layout/auth-layout/auth-layout
|
||||
import { MainLayoutComponent } from './layout/app-layout/main-layout/main-layout.component';
|
||||
import { ErrorInterceptor } from './core/interceptor/error.interceptor';
|
||||
import { JwtInterceptor } from './core/interceptor/jwt.interceptor';
|
||||
import { UpdateDateHttpInterceptor } from './core/interceptor/update.date.http.interceptor ';
|
||||
|
||||
export function createTranslateLoader(http: HttpClient) {
|
||||
return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
|
||||
@@ -63,6 +64,7 @@ export function createTranslateLoader(http: HttpClient) {
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: UpdateDateHttpInterceptor, multi: true },
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
</form>
|
||||
<h6 class="social-login-title">OR</h6>
|
||||
<ul class="list-unstyled social-icon mb-0 mt-3">
|
||||
<li class="list-inline-item">
|
||||
<li class="list-inline-item" *ngIf="allowAuth">
|
||||
<a href="javascript:void(0)" class="rounded" (click)="loginAuth0()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 135 150">
|
||||
<title>auth0-glyph</title>
|
||||
@@ -81,7 +81,7 @@
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<li class="list-inline-item" *ngIf="allowGoogle">
|
||||
<!--
|
||||
<a href="javascript:void(0)" class="rounded" (click)="loginGoogle()">
|
||||
<i class="fab fa-google"></i>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SocialAuthService } from '@abacritt/angularx-social-login';
|
||||
// app
|
||||
import { AuthService } from 'src/app/core/service/auth.service';
|
||||
import { UnsubscribeOnDestroyAdapter } from 'src/app/shared/UnsubscribeOnDestroyAdapter';
|
||||
import { environment } from '../../../environments/environment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-signin',
|
||||
@@ -22,15 +23,24 @@ implements OnInit {
|
||||
loading = false;
|
||||
error?= '';
|
||||
hide = true;
|
||||
allowGoogle: boolean;
|
||||
allowAuth: boolean;
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private authService: AuthService,
|
||||
//private externalAuthService: SocialAuthService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.allowGoogle = !!(environment.externalLogins &&
|
||||
environment.externalLogins.google &&
|
||||
environment.externalLogins.google.clientId);
|
||||
|
||||
this.allowAuth = !!(environment.externalLogins &&
|
||||
environment.externalLogins.auth0 &&
|
||||
environment.externalLogins.auth0.clientId);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -48,7 +48,6 @@ export class ExternalLoginConfig {
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
autoLogin: false,
|
||||
providers: providers,
|
||||
|
||||
@@ -18,6 +18,7 @@ import { OidcHelperService } from './service/oidc-helper.service';
|
||||
import { AuthCodeFlowConfig, AuthModuleConfig } from '../config.oidc';
|
||||
import { SubjectExtensions } from './extensions/general.extensions';
|
||||
import { ExternalLoginConfig } from '../config.external-login';
|
||||
import { AccountCategoryService } from '../services/account.category.service';
|
||||
|
||||
export function storageFactory(): OAuthStorage {
|
||||
return localStorage;
|
||||
@@ -43,6 +44,7 @@ export function storageFactory(): OAuthStorage {
|
||||
DirectionService,
|
||||
OidcHelperService,
|
||||
SubjectExtensions,
|
||||
AccountCategoryService,
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpInterceptor } from '@angular/common/http';
|
||||
import { HttpRequest } from '@angular/common/http';
|
||||
import { HttpHandler } from '@angular/common/http';
|
||||
import { HttpEvent } from '@angular/common/http';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class UpdateDateHttpInterceptor implements HttpInterceptor {
|
||||
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
if (request.method === 'POST' || request.method === 'PUT') {
|
||||
this.shiftDates(request.body);
|
||||
}
|
||||
|
||||
return next.handle(request);
|
||||
}
|
||||
|
||||
shiftDates(body: any) {
|
||||
if (body === null || body === undefined) {
|
||||
return body;
|
||||
}
|
||||
|
||||
if (typeof body !== 'object') {
|
||||
return body;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(body)) {
|
||||
const value = body[key];
|
||||
if (value instanceof Date) {
|
||||
body[key] = new Date(Date.UTC(value.getFullYear(),
|
||||
value.getMonth(),
|
||||
value.getDate(),
|
||||
value.getHours(),
|
||||
value.getMinutes(),
|
||||
value.getSeconds()));
|
||||
} else if (typeof value === 'object') {
|
||||
this.shiftDates(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,5 @@ export interface UserProfileModel {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
fullName?: string;
|
||||
currency?: string;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
export interface UserModel {
|
||||
id: string;
|
||||
username: string;
|
||||
userName: string;
|
||||
img?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
//token?: string;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export class AuthService {
|
||||
this.currentUserSubject.next({
|
||||
id: id,
|
||||
img: 'assets/images/user/admin.jpg',
|
||||
username: profile?.email || '',
|
||||
userName: profile?.email || '',
|
||||
firstName: profile?.firstName || '',
|
||||
lastName: profile?.lastName || '',
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
FormGroup,
|
||||
ValidationErrors,
|
||||
ValidatorFn,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
|
||||
export const atLeastOne = (validator: ValidatorFn, controls: string[] = []) => (
|
||||
group: FormGroup,
|
||||
): ValidationErrors | null => {
|
||||
if (!controls) {
|
||||
controls = Object.keys(group.controls);
|
||||
}
|
||||
|
||||
const hasAtLeastOne = group && group.controls && controls
|
||||
.some(k => !validator(group.controls[k]));
|
||||
|
||||
return hasAtLeastOne ? null : {
|
||||
atLeastOne: true,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
FormGroup,
|
||||
ValidationErrors,
|
||||
ValidatorFn,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
|
||||
export const atLeastOneNumber = (validator: ValidatorFn, controls: string[] = []) => (
|
||||
group: FormGroup,
|
||||
): ValidationErrors | null => {
|
||||
if (!controls) {
|
||||
controls = Object.keys(group.controls);
|
||||
}
|
||||
|
||||
const hasAtLeastOne = group && group.controls && controls
|
||||
.some(k => {
|
||||
var v = group.controls[k].value;
|
||||
return v && !isNaN(v) && v !== 0 && !validator(group.controls[k]);
|
||||
});
|
||||
|
||||
return hasAtLeastOne ? null : {
|
||||
atLeastOne: true,
|
||||
};
|
||||
};
|
||||
@@ -254,6 +254,6 @@ export class HeaderComponent extends UnsubscribeOnDestroyAdapter implements OnIn
|
||||
this.authService.currentUserValue.lastName
|
||||
].join(' ').trim();
|
||||
|
||||
this.userName = this.userName || this.authService.currentUserValue.username;
|
||||
this.userName = this.userName || this.authService.currentUserValue.userName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RouteInfo } from './sidebar.metadata';
|
||||
|
||||
export const ROUTES: RouteInfo[] = [
|
||||
{
|
||||
path: '',
|
||||
@@ -104,31 +105,9 @@ export const ROUTES: RouteInfo[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'accounts',
|
||||
path: '',
|
||||
title: 'Extra Pages',
|
||||
iconType: 'feather',
|
||||
icon: 'anchor',
|
||||
class: 'menu-toggle',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: '/extra-pages/blank',
|
||||
title: 'Blank Page',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
title: 'Multi level Menu',
|
||||
title: 'Accounts',
|
||||
iconType: 'feather',
|
||||
icon: 'chevrons-down',
|
||||
class: 'menu-toggle',
|
||||
@@ -136,7 +115,7 @@ export const ROUTES: RouteInfo[] = [
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
/*{
|
||||
path: '/multilevel/first1',
|
||||
title: 'First',
|
||||
iconType: '',
|
||||
@@ -203,6 +182,73 @@ export const ROUTES: RouteInfo[] = [
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},*/
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
title: 'Settings',
|
||||
iconType: 'feather',
|
||||
icon: 'settings',
|
||||
class: 'menu-toggle',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: '/settings/currencies',
|
||||
title: 'Currencies',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/settings/account-categories',
|
||||
title: 'Account categories',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/settings/accounts',
|
||||
title: 'Accounts',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/settings/motion-categories',
|
||||
title: 'Motion categories',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/settings/motions',
|
||||
title: 'Motions',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Component, Inject, ElementRef, OnInit, Renderer2, HostListener, OnDestr
|
||||
// app
|
||||
import { ROUTES } from './sidebar-items';
|
||||
import { AuthService } from 'src/app/core/service/auth.service';
|
||||
import { AccountCategoryService } from '../../services/account.category.service';
|
||||
import { RouteInfo } from './sidebar.metadata';
|
||||
|
||||
@Component({
|
||||
@@ -33,7 +34,8 @@ export class SidebarComponent implements OnInit, OnDestroy {
|
||||
private renderer: Renderer2,
|
||||
public elementRef: ElementRef,
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
private accountCategoryService: AccountCategoryService,
|
||||
) {
|
||||
this.elementRef.nativeElement.closest('body');
|
||||
this.routerObj = this.router.events.subscribe((event) => {
|
||||
@@ -79,6 +81,26 @@ export class SidebarComponent implements OnInit, OnDestroy {
|
||||
this.userImg = this.authService.currentUserValue.img;
|
||||
this.userType = 'Admin';
|
||||
this.sidebarItems = ROUTES.filter((sidebarItem) => sidebarItem);
|
||||
|
||||
this.accountCategoryService
|
||||
.getCategories()
|
||||
.subscribe(categories => {
|
||||
var accounts = this.sidebarItems.filter(x => x.id === 'accounts');
|
||||
for (var category of categories) {
|
||||
accounts[0].submenu.push({
|
||||
path: '/account-category/' + category.id,
|
||||
title: category.name!,
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// this.sidebarItems = ROUTES.filter((sidebarItem) => sidebarItem);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Sidebar route metadata
|
||||
export interface RouteInfo {
|
||||
id?: string;
|
||||
path: string;
|
||||
title: string;
|
||||
iconType: string;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { UserModel } from '../core/models/user.model';
|
||||
|
||||
export interface AccountAccessRightModel {
|
||||
user: UserModel;
|
||||
isAllowRead: boolean;
|
||||
isAllowWrite: boolean;
|
||||
isAllowManage: boolean;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface AccountCategoryModel {
|
||||
id?: string,
|
||||
name?: string,
|
||||
allowDelete: boolean,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { AccountModel } from './account.model';
|
||||
|
||||
export interface AccountDetailedModel {
|
||||
account: AccountModel;
|
||||
rest: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { AccountCategoryModel } from './account.category.model';
|
||||
import { AccountAccessRightModel } from './account.accessRight.model';
|
||||
|
||||
export interface AccountModel {
|
||||
id?: string,
|
||||
name?: string,
|
||||
currencyId?: string,
|
||||
currencyName?: string,
|
||||
categories?: AccountCategoryModel[],
|
||||
accessRights?: AccountAccessRightModel[],
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface AccountMotionModel {
|
||||
id?: string;
|
||||
date?: Date;
|
||||
motion?: string;
|
||||
description?: string;
|
||||
plus?: number;
|
||||
minus?: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface CurrencyModel {
|
||||
id?: string,
|
||||
name?: string,
|
||||
code?: string,
|
||||
shortName?: string,
|
||||
symbol?: string,
|
||||
quantity?: number,
|
||||
rate?: number,
|
||||
rateDate?: Date,
|
||||
isConnected?: boolean,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface MotionCategoryModel {
|
||||
id?: string,
|
||||
name?: string,
|
||||
allowDelete: boolean,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface MotionModel {
|
||||
id?: string,
|
||||
name?: string,
|
||||
categoryId?: string,
|
||||
category?: string,
|
||||
allowDelete: boolean,
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Blank'" [items]="['Home','Settings']" [active_item]="'Accounts'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<mat-accordion class="main-headers-align" multi>
|
||||
<mat-expansion-panel *ngFor="let account of accounts; let i = index" [expanded]="i === 0" >
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-description>
|
||||
<div>{{account.account.name}}</div>
|
||||
<div>
|
||||
{{account.rest}}
|
||||
({{account.account.currencyId}})
|
||||
</div>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<account-motion [motion]="newMotion">
|
||||
</account-motion>
|
||||
<account-motion *ngFor="let motion of motions" [motion]="motion">
|
||||
</account-motion>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,91 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { AccountCategoryService } from '../../services/account.category.service';
|
||||
import { ApiRoutes } from '../../api-routes';
|
||||
import { AccountCategoryModel } from '../../model/account.category.model';
|
||||
import { SettingsAccountAddComponent } from '../settings/account/add.account.component';
|
||||
import { SettingsAccountEditComponent } from '../settings/account/edit.account.component';
|
||||
import { AccountModel } from '../../model/account.model';
|
||||
import { AccountDetailedModel } from '../../model/account.detailed.model';
|
||||
import { AccountMotionModel } from '../../model/account.motion';
|
||||
|
||||
@Component({
|
||||
templateUrl: './account.component.html',
|
||||
styleUrls: ['./account.component.scss'],
|
||||
})
|
||||
export class AccountComponent {
|
||||
public accounts?: AccountDetailedModel[];
|
||||
public motions?: AccountMotionModel[];
|
||||
public newMotion: AccountMotionModel;
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private dialogModel: MatDialog,
|
||||
private activatedRoute: ActivatedRoute
|
||||
) {
|
||||
this.newMotion = {
|
||||
date: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.activatedRoute.params.subscribe(params => {
|
||||
this.httpClient
|
||||
.get<AccountDetailedModel[]>(ApiRoutes.Accounts + "?category=" + params['id'])
|
||||
.subscribe(data => {
|
||||
this.accounts = data;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*private loadAccounts() {
|
||||
}*/
|
||||
|
||||
/*private loadCategories() {
|
||||
this.accountCategoryService
|
||||
.getCategories()
|
||||
.subscribe(x => {
|
||||
this.accountCategories = x;
|
||||
this.loadAccounts();
|
||||
});
|
||||
}*/
|
||||
|
||||
/*public accountInCategory(category?: AccountCategoryModel) {
|
||||
if (this.accounts) {
|
||||
return this.accounts.filter(acc => (!category && (!acc.categories || acc.categories.length === 0)) || (category && acc.categories && acc.categories.filter(cat => cat.id === category.id).length > 0));
|
||||
}
|
||||
return null;
|
||||
}*/
|
||||
|
||||
/*public add() {
|
||||
this.dialogModel.open(SettingsAccountAddComponent, {
|
||||
width: '640px',
|
||||
disableClose: true,
|
||||
data: this.accounts,
|
||||
}).afterClosed().subscribe(x => {
|
||||
if (x && x.refresh) {
|
||||
this.loadAccounts();
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
/*public edit(account: AccountModel) {
|
||||
this.dialogModel.open(SettingsAccountEditComponent, {
|
||||
width: '640px',
|
||||
disableClose: true,
|
||||
data: account,
|
||||
}).afterClosed().subscribe(x => {
|
||||
if (x && x.refresh) {
|
||||
this.loadAccounts();
|
||||
}
|
||||
});
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<form [formGroup]="form!" (ngSubmit)="onSubmitClick()">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<a href="javascript:void(0)" matSuffix (click)="addDay(-1)">
|
||||
<i class="material-icons font-40">arrow_back</i>
|
||||
</a>
|
||||
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<input matInput [matDatepicker]="picker3" (focus)="picker3.open()" value={{motion.date}} formControlName="date" required>
|
||||
<mat-hint>YYYY/MM/DD</mat-hint>
|
||||
<mat-datepicker-toggle tabindex="-1" matSuffix [for]="picker3"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker3></mat-datepicker>
|
||||
<mat-error *ngIf="form.controls?.['date']?.hasError('required')">
|
||||
Please enter rate date
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<a href="javascript:void(0)" matSuffix (click)="addDay(1)">
|
||||
<i class="material-icons font-40">arrow_forward</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<input matInput formControlName="motion">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<input matInput formControlName="description">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<input matInput [value]="motion.plus | number:'0.2-2'" formControlName="plus" currencyMask [options]="{ prefix: '', precision: 4, inputMode: 1 }">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<input matInput [value]="motion.minus | number:'0.2-2'" formControlName="minus" currencyMask [options]="{ prefix: '', precision: 4, inputMode: 1 }">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-md-2 mb-2">
|
||||
<a *ngIf="!motion.id" href="javascript:void(0)" (click)="onSubmitClick()">
|
||||
<i class="material-icons font-40">add</i>
|
||||
</a>
|
||||
<a *ngIf="!motion.id" href="javascript:void(0)" (click)="onSubmitClick()">
|
||||
<i class="material-icons font-40">import_export</i>
|
||||
</a>
|
||||
<a *ngIf="motion.id" href="javascript:void(0)" (click)="onSubmitClick()">
|
||||
<i class="material-icons font-40">save</i>
|
||||
</a>
|
||||
<a *ngIf="motion.id" href="javascript:void(0)" (click)="onSubmitClick()">
|
||||
<i class="material-icons font-40">delete</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,90 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { UntypedFormBuilder } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { Input } from '@angular/core';
|
||||
import { Output } from '@angular/core';
|
||||
import { EventEmitter } from '@angular/core';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import * as moment from 'moment';
|
||||
|
||||
// app
|
||||
import { AccountCategoryService } from '../../services/account.category.service';
|
||||
import { ApiRoutes } from '../../api-routes';
|
||||
import { AccountCategoryModel } from '../../model/account.category.model';
|
||||
import { SettingsAccountAddComponent } from '../settings/account/add.account.component';
|
||||
import { SettingsAccountEditComponent } from '../settings/account/edit.account.component';
|
||||
import { AccountModel } from '../../model/account.model';
|
||||
import { AccountDetailedModel } from '../../model/account.detailed.model';
|
||||
import { AccountMotionModel } from '../../model/account.motion';
|
||||
import { atLeastOne } from '../../core/validators/atleastone.validator';
|
||||
import { atLeastOneNumber } from '../../core/validators/atleastonenumber.validator';
|
||||
|
||||
@Component({
|
||||
selector: 'account-motion',
|
||||
templateUrl: './motion.component.html',
|
||||
styleUrls: ['./motion.component.scss'],
|
||||
})
|
||||
export class MotionComponent {
|
||||
public form!: UntypedFormGroup;
|
||||
public errorMessage?: string;
|
||||
|
||||
@Input() motion!: AccountMotionModel;
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private httpClient: HttpClient,
|
||||
private dialogModel: MatDialog,
|
||||
private activatedRoute: ActivatedRoute
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.form = this.fb.group({
|
||||
id: [
|
||||
this.motion.id,
|
||||
],
|
||||
date: [
|
||||
this.motion.date,
|
||||
[Validators.required],
|
||||
],
|
||||
motion: [
|
||||
this.motion.motion,
|
||||
[Validators.required],
|
||||
],
|
||||
description: [
|
||||
this.motion.description,
|
||||
],
|
||||
plus: [
|
||||
this.motion.plus,
|
||||
],
|
||||
minus: [
|
||||
this.motion.minus,
|
||||
],
|
||||
}, { validator: atLeastOneNumber(Validators.required, ['plus', 'minus']) });
|
||||
}
|
||||
|
||||
onSubmitClick() {
|
||||
if (this.form.valid) {
|
||||
this.httpClient
|
||||
.post<AccountMotionModel[]>(ApiRoutes.SettingsCurrencies, this.form.value)
|
||||
.subscribe(response => {
|
||||
}, error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addDay(days: number) {
|
||||
var date = moment(this.form!.get('date')!.value).add(days, 'days').toDate();
|
||||
this.form.patchValue({
|
||||
date: date,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
import { SettingsCurrencyComponent } from './settings/currency/currency.component';
|
||||
import { SettingsAccountCategoryComponent } from './settings/account/account.category.component';
|
||||
import { SettingsAccountComponent } from './settings/account/account.component';
|
||||
import { SettingsMotionCategoryComponent } from './settings/motion/motion.category.component';
|
||||
import { SettingsMotionComponent } from './settings/motion/motion.component';
|
||||
import { AccountComponent } from './accounts/account.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -12,6 +18,30 @@ const routes: Routes = [
|
||||
path: 'user/profile',
|
||||
component: UserProfileComponent,
|
||||
},
|
||||
{
|
||||
path: 'settings/currencies',
|
||||
component: SettingsCurrencyComponent,
|
||||
},
|
||||
{
|
||||
path: 'settings/account-categories',
|
||||
component: SettingsAccountCategoryComponent,
|
||||
},
|
||||
{
|
||||
path: 'settings/accounts',
|
||||
component: SettingsAccountComponent,
|
||||
},
|
||||
{
|
||||
path: 'settings/motion-categories',
|
||||
component: SettingsMotionCategoryComponent,
|
||||
},
|
||||
{
|
||||
path: 'settings/motions',
|
||||
component: SettingsMotionComponent,
|
||||
},
|
||||
{
|
||||
path: 'account-category/:id',
|
||||
component: AccountComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -1,18 +1,63 @@
|
||||
// angular
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { PagesRoutingModule } from './pages-routing.module';
|
||||
import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MAT_DATE_LOCALE } from '@angular/material/core';
|
||||
|
||||
// libs
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { NgxMaskModule } from 'ngx-mask';
|
||||
import { NgxCurrencyModule } from "ngx-currency";
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
|
||||
// app
|
||||
import { PagesRoutingModule } from './pages-routing.module';
|
||||
import { ComponentsModule } from '../shared/components/components.module';
|
||||
import { SettingsCurrencyComponent } from './settings/currency/currency.component';
|
||||
import { SettingsConnectCurrencyComponent } from './settings/currency/connect.currency.component';
|
||||
import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
import { SettingsRateCurrencyComponent } from './settings/currency/rate.currency.component';
|
||||
import { SettingsAccountCategoryComponent } from './settings/account/account.category.component';
|
||||
import { SettingsAccountComponent } from './settings/account/account.component';
|
||||
import { SettingsAccountAddComponent } from './settings/account/add.account.component';
|
||||
import { SettingsAccountEditComponent } from './settings/account/edit.account.component';
|
||||
import { CurrencyService } from '../services/currency.service';
|
||||
import { MotionService } from '../services/motion.service';
|
||||
import { SettingsMotionCategoryComponent } from './settings/motion/motion.category.component';
|
||||
import { SettingsMotionComponent } from './settings/motion/motion.component';
|
||||
import { SettingsMotionEditComponent } from './settings/motion/edit.motion.component';
|
||||
import { AccountComponent } from './accounts/account.component';
|
||||
import { MotionComponent } from './accounts/motion.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
UserProfileComponent,
|
||||
|
||||
SettingsCurrencyComponent,
|
||||
SettingsConnectCurrencyComponent,
|
||||
SettingsRateCurrencyComponent,
|
||||
|
||||
SettingsAccountCategoryComponent,
|
||||
SettingsAccountComponent,
|
||||
SettingsAccountAddComponent,
|
||||
SettingsAccountEditComponent,
|
||||
|
||||
SettingsMotionCategoryComponent,
|
||||
SettingsMotionComponent,
|
||||
SettingsMotionEditComponent,
|
||||
|
||||
AccountComponent,
|
||||
MotionComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -21,9 +66,23 @@ import { ComponentsModule } from '../shared/components/components.module';
|
||||
ReactiveFormsModule,
|
||||
PagesRoutingModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
MatTabsModule,
|
||||
MatDialogModule,
|
||||
MatDatepickerModule,
|
||||
MatGridListModule,
|
||||
MatCardModule,
|
||||
MatExpansionModule,
|
||||
NgxMaskModule,
|
||||
NgxCurrencyModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: MAT_DATE_LOCALE, useValue: 'en-GB' },
|
||||
CurrencyService,
|
||||
MotionService,
|
||||
],
|
||||
})
|
||||
export class PagesModule {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Blank'" [items]="['Home','Settings']" [active_item]="'Account categories'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<mat-card class="card">
|
||||
<mat-card-header>
|
||||
<mat-card-title-group>
|
||||
<mat-card-title>
|
||||
Account categories
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
</mat-card-subtitle>
|
||||
</mat-card-title-group>
|
||||
<div fxFlex></div>
|
||||
<button class="btn-space " (click)="add()" mat-raised-button color="primary">
|
||||
Add
|
||||
</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="body table-responsive">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let item of categories">
|
||||
<td>{{item.name}}</td>
|
||||
<td>
|
||||
<button class="btn-space" (click)="edit(item)" mat-raised-button color="primary">
|
||||
Edit
|
||||
</button>
|
||||
<button class="btn-space" *ngIf="item.allowDelete" (click)="remove(item)" mat-raised-button color="primary">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,122 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { AccountCategoryModel } from '../../../model/account.category.model';
|
||||
|
||||
@Component({
|
||||
templateUrl: './account.category.component.html',
|
||||
styleUrls: ['./account.category.component.scss'],
|
||||
})
|
||||
export class SettingsAccountCategoryComponent {
|
||||
public categories?: AccountCategoryModel[];
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private load() {
|
||||
this.httpClient
|
||||
.get<AccountCategoryModel[]>(ApiRoutes.SettingsAccountCategories)
|
||||
.subscribe(data => {
|
||||
this.categories = data;
|
||||
});
|
||||
}
|
||||
|
||||
public add() {
|
||||
Swal.fire({
|
||||
title: 'Account category name',
|
||||
input: 'text',
|
||||
inputAttributes: {
|
||||
autocapitalize: 'off',
|
||||
},
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Add',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.post(ApiRoutes.SettingsAccountCategories, { name: name })
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
|
||||
public edit(category: AccountCategoryModel) {
|
||||
Swal.fire({
|
||||
title: 'Account category name',
|
||||
input: 'text',
|
||||
inputValue: category.name,
|
||||
inputAttributes: {
|
||||
autocapitalize: 'off',
|
||||
},
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Update',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.put(ApiRoutes.SettingsAccountCategory.replace(':id', category.id!), { name: name })
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
|
||||
public remove(category: AccountCategoryModel) {
|
||||
Swal.fire({
|
||||
title: 'Remove account category ' + category.name,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Delete',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.delete(ApiRoutes.SettingsAccountCategory.replace(':id', category.id!))
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Blank'" [items]="['Home','Settings']" [active_item]="'Accounts'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<mat-card class="card">
|
||||
<mat-card-header>
|
||||
<mat-card-title-group>
|
||||
<mat-card-title>
|
||||
Accounts
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
</mat-card-subtitle>
|
||||
</mat-card-title-group>
|
||||
<div fxFlex></div>
|
||||
<button class="btn-space " (click)="add()" mat-raised-button color="primary">
|
||||
Add
|
||||
</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="body table-responsive">
|
||||
<mat-tab-group mat-stretch-tabs="false" mat-align-tabs="start">
|
||||
<mat-tab *ngFor="let category of accountCategories" label="{{category.name}}">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let account of accountInCategory(category)">
|
||||
<td>{{account.name}}</td>
|
||||
<td>{{account.currencyId}}</td>
|
||||
<td>
|
||||
<button class="btn-space " (click)="edit(account)" mat-raised-button color="primary">
|
||||
Edit
|
||||
</button>
|
||||
<button class="btn-space " (click)="remove(account)" mat-raised-button color="primary">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</mat-tab>
|
||||
<mat-tab *ngIf="accountCategories && accounts" label="Without category">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let account of accountInCategory()">
|
||||
<td>{{account.name}}</td>
|
||||
<td>{{account.currencyId}}</td>
|
||||
<td>
|
||||
<button class="btn-space " (click)="edit(account)" mat-raised-button color="primary">
|
||||
Edit
|
||||
</button>
|
||||
<button class="btn-space " (click)="remove(account)" mat-raised-button color="primary">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</mat-tab>
|
||||
<mat-tab *ngIf="accountCategories && accounts" label="All">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let account of accounts">
|
||||
<td>{{account.name}}</td>
|
||||
<td>{{account.currencyId}}</td>
|
||||
<td>
|
||||
<button class="btn-space " (click)="edit(account)" mat-raised-button color="primary">
|
||||
Edit
|
||||
</button>
|
||||
<button class="btn-space " (click)="remove(account)" mat-raised-button color="primary">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,109 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { AccountModel } from '../../../model/account.model';
|
||||
import { AccountCategoryModel } from '../../../model/account.category.model';
|
||||
import { SettingsAccountAddComponent } from './add.account.component';
|
||||
import { SettingsAccountEditComponent } from './edit.account.component';
|
||||
import { AccountCategoryService } from '../../../services/account.category.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './account.component.html',
|
||||
styleUrls: ['./account.component.scss'],
|
||||
})
|
||||
export class SettingsAccountComponent {
|
||||
public accountCategories?: AccountCategoryModel[];
|
||||
public accounts?: AccountModel[];
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private dialogModel: MatDialog,
|
||||
private accountCategoryService: AccountCategoryService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadCategories();
|
||||
}
|
||||
|
||||
private loadAccounts() {
|
||||
this.httpClient
|
||||
.get<AccountModel[]>(ApiRoutes.SettingsAccounts)
|
||||
.subscribe(data => {
|
||||
this.accounts = data;
|
||||
});
|
||||
}
|
||||
|
||||
private loadCategories() {
|
||||
this.accountCategoryService
|
||||
.getCategories()
|
||||
.subscribe(x => {
|
||||
this.accountCategories = x;
|
||||
this.loadAccounts();
|
||||
});
|
||||
}
|
||||
|
||||
public accountInCategory(category?: AccountCategoryModel) {
|
||||
if (this.accounts) {
|
||||
return this.accounts.filter(acc => (!category && (!acc.categories || acc.categories.length === 0)) || (category && acc.categories && acc.categories.filter(cat => cat.id === category.id).length > 0));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public add() {
|
||||
this.dialogModel.open(SettingsAccountAddComponent, {
|
||||
width: '640px',
|
||||
disableClose: true,
|
||||
data: this.accounts,
|
||||
}).afterClosed().subscribe(x => {
|
||||
if (x && x.refresh) {
|
||||
this.loadAccounts();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public edit(account: AccountModel) {
|
||||
this.dialogModel.open(SettingsAccountEditComponent, {
|
||||
width: '640px',
|
||||
disableClose: true,
|
||||
data: account,
|
||||
}).afterClosed().subscribe(x => {
|
||||
if (x && x.refresh) {
|
||||
this.loadAccounts();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public remove(category: AccountModel) {
|
||||
/*Swal.fire({
|
||||
title: 'Remove account category ' + category.name,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Add',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.delete(ApiRoutes.SettingsAccountCategory.replace(':id', category.id!))
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<div>
|
||||
<h2 mat-dialog-title>
|
||||
Add account
|
||||
</h2>
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="editForm!" (ngSubmit)="onSubmitClick()">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput value={{account.name}} formControlName="name">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select formControlName="currencyId">
|
||||
<mat-option *ngFor="let currency of currencies" [value]="currency.code">
|
||||
{{currency.code}} ({{currency.name}})
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Category</mat-label>
|
||||
<mat-select formControlName="categoryId">
|
||||
<mat-option *ngFor="let category of categories" [value]="category.id">
|
||||
{{category.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>User</mat-label>
|
||||
<input matInput value={{username}} readonly>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-dialog-actions class="mat-dialog-actions">
|
||||
<div class="mat-dialog-left">
|
||||
<div class="alert alert-danger mat-dialog-error" *ngIf="errorMessage">
|
||||
{{errorMessage}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mat-dialog-right">
|
||||
<div class="button-bottom">
|
||||
<button type="button" mat-button (click)="closeDialog()">Cancel</button>
|
||||
<button class="btn-space" mat-raised-button color="primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,90 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject } from '@angular/core';
|
||||
import { UntypedFormBuilder } from '@angular/forms';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { AccountModel } from '../../../model/account.model';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { CurrencyService } from '../../../services/currency.service';
|
||||
import { CurrencyModel } from '../../../model/currency.model';
|
||||
import { AccountCategoryService } from '../../../services/account.category.service';
|
||||
import { AccountCategoryModel } from '../../../model/account.category.model';
|
||||
import { AuthService } from '../../../core/service/auth.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './add.account.component.html',
|
||||
styleUrls: ['./add.account.component.scss'],
|
||||
})
|
||||
export class SettingsAccountAddComponent {
|
||||
public editForm!: UntypedFormGroup;
|
||||
public errorMessage?: string;
|
||||
public currencies?: CurrencyModel[];
|
||||
public categories?: AccountCategoryModel[];
|
||||
public username: string;
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private httpClient: HttpClient,
|
||||
private dialogRef: MatDialogRef<SettingsAccountAddComponent>,
|
||||
private currencyService: CurrencyService,
|
||||
private accountCategoryService: AccountCategoryService,
|
||||
private authService: AuthService,
|
||||
@Inject(MAT_DIALOG_DATA) public account: AccountModel
|
||||
) {
|
||||
this.username = authService.currentUserValue.userName;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.editForm = this.fb.group({
|
||||
id: [
|
||||
this.account.id,
|
||||
],
|
||||
name: [
|
||||
this.account.name,
|
||||
[Validators.required],
|
||||
],
|
||||
currencyId: [
|
||||
this.account.currencyId,
|
||||
[Validators.required],
|
||||
],
|
||||
categoryId: [
|
||||
'',
|
||||
[Validators.required],
|
||||
],
|
||||
});
|
||||
|
||||
this.currencyService
|
||||
.getCurrencies()
|
||||
.subscribe(x => this.currencies = x);
|
||||
|
||||
this.accountCategoryService
|
||||
.getCategories()
|
||||
.subscribe(x => this.categories = x);
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onSubmitClick() {
|
||||
if (this.editForm.valid) {
|
||||
this.errorMessage = undefined;
|
||||
this.httpClient
|
||||
.post<AccountModel>(ApiRoutes.SettingsAccounts, this.editForm.value)
|
||||
.subscribe(response => {
|
||||
this.dialogRef.close({ refresh: true });
|
||||
}, error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<div>
|
||||
<h2 mat-dialog-title>
|
||||
Edit account
|
||||
</h2>
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="editForm!" (ngSubmit)="onSubmitClick()">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput value={{account.name}} formControlName="name">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select formControlName="currencyId">
|
||||
<mat-option *ngFor="let currency of currencies" [value]="currency.code">
|
||||
{{currency.code}} ({{currency.name}})
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label>Categories</label>
|
||||
<div class="col-md-12">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Category</mat-label>
|
||||
<mat-select formControlName="categoryId">
|
||||
<mat-option *ngFor="let category of categories" [value]="category.id">
|
||||
{{category.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let category of account.categories">
|
||||
<td>{{category.name}}</td>
|
||||
<td>
|
||||
<button class="btn-space" (click)="removeCategory(category)" mat-raised-button color="primary">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label>Users</label>
|
||||
<div class="col-md-12">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Category</mat-label>
|
||||
<mat-select formControlName="categoryId">
|
||||
<mat-option *ngFor="let category of categories" [value]="category.id">
|
||||
{{category.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let accessRight of account.accessRights">
|
||||
<td>{{accessRight.user.email}}</td>
|
||||
<td>
|
||||
<button class="btn-space" [disabled]="account.accessRights!.length <= 1" click="removeAccessRight(accessRight)" mat-raised-button color="primary">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<mat-dialog-actions class="mat-dialog-actions">
|
||||
<div class="mat-dialog-left">
|
||||
<div class="alert alert-danger mat-dialog-error" *ngIf="errorMessage">
|
||||
{{errorMessage}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mat-dialog-right">
|
||||
<div class="button-bottom">
|
||||
<button type="button" mat-button (click)="closeDialog()">Cancel</button>
|
||||
<button class="btn-space" mat-raised-button color="primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,104 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject } from '@angular/core';
|
||||
import { UntypedFormBuilder } from '@angular/forms';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { AccountModel } from '../../../model/account.model';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { CurrencyService } from '../../../services/currency.service';
|
||||
import { CurrencyModel } from '../../../model/currency.model';
|
||||
import { AccountCategoryService } from '../../../services/account.category.service';
|
||||
import { AccountCategoryModel } from '../../../model/account.category.model';
|
||||
import { AccountAccessRightModel } from '../../../model/account.accessRight.model';
|
||||
|
||||
@Component({
|
||||
templateUrl: './edit.account.component.html',
|
||||
styleUrls: ['./edit.account.component.scss'],
|
||||
})
|
||||
export class SettingsAccountEditComponent {
|
||||
public editForm!: UntypedFormGroup;
|
||||
public errorMessage?: string;
|
||||
public currencies?: CurrencyModel[];
|
||||
public categories?: AccountCategoryModel[];
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private httpClient: HttpClient,
|
||||
private dialogRef: MatDialogRef<SettingsAccountEditComponent>,
|
||||
private currencyService: CurrencyService,
|
||||
private accountCategoryService: AccountCategoryService,
|
||||
@Inject(MAT_DIALOG_DATA) public account: AccountModel
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.editForm = this.fb.group({
|
||||
id: [
|
||||
this.account.id,
|
||||
],
|
||||
name: [
|
||||
this.account.name,
|
||||
[Validators.required],
|
||||
],
|
||||
currencyId: [
|
||||
this.account.currencyId,
|
||||
[Validators.required],
|
||||
],
|
||||
categoryId: [
|
||||
'',
|
||||
],
|
||||
});
|
||||
|
||||
this.currencyService
|
||||
.getCurrencies()
|
||||
.subscribe(x => this.currencies = x);
|
||||
|
||||
this.accountCategoryService
|
||||
.getCategories()
|
||||
.subscribe(x => this.categories = x);
|
||||
}
|
||||
|
||||
removeCategory(category: AccountCategoryModel) {
|
||||
var url = ApiRoutes.SettingsAccountAccountCategory
|
||||
.replace(':id', this.account.id!)
|
||||
.replace(':categoryId', category.id!);
|
||||
|
||||
this.httpClient
|
||||
.delete(url)
|
||||
.subscribe(response => {
|
||||
this.dialogRef.close({ refresh: true });
|
||||
}, error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
|
||||
removeAccessRight(accessRight: AccountAccessRightModel) {
|
||||
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onSubmitClick() {
|
||||
if (this.editForm.valid) {
|
||||
this.errorMessage = undefined;
|
||||
this.httpClient
|
||||
.put<AccountModel>(ApiRoutes.SettingsAccount.replace(':id', this.account.id!), this.editForm.value)
|
||||
.subscribe(response => {
|
||||
this.dialogRef.close({ refresh: true });
|
||||
}, error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<div>
|
||||
<h2 mat-dialog-title>Add currency</h2>
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="addForm!" (ngSubmit)="onSubmitClick()">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Symbol</mat-label>
|
||||
<input matInput value={{currency.symbol}} formControlName="symbol" readonly="">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Code</mat-label>
|
||||
<input matInput value={{currency.id}} formControlName="id" readonly="">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput value={{currency.name}} formControlName="name">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Short Name</mat-label>
|
||||
<input matInput value={{currency.shortName}} formControlName="shortName">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Rate</mat-label>
|
||||
<input matInput [value]="currency.rate | number:'0.2-2'" formControlName="rate" currencyMask [options]="{ prefix: currency.symbol, precision: 4 }" required>
|
||||
<mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')">
|
||||
Please enter rate
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Quantity</mat-label>
|
||||
<input matInput [value]="currency.rate | number:'0.0-0'" formControlName="quantity" currencyMask [options]="{ prefix: '', precision: 0 }" required>
|
||||
<mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')">
|
||||
Please enter rate
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Rate Date</mat-label>
|
||||
<input matInput [matDatepicker]="picker3" (focus)="picker3.open()" value={{currency.rateDate}} formControlName="rateDate" required>
|
||||
<mat-hint>YYYY/MM/DD</mat-hint>
|
||||
<mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker3></mat-datepicker>
|
||||
<mat-error *ngIf="addForm.controls?.['rateDate']?.hasError('required')">
|
||||
Please enter rate date
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-dialog-actions class="mat-dialog-actions">
|
||||
<div class="mat-dialog-left">
|
||||
<div class="alert alert-danger mat-dialog-error" *ngIf="errorMessage">
|
||||
{{errorMessage}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mat-dialog-right">
|
||||
<div class="button-bottom">
|
||||
<button type="button" mat-button (click)="closeDialog()">Cancel</button>
|
||||
<button class="btn-space" mat-raised-button color="primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,78 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { Inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { UntypedFormBuilder } from '@angular/forms';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
|
||||
// libs
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { CurrencyModel } from '../../../model/currency.model';
|
||||
|
||||
@Component({
|
||||
templateUrl: './connect.currency.component.html',
|
||||
styleUrls: ['./connect.currency.component.scss'],
|
||||
})
|
||||
export class SettingsConnectCurrencyComponent {
|
||||
public addForm!: UntypedFormGroup;
|
||||
public errorMessage?: string;
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private httpClient: HttpClient,
|
||||
private dialogRef: MatDialogRef<SettingsConnectCurrencyComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public currency: CurrencyModel
|
||||
) {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.addForm = this.fb.group({
|
||||
id: [
|
||||
this.currency.id,
|
||||
],
|
||||
symbol: [
|
||||
this.currency.symbol,
|
||||
],
|
||||
name: [
|
||||
this.currency.name,
|
||||
],
|
||||
shortName: [
|
||||
this.currency.id,
|
||||
],
|
||||
quantity: [
|
||||
this.currency.quantity || 1,
|
||||
[Validators.required],
|
||||
],
|
||||
rate: [
|
||||
this.currency.rate || 1,
|
||||
[Validators.required],
|
||||
],
|
||||
rateDate: [
|
||||
this.currency.rateDate,
|
||||
[Validators.required],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onSubmitClick() {
|
||||
if (this.addForm.valid) {
|
||||
this.errorMessage = undefined;
|
||||
this.httpClient
|
||||
.post<CurrencyModel[]>(ApiRoutes.SettingsCurrencies, this.addForm.value)
|
||||
.subscribe(response => {
|
||||
this.dialogRef.close({ refresh: true });
|
||||
}, error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Blank'" [items]="['Home','Settings']" [active_item]="'Currency'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2><strong>My currencies</strong></h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<mat-tab-group [(selectedIndex)]="tabIndex">
|
||||
<mat-tab label="My currencies">
|
||||
<div class="body table-responsive">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let item of myCurrencies">
|
||||
<td>{{item.code}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td style="text-align: end">
|
||||
{{item.quantity}}
|
||||
</td>
|
||||
<td style="text-align: end">
|
||||
{{item.rate | number: '1.2-6'}}
|
||||
{{item.symbol}}
|
||||
</td>
|
||||
<td>{{item.rateDate | date:'yyyy-MM-dd'}}</td>
|
||||
<td>
|
||||
<button class="btn-space" (click)="setRate(item)" mat-raised-button color="primary">
|
||||
Rate
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="All currencies">
|
||||
<div class="body table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of currencies">
|
||||
<td>{{item.symbol}}</td>
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>
|
||||
<button class="btn-space" (click)="connect(item)" [disabled]="item.isConnected" mat-raised-button color="primary">
|
||||
Add
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,90 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { SettingsConnectCurrencyComponent } from './connect.currency.component';
|
||||
import { CurrencyModel } from '../../../model/currency.model';
|
||||
import { SettingsRateCurrencyComponent } from './rate.currency.component';
|
||||
|
||||
@Component({
|
||||
templateUrl: './currency.component.html',
|
||||
styleUrls: ['./currency.component.scss'],
|
||||
})
|
||||
export class SettingsCurrencyComponent {
|
||||
public tabIndex = 0;
|
||||
|
||||
public currencies?: CurrencyModel[];
|
||||
public myCurrencies?: CurrencyModel[];
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private dialogModel: MatDialog
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.load();
|
||||
}
|
||||
|
||||
connect(currency: CurrencyModel) {
|
||||
this.dialogModel.open(SettingsConnectCurrencyComponent, {
|
||||
width: '640px',
|
||||
disableClose: true,
|
||||
data: currency,
|
||||
}).afterClosed().subscribe(x => {
|
||||
if (x && x.refresh) {
|
||||
this.tabIndex = 0;
|
||||
this.loadMyCurrency();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
disconnect(currency: CurrencyModel) {
|
||||
}
|
||||
|
||||
setRate(currency: CurrencyModel) {
|
||||
this.dialogModel.open(SettingsRateCurrencyComponent, {
|
||||
width: '640px',
|
||||
disableClose: true,
|
||||
data: currency,
|
||||
}).afterClosed().subscribe(x => {
|
||||
if (x && x.refresh) {
|
||||
this.loadMyCurrency();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private load() {
|
||||
this.httpClient
|
||||
.get<CurrencyModel[]>(ApiRoutes.GeneralCurrencies)
|
||||
.subscribe(data => {
|
||||
this.currencies = data;
|
||||
this.loadMyCurrency();
|
||||
});
|
||||
}
|
||||
|
||||
private loadMyCurrency() {
|
||||
this.httpClient
|
||||
.get<CurrencyModel[]>(ApiRoutes.SettingsCurrencies)
|
||||
.subscribe(data => {
|
||||
this.myCurrencies = data.map(myCurrency => {
|
||||
var globalCurrency = this.currencies?.find(x => x.id === myCurrency.code);
|
||||
return ({
|
||||
id: myCurrency.id,
|
||||
code: myCurrency.code,
|
||||
name: myCurrency.name,
|
||||
quantity: myCurrency.quantity,
|
||||
rate: myCurrency.rate,
|
||||
rateDate: myCurrency.rateDate,
|
||||
shortName: globalCurrency?.name,
|
||||
symbol: globalCurrency?.symbol,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<div>
|
||||
<h2 mat-dialog-title>Add currency</h2>
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="addForm!" (ngSubmit)="onSubmitClick()">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Symbol</mat-label>
|
||||
<input matInput value={{currency.symbol}} formControlName="symbol" readonly="">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Code</mat-label>
|
||||
<input matInput value={{currency.code}} formControlName="code" readonly="">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput value={{currency.name}} formControlName="name" readonly="">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Rate</mat-label>
|
||||
<input matInput [value]="currency.rate | number:'0.2-2'" formControlName="rate" currencyMask [options]="{ prefix: '', precision: 4, inputMode: 1 }" required>
|
||||
<mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')">
|
||||
Please enter rate
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Quantity</mat-label>
|
||||
<input matInput [value]="currency.rate | number:'0.0-0'" formControlName="quantity" currencyMask [options]="{ prefix: '', precision: 0 }" required>
|
||||
<mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')">
|
||||
Please enter rate
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Rate Date</mat-label>
|
||||
<input matInput [matDatepicker]="picker3" (focus)="picker3.open()" value={{currency.rateDate}} formControlName="rateDate" required>
|
||||
<mat-hint>YYYY/MM/DD</mat-hint>
|
||||
<mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker3></mat-datepicker>
|
||||
<mat-error *ngIf="addForm.controls?.['rateDate']?.hasError('required')">
|
||||
Please enter rate date
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-dialog-actions class="mat-dialog-actions">
|
||||
<div class="mat-dialog-left">
|
||||
<div class="alert alert-danger mat-dialog-error" *ngIf="errorMessage">
|
||||
{{errorMessage}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mat-dialog-right">
|
||||
<div class="button-bottom">
|
||||
<button type="button" mat-button (click)="closeDialog()">Cancel</button>
|
||||
<button class="btn-space" mat-raised-button color="primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,79 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { Inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { UntypedFormBuilder } from '@angular/forms';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
|
||||
// libs
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { CurrencyMaskInputMode, NgxCurrencyModule } from "ngx-currency";
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { CurrencyModel } from '../../../model/currency.model';
|
||||
|
||||
@Component({
|
||||
templateUrl: './rate.currency.component.html',
|
||||
styleUrls: ['./rate.currency.component.scss'],
|
||||
})
|
||||
export class SettingsRateCurrencyComponent {
|
||||
public addForm!: UntypedFormGroup;
|
||||
public errorMessage?: string;
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private httpClient: HttpClient,
|
||||
private dialogRef: MatDialogRef<SettingsRateCurrencyComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public currency: CurrencyModel
|
||||
) {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.addForm = this.fb.group({
|
||||
id: [
|
||||
this.currency.id,
|
||||
],
|
||||
code: [
|
||||
this.currency.code,
|
||||
],
|
||||
symbol: [
|
||||
this.currency.symbol,
|
||||
],
|
||||
name: [
|
||||
this.currency.name,
|
||||
],
|
||||
quantity: [
|
||||
this.currency.quantity || 1,
|
||||
[Validators.required],
|
||||
],
|
||||
rate: [
|
||||
this.currency.rate || 1,
|
||||
[Validators.required],
|
||||
],
|
||||
rateDate: [
|
||||
this.currency.rateDate,
|
||||
[Validators.required],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onSubmitClick() {
|
||||
if (this.addForm.valid) {
|
||||
this.errorMessage = undefined;
|
||||
this.httpClient
|
||||
.post<CurrencyModel[]>(ApiRoutes.SettingsCurrenciesRate.replace(':id', this.addForm.value.id), this.addForm.value)
|
||||
.subscribe(response => {
|
||||
this.dialogRef.close({ refresh: true });
|
||||
}, error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<div>
|
||||
<h2 mat-dialog-title>
|
||||
Edit motion
|
||||
</h2>
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="editForm!" (ngSubmit)="onSubmitClick()">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput value={{motion.name}} formControlName="name" readonly>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-inside">
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Category</mat-label>
|
||||
<mat-select formControlName="category">
|
||||
<mat-option *ngFor="let category of categories" [value]="category.id">
|
||||
{{category.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-dialog-actions class="mat-dialog-actions">
|
||||
<div class="mat-dialog-left">
|
||||
<div class="alert alert-danger mat-dialog-error" *ngIf="errorMessage">
|
||||
{{errorMessage}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mat-dialog-right">
|
||||
<div class="button-bottom">
|
||||
<button type="button" mat-button (click)="closeDialog()">Cancel</button>
|
||||
<button class="btn-space" mat-raised-button color="primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,74 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject } from '@angular/core';
|
||||
import { UntypedFormBuilder } from '@angular/forms';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { AccountCategoryModel } from '../../../model/account.category.model';
|
||||
import { MotionModel } from '../../../model/motion.model';
|
||||
import { MotionService } from '../../../services/motion.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './edit.motion.component.html',
|
||||
styleUrls: ['./edit.motion.component.scss'],
|
||||
})
|
||||
export class SettingsMotionEditComponent {
|
||||
public editForm!: UntypedFormGroup;
|
||||
public errorMessage?: string;
|
||||
public categories?: AccountCategoryModel[];
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private httpClient: HttpClient,
|
||||
private dialogRef: MatDialogRef<SettingsMotionEditComponent>,
|
||||
private motionService: MotionService,
|
||||
@Inject(MAT_DIALOG_DATA) public motion: MotionModel
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.editForm = this.fb.group({
|
||||
id: [
|
||||
this.motion.id,
|
||||
],
|
||||
name: [
|
||||
this.motion.name,
|
||||
[Validators.required],
|
||||
],
|
||||
category: [
|
||||
this.motion.categoryId,
|
||||
[Validators.required],
|
||||
],
|
||||
});
|
||||
|
||||
this.motionService
|
||||
.getCategories()
|
||||
.subscribe(x => this.categories = x);
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onSubmitClick() {
|
||||
if (this.editForm.valid) {
|
||||
this.errorMessage = undefined;
|
||||
this.httpClient
|
||||
.put<MotionModel>(ApiRoutes.SettingsMotion.replace(':id', this.motion.id!), this.editForm.value)
|
||||
.subscribe(response => {
|
||||
this.dialogRef.close({ motion: response, refresh: true });
|
||||
}, error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Blank'" [items]="['Home','Settings']" [active_item]="'Motion categories'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<mat-card class="card">
|
||||
<mat-card-header>
|
||||
<mat-card-title-group>
|
||||
<mat-card-title>
|
||||
Motion categories
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
</mat-card-subtitle>
|
||||
</mat-card-title-group>
|
||||
<div fxFlex></div>
|
||||
<button class="btn-space " (click)="add()" mat-raised-button color="primary">
|
||||
Add
|
||||
</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="body table-responsive">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let item of categories">
|
||||
<td>{{item.name}}</td>
|
||||
<td>
|
||||
<button class="btn-space" (click)="edit(item)" mat-raised-button color="primary">
|
||||
Edit
|
||||
</button>
|
||||
<button class="btn-space" *ngIf="item.allowDelete" (click)="remove(item)" mat-raised-button color="primary">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,122 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { MotionCategoryModel } from '../../../model/motion.category.model';
|
||||
|
||||
@Component({
|
||||
templateUrl: './motion.category.component.html',
|
||||
styleUrls: ['./motion.category.component.scss'],
|
||||
})
|
||||
export class SettingsMotionCategoryComponent {
|
||||
public categories?: MotionCategoryModel[];
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private load() {
|
||||
this.httpClient
|
||||
.get<MotionCategoryModel[]>(ApiRoutes.SettingsMotionCategories)
|
||||
.subscribe(data => {
|
||||
this.categories = data;
|
||||
});
|
||||
}
|
||||
|
||||
public add() {
|
||||
Swal.fire({
|
||||
title: 'Motion category name',
|
||||
input: 'text',
|
||||
inputAttributes: {
|
||||
autocapitalize: 'off',
|
||||
},
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Add',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.post(ApiRoutes.SettingsMotionCategories, { name: name })
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
|
||||
public edit(category: MotionCategoryModel) {
|
||||
Swal.fire({
|
||||
title: 'Motion category name',
|
||||
input: 'text',
|
||||
inputValue: category.name,
|
||||
inputAttributes: {
|
||||
autocapitalize: 'off',
|
||||
},
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Update',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.put(ApiRoutes.SettingsMotionCategory.replace(':id', category.id!), { name: name })
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
|
||||
public remove(category: MotionCategoryModel) {
|
||||
Swal.fire({
|
||||
title: 'Remove motion category ' + category.name,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Delete',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.delete(ApiRoutes.SettingsMotionCategory.replace(':id', category.id!))
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Blank'" [items]="['Home','Settings']" [active_item]="'Motions'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="body">
|
||||
<div id="mail-nav">
|
||||
<ul class="" id="mail-folders">
|
||||
<li *ngFor="let category of categories" [class.active]="selectedCategory && selectedCategory.id == category.id">
|
||||
<a href="javascript:;" (click)="selectCategory(category)" title="{{category.name}}">{{category.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-9 col-lg-9">
|
||||
<mat-card class="card">
|
||||
<mat-card-header>
|
||||
<mat-card-title-group>
|
||||
<mat-card-title>
|
||||
Motions
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
</mat-card-subtitle>
|
||||
</mat-card-title-group>
|
||||
<div fxFlex></div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="body table-responsive">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr *ngFor="let item of motions">
|
||||
<td>{{item.name}}</td>
|
||||
<td>
|
||||
<button class="btn-space" (click)="edit(item)" mat-raised-button color="primary">
|
||||
Edit
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,98 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../../api-routes';
|
||||
import { SettingsMotionEditComponent } from './edit.motion.component';
|
||||
import { MotionService } from '../../../services/motion.service';
|
||||
import { MotionModel } from '../../../model/motion.model';
|
||||
import { MotionCategoryModel } from '../../../model/motion.category.model';
|
||||
|
||||
@Component({
|
||||
templateUrl: './motion.component.html',
|
||||
styleUrls: ['./motion.component.scss'],
|
||||
})
|
||||
export class SettingsMotionComponent {
|
||||
public motions?: MotionModel[];
|
||||
public categories?: MotionCategoryModel[];
|
||||
public selectedCategory?: MotionCategoryModel;
|
||||
|
||||
constructor(
|
||||
private dialogModel: MatDialog,
|
||||
private httpClient: HttpClient,
|
||||
private motionService: MotionService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private load(categoryId?: string) {
|
||||
this.motionService.getCategories()
|
||||
.subscribe(data => {
|
||||
this.categories = data;
|
||||
var selected = data[0];
|
||||
if (categoryId) {
|
||||
var categories = this.categories.filter(x => x.id === categoryId);
|
||||
selected = categories.length === 0
|
||||
? selected
|
||||
: categories[0];
|
||||
}
|
||||
|
||||
this.selectCategory(selected);
|
||||
});
|
||||
}
|
||||
|
||||
public selectCategory(category: MotionCategoryModel) {
|
||||
this.selectedCategory = category;
|
||||
this.motionService.getMotions(category.id)
|
||||
.subscribe(data => {
|
||||
this.motions = data;
|
||||
});
|
||||
}
|
||||
|
||||
public edit(motion: MotionModel) {
|
||||
this.dialogModel.open(SettingsMotionEditComponent, {
|
||||
width: '640px',
|
||||
disableClose: true,
|
||||
data: motion,
|
||||
}).afterClosed().subscribe(x => {
|
||||
if (x && x.refresh) {
|
||||
console.log(x.motion.categoryId);
|
||||
this.load(x.motion.categoryId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public remove(category: MotionModel) {
|
||||
Swal.fire({
|
||||
title: 'Remove motion ' + category.name,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Delete',
|
||||
showLoaderOnConfirm: true,
|
||||
preConfirm: (name) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.httpClient.delete(ApiRoutes.SettingsMotionCategory.replace(':id', category.id!))
|
||||
.subscribe(data => {
|
||||
resolve(data);
|
||||
}, error => {
|
||||
Swal.showValidationMessage(error.detail);
|
||||
reject();
|
||||
});
|
||||
|
||||
}).catch(x => {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
allowOutsideClick: () => !Swal.isLoading(),
|
||||
}).then((result) => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,19 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select formControlName="currency">
|
||||
<mat-option *ngFor="let item of currencies" [value]="item.id">
|
||||
({{item.symbol}}) {{item.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<button class="btn-space" [disabled]="!form.valid" mat-raised-button color="primary">
|
||||
@@ -88,21 +101,21 @@
|
||||
<div class="col-md-2">
|
||||
<span class="fl-r" *ngIf="!provider.isConnected">
|
||||
<button color="accent" class="btn-block mw-100-imp"
|
||||
mat-raised-button
|
||||
(click)="connect(provider)"
|
||||
[disabled]="provider.isLoading"
|
||||
[class.spinner]="provider.isLoading"
|
||||
type="button">
|
||||
mat-raised-button
|
||||
(click)="connect(provider)"
|
||||
[disabled]="provider.isLoading"
|
||||
[class.spinner]="provider.isLoading"
|
||||
type="button">
|
||||
Connect
|
||||
</button>
|
||||
</span>
|
||||
<span class="fl-r" *ngIf="provider.isConnected">
|
||||
<button color="warn" class="btn-block mw-100-imp"
|
||||
mat-raised-button
|
||||
(click)="disconnect(provider.provider)"
|
||||
[disabled]="provider.isLoading"
|
||||
[class.spinner]="provider.isLoading"
|
||||
type="button">
|
||||
mat-raised-button
|
||||
(click)="disconnect(provider.provider)"
|
||||
[disabled]="provider.isLoading"
|
||||
[class.spinner]="provider.isLoading"
|
||||
type="button">
|
||||
Disconect
|
||||
</button>
|
||||
</span>
|
||||
|
||||
@@ -22,6 +22,8 @@ import { AuthService } from '../../core/service/auth.service';
|
||||
export class UserProfileComponent {
|
||||
form: UntypedFormGroup;
|
||||
providers?: ProviderModel[];
|
||||
profile?: ProfileModel;
|
||||
currencies?: CurrencyModel[];
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
@@ -33,6 +35,7 @@ export class UserProfileComponent {
|
||||
lastName: ['', [Validators.pattern('[a-zA-Z0-9]+')]],
|
||||
fullName: ['', [Validators.pattern('[a-zA-Z0-9 ]+')]],
|
||||
email: [{ value: '', disabled: true }, []],
|
||||
currency: [{ value: '' }, []],
|
||||
});
|
||||
|
||||
this.providers = ExternalLoginConfig
|
||||
@@ -54,19 +57,36 @@ export class UserProfileComponent {
|
||||
}
|
||||
|
||||
private load() {
|
||||
this.httpClient.get<ProfileModel>(ApiRoutes.UserProfile).subscribe(data => {
|
||||
this.updateForm(data);
|
||||
this.loadProfile();
|
||||
}
|
||||
|
||||
this.providers?.map((provider) => {
|
||||
var attached = data.providers?.find(x => x.provider === provider.provider);
|
||||
provider.isConnected = false;
|
||||
provider.isEmailConfirmed = false;
|
||||
if (attached) {
|
||||
provider.isConnected = true;
|
||||
provider.isEmailConfirmed = attached.isEmailConfirmed;
|
||||
}
|
||||
private loadProfile() {
|
||||
this.httpClient
|
||||
.get<ProfileModel>(ApiRoutes.UserProfile)
|
||||
.subscribe(data => {
|
||||
this.profile = data;
|
||||
this.loadCurrencies();
|
||||
|
||||
this.updateForm(data);
|
||||
|
||||
this.providers?.map((provider) => {
|
||||
var attached = data.providers?.find(x => x.provider === provider.provider);
|
||||
provider.isConnected = false;
|
||||
provider.isEmailConfirmed = false;
|
||||
if (attached) {
|
||||
provider.isConnected = true;
|
||||
provider.isEmailConfirmed = attached.isEmailConfirmed;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private loadCurrencies() {
|
||||
this.httpClient
|
||||
.get<CurrencyModel[]>(ApiRoutes.GeneralCurrencies)
|
||||
.subscribe(data => {
|
||||
this.currencies = data;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
@@ -134,6 +154,15 @@ interface ProfileModel {
|
||||
firstName?: string,
|
||||
lastName?: string,
|
||||
fullName?: string,
|
||||
currency?: string,
|
||||
isEmailConfirmed?: boolean,
|
||||
providers?: ProviderModel[],
|
||||
}
|
||||
|
||||
interface CurrencyModel {
|
||||
id?: string,
|
||||
name?: string,
|
||||
symbol?: string,
|
||||
rate?: number,
|
||||
rateDate?: string,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// angular
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../api-routes';
|
||||
import { AccountCategoryModel } from '../model/account.category.model';
|
||||
|
||||
@Injectable()
|
||||
export class AccountCategoryService {
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
) {
|
||||
}
|
||||
|
||||
public getCategories(): Observable<AccountCategoryModel[]> {
|
||||
return this.httpClient
|
||||
.get<AccountCategoryModel[]>(ApiRoutes.SettingsAccountCategories);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// angular
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../api-routes';
|
||||
import { CurrencyModel } from '../model/currency.model';
|
||||
|
||||
@Injectable()
|
||||
export class CurrencyService {
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
) {
|
||||
}
|
||||
|
||||
public getCurrencies(): Observable<CurrencyModel[]> {
|
||||
return this.httpClient
|
||||
.get<CurrencyModel[]>(ApiRoutes.SettingsCurrencies);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// angular
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../api-routes';
|
||||
import { MotionModel } from '../model/motion.model';
|
||||
import { AccountCategoryModel } from '../model/account.category.model';
|
||||
import { MotionCategoryModel } from '../model/motion.category.model';
|
||||
|
||||
@Injectable()
|
||||
export class MotionService {
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
) {
|
||||
}
|
||||
|
||||
public getCategories(category?: string): Observable<MotionCategoryModel[]> {
|
||||
return this.httpClient
|
||||
.get<MotionCategoryModel[]>(ApiRoutes.SettingsMotionCategories);
|
||||
}
|
||||
|
||||
public getMotions(categoryId?: string): Observable<MotionModel[]> {
|
||||
var url = categoryId
|
||||
? ApiRoutes.SettingsMotions + "?category=" + categoryId
|
||||
: ApiRoutes.SettingsMotions;
|
||||
|
||||
return this.httpClient
|
||||
.get<MotionCategoryModel[]>(url);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,13 @@
|
||||
/*
|
||||
* Document : _expansion
|
||||
* Author : RedStar Template
|
||||
* Description: This scss file for collapse style classes
|
||||
*/
|
||||
|
||||
.example-headers-align .mat-expansion-panel-header-title,
|
||||
.example-headers-align .mat-expansion-panel-header-description {
|
||||
flex-basis: 0;
|
||||
.main-headers-align .mat-expansion-panel-header-title,
|
||||
.main-headers-align .mat-expansion-panel-header-description {
|
||||
flex-basis: 0;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-expansion-panel-header-description {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.main-headers-align .mat-expansion-panel-header-description {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-form-field + .mat-form-field {
|
||||
margin-left: 8px;
|
||||
.main-headers-align .mat-form-field + .mat-form-field {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ const PROXY_CONFIG = [
|
||||
'/connect',
|
||||
'/weatherforecast',
|
||||
],
|
||||
target: 'https://localhost:9101',
|
||||
target: 'http://localhost:9300',
|
||||
secure: false
|
||||
}
|
||||
];
|
||||
|
||||
@@ -25,3 +25,44 @@
|
||||
border-top-color: #000000;
|
||||
animation: spinner .8s linear infinite;
|
||||
}
|
||||
|
||||
.mat-dialog-actions {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr auto !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.mat-dialog-actions {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr auto !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.mat-dialog-left {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin: 0 !important;
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.mat-dialog-right {
|
||||
display: flex !important;
|
||||
justify-content: flex-end !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.button-bottom {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.button-bottom button {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user