Added angular frontend #70

This commit is contained in:
2022-10-16 00:13:03 +02:00
parent ee49bde961
commit 8c3cd1fae7
151 changed files with 29104 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
<div class="content-row">
<div class="content-column">
</div>
</div>
<div class="content-wrapper">
<div class="content-header">
<h2>
{{'common.error' | translate}}
</h2>
</div>
<div class="content">
{{'common.404' | translate}}
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotFoundComponent } from './not-found.component';
describe('NotFoundComponent', () => {
let component: NotFoundComponent;
let fixture: ComponentFixture<NotFoundComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NotFoundComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NotFoundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,18 @@
import { Location } from '@angular/common';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss']
})
export class NotFoundComponent implements OnInit {
constructor(
public location: Location
) { }
ngOnInit(): void {
}
}

View File

@@ -0,0 +1,26 @@
<footer>
<div class="left">
<div class="frontend-version">
<span>
{{'footer.frontend' | translate}}:
</span>
<span>
{{frontendVersion.getVersionString()}}
</span>
</div>
<span class="version-divider">
|
</span>
<div class="backend-version">
<span>
{{'footer.backend' | translate}}:
</span>
<span>
{{backendVersion.getVersionString()}}
</span>
</div>
</div>
<div class="right">
<a href="https://www.sh-edraft.de/Impressum" target="_blank">{{'footer.imprint' | translate}}</a>
</div>
</footer>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FooterComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,46 @@
import { Component, OnInit } from '@angular/core';
import { type } from 'os';
import { catchError } from 'rxjs/operators';
import { SoftwareVersion } from 'src/app/models/config/software-version';
import { GuiService } from 'src/app/services/gui/gui.service';
import { SettingsService } from 'src/app/services/settings/settings.service';
import { SpinnerService } from 'src/app/services/spinner/spinner.service';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss']
})
export class FooterComponent implements OnInit {
frontendVersion!: SoftwareVersion;
backendVersion!: SoftwareVersion;
constructor(
private settings: SettingsService,
private guiService: GuiService,
private spinnerService: SpinnerService
) { }
ngOnInit(): void {
this.frontendVersion = this.settings.getWebVersion() ?? new SoftwareVersion('0', '0', '0');
this.spinnerService.showSpinner();
this.guiService.getApiVersion()
.pipe(catchError(err => {
this.spinnerService.hideSpinner();
throw err;
}))
.subscribe(version => {
this.spinnerService.hideSpinner();
const webVersion = new SoftwareVersion(
version.major,
version.minor,
version.micro
);
this.backendVersion = webVersion;
});
}
}

View File

