This commit is contained in:
2023-02-22 12:12:38 +01:00
parent 4ce7d62433
commit 185746ee72
54 changed files with 3701 additions and 3701 deletions

View File

@@ -1,7 +1,7 @@
{
"folders": [
{
"path": ".."
}
]
{
"folders": [
{
"path": ".."
}
]
}

View File

@@ -1,13 +1,13 @@
<div class="loading-container" *ngIf="loading">
<img class="loading-img" src="../../assets/loader.gif" />
</div>
<div class="logo"></div>
<div class="bg">
<div class="container">
<mat-card appearance="outlined" class="form">
<mat-card-content>
<div class="" id="google-button"></div>
</mat-card-content>
</mat-card>
</div>
<div class="loading-container" *ngIf="loading">
<img class="loading-img" src="../../assets/loader.gif" />
</div>
<div class="logo"></div>
<div class="bg">
<div class="container">
<mat-card appearance="outlined" class="form">
<mat-card-content>
<div class="" id="google-button"></div>
</mat-card-content>
</mat-card>
</div>
</div>

View File

@@ -1,63 +1,63 @@
.bg {
background-image: url("../../../assets/bg.jpg");
height: 70vh;
background-size: cover;
padding-top: 30vh;
}
.container {
width: fit-content;
display: block;
margin: auto;
}
.form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.logo {
background-image: url('../../../assets/logo.png');
background-size: cover;
position: absolute;
top: 10px;
right: 10px;
width: 250px;
height: 250px;
opacity: 0.6;
}
mat-form-field {
width: 100%;
}
.user {
text-align: right;
}
.load {
text-align: center;
}
.loading-container {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(100, 100, 100, 0.3);
z-index: 1400;
}
.loading-img {
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* for mobile */
@media screen and (max-width: 700px) {
.container {
width: 90%;
}
.logo {
width: 150px;
height: 150px;
}
}
.bg {
background-image: url("../../../assets/bg.jpg");
height: 70vh;
background-size: cover;
padding-top: 30vh;
}
.container {
width: fit-content;
display: block;
margin: auto;
}
.form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.logo {
background-image: url('../../../assets/logo.png');
background-size: cover;
position: absolute;
top: 10px;
right: 10px;
width: 250px;
height: 250px;
opacity: 0.6;
}
mat-form-field {
width: 100%;
}
.user {
text-align: right;
}
.load {
text-align: center;
}
.loading-container {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(100, 100, 100, 0.3);
z-index: 1400;
}
.loading-img {
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* for mobile */
@media screen and (max-width: 700px) {
.container {
width: 90%;
}
.logo {
width: 150px;
height: 150px;
}
}

View File

@@ -1,98 +1,98 @@
import { Component, NgZone, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import jwt_decode from "jwt-decode";
import { AuthService } from 'src/app/auth/auth.service';
import { User } from 'src/app/models/user.model';
import { NotificationsService } from 'src/app/services/notifications.service';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.scss']
})
export class LoginPageComponent implements OnInit {
constructor(
private router$: Router,
private auth$: AuthService,
private ngZone$: NgZone,
private notifications$: NotificationsService
) { }
loading = false;
ngOnInit(): void {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.onGoogleLibraryLoad = () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
google.accounts.id.initialize({
client_id: environment.google.clientId,
callback: this.handleCredentialResponse.bind(this),
auto_select: true,
cancel_on_tap_outside: true
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
google.accounts.id.renderButton(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
document.getElementById("google-button"),
{ theme: "outline", size: "large", width: "100%" }
);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
google.accounts.id.prompt();
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async handleCredentialResponse(response: any) {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const responsePayload: any = jwt_decode(response.credential);
this.auth$.user = new User({
googleCredentials: response.credential,
userName: `${responsePayload.given_name} ${responsePayload.family_name}`,
email: responsePayload.email,
avatar: responsePayload.picture
});
this.ngZone$.run(() => {
this.loading = true;
});
this.auth$.googleCredential = response.credential;
await this.auth$.getAPIToken();
this.ngZone$.run(() => {
this.router$.navigate(['/app']);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
console.error('handleCredentialResponse', e);
this.ngZone$.run(() => {
this.loading = false;
});
if (e.status === 401) {
this.ngZone$.run(() => {
this.notifications$.add({
text: "User not exists in DiunaBI database.",
btn: "OK",
duration: 15000
});
});
} else {
this.ngZone$.run(() => {
this.notifications$.add({
text: "DiunaBI server not responded.",
btn: "OK",
duration: 15000
});
});
}
} finally {
this.loading = false;
}
}
}
import { Component, NgZone, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import jwt_decode from "jwt-decode";
import { AuthService } from 'src/app/auth/auth.service';
import { User } from 'src/app/models/user.model';
import { NotificationsService } from 'src/app/services/notifications.service';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.scss']
})
export class LoginPageComponent implements OnInit {
constructor(
private router$: Router,
private auth$: AuthService,
private ngZone$: NgZone,
private notifications$: NotificationsService
) { }
loading = false;
ngOnInit(): void {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.onGoogleLibraryLoad = () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
google.accounts.id.initialize({
client_id: environment.google.clientId,
callback: this.handleCredentialResponse.bind(this),
auto_select: true,
cancel_on_tap_outside: true
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
google.accounts.id.renderButton(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
document.getElementById("google-button"),
{ theme: "outline", size: "large", width: "100%" }
);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
google.accounts.id.prompt();
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async handleCredentialResponse(response: any) {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const responsePayload: any = jwt_decode(response.credential);
this.auth$.user = new User({
googleCredentials: response.credential,
userName: `${responsePayload.given_name} ${responsePayload.family_name}`,
email: responsePayload.email,
avatar: responsePayload.picture
});
this.ngZone$.run(() => {
this.loading = true;
});
this.auth$.googleCredential = response.credential;
await this.auth$.getAPIToken();
this.ngZone$.run(() => {
this.router$.navigate(['/app']);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
console.error('handleCredentialResponse', e);
this.ngZone$.run(() => {
this.loading = false;
});
if (e.status === 401) {
this.ngZone$.run(() => {
this.notifications$.add({
text: "User not exists in DiunaBI database.",
btn: "OK",
duration: 15000
});
});
} else {
this.ngZone$.run(() => {
this.notifications$.add({
text: "DiunaBI server not responded.",
btn: "OK",
duration: 15000
});
});
}
} finally {
this.loading = false;
}
}
}

View File

@@ -1,15 +1,15 @@
mat-card {
margin-bottom: 3px;
background-color: rgba(255, 145, 0, 0.4);
}
.action-button {
cursor: pointer;
}
.text {
float: left;
}
.btn {
float: right;
color: red;
margin-left: 5px;
mat-card {
margin-bottom: 3px;
background-color: rgba(255, 145, 0, 0.4);
}
.action-button {
cursor: pointer;
}
.text {
float: left;
}
.btn {
float: right;
color: red;
margin-left: 5px;
}

View File

@@ -1,56 +1,56 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { AuthService } from '../auth/auth.service';
import { EMPTY, Observable } from 'rxjs';
import moment from 'moment';
import { catchError, mergeMap } from 'rxjs/operators';
import { NotificationsService } from '../services/notifications.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(
private auth$: AuthService,
private notifications$: NotificationsService
) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
console.log('Welcome to interceptor:', request.url);
console.log('IsAuth?', request.url.includes('/auth/apiToken'));
if (!request.url.includes('/auth/apiToken')) {
console.log(this.auth$.expirationTime.format(), moment.utc().format());
if (this.auth$.expirationTime.isBefore(moment.utc())) {
console.log('Need to refresh token');
return this.auth$.getAPITokenObservable().pipe(
mergeMap(() => {
console.log('New token is ready');
return next.handle(request.clone({
headers: request.headers.set('Authorization', `Bearer ${this.auth$.apiToken}`),
}));
}),
catchError(() => {
this.notifications$.add({
text: "User session is expired and unable to restore. Please restart the app.",
btn: "Restart",
action: () => { window.location.reload(); },
duration: 5000,
});
return EMPTY;
})
);
} else {
console.log('TOken is fine');
return next.handle(request.clone({
headers: request.headers.set('Authorization', `Bearer ${this.auth$.apiToken}`),
}));
}
} else {
return next.handle(request);
}
}
}
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { AuthService } from '../auth/auth.service';
import { EMPTY, Observable } from 'rxjs';
import moment from 'moment';
import { catchError, mergeMap } from 'rxjs/operators';
import { NotificationsService } from '../services/notifications.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(
private auth$: AuthService,
private notifications$: NotificationsService
) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
console.log('Welcome to interceptor:', request.url);
console.log('IsAuth?', request.url.includes('/auth/apiToken'));
if (!request.url.includes('/auth/apiToken')) {
console.log(this.auth$.expirationTime.format(), moment.utc().format());
if (this.auth$.expirationTime.isBefore(moment.utc())) {
console.log('Need to refresh token');
return this.auth$.getAPITokenObservable().pipe(
mergeMap(() => {
console.log('New token is ready');
return next.handle(request.clone({
headers: request.headers.set('Authorization', `Bearer ${this.auth$.apiToken}`),
}));
}),
catchError(() => {
this.notifications$.add({
text: "User session is expired and unable to restore. Please restart the app.",
btn: "Restart",
action: () => { window.location.reload(); },
duration: 5000,
});
return EMPTY;
})
);
} else {
console.log('TOken is fine');
return next.handle(request.clone({
headers: request.headers.set('Authorization', `Bearer ${this.auth$.apiToken}`),
}));
}
} else {
return next.handle(request);
}
}
}

View File

@@ -1,42 +1,42 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { finalize, Observable } from 'rxjs';
import { DataService } from '../services/data.service';
@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
private tasks = 0;
constructor(
private data$: DataService
) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
this.addTask();
return next.handle(request).pipe(
finalize(() => {
this.removeTask();
})
);
}
addTask() {
this.tasks++;
if (this.tasks === 1) {
this.data$.showLoader.next(true);
}
}
removeTask() {
this.tasks--;
if (this.tasks === 0) {
this.data$.showLoader.next(false);
}
}
}
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { finalize, Observable } from 'rxjs';
import { DataService } from '../services/data.service';
@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
private tasks = 0;
constructor(
private data$: DataService
) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
this.addTask();
return next.handle(request).pipe(
finalize(() => {
this.removeTask();
})
);
}
addTask() {
this.tasks++;
if (this.tasks === 1) {
this.data$.showLoader.next(true);
}
}
removeTask() {
this.tasks--;
if (this.tasks === 0) {
this.data$.showLoader.next(false);
}
}
}

