??
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
]
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -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 { }
|
||||
|
||||
@@ -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 { }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 { }
|
||||
|
||||
Reference in New Issue
Block a user