@@ -0,0 +1,25 @@
<header>
<div class="logo-button-wrapper">
<button pButton type="button" icon="pi pi-bars" class="btn p-button-text" (click)="toggleMenu()"></button>
</div>
<div class="logo">
<h1 class="app-name">{{'header.header' | translate}}</h1>
</div>
<div class="header-menu logo-button-wrapper">
<div class="logo-button-wrapper">
<button type="button" pButton icon="pi pi-globe" class="btn icon-btn p-button-text"
(click)="langMenu.toggle($event)"></button>
<p-menu #langMenu [popup]="true" [model]="langList" class="lang-menu"></p-menu>
</div>
<div class="logo-button-wrapper">
<button type="button" pButton icon="pi pi-palette" class="btn icon-btn p-button-text"
(click)="themeMenu.toggle($event)"></button>
<p-menu #themeMenu [popup]="true" [model]="themeList" class="theme-menu"></p-menu>
</div>
<div class="logo-button-wrapper">
<button type="button" pButton icon="pi pi-user" class="btn icon-btn p-button-text"
(click)="userMenu.toggle($event)"></button>
<p-menu #userMenu [popup]="true" [model]="userMenuList" class="user-menu"></p-menu>
</div>
</div>
</header>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,168 @@
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Router } from '@angular/router';
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
import { MenuItem, PrimeNGConfig } from 'primeng/api';
import { catchError } from 'rxjs/operators';
import { AuthService } from 'src/app/services/auth/auth.service';
import { SettingsService } from 'src/app/services/settings/settings.service';
import { SpinnerService } from 'src/app/services/spinner/spinner.service';
import { ThemeService } from 'src/app/services/theme/theme.service';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
@Output() isSidebarFullWidth: EventEmitter<boolean> = new EventEmitter<boolean>(this.themeService.isSidebarOpen);
langList: MenuItem[] = [];
themeList: MenuItem[] = [];
userMenuList!: MenuItem[];
constructor(
private authService: AuthService,
private router: Router,
private themeService: ThemeService,
private spinnerService: SpinnerService,
private settings: SettingsService,
private translateService: TranslateService,
private config: PrimeNGConfig
) { }
ngOnInit(): void {
this.translateService.setDefaultLang('en');
this.initMenuLists();
this.loadLang();
this.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.initUserMenuList();
});
}
initUserMenuList(): void {
this.spinnerService.showSpinner();
const mail = this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken());
this.authService.getUserByEMail(mail ?? '')
.pipe(catchError(err => {
this.spinnerService.hideSpinner();
this.authService.logout();
throw err;
}))
.subscribe(user => {
this.spinnerService.hideSpinner();
this.userMenuList = [
{
label: `${user.firstName} ${user.lastName}`,
disabled: true
},
{
separator: true
},
{
label: this.translateService.instant('header.change_password'), command: () => {
this.changePassword();
},
icon: 'pi pi-key'
},
{
label: this.translateService.instant('header.settings'), command: () => {
this.userSettings();
},
icon: 'pi pi-cog'
},
{
label: this.translateService.instant('header.logout'), command: () => {
this.logout();
},
icon: 'pi pi-sign-out'
}
];
});
}
initMenuLists(): void {
this.langList = [
{
label: 'English', command: () => {
this.translate('en');
this.setLang('en');
},
},
{
label: 'Deutsch', command: () => {
this.translate('de');
this.setLang('de');
},
},
];
this.initUserMenuList();
this.settings.getThemes()?.forEach(theme => {
this.themeList.push({
label: theme.Label,
command: () => {
this.changeTheme(theme.Name);
}
});
});
}
toggleMenu(): void {
this.themeService.setIsMenuOpen(!this.themeService.isSidebarOpen);
this.isSidebarFullWidth.emit(this.themeService.isSidebarOpen);
}
changeTheme(name: string): void {
this.themeService.setTheme(name);
}
changePassword(): void {
this.router.navigate(['/change-password']);
}
userSettings(): void {
this.router.navigate(['/user-settings']);
}
logout(): void {
this.authService.logout();
}
translate(lang: string) {
this.translateService.use(lang);
this.translateService.get('primeng').subscribe(res => this.config.setTranslation(res));
}
loadLang(): void {
const token = this.authService.getDecodedToken();
const mail = this.authService.getEMailFromDecodedToken(token);
if (!mail) {
this.translate('en');
return;
}
let lang = localStorage.getItem(`${mail}_lang`);
if (!lang) {
lang = 'en';
this.setLang(lang);
}
this.translate(lang);
}
setLang(lang: string): void {
this.authService.isUserLoggedInAsync().then(result => {
if (!result) {
return;
}
const token = this.authService.getDecodedToken();
const mail = this.authService.getEMailFromDecodedToken(token);
localStorage.setItem(`${mail}_lang`, lang);
});
}
}

View File

@@ -0,0 +1,3 @@
<div class="menu">
<p-menu [model]="menuItems"></p-menu>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SidebarComponent } from './sidebar.component';
describe('SidebarComponent', () => {
let component: SidebarComponent;
let fixture: ComponentFixture<SidebarComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SidebarComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SidebarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,52 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
import { MenuItem } from 'primeng/api';
import { AuthRoles } from 'src/app/models/auth/auth-roles.enum';
import { AuthService } from 'src/app/services/auth/auth.service';
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss']
})
export class SidebarComponent implements OnInit, OnChanges {
@Input() isSidebarOpen!: boolean;
menuItems!: MenuItem[];
constructor(
private authService: AuthService,
private translateService: TranslateService
) { }
ngOnInit(): void {
this.translateService.onLangChange.subscribe(async (event: LangChangeEvent) => {
await this.setMenu(this.isSidebarOpen);
});
}
async setMenu(isSidebarOpen: boolean) {
this.menuItems = [];
this.menuItems = [
{ label: isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' },
];
if (await this.authService.hasUserPermission(AuthRoles.Admin)) {
this.menuItems.push(
{ separator: true },
{ label: isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' },
{ label: isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' },
);
this.menuItems = this.menuItems.slice();
}
}
async ngOnChanges(changes: SimpleChanges): Promise<void> {
if (!changes)
return;
await this.setMenu(changes['isSidebarOpen'].currentValue);
}
}

View File

@@ -0,0 +1,7 @@
<ng-container *ngIf="spinnerService.showSpinnerState">
<div class="spinner-component-wrapper">
<div class="spinner-wrapper">
<p-progressSpinner styleClass="custom-spinner" animationDuration=".8s"></p-progressSpinner>
</div>
</div>
</ng-container>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SpinnerComponent } from './spinner.component';
describe('SpinnerComponent', () => {
let component: SpinnerComponent;
let fixture: ComponentFixture<SpinnerComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SpinnerComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SpinnerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { SpinnerService } from 'src/app/services/spinner/spinner.service';
@Component({
selector: 'app-spinner',
templateUrl: './spinner.component.html',
styleUrls: ['./spinner.component.scss']
})
export class SpinnerComponent implements OnInit {
constructor(
public spinnerService: SpinnerService
) { }
ngOnInit(): void {
}
}