View File

@@ -1,128 +1,128 @@
.main-container {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.sidenav-container {
flex: 1;
}
mat-icon.menu-icon {
margin-right: 3px;
color: gray;
}
mat-nav-list.menu-sublist {
padding-left: 10px;
}
mat-nav-list.menu-sublist > a {
font-size: small;
}
div.footer {
text-align: right;
font-size: smaller;
color: gray;
padding-right: 5px;
}
.fill-to-right {
flex: 1 1 auto;
}
span.topbar-user-name {
font-size: small;
}
h1.topbar-app-name {
margin-bottom: 0px;
}
mat-sidenav {
width: 200px;
}
.list-container {
display: flex;
flex-direction: column;
min-width: 300px;
height: 98%;
}
.top-list-container {
display: flex;
flex-direction: column;
min-width: 30vh;
height: 54vh;
}
.bottom-list-container {
display: flex;
flex-direction: column;
height: 35vh;
}
.table {
overflow: auto;
height: 100%;
}
.list-header {
min-height: 50px;
padding: 4px 12px 0;
}
.mat-mdc-form-field {
font-size: 14px;
width: 100%;
}
mat-form-field.detail-input {
width: 90%;
text-align: left;
color: black;
}
mat-form-field.detail-input-full-width {
width: 96%;
text-align: left;
color: black;
}
input[disabled] {
color: black;
-webkit-text-fill-color: black;
opacity: 1; /* required on iOS */
}
textarea[disabled] {
color: black;
-webkit-text-fill-color: black;
opacity: 1; /* required on iOS */
}
.loading-container {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(100, 100, 100, 0.3);
z-index: 1400;
}
.loading-img {
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.flip-container {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(100, 100, 100, 0.95);
z-index: 1500;
}
.flip-msg {
margin: auto;
text-align: center;
padding-top: 45vh;
color: rgb(205, 206, 177);
font-size: larger;
}
/* links */
a:link,
a:visited {
color: black;
.main-container {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.sidenav-container {
flex: 1;
}
mat-icon.menu-icon {
margin-right: 3px;
color: gray;
}
mat-nav-list.menu-sublist {
padding-left: 10px;
}
mat-nav-list.menu-sublist > a {
font-size: small;
}
div.footer {
text-align: right;
font-size: smaller;
color: gray;
padding-right: 5px;
}
.fill-to-right {
flex: 1 1 auto;
}
span.topbar-user-name {
font-size: small;
}
h1.topbar-app-name {
margin-bottom: 0px;
}
mat-sidenav {
width: 200px;
}
.list-container {
display: flex;
flex-direction: column;
min-width: 300px;
height: 98%;
}
.top-list-container {
display: flex;
flex-direction: column;
min-width: 30vh;
height: 54vh;
}
.bottom-list-container {
display: flex;
flex-direction: column;
height: 35vh;
}
.table {
overflow: auto;
height: 100%;
}
.list-header {
min-height: 50px;
padding: 4px 12px 0;
}
.mat-mdc-form-field {
font-size: 14px;
width: 100%;
}
mat-form-field.detail-input {
width: 90%;
text-align: left;
color: black;
}
mat-form-field.detail-input-full-width {
width: 96%;
text-align: left;
color: black;
}
input[disabled] {
color: black;
-webkit-text-fill-color: black;
opacity: 1; /* required on iOS */
}
textarea[disabled] {
color: black;
-webkit-text-fill-color: black;
opacity: 1; /* required on iOS */
}
.loading-container {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(100, 100, 100, 0.3);
z-index: 1400;
}
.loading-img {
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.flip-container {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(100, 100, 100, 0.95);
z-index: 1500;
}
.flip-msg {
margin: auto;
text-align: center;
padding-top: 45vh;
color: rgb(205, 206, 177);
font-size: larger;
}
/* links */
a:link,
a:visited {
color: black;
}

View File

@@ -1,73 +1,73 @@
import {NgModule} from '@angular/core';
//import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
//import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
//import { MatCheckboxModule } from '@angular/material/checkbox';
//import { MatChipsModule } from '@angular/material/chips';
//import { MatStepperModule } from '@angular/material/stepper';
//import { MatDatepickerModule } from '@angular/material/datepicker';
//import { MatDialogModule } from '@angular/material/dialog';
//import { MatExpansionModule } from '@angular/material/expansion';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
//import { MatProgressBarModule } from '@angular/material/progress-bar';
//import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
//import { MatRadioModule } from '@angular/material/radio';
//import { MatRippleModule } from '@angular/material/core';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
//import { MatSliderModule } from '@angular/material/slider';
//import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
//import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
//import { MatTooltipModule } from '@angular/material/tooltip';
//import {CdkTableModule} from '@angular/cdk/table';
//import {MatBadgeModule} from '@angular/material/badge';
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
@NgModule({
exports: [
// CdkTableModule,
// MatAutocompleteModule,
MatButtonModule,
// MatButtonToggleModule,
MatCardModule,
// MatCheckboxModule,
// MatChipsModule,
// MatStepperModule,
// MatDatepickerModule,
// MatDialogModule,
// MatExpansionModule,
MatGridListModule,
MatIconModule,
MatInputModule,
MatListModule,
MatMenuModule,
MatPaginatorModule,
// MatProgressBarModule,
// MatProgressSpinnerModule,
// MatRadioModule,
// MatRippleModule,
MatSelectModule,
// MatSidenavModule,
// MatSliderModule,
// MatSlideToggleModule,
MatSortModule,
MatTableModule,
// MatTabsModule,
MatToolbarModule,
// MatTooltipModule,
MatSidenavModule,
// MatBadgeModule,
MatBottomSheetModule
],
providers: []
})
import {NgModule} from '@angular/core';
//import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
//import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
//import { MatCheckboxModule } from '@angular/material/checkbox';
//import { MatChipsModule } from '@angular/material/chips';
//import { MatStepperModule } from '@angular/material/stepper';
//import { MatDatepickerModule } from '@angular/material/datepicker';
//import { MatDialogModule } from '@angular/material/dialog';
//import { MatExpansionModule } from '@angular/material/expansion';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
//import { MatProgressBarModule } from '@angular/material/progress-bar';
//import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
//import { MatRadioModule } from '@angular/material/radio';
//import { MatRippleModule } from '@angular/material/core';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
//import { MatSliderModule } from '@angular/material/slider';
//import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
//import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
//import { MatTooltipModule } from '@angular/material/tooltip';
//import {CdkTableModule} from '@angular/cdk/table';
//import {MatBadgeModule} from '@angular/material/badge';
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
@NgModule({
exports: [
// CdkTableModule,
// MatAutocompleteModule,
MatButtonModule,
// MatButtonToggleModule,
MatCardModule,
// MatCheckboxModule,
// MatChipsModule,
// MatStepperModule,
// MatDatepickerModule,
// MatDialogModule,
// MatExpansionModule,
MatGridListModule,
MatIconModule,
MatInputModule,
MatListModule,
MatMenuModule,
MatPaginatorModule,
// MatProgressBarModule,
// MatProgressSpinnerModule,
// MatRadioModule,
// MatRippleModule,
MatSelectModule,
// MatSidenavModule,
// MatSliderModule,
// MatSlideToggleModule,
MatSortModule,
MatTableModule,
// MatTabsModule,
MatToolbarModule,
// MatTooltipModule,
MatSidenavModule,
// MatBadgeModule,
MatBottomSheetModule
],
providers: []
})
export class MaterialModule {}

View File

@@ -1,35 +1,35 @@
import { Moment } from 'moment';
import { Deserializable } from './deserializable.model';
import { Serializable } from './serializable.model';
import * as moment from 'moment';
import { User } from './user.model';
export class Base implements Deserializable, Serializable {
id?: string;
createdAt?: Moment;
modifiedAt?: Moment;
createdById?: string;
modifiedById?: string;
createdBy?: User;
modifiedBy?: User;
constructor(data: Partial<Base> = {}) {
Object.assign(this, data);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deserialize(input: any): this {
if (input.createdAt) { input.createdAt = moment(input.createdAt).utc(true); }
if (input.modifiedAt) { input.modifiedAt = moment(input.modifiedAt).utc(true); }
if (input.createdBy) { input.createdBy = new User(input.createdBy); }
if (input.modifiedBy) { input.modifiedBy = new User(input.modifiedBy); }
Object.assign(this, input);
return this;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
serialize() : any {
return Object.assign({}, this);
}
import { Moment } from 'moment';
import { Deserializable } from './deserializable.model';
import { Serializable } from './serializable.model';
import * as moment from 'moment';
import { User } from './user.model';
export class Base implements Deserializable, Serializable {
id?: string;
createdAt?: Moment;
modifiedAt?: Moment;
createdById?: string;
modifiedById?: string;
createdBy?: User;
modifiedBy?: User;
constructor(data: Partial<Base> = {}) {
Object.assign(this, data);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deserialize(input: any): this {
if (input.createdAt) { input.createdAt = moment(input.createdAt).utc(true); }
if (input.modifiedAt) { input.modifiedAt = moment(input.modifiedAt).utc(true); }
if (input.createdBy) { input.createdBy = new User(input.createdBy); }
if (input.modifiedBy) { input.modifiedBy = new User(input.modifiedBy); }
Object.assign(this, input);
return this;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
serialize() : any {
return Object.assign({}, this);
}
}

View File

@@ -1,4 +1,4 @@
export interface Deserializable {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deserialize(input: any): this;
export interface Deserializable {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deserialize(input: any): this;
}

View File

@@ -1,124 +1,124 @@
import { Base } from './base.model';
import { UntypedFormBuilder, Validators, UntypedFormGroup } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { map } from 'rxjs';
import { Record } from 'src/app/models/record.model';
export class Layer extends Base {
// eslint-disable-next-line @typescript-eslint/ban-types
number?: Number;
source?: string;
name?: string;
records: Record[] = [];
created?: string;
constructor(data: Partial<Layer> = {}) {
super();
Object.assign(this, data);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override deserialize(input: any): this {
Object.assign(this, Object.assign(this, super.deserialize(input)));
if (this.records) { this.records = this.records.map(x => new Record().deserialize(x)); }
return this;
}
override serialize() {
this.number = 0; // will be overrided in backend
return Object.assign({}, this);
}
static getForm(fb: UntypedFormBuilder) {
return fb.group({
id: [null],
name: ['', Validators.required],
source: ['', Validators.required],
sheetId: '1G_Hu8DTP-PSPNXTaVYhc_ppnTQi6HWoA4oXSSdUmM9E',
createdAt: '',
modifiedAt: '',
createdBy: '',
modifiedBy: '',
modified: '',
created: ''
});
}
fillForm(form: UntypedFormGroup) {
form.patchValue(this);
form.patchValue({
createdBy: this.createdBy?.userName,
modifiedBy: this.modifiedBy?.userName
});
}
loadForm(form: UntypedFormGroup) {
for (const field of Object.keys(form.controls)) {
this[field as keyof Layer] = form.controls[field].value;
}
this.createdBy = undefined;
this.modifiedBy = undefined;
}
//API Actions
static add(input: Layer, _http: HttpClient): Promise<string> {
return new Promise((resolve, reject) => {
_http.post<string>(`${environment.api.url}/layers`, { ...input.serialize(), }).subscribe({
next: (data) => resolve(data),
error: (e) => reject(e)
}
);
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static getList(_http: HttpClient): any {
return new Promise((resolve, reject) => {
_http.get<Layer[]>(`${environment.api.url}/layers`)
.pipe(map(data => data.map(x => new Layer().deserialize(x))))
.subscribe({
next: (data) => resolve(data),
error: (e) => reject(e)
})
});
}
static getById(id: string, _http: HttpClient): Promise<Layer> {
return new Promise((resolve, reject) => {
_http.get<Layer>(`${environment.api.url}/layers/${id}`).pipe(map(x => new Layer().deserialize(x))).subscribe({
next: (data) => resolve(data),
error: (e) => reject(e)
})
});
}
static parseFile(file: File, _http: HttpClient): Promise<Layer[]> {
const formData = new FormData();
formData.append(file.name, file);
return new Promise((resolve, reject) => {
_http.post<Layer[]>(`${environment.api.url}/layers/parseFile`, formData,
).pipe(map(data => data.map(x => new Layer().deserialize(x))))
.subscribe({
next: (data) => {
resolve(data);
},
error: (e) => reject(e)
})
})
}
static parseGoogleSheet(sheetId: string, _http: HttpClient): Promise<Layer> {
return new Promise((resolve, reject) => {
_http.get<Layer>(`${environment.api.url}/layers/parseGoogleSheet/${sheetId}`,
).pipe(map(data => new Layer().deserialize(data)))
.subscribe({
next: (data) => {
resolve(data);
},
error: (e) => reject(e)
})
})
}
static exportToGoogleSheet(id: string, _http: HttpClient): Promise<boolean> {
return new Promise((resolve, reject) => {
_http.get<boolean>(`${environment.api.url}/layers/exportToGoogleSheet/${id}`,
).subscribe({
next: (data) => {
resolve(data);
},
error: (e) => reject(e)
})
})
}
import { Base } from './base.model';
import { UntypedFormBuilder, Validators, UntypedFormGroup } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { map } from 'rxjs';
import { Record } from 'src/app/models/record.model';
export class Layer extends Base {
// eslint-disable-next-line @typescript-eslint/ban-types
number?: Number;
source?: string;
name?: string;
records: Record[] = [];
created?: string;
constructor(data: Partial<Layer> = {}) {
super();
Object.assign(this, data);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override deserialize(input: any): this {
Object.assign(this, Object.assign(this, super.deserialize(input)));
if (this.records) { this.records = this.records.map(x => new Record().deserialize(x)); }
return this;
}
override serialize() {
this.number = 0; // will be overrided in backend
return Object.assign({}, this);
}
static getForm(fb: UntypedFormBuilder) {
return fb.group({
id: [null],
name: ['', Validators.required],
source: ['', Validators.required],
sheetId: '1G_Hu8DTP-PSPNXTaVYhc_ppnTQi6HWoA4oXSSdUmM9E',
createdAt: '',
modifiedAt: '',
createdBy: '',
modifiedBy: '',
modified: '',
created: ''
});
}
fillForm(form: UntypedFormGroup) {
form.patchValue(this);
form.patchValue({
createdBy: this.createdBy?.userName,
modifiedBy: this.modifiedBy?.userName
});
}
loadForm(form: UntypedFormGroup) {
for (const field of Object.keys(form.controls)) {
this[field as keyof Layer] = form.controls[field].value;
}
this.createdBy = undefined;
this.modifiedBy = undefined;
}
//API Actions
static add(input: Layer, _http: HttpClient): Promise<string> {
return new Promise((resolve, reject) => {
_http.post<string>(`${environment.api.url}/layers`, { ...input.serialize(), }).subscribe({
next: (data) => resolve(data),
error: (e) => reject(e)
}
);
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static getList(_http: HttpClient): any {
return new Promise((resolve, reject) => {
_http.get<Layer[]>(`${environment.api.url}/layers`)
.pipe(map(data => data.map(x => new Layer().deserialize(x))))
.subscribe({
next: (data) => resolve(data),
error: (e) => reject(e)
})
});
}
static getById(id: string, _http: HttpClient): Promise<Layer> {
return new Promise((resolve, reject) => {
_http.get<Layer>(`${environment.api.url}/layers/${id}`).pipe(map(x => new Layer().deserialize(x))).subscribe({
next: (data) => resolve(data),
error: (e) => reject(e)
})
});
}
static parseFile(file: File, _http: HttpClient): Promise<Layer[]> {
const formData = new FormData();
formData.append(file.name, file);
return new Promise((resolve, reject) => {
_http.post<Layer[]>(`${environment.api.url}/layers/parseFile`, formData,
).pipe(map(data => data.map(x => new Layer().deserialize(x))))
.subscribe({
next: (data) => {
resolve(data);
},
error: (e) => reject(e)
})
})
}
static parseGoogleSheet(sheetId: string, _http: HttpClient): Promise<Layer> {
return new Promise((resolve, reject) => {
_http.get<Layer>(`${environment.api.url}/layers/parseGoogleSheet/${sheetId}`,
).pipe(map(data => new Layer().deserialize(data)))
.subscribe({
next: (data) => {
resolve(data);
},
error: (e) => reject(e)
})
})
}
static exportToGoogleSheet(id: string, _http: HttpClient): Promise<boolean> {
return new Promise((resolve, reject) => {
_http.get<boolean>(`${environment.api.url}/layers/exportToGoogleSheet/${id}`,
).subscribe({
next: (data) => {
resolve(data);
},
error: (e) => reject(e)
})
})
}
}

View File

@@ -1,22 +1,22 @@
import { Base } from './base.model';
export class Record extends Base {
code?: string;
value?: number;
desc1?: string;
desc2?: string;
desc3?: string;
desc4?: string;
desc5?: string;
constructor(data: Partial<Record> = {}) {
super();
Object.assign(this, data);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override deserialize(input: any): this {
Object.assign(this, Object.assign(this, super.deserialize(input)));
return this;
}
import { Base } from './base.model';
export class Record extends Base {
code?: string;
value?: number;
desc1?: string;
desc2?: string;
desc3?: string;
desc4?: string;
desc5?: string;
constructor(data: Partial<Record> = {}) {
super();
Object.assign(this, data);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override deserialize(input: any): this {
Object.assign(this, Object.assign(this, super.deserialize(input)));
return this;
}
}

View File

@@ -1,4 +1,4 @@
export interface Serializable {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
serialize(input: this): any;
export interface Serializable {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
serialize(input: this): any;
}

View File

@@ -1,11 +1,11 @@
export class User {
id!: string;
email!: string;
userName!: string;
googleCredentials!: string;
avatar?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(input: any) {
Object.assign(this, input)
}
export class User {
id!: string;
email!: string;
userName!: string;
googleCredentials!: string;
avatar?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(input: any) {
Object.assign(this, input)
}
}

View File

@@ -1,14 +1,14 @@
import { Component } from '@angular/core';
import { DeviceService } from 'src/app/services/device.service';
@Component({
selector: 'app-board',
templateUrl: './board.component.html',
styleUrls: ['./board.component.scss']
})
export class BoardComponent {
constructor(
public _device: DeviceService
) { }
}
import { Component } from '@angular/core';
import { DeviceService } from 'src/app/services/device.service';
@Component({
selector: 'app-board',
templateUrl: './board.component.html',
styleUrls: ['./board.component.scss']
})
export class BoardComponent {
constructor(
public _device: DeviceService
) { }
}

View File

@@ -1,16 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BoardComponent } from './board/board.component';
const routes: Routes = [
{
path: '',
component: BoardComponent
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DashboardRoutingModule { }
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BoardComponent } from './board/board.component';
const routes: Routes = [
{
path: '',
component: BoardComponent
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DashboardRoutingModule { }

View File

@@ -1,17 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DashboardRoutingModule } from './dashboard-routing.module';
import { BoardComponent } from './board/board.component';
@NgModule({
declarations: [
BoardComponent
],
imports: [
CommonModule,
DashboardRoutingModule
]
})
export class DashboardModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DashboardRoutingModule } from './dashboard-routing.module';
import { BoardComponent } from './board/board.component';
@NgModule({
declarations: [
BoardComponent
],
imports: [
CommonModule,
DashboardRoutingModule
]
})
export class DashboardModule { }

View File

@@ -1,5 +1,5 @@
@import "../../../main-view/main-view.component.scss";
.file-input {
display: none;
@import "../../../main-view/main-view.component.scss";
.file-input {
display: none;
}

View File

@@ -1,17 +1,17 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LayerDetailComponent } from './layer-detail/layer-detail.component';
import { LayerEditComponent } from './layer-edit/layer-edit.component';
import { LayersListComponent } from './layers-list/layers-list.component';
const routes: Routes = [
{ path: '', component: LayersListComponent },
{ path: 'Edit/:id', component: LayerEditComponent },
{ path: 'Detail/:id', component: LayerDetailComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LayersRoutingModule { }
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LayerDetailComponent } from './layer-detail/layer-detail.component';
import { LayerEditComponent } from './layer-edit/layer-edit.component';
import { LayersListComponent } from './layers-list/layers-list.component';
const routes: Routes = [
{ path: '', component: LayersListComponent },
{ path: 'Edit/:id', component: LayerEditComponent },
{ path: 'Detail/:id', component: LayerDetailComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LayersRoutingModule { }