Moved folders #405

This commit is contained in:
2023-10-13 17:10:00 +02:00
parent a87380f6f8
commit 3810dec927
807 changed files with 3801 additions and 1297 deletions

View File

@@ -0,0 +1,23 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { NotFoundComponent } from './components/error/not-found/not-found.component';
import { AuthRoles } from './models/auth/auth-roles.enum';
import { AuthGuard } from './modules/shared/guards/auth/auth.guard';
const routes: Routes = [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{ path: 'dashboard', loadChildren: () => import('./modules/view/dashboard/dashboard.module').then(m => m.DashboardModule), canActivate: [AuthGuard] },
{ path: 'server/:serverId', loadChildren: () => import('./modules/view/server/server.module').then(m => m.ServerModule), canActivate: [AuthGuard] },
{ path: 'change-password', loadChildren: () => import('./modules/view/change-password/change-password.module').then(m => m.ChangePasswordModule), canActivate: [AuthGuard] },
{ path: 'user-settings', loadChildren: () => import('./modules/view/user-settings/user-settings.module').then(m => m.UserSettingsModule), canActivate: [AuthGuard] },
{ path: 'auth', loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule) },
{ path: 'admin/settings', loadChildren: () => import('./modules/admin/settings/settings.module').then(m => m.SettingsModule), canActivate: [AuthGuard], data: { role: AuthRoles.Admin } },
{ path: 'admin/users', loadChildren: () => import('./modules/admin/auth-users/auth-user.module').then(m => m.AuthUserModule), canActivate: [AuthGuard], data: { role: AuthRoles.Admin } },
{ path: '404', component: NotFoundComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@@ -0,0 +1,37 @@
<main [class]="themeName">
<ng-container *ngIf="isLoggedIn; else login">
<app-header></app-header>
<section class="app">
<div>
<section [ngClass]="{'sidebar-open': isSidebarOpen, 'sidebar-closed': !isSidebarOpen}">
<app-sidebar></app-sidebar>
</section>
</div>
<div class="component-wrapper">
<section class="component">
<router-outlet></router-outlet>
</section>
</div>
</section>
<app-footer></app-footer>
</ng-container>
<ng-template #login>
<router-outlet></router-outlet>
</ng-template>
<app-spinner></app-spinner>
<p-confirmDialog #cd key="confirmConfirmationDialog" [baseZIndex]="10000">
<ng-template pTemplate="footer">
<div class="wrapper-right btn-wrapper">
<button pButton label="{{'dialog.abort' | translate}}" class="btn icon-btn danger-icon-btn" icon="pi pi-times-circle" (click)="cd.reject()"></button>
<button pButton label="{{'dialog.confirm' | translate}}" class="btn" icon="pi pi-check-circle" (click)="cd.accept()"></button>
</div>
</ng-template>
</p-confirmDialog>
<p-toast></p-toast>
</main>

View File

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
});

View File

@@ -0,0 +1,83 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { PrimeNGConfig } from "primeng/api";
import { AuthService } from "./services/auth/auth.service";
import { SocketService } from "./services/socket/socket.service";
import { ThemeService } from "./services/theme/theme.service";
import { Subject } from "rxjs";
import { Themes } from "./models/view/themes.enum";
import { takeUntil } from "rxjs/operators";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit, OnDestroy {
themeName: string = Themes.Default;
isSidebarOpen: boolean = true;
isLoggedIn: boolean = false;
private unsubscriber = new Subject<void>();
constructor(
private authService: AuthService,
private themeService: ThemeService,
private socket: SocketService,
private translateService: TranslateService,
private config: PrimeNGConfig,
) {
this.themeService.isSidebarOpen$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.isSidebarOpen = value;
});
this.themeService.themeName$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.themeName = value;
});
this.authService.isLoggedIn$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.isLoggedIn = value;
});
}
ngOnInit(): void {
this.translateService.setDefaultLang("en");
this.themeService.loadTheme();
this.socket.startSocket();
}
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
loadLang(): void {
let lang = localStorage.getItem(`default_lang`);
if (!lang) {
lang = "en";
this.setLang(lang);
}
this.translate(lang);
}
setLang(lang: string): void {
localStorage.setItem(`default_lang`, lang);
}
translate(lang: string) {
this.translateService.use(lang);
this.translateService.get("primeng").subscribe(res => this.config.setTranslation(res));
}
setSideWidth($event: any): void {
this.themeService.setSideWidth($event);
}
}

83
web/src/app/app.module.ts Normal file
View File

@@ -0,0 +1,83 @@
import { HttpClient, HttpClientModule } from "@angular/common/http";
import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { JwtModule } from "@auth0/angular-jwt";
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { ConfirmationService, MessageService } from "primeng/api";
import { DialogService } from "primeng/dynamicdialog";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { NotFoundComponent } from "./components/error/not-found/not-found.component";
import { FooterComponent } from "./components/footer/footer.component";
import { HeaderComponent } from "./components/header/header.component";
import { SidebarComponent } from "./components/sidebar/sidebar.component";
import { SpinnerComponent } from "./components/spinner/spinner.component";
import { SharedModule } from "./modules/shared/shared.module";
import { ErrorHandlerService } from "./services/error-handler/error-handler.service";
import { SettingsService } from "./services/settings/settings.service";
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
SidebarComponent,
FooterComponent,
SpinnerComponent,
NotFoundComponent,
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
SharedModule,
JwtModule.forRoot({
config: {
tokenGetter,
allowedDomains: ['localhost:8044', 'localhost:8041', 'localhost:5000'],
disallowedRoutes: []
}
}),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
HttpClientModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: configurationFactory,
deps: [SettingsService],
multi: true
},
{
provide: ErrorHandler,
useClass: ErrorHandlerService
},
MessageService,
ConfirmationService,
DialogService,
],
bootstrap: [AppComponent]
})
export class AppModule { }
export function configurationFactory(settingsService: SettingsService): () => Promise<unknown> {
return (): Promise<unknown> => {
return settingsService.loadSettings();
};
}
export function tokenGetter(): string {
return localStorage.getItem('jwt') ?? '';
}
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http);
}

View File

@@ -0,0 +1,7 @@
import { ComponentWithTable } from './component-with-table';
describe('ComponentWithTable', () => {
it('should create an instance', () => {
expect(new ComponentWithTable()).toBeTruthy();
});
});

View File

@@ -0,0 +1,44 @@
export interface Column {
key: string;
name: string;
}
export class ComponentWithTable {
private _hiddenColumns: Column[] = [];
set hiddenColumns(value: Column[]) {
this._hiddenColumns = value;
localStorage.setItem("hiddenColumns", JSON.stringify(value));
}
get hiddenColumns(): Column[] {
return this._hiddenColumns;
}
public name: string = "";
public columns: Column[] = [];
constructor(
name: string,
columns: string[]
) {
this.name = name;
this.columns = columns.map(column => {
return { key: this.getKey(column), name: column };
});
let hiddenColumns = localStorage.getItem("hiddenColumns");
if (!hiddenColumns) {
localStorage.setItem("hiddenColumns", JSON.stringify([{}]));
hiddenColumns = localStorage.getItem("hiddenColumns") ?? JSON.stringify([{}]);
}
this._hiddenColumns = JSON.parse(hiddenColumns);
}
private getKey(column: string): string {
return `${this.name}_${column}`;
}
public isColumnVisible(column: string): boolean {
return !this._hiddenColumns.map(column => column.key).includes(this.getKey(column));
}
}

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,28 @@
<footer>
<div class="left">
<div class="frontend-version">
<span>
{{'footer.frontend' | translate}}:
</span>
<span>
{{frontendVersion.getVersionString()}}
</span>
</div>
<span class="divider">
|
</span>
<div class="backend-version">
<span>
{{'footer.backend' | translate}}:
</span>
<span>
{{backendVersion.getVersionString()}}
</span>
</div>
</div>
<div class="right">
<button pButton label="{{'footer.privacy' | translate}}" class="btn text-btn" (click)="navigateToPrivacy()"></button>
<span class="divider"> | </span>
<button pButton label="{{'footer.imprint' | translate}}" class="btn text-btn" (click)="navigateToImprint()"></button>
</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,56 @@
import { Component, OnInit } from "@angular/core";
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";
import { throwError } from "rxjs";
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss']
})
export class FooterComponent implements OnInit {
public frontendVersion: SoftwareVersion = new SoftwareVersion("0", "0", "0");
public backendVersion: SoftwareVersion = new SoftwareVersion("0", "0", "0");
public privacy: string = "";
public imprint: string = "";
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(error => {
this.spinnerService.hideSpinner();
return throwError(() => error);
}))
.subscribe(version => {
this.spinnerService.hideSpinner();
this.backendVersion = new SoftwareVersion(
version.major,
version.minor,
version.micro
);
});
}
public navigateToPrivacy(): void {
window.open(this.settings.getPrivacyURL(), "_blank");
}
public navigateToImprint(): void {
window.open(this.settings.getImprintURL(), "_blank");
}
}

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,169 @@
import { Component, OnInit } 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";
import { throwError } from "rxjs";
import { SidebarService } from "../../services/sidebar/sidebar.service";
@Component({
selector: "app-header",
templateUrl: "./header.component.html",
styleUrls: ["./header.component.scss"]
})
export class HeaderComponent implements OnInit {
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,
private sidebarService: SidebarService,
) {
}
ngOnInit(): void {
this.initMenuLists();
this.loadLang();
this.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.initUserMenuList();
});
}
initUserMenuList(): void {
if (!this.authService.isLoggedIn$.value) {
return;
}
this.spinnerService.showSpinner();
const mail = this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken());
this.authService.getUserByEMail(mail ?? "")
.pipe(catchError(error => {
this.spinnerService.hideSpinner();
this.authService.logout();
return throwError(() => error);
}))
.subscribe(user => {
this.spinnerService.hideSpinner();
this.sidebarService.setMenu(true);
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);
}
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 {
if (!this.authService.isLoggedIn$.value) {
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-panelMenu [model]="menuItems"></p-panelMenu>
</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,42 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { MenuItem } from "primeng/api";
import { AuthService } from "src/app/services/auth/auth.service";
import { ThemeService } from "src/app/services/theme/theme.service";
import { SidebarService } from "../../services/sidebar/sidebar.service";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
@Component({
selector: "app-sidebar",
templateUrl: "./sidebar.component.html",
styleUrls: ["./sidebar.component.scss"]
})
export class SidebarComponent implements OnInit, OnDestroy {
menuItems: MenuItem[]= [];
private unsubscriber = new Subject<void>();
constructor(
private authService: AuthService,
private translateService: TranslateService,
private themeService: ThemeService,
private sidebar: SidebarService
) {
this.sidebar.menuItems$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.menuItems = value;
});
}
ngOnInit(): void {
this.themeService.loadMenu();
}
ngOnDestroy() {
this.unsubscriber.next();
this.unsubscriber.complete();
}
}

View File

@@ -0,0 +1,7 @@
<ng-container *ngIf="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,24 @@
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 {
showSpinnerState: boolean = false;
constructor(
public spinnerService: SpinnerService
) {
this.spinnerService.showSpinnerState$.subscribe(value => {
this.showSpinnerState = value;
});
}
ngOnInit(): void {
}
}

View File

@@ -0,0 +1,7 @@
import { AuthUserDTO } from "./auth-user.dto";
export interface AdminUpdateUserDTO {
authUserDTO: AuthUserDTO;
newAuthUserDTO: AuthUserDTO;
changePassword: boolean;
}

View File

@@ -0,0 +1,7 @@
export enum AuthErrorMessages {
UserIsEmpty = "User is empty",
UserNotFound = "User not found",
WrongPassword = "Wrong password",
UserAlreadyExists = "User already exists",
EMailNotConfirmed = "E-Mail not confirmed"
}

View File

@@ -0,0 +1,4 @@
export enum AuthRoles {
Normal = 0,
Admin = 1
}

View File

@@ -0,0 +1,12 @@
export class AuthUserAtrErrors {
firstName: AuthUserAtrErrorType = new AuthUserAtrErrorType();
lastName: AuthUserAtrErrorType = new AuthUserAtrErrorType();
email: AuthUserAtrErrorType = new AuthUserAtrErrorType();
password: AuthUserAtrErrorType = new AuthUserAtrErrorType();
}
export class AuthUserAtrErrorType {
wrongData: boolean = false;
required: boolean = false;
notConfirmed: boolean = false;
}

View File

@@ -0,0 +1,33 @@
import {AuthRoles} from "./auth-roles.enum";
export interface AuthUserDTO {
id?: number;
firstName: string | null;
lastName: string | null;
email: string | null;
password: string | null;
isConfirmed?: boolean;
authRole?: AuthRoles;
users?: UserDTO[];
createdAt?: string;
modifiedAt?: string;
}
export interface UserDTO {
id: number;
discordId: number;
xp: number;
server: number;
createdAt: string;
modifiedAt: string;
isTechnician: boolean;
isAdmin: boolean;
isModerator: boolean;
}
export enum MemberRoles {
Moderator = 0,
Admin = 1,
Technician = 2,
}

View File

@@ -0,0 +1,3 @@
export interface DiscordAuthURL {
loginUrl: string;
}

View File

@@ -0,0 +1,3 @@
export interface EMailStringDTO {
email: string;
}

View File

@@ -0,0 +1,6 @@
import { AuthUserDTO } from "./auth-user.dto";
export interface OAuthDTO {
user: AuthUserDTO;
oAuthId: string;
}

View File

@@ -0,0 +1,4 @@
export enum RegisterErrorMessages {
InvalidEMail = "Invalid E-Mail",
UserAlreadyExists = "User already exists",
}

View File

@@ -0,0 +1,6 @@
import { AuthUserDTO } from "./auth-user.dto";
export interface ResetPasswordDTO {
id: string;
password: string;
}

View File

@@ -0,0 +1,5 @@
export interface TokenDTO {
token: string;
refreshToken: string;
firstLogin?: boolean;
}

View File

@@ -0,0 +1,6 @@
import { AuthUserDTO } from "./auth-user.dto";
export interface UpdateUserDTO {
authUserDTO: AuthUserDTO;
newAuthUserDTO: AuthUserDTO;
}

View File

@@ -0,0 +1,5 @@
export interface ApiVersion {
Major: string;
Minor: string;
Micro: string;
}

View File

@@ -0,0 +1,21 @@
import { SoftwareVersion } from "./software-version";
import { Theme } from '../view/theme';
export interface AppAndVersionSettings {
ApiURL: string;
PrivacyURL: string;
ImprintURL: string;
Themes: Theme[];
WebVersion: SoftwareVersion;
}
export interface AppSettings {
ApiURL: string;
PrivacyURL: string;
ImprintURL: string;
Themes: Theme[];
}
export interface VersionSettings {
WebVersion: SoftwareVersion;
}

View File

@@ -0,0 +1,4 @@
export interface FeatureFlag {
key: string;
value: boolean;
}

View File

@@ -0,0 +1,27 @@
import { DataWithHistory } from "../data/data.model";
import { FeatureFlag } from "./feature-flags.model";
export interface ServerConfig extends DataWithHistory {
id?: number;
messageDeleteTimer?: number;
notificationChatId?: string;
maxVoiceStateHours?: number;
xpPerMessage?: number;
xpPerReaction?: number;
maxMessageXpPerHour?: number;
xpPerOntimeHour?: number;
xpPerEventParticipation?: number;
xpPerAchievement?: number;
xpForBirthday?: number;
afkCommandChannelId?: string;
helpVoiceChannelId?: string;
teamChannelId?: string;
loginMessageChannelId?: string;
defaultRoleId?: string;
shortRoleNameOnlySetHighestRole?: boolean;
gameOfferNotificationChatId?: string;
featureFlags: FeatureFlag[];
afkChannelIds: string[];
moderatorRoleIds: string[];
adminRoleIds: string[];
}

View File

@@ -0,0 +1,16 @@
export interface SettingsDTO {
webVersion: string;
apiVersion: string;
configPath: string;
webBaseURL: string;
apiBaseURL: string;
tokenExpireTime: number;
refreshTokenExpireTime: number;
mailUser: string;
mailPort: number;
mailHost: string;
mailTransceiver: string;
mailTransceiverAddress: string;
}

View File

@@ -0,0 +1,5 @@
export interface SoftwareVersionDTO {
major: string;
minor: string;
micro: string
}

View File

@@ -0,0 +1,19 @@
export class SoftwareVersion {
Major: string;
Minor: string;
Micro: string;
constructor(
major: string,
minor: string,
micro: string
) {
this.Major = major;
this.Minor = minor;
this.Micro = micro;
}
getVersionString(): string {
return `${this.Major}.${this.Minor}.${this.Micro}`;
}
}

View File

@@ -0,0 +1,13 @@
import { DataWithHistory } from "../data/data.model";
import { FeatureFlag } from "./feature-flags.model";
export interface TechnicianConfig extends DataWithHistory {
id?: number;
helpCommandReferenceUrl?: string;
waitForRestart?: number;
waitForShutdown?: number;
cacheMaxMessages?: number;
featureFlags: FeatureFlag[];
pingURLs: string[];
technicianIds: string[];
}

View File

@@ -0,0 +1,29 @@
import { DataWithHistory } from "./data.model";
import { Server, ServerFilter } from "./server.model";
export interface AchievementAttribute {
name?: string;
type?: string;
}
export interface Achievement extends DataWithHistory {
id?: number;
name?: string;
description?: string;
attribute?: string | AchievementAttribute;
operator?: string;
value?: string;
server?: Server;
createdAt?: string;
}
export interface AchievementFilter {
id?: number;
name?: string;
description?: string;
attribute?: string;
operator?: string;
value?: string;
server?: ServerFilter;
}

View File

@@ -0,0 +1,36 @@
import { Data } from "./data.model";
import { Server, ServerFilter } from "./server.model";
export interface AutoRole extends Data {
id?: number;
channelId?: string;
channelName?: string;
messageId?: string;
server?: Server;
autoRoleRuleCount?: number;
autoRoleRules?: AutoRoleRule[];
}
export interface AutoRoleFilter {
id?: number;
channelId?: string;
channelName?: string;
messageId?: string;
server?: ServerFilter;
}
export interface AutoRoleRule extends Data {
id?: number;
emojiName?: string;
roleId?: string;
roleName?: string;
autoRole?: AutoRole;
}
export interface AutoRoleRuleFilter {
id?: number;
emojiName?: string;
roleId?: string;
autoRole?: AutoRoleFilter;
}

View File

@@ -0,0 +1,16 @@
import {Server} from "./server.model";
import {Data} from "./data.model";
export interface Client extends Data {
id?: number;
discordId?: string;
name?: string;
sentMessageCount?: number;
receivedMessageCount?: number;
deletedMessageCount?: number;
receivedCommandCount?: number;
movedUsersCount?: number;
server?: Server;
}

View File

@@ -0,0 +1,17 @@
export interface Data {
createdAt?: string;
modifiedAt?: string;
}
export interface DataWithHistory {
createdAt?: string;
modifiedAt?: string;
history?: History[];
}
export interface History {
deleted?: boolean;
dateFrom?: string;
dateTo?: string;
[x: string | number | symbol]: unknown;
}

View File

@@ -0,0 +1,42 @@
export interface Discord {
guilds?: Guild[];
users?: DiscordUser[];
}
export interface Guild {
id?: string;
name?: string;
channels: Channel[];
roles: Role[];
emojis: Emoji[];
}
export interface Channel {
id?: string;
name?: string;
type?: ChannelType;
}
export enum ChannelType {
category = "CategoryChannel",
text = "TextChannel",
voice = "VoiceChannel"
}
export interface Role {
id?: string;
name?: string;
}
export interface Emoji {
id?: string;
name?: string;
url?: string;
}
export interface DiscordUser {
id?: string;
name?: string;
bot?: boolean;
}

View File

@@ -0,0 +1,17 @@
import {Data} from "./data.model";
import {Server, ServerFilter} from "./server.model";
export interface Level extends Data {
id?: number;
name?: string;
color?: string;
minXp?: number;
permissions?: string;
server?: Server;
}
export interface LevelFilter {
id?: number;
name?: string;
server?: ServerFilter;
}

View File

@@ -0,0 +1,36 @@
import {Data} from "./data.model";
import {User} from "./user.model";
import {Level} from "./level.model";
import {Client} from "./client.model";
import { AutoRole } from "./auto_role.model";
import { ServerConfig } from "../config/server-config.model";
import { FeatureFlag } from "../config/feature-flags.model";
export interface GameServer {
id?: number;
name?: string;
}
export interface Server extends Data {
id?: number;
discordId?: String;
name?: string;
iconURL?: string;
autoRoleCount?: number;
autoRoles?: AutoRole[];
clientCount?: number;
clients?: Client[];
levelCount?: number;
levels?: Level[];
userCount?: number;
users?: User[];
config?: ServerConfig;
hasFeatureFlag?: FeatureFlag;
activeFeatureFlags?: FeatureFlag[];
}
export interface ServerFilter {
id?: number;
discordId?: String;
name?: String;
}

View File

@@ -0,0 +1,20 @@
import { Data } from "./data.model";
import { Server, ServerFilter } from "./server.model";
export interface ShortRoleName extends Data {
id?: number;
shortName?: string;
roleId?: string;
roleName?: string;
position?: string;
server?: Server;
}
export interface ShortRoleNameFilter {
id?: number;
shortName?: string;
roleId?: string;
roleName?: string;
position?: string;
server?: ServerFilter;
}

View File

@@ -0,0 +1,48 @@
import { DataWithHistory } from "./data.model";
import { Level, LevelFilter } from "./level.model";
import { Server, ServerFilter } from "./server.model";
import { UserJoinedServer } from "./user_joined_server.model";
import { UserJoinedVoiceChannel } from "./user_joined_voice_channel.model";
import { UserJoinedGameServer } from "./user_joined_game_server.model";
import { Achievement } from "./achievement.model";
import { UserWarning } from "./user_warning.model";
export interface User extends DataWithHistory {
id?: number;
discordId?: number;
name?: string;
xp?: number;
messageCount?: number;
reactionCount?: number;
birthday?: string;
ontime?: number;
level?: Level;
server?: Server;
leftServer?: boolean;
joinedServerCount?: number;
joinedServers?: UserJoinedServer[];
joinedVoiceChannelCount?: number;
joinedVoiceChannels?: UserJoinedVoiceChannel[];
userJoinedGameServerCount?: number;
userJoinedGameServers?: UserJoinedGameServer[];
achievementCount?: number;
achievements?: Achievement[];
userWarningCount?: number;
userWarnings?: UserWarning[];
}
export interface UserFilter {
id?: number;
discordId?: number;
name?: string;
xp?: number;
ontime?: number;
level?: LevelFilter;
server?: ServerFilter;
leftServer?: boolean;
}

View File

@@ -0,0 +1,11 @@
import { Data } from "./data.model";
import { User } from "./user.model";
export interface UserJoinedGameServer extends Data {
id: number;
gameServer: string;
user: User;
time: number;
joinedOn: string;
leavedOn: string;
}

View File

@@ -0,0 +1,9 @@
import { Data } from "./data.model";
import { User } from "./user.model";
export interface UserJoinedServer extends Data {
id: number;
user: User;
joinedOn: string;
leavedOn: string;
}

View File

@@ -0,0 +1,12 @@
import { Data } from "./data.model";
import { User } from "./user.model";
export interface UserJoinedVoiceChannel extends Data {
id: number;
channelId: string;
channelName: string;
user: User;
time: number;
joinedOn: string;
leavedOn: string;
}

View File

@@ -0,0 +1,16 @@
import { DataWithHistory } from "./data.model";
import { User, UserFilter } from "./user.model";
export interface UserWarning extends DataWithHistory {
id?: number;
user?: User;
description?: string;
author?: User;
}
export interface UserWarningFilter {
id?: number;
user?: UserFilter;
description?: string;
author?: UserFilter;
}

View File

@@ -0,0 +1,8 @@
import { ServiceErrorCode } from "./service-error-code.enum";
export class ErrorDTO {
errorCode!: ServiceErrorCode;
message!: string;
}

View File

@@ -0,0 +1,17 @@
export enum ServiceErrorCode {
Unknown = 0,
InvalidDependencies = 1,
InvalidData = 2,
NotFound = 3,
DataAlreadyExists = 4,
UnableToAdd = 5,
UnableToDelete = 6,
InvalidUser = 7,
ConnectionFailed = 8,
Timeout = 9,
MailError = 10,
Unauthorized = 11
}

View File

@@ -0,0 +1,4 @@
export interface Page {
pageIndex?: number;
pageSize?: number;
}

View File

@@ -0,0 +1,9 @@
export interface Sort {
sortColumn?: string;
sortDirection?: SortDirection;
}
export enum SortDirection {
ASC = "ASC",
DESC = "DESC",
}

View File

@@ -0,0 +1,376 @@
export class Mutations {
static updateUser = `
mutation updateUser($id: ID, $xp: Int $birthday: String, $levelId: ID, $userWarnings: [UserWarningInput]) {
user {
updateUser(input: { id: $id, xp: $xp, birthday: $birthday, levelId: $levelId, userWarnings: $userWarnings }) {
id
name
xp
messageCount
reactionCount
birthday
level {
id
name
}
userWarnings {
id
description
}
}
}
}
`;
static createAutoRole = `
mutation createAutoRole($serverId: ID, $channelId: String, $messageId: String) {
autoRole {
createAutoRole(input: { serverId: $serverId, channelId: $channelId, messageId: $messageId }) {
id
channelId
channelName
messageId
}
}
}
`;
static deleteAutoRole = `
mutation deleteAutoRole($id: ID) {
autoRole {
deleteAutoRole(id: $id) {
id
channelId
channelName
messageId
}
}
}
`;
static createAutoRoleRule = `
mutation createAutoRoleRule($autoRoleId: ID, $emojiName: String, $roleId: String) {
autoRoleRule {
createAutoRoleRule(input: { autoRoleId: $autoRoleId, emojiName: $emojiName, roleId: $roleId }) {
id
emojiName
roleId
roleName
}
}
}
`;
static updateAutoRoleRule = `
mutation updateAutoRoleRule($id: ID, $emojiName: String, $roleId: String) {
autoRoleRule {
updateAutoRoleRule(input: { id: $id, emojiName: $emojiName, roleId: $roleId }) {
id
emojiName
roleId
roleName
}
}
}
`;
static deleteAutoRoleRule = `
mutation deleteAutoRoleRule($id: ID) {
autoRoleRule {
deleteAutoRoleRule(id: $id) {
id
emojiName
roleId
roleName
}
}
}
`;
static createLevel = `
mutation createLevel($name: String, $color: String, $minXp: Int, $permissions: String, $serverId: ID) {
level {
createLevel(input: { name: $name, color: $color, minXp: $minXp, permissions: $permissions, serverId: $serverId}) {
id
name
color
minXp
permissions
server {
id
}
}
}
}
`;
static updateLevel = `
mutation updateLevel($id: ID, $name: String, $color: String, $minXp: Int, $permissions: String) {
level {
updateLevel(input: { id: $id, name: $name, color: $color, minXp: $minXp, permissions: $permissions }) {
id
name
color
minXp
permissions
}
}
}
`;
static deleteLevel = `
mutation deleteLevel($id: ID) {
level {
deleteLevel(id: $id) {
id
name
}
}
}
`;
static createAchievement = `
mutation createAchievement($name: String, $description: String, $attribute: String, $operator: String, $value: String, $serverId: ID) {
achievement {
createAchievement(input: { name: $name, description: $description, attribute: $attribute, operator: $operator, value: $value, serverId: $serverId}) {
id
name
description
attribute
operator
value
server {
id
}
}
}
}
`;
static updateAchievement = `
mutation updateAchievement($id: ID, $name: String, $description: String, $attribute: String, $operator: String, $value: String) {
achievement {
updateAchievement(input: { id: $id, name: $name, description: $description, attribute: $attribute, operator: $operator, value: $value}) {
id
name
description
attribute
operator
value
}
}
}
`;
static deleteAchievement = `
mutation deleteAchievement($id: ID) {
achievement {
deleteAchievement(id: $id) {
id
name
}
}
}
`;
static createShortRoleName = `
mutation createShortRoleName($shortName: String, $roleId: String, $position: String, $serverId: ID) {
shortRoleName {
createShortRoleName(input: { shortName: $shortName, roleId: $roleId, position: $position, serverId: $serverId}) {
id
shortName
roleId
roleName
position
server {
id
}
}
}
}
`;
static updateShortRoleName = `
mutation updateShortRoleName($id: ID, $shortName: String, $roleId: String, $position: String, $serverId: ID) {
shortRoleName {
updateShortRoleName(input: { id: $id, shortName: $shortName, roleId: $roleId, position: $position, serverId: $serverId }) {
id
shortName
roleId
roleName
position
}
}
}
`;
static deleteShortRoleName = `
mutation deleteShortRoleName($id: ID) {
shortRoleName {
deleteShortRoleName(id: $id) {
id
shortName
}
}
}
`;
static updateTechnicianConfig = `
mutation updateTechnicianConfig($id: ID, $helpCommandReferenceUrl: String, $waitForRestart: Int, $waitForShutdown: Int, $cacheMaxMessages: Int, $featureFlags: [FeatureFlagInput], $pingURLs: [String], $technicianIds: [String]) {
technicianConfig {
updateTechnicianConfig(input: {
id: $id,
helpCommandReferenceUrl: $helpCommandReferenceUrl,
waitForRestart: $waitForRestart,
waitForShutdown: $waitForShutdown,
cacheMaxMessages: $cacheMaxMessages,
featureFlags: $featureFlags,
pingURLs: $pingURLs,
technicianIds: $technicianIds
}) {
id
helpCommandReferenceUrl
waitForRestart
waitForShutdown
cacheMaxMessages
featureFlags {
key
value
}
pingURLs
technicianIds
}
}
}
`;
static updateServerConfig = `
mutation updateServerConfig(
$id: ID,
$messageDeleteTimer: Int,
$notificationChatId: String,
$maxVoiceStateHours: Int,
$xpPerMessage: Int,
$xpPerReaction: Int,
$maxMessageXpPerHour: Int,
$xpPerOntimeHour: Int,
$xpPerEventParticipation: Int,
$xpPerAchievement: Int,
$xpForBirthday: Int,
$afkCommandChannelId: String,
$helpVoiceChannelId: String,
$teamChannelId: String,
$loginMessageChannelId: String,
$defaultRoleId: String,
$shortRoleNameOnlySetHighestRole: Boolean,
$gameOfferNotificationChatId: String,
$featureFlags: [FeatureFlagInput],
$afkChannelIds: [String],
$moderatorRoleIds: [String],
$adminRoleIds: [String]
) {
serverConfig {
updateServerConfig(input: {
id: $id,
messageDeleteTimer: $messageDeleteTimer,
notificationChatId: $notificationChatId,
maxVoiceStateHours: $maxVoiceStateHours,
xpPerMessage: $xpPerMessage,
xpPerReaction: $xpPerReaction,
maxMessageXpPerHour: $maxMessageXpPerHour,
xpPerOntimeHour: $xpPerOntimeHour,
xpPerEventParticipation: $xpPerEventParticipation,
xpPerAchievement: $xpPerAchievement,
xpForBirthday: $xpForBirthday,
afkCommandChannelId: $afkCommandChannelId,
helpVoiceChannelId: $helpVoiceChannelId,
teamChannelId: $teamChannelId,
loginMessageChannelId: $loginMessageChannelId,
defaultRoleId: $defaultRoleId,
shortRoleNameOnlySetHighestRole: $shortRoleNameOnlySetHighestRole,
gameOfferNotificationChatId: $gameOfferNotificationChatId,
featureFlags: $featureFlags,
afkChannelIds: $afkChannelIds,
moderatorRoleIds: $moderatorRoleIds,
adminRoleIds: $adminRoleIds
}) {
id
messageDeleteTimer
notificationChatId
maxVoiceStateHours
xpPerMessage
xpPerReaction
maxMessageXpPerHour
xpPerOntimeHour
xpPerEventParticipation
xpPerAchievement
xpForBirthday
afkCommandChannelId
helpVoiceChannelId
teamChannelId
loginMessageChannelId
defaultRoleId
shortRoleNameOnlySetHighestRole
gameOfferNotificationChatId
featureFlags {
key
value
}
afkChannelIds
moderatorRoleIds
adminRoleIds
server {
id
}
}
}
}
`;
static createUserWarning = `
mutation createUserWarning($name: String, $description: String, $attribute: String, $operator: String, $value: String, $serverId: ID) {
userWarning {
createUserWarning(input: { name: $name, description: $description, attribute: $attribute, operator: $operator, value: $value, serverId: $serverId}) {
id
name
description
attribute
operator
value
server {
id
}
}
}
}
`;
static updateUserWarning = `
mutation updateUserWarning($id: ID, $name: String, $description: String, $attribute: String, $operator: String, $value: String) {
userWarning {
updateUserWarning(input: { id: $id, name: $name, description: $description, attribute: $attribute, operator: $operator, value: $value}) {
id
name
description
attribute
operator
value
}
}
}
`;
static deleteUserWarning = `
mutation deleteUserWarning($id: ID) {
userWarning {
deleteUserWarning(id: $id) {
id
name
}
}
}
`;
}

View File

@@ -0,0 +1,550 @@
export class Queries {
static guildsQuery = `
query GuildsQuery($id: ID, $filter: ChannelFilter) {
discord {
guilds(filter: {id: $id}) {
id
name
channels(filter: $filter) {
id
name
type
}
roles {
id
name
}
emojis {
id
name
url
}
}
}
}
`;
static discordUsersQuery = `
query DiscordUsersQuery {
discord {
users {
id
name
}
}
}
`;
static serverConfigDiscordQuery = `
query ServerConfigDiscordQuery($id: ID) {
discord {
guilds(filter: {id: $id}) {
id
name
roles {
id
name
}
channels {
id
name
type
}
}
}
}
`;
static serversQuery = `
query ServerInfo($filter: ServerFilter, $page: Page, $sort: Sort) {
serverCount
servers(filter: $filter, page: $page, sort: $sort) {
id
discordId
name
iconURL
userCount
clients {
id
discordId
name
sentMessageCount
receivedMessageCount
deletedMessageCount
receivedCommandCount
movedUsersCount
}
}
}
`;
static hasServerFeatureFlag = `
query HasServerFeatureFlag($filter: ServerFilter, $flag: String) {
servers(filter: $filter) {
hasFeatureFlag(flag: $flag) {
key
value
}
}
}
`;
static gameServerQuery = `
query GameServersList($serverId: ID) {
servers(filter: {id: $serverId}) {
gameServers {
name
}
}
}
`;
static levelQuery = `
query LevelsList($serverId: ID, $filter: LevelFilter, $page: Page, $sort: Sort) {
servers(filter: {id: $serverId}) {
levelCount
levels(filter: $filter, page: $page, sort: $sort) {
id
name
color
minXp
permissions
server {
id
name
}
createdAt
modifiedAt
}
}
}
`;
static levelWithHistoryQuery = `
query LevelHistory($serverId: ID, $id: ID) {
servers(filter: {id: $serverId}) {
levelCount
levels(filter: {id: $id}) {
id
history {
id
name
color
minXp
permissions
server
deleted
dateFrom
dateTo
}
}
}
}
`;
static achievementTypeQuery = `
query AchievementType {
achievementOperators
achievementAttributes {
name
type
}
}
`;
static achievementQuery = `
query AchievementList($serverId: ID, $filter: AchievementFilter, $page: Page, $sort: Sort) {
servers(filter: {id: $serverId}) {
achievementCount
achievements(filter: $filter, page: $page, sort: $sort) {
id
name
description
attribute
operator
value
server {
id
name
}
createdAt
modifiedAt
}
}
}
`;
static achievementWithHistoryQuery = `
query AchievementHistory($serverId: ID, $id: ID) {
servers(filter: {id: $serverId}) {
achievementCount
achievements(filter: {id: $id}) {
id
history {
id
name
description
attribute
operator
value
server
deleted
dateFrom
dateTo
}
}
}
}
`;
static shortRoleNamePositionsQuery = `
query {
shortRoleNamePositions
}
`;
static shortRoleNameQuery = `
query ShortRoleNameList($serverId: ID, $filter: ShortRoleNameFilter, $page: Page, $sort: Sort) {
servers(filter: {id: $serverId}) {
shortRoleNameCount
shortRoleNames(filter: $filter, page: $page, sort: $sort) {
id
shortName
roleId
roleName
position
server {
id
name
}
createdAt
modifiedAt
}
}
}
`;
static shortRoleNameWithHistoryQuery = `
query ShortRoleNameListHistory($serverId: ID, $id: ID) {
servers(filter: {id: $serverId}) {
shortRoleNameCount
shortRoleNames(filter: {id: $id}) {
id
history {
id
shortName
roleId
roleName
position
server
deleted
dateFrom
dateTo
}
}
}
}
`;
static usersQuery = `
query UsersList($serverId: ID, $filter: UserFilter, $page: Page, $sort: Sort) {
servers(filter: {id: $serverId}) {
userCount
users(filter: $filter, page: $page, sort: $sort) {
id
discordId
name
xp
ontime
level {
id
name
}
leftServer
createdAt
modifiedAt
}
}
}
`;
static userProfile = `
query UserProfile($serverId: ID, $userId: ID, $page: Page, $sort: Sort) {
userCount
users(filter: {server: {id: $serverId}, id: $userId}, page: $page, sort: $sort) {
id
discordId
name
xp
messageCount
reactionCount
birthday
ontime
level {
id
name
}
leftServer
server {
id
name
}
joinedServerCount
joinedServers {
id
joinedOn
leavedOn
}
createdAt
modifiedAt
}
}
`;
static userProfileAchievements = `
query UserProfile($serverId: ID, $userId: ID, $page: Page, $sort: Sort) {
users(filter: {server: {id: $serverId}, id: $userId}, page: $page, sort: $sort) {
achievementCount
achievements {
id
name
description
createdAt
}
}
}
`;
static userProfileVoiceChannelJoins = `
query UserProfile($serverId: ID, $userId: ID, $page: Page, $sort: Sort) {
users(filter: {server: {id: $serverId}, id: $userId}, page: $page, sort: $sort) {
joinedVoiceChannelCount
joinedVoiceChannels {
id
channelId
channelName
time
joinedOn
leavedOn
}
}
}
`;
static userProfileGameserverJoins = `
query UserProfile($serverId: ID, $userId: ID, $page: Page, $sort: Sort) {
users(filter: {server: {id: $serverId}, id: $userId}, page: $page, sort: $sort) {
userJoinedGameServerCount
userJoinedGameServers {
id
gameServer
time
joinedOn
leavedOn
}
}
}
`;
static userProfileWarnings = `
query UserProfile($serverId: ID, $userId: ID, $page: Page, $sort: Sort) {
users(filter: {server: {id: $serverId}, id: $userId}, page: $page, sort: $sort) {
userWarningCount
userWarnings {
id
user {
id
name
}
description
author {
id
name
}
createdAt
}
}
}
`;
static userQueryWithHistory = `
query UsersWithHistory($serverId: ID, $id: ID) {
servers(filter: {id: $serverId}) {
userCount
users(filter: {id: $id}) {
id
history {
id
discordId
xp
server
deleted
dateFrom
dateTo
}
}
}
}
`;
static autoRolesQuery = `
query AutoRoleQuery($serverId: ID, $filter: AutoRoleFilter, $page: Page, $sort: Sort) {
servers(filter: {id: $serverId}) {
autoRoleCount
autoRoles(filter: $filter, page: $page, sort: $sort) {
id
channelId
channelName
messageId
autoRoleRuleCount
server {
id
}
createdAt
modifiedAt
}
}
}
`;
static autoRolesWithHistoryQuery = `
query AutoRoleWithHistoryQuery($serverId: ID, $id: ID) {
servers(filter: {id: $serverId}) {
autoRoleCount
autoRoles(filter: {id: $id}) {
id
history {
id
channelId
messageId
server
deleted
dateFrom
dateTo
}
}
}
}
`;
static autoRoleRulesQuery = `
query AutoRoleRuleQuery($serverId: ID, $autoRoleId: ID, $filter: AutoRoleRuleFilter, $page: Page, $sort: Sort) {
servers(filter: {id: $serverId}) {
autoRoles(filter: {id: $autoRoleId}) {
autoRoleRuleCount
autoRoleRules(filter: $filter, page: $page, sort: $sort) {
id
emojiName
roleId
roleName
autoRole {
id
}
createdAt
modifiedAt
}
}
}
}
`;
static autoRoleRulesHistoryQuery = `
query AutoRoleRuleHistoryQuery($serverId: ID, $autoRoleId: ID, $id: ID) {
servers(filter: {id: $serverId}) {
autoRoles(filter: {id: $autoRoleId}) {
autoRoleRuleCount
autoRoleRules(filter: {id: $id}) {
id
history {
id
emojiName
roleId
autoRole
deleted
dateFrom
dateTo
}
}
}
}
}
`;
static technicianConfigQuery = `
query technicianConfigQuery {
technicianConfig {
id
helpCommandReferenceUrl
waitForRestart
waitForShutdown
cacheMaxMessages
featureFlags {
key
value
}
pingURLs
technicianIds
createdAt
modifiedAt
}
}
`;
static serverConfigQuery = `
query serverConfigQuery($serverId: ID) {
servers(filter: { id: $serverId }) {
name
config {
id
messageDeleteTimer
notificationChatId
maxVoiceStateHours
xpPerMessage
xpPerReaction
maxMessageXpPerHour
xpPerOntimeHour
xpPerEventParticipation
xpPerAchievement
xpForBirthday
afkCommandChannelId
helpVoiceChannelId
teamChannelId
loginMessageChannelId
defaultRoleId
shortRoleNameOnlySetHighestRole
featureFlags {
key
value
}
afkChannelIds
moderatorRoleIds
adminRoleIds
server {
id
}
}
}
}
`;
}

View File

@@ -0,0 +1,87 @@
import { GameServer, Server } from "../data/server.model";
import { User } from "../data/user.model";
import { AutoRole, AutoRoleRule } from "../data/auto_role.model";
import { Discord, Guild } from "../data/discord.model";
import { Level } from "../data/level.model";
import { Achievement, AchievementAttribute } from "../data/achievement.model";
import { TechnicianConfig } from "../config/technician-config.model";
import { ServerConfig } from "../config/server-config.model";
import { ShortRoleName } from "../data/short_role_name.model";
import { FeatureFlag } from "../config/feature-flags.model";
import { UserWarning } from "../data/user_warning.model";
export interface Query {
serverCount: number;
servers: Server[];
}
export interface TechnicianConfigQuery {
technicianConfig: TechnicianConfig;
}
export interface ServerConfigQuery {
config: ServerConfig;
}
export interface SingleDiscordQuery {
discord: Discord;
}
export interface UserListQuery {
userCount: number;
users: User[];
}
export interface UserWarningQuery {
userWarningCount: number;
userWarnings: UserWarning[];
}
export interface GameServerListQuery {
gameServerCount: number;
gameServers: GameServer[];
}
export interface LevelListQuery {
levelCount: number;
levels: Level[];
}
export interface AchievementTypeQuery {
achievementAttributes: AchievementAttribute[];
achievementOperators: string[];
}
export interface AchievementListQuery {
achievementCount: number;
achievements: Achievement[];
}
export interface AutoRoleQuery {
autoRoleCount: number;
autoRoles: AutoRole[];
}
export interface AutoRoleRuleQuery {
autoRoleRuleCount: number;
autoRoleRules: AutoRoleRule[];
}
export interface PossibleFeatureFlagsQuery {
possibleFeatureFlags: string[];
}
export interface HasServerFeatureFlagQuery {
hasFeatureFlag: FeatureFlag;
}
export interface ShortRoleNameListQuery {
shortRoleNameCount: number;
shortRoleNames: ShortRoleName[];
}
export interface ShortRoleNamePositionsQuery {
shortRoleNamePositions: string[];
}

View File

@@ -0,0 +1,88 @@
import { User } from "../data/user.model";
import { AutoRole, AutoRoleRule } from "../data/auto_role.model";
import { Level } from "../data/level.model";
import { Server } from "../data/server.model";
import { Achievement } from "../data/achievement.model";
import { TechnicianConfig } from "../config/technician-config.model";
import { ServerConfig } from "../config/server-config.model";
import { ShortRoleName } from "../data/short_role_name.model";
import { UserWarning } from "../data/user_warning.model";
export interface GraphQLResult {
data: {
servers?: Server[];
};
errors?: [];
}
export interface QueryResult {
data: {
servers?: Server[];
};
}
export interface UpdateUserMutationResult {
user: {
updateUser: User
};
}
export interface AutoRoleMutationResult {
autoRole: {
createAutoRole?: AutoRole
updateAutoRole?: AutoRole
deleteAutoRole?: AutoRole
};
}
export interface AutoRoleRuleMutationResult {
autoRoleRule: {
createAutoRoleRule?: AutoRoleRule
updateAutoRoleRule?: AutoRoleRule
deleteAutoRoleRule?: AutoRoleRule
};
}
export interface LevelMutationResult {
level: {
createLevel?: Level
updateLevel?: Level
deleteLevel?: Level
};
}
export interface TechnicianConfigMutationResult {
technicianConfig: {
updateTechnicianConfig?: TechnicianConfig
};
}
export interface ServerConfigMutationResult {
serverConfig: {
updateServerConfig?: ServerConfig
};
}
export interface AchievementMutationResult {
achievement: {
createAchievement?: Achievement
updateAchievement?: Achievement
deleteAchievement?: Achievement
};
}
export interface ShortRoleNameMutationResult {
shortRoleName: {
createShortRoleName?: ShortRoleName
updateShortRoleName?: ShortRoleName
deleteShortRoleName?: ShortRoleName
};
}
export interface UserWarningMutationResult {
userWarning: {
createUserWarning?: UserWarning
updateUserWarning?: UserWarning
deleteUserWarning?: UserWarning
};
}

View File

@@ -0,0 +1,9 @@
import { Page } from "./filter/page.model";
import { Sort } from "./filter/sort.model";
export interface Variables {
filter?: object;
page?: Page;
sort?: Sort;
[x: string | number | symbol]: unknown;
}

View File

@@ -0,0 +1,8 @@
import { SelectCriterion } from "../select-criterion.model";
export interface AuthUserSelectCriterion extends SelectCriterion {
firstName: string | null;
lastName: string | null;
email: string | null;
authRole?: number | null;
}

View File

@@ -0,0 +1,6 @@
import { AuthUserDTO } from "../../auth/auth-user.dto";
export interface GetFilteredAuthUsersResultDTO {
users: AuthUserDTO[];
totalCount: number;
}

View File

@@ -0,0 +1,6 @@
export interface SelectCriterion {
pageIndex: number;
pageSize: number;
sortDirection: string | null;
sortColumn: string | null;
}

View File

@@ -0,0 +1,7 @@
export interface ConfirmationDialog {
key?: string;
header: string;
message: string;
accept?: () => void;
reject?: () => void;
}

View File

@@ -0,0 +1,5 @@
export interface ToastOptions {
life?: number;
sticky?: boolean;
closable?: boolean;
}

View File

@@ -0,0 +1,4 @@
export interface Theme {
Label: string;
Name: string;
}

View File

@@ -0,0 +1,7 @@
export enum Themes {
Default = "sh-edraft-dark-theme",
DefaultLight = "default-light-theme",
DefaultDark = "default-dark-theme",
ShEdraftLight = "sh-edraft-light-theme",
ShEdraftDark = "sh-edraft-dark-theme",
}

View File

@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthUserComponent } from './components/auth-user/auth-user.component';
const routes: Routes = [
{path: '', component: AuthUserComponent}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AuthUserRoutingModule { }

View File

@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AuthUserRoutingModule } from './auth-user-routing.module';
import { AuthUserComponent } from './components/auth-user/auth-user.component';
import { SharedModule } from '../../shared/shared.module';
@NgModule({
declarations: [
AuthUserComponent
],
imports: [
CommonModule,
AuthUserRoutingModule,
SharedModule
]
})
export class AuthUserModule { }

View File

@@ -0,0 +1,247 @@
<h1>
{{'admin.auth_users.header' | translate}}
</h1>
<div class="content-wrapper">
<div class="content">
<p-table #dt [value]="users" [responsive]="true" responsiveLayout="stack" [breakpoint]="'720px'" dataKey="id" editMode="row" [rowHover]="true" [rows]="10"
[rowsPerPageOptions]="[10,25,50]" [paginator]="true" [loading]="loading" [totalRecords]="totalRecords"
[lazy]="true" (onLazyLoad)="nextPage($event)">
<ng-template pTemplate="caption">
<div class="table-caption">
<div class="table-caption-table-info">
<div class="table-caption-text">
<ng-container *ngIf="!loading">{{users.length}} {{'common.of' | translate}}
{{dt.totalRecords}}
</ng-container>
{{'admin.auth_users.users' | translate}}
</div>
<app-multi-select-columns [table]="name" [columns]="columns" [(hiddenColumns)]="hiddenColumns"></app-multi-select-columns>
</div>
<div class="table-caption-btn-wrapper btn-wrapper">
<button pButton label="{{'common.add' | translate}}" class="icon-btn btn"
icon="pi pi-user-plus" (click)="addUser(dt)" [disabled]="isEditingNew">
</button>
<button pButton label="{{'common.reset_filters' | translate}}" icon="pi pi-undo"
class="icon-btn btn" (click)="resetFilters()">
</button>
</div>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th hideable-th="first_name" [parent]="this" [sortable]="true">
<div class="table-header-label">
<div class="table-header-text">{{'common.first_name' | translate}}</div>
<p-sortIcon field="firstName" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th hideable-th="last_name" [parent]="this" [sortable]="true">
<div class="table-header-label">
<div class="table-header-text">{{'common.last_name' | translate}}</div>
<p-sortIcon field="lastName" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th hideable-th="email" [parent]="this" [sortable]="true">
<div class="table-header-label">
<div class="table-header-text">{{'common.email' | translate}}</div>
<p-sortIcon field="email" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th class="table-header-actions" hideable-th="active" [parent]="this" [sortable]="true">
<div class="table-header-label">
<div class="table-header-text">{{'common.active' | translate}}</div>
<p-sortIcon field="confirmationId" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th class="table-header-small-dropdown" hideable-th="auth_role" [parent]="this" [sortable]="true">
<div class="table-header-label">
<div class="table-header-text">{{'common.role' | translate}}</div>
<p-sortIcon field="authRole" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th hideable-th="password" [parent]="this">
<div class="table-header-label">
<div class="table-header-text">{{'common.password' | translate}}</div>
</div>
</th>
<th class="table-header-small-dropdown">
<div class="table-header-label">
<div class="table-header-text">{{'common.created_at' | translate}}</div>
</div>
</th>
<th class="table-header-small-dropdown">
<div class="table-header-label">
<div class="table-header-text">{{'common.modified_at' | translate}}</div>
</div>
</th>
<th class="table-header-actions">
<div class="table-header-label">
<div class="table-header-text">{{'common.actions' | translate}}</div>
</div>
</th>
</tr>
<tr>
<th hideable-th="first_name" [parent]="this">
<form [formGroup]="filterForm">
<input type="text" pInputText formControlName="firstName" placeholder="{{'common.first_name' | translate}}">
</form>
</th>
<th hideable-th="last_name" [parent]="this">
<form [formGroup]="filterForm">
<input type="text" pInputText formControlName="lastName" placeholder="{{'common.last_name' | translate}}">
</form>
</th>
<th hideable-th="email" [parent]="this">
<form [formGroup]="filterForm">
<input type="email" pInputText formControlName="email" placeholder="{{'common.email' | translate}}">
</form>
</th>
<th hideable-th="active" [parent]="this"></th>
<th hideable-th="auth_role" [parent]="this">
<form [formGroup]="filterForm">
<p-dropdown formControlName="authRole" [options]="authRoles" placeholder="{{'common.auth_role' | translate}}"></p-dropdown>
</form>
</th>
<th hideable-th="password" [parent]="this"></th>
<th></th>
<th></th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user let-editing="editing" let-ri="rowIndex">
<tr [pEditableRow]="user">
<td hideable-td="first_name" [parent]="this">
<span class="p-column-title">{{'common.first_name' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="user.firstName"
[ngClass]="{ 'invalid-feedback-input': isFirstNameInvalid}">
</ng-template>
<ng-template pTemplate="output">
{{user.firstName}}
</ng-template>
</p-cellEditor>
</td>
<td hideable-td="last_name" [parent]="this">
<span class="p-column-title">{{'common.last_name' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="user.lastName"
[ngClass]="{ 'invalid-feedback-input': isLastNameInvalid}">
</ng-template>
<ng-template pTemplate="output">
{{user.lastName}}
</ng-template>
</p-cellEditor>
</td>
<td hideable-td="email" [parent]="this">
<span class="p-column-title">{{'common.email' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="email" [(ngModel)]="user.email"
[ngClass]="{ 'invalid-feedback-input': isEMailInvalid}">
</ng-template>
<ng-template pTemplate="output">
{{user.email}}
</ng-template>
</p-cellEditor>
</td>
<td hideable-td="active" [parent]="this">
<span class="p-column-title">{{'common.active' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<p-checkbox [binary]="true" [(ngModel)]="user.isConfirmed"
[ngClass]="{ 'invalid-feedback-input': isEMailInvalid}" [disabled]="isEditingNew">
</p-checkbox>
</ng-template>
<ng-template pTemplate="output">
<p-checkbox [binary]="true" [ngModel]="user.isConfirmed" [disabled]="true">
</p-checkbox>
</ng-template>
</p-cellEditor>
</td>
<td hideable-td="auth_role" [parent]="this">
<span class="p-column-title">{{'common.auth_role' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<p-dropdown [options]="authRoles" [(ngModel)]="user.authRole"
[disabled]="user.email == loggedInUserEMail"></p-dropdown>
</ng-template>
<ng-template pTemplate="output">
{{user.authRole | authRole}}
</ng-template>
</p-cellEditor>
</td>
<td hideable-td="password" [parent]="this">
<span class="p-column-title">{{'common.password' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="password" [(ngModel)]="user.password"
[ngClass]="{ 'invalid-feedback-input': isPasswordInvalid}">
</ng-template>
<ng-template pTemplate="output">
</ng-template>
</p-cellEditor>
</td>
<td>
<span class="p-column-title">{{'common.created_at' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
{{user.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{user.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
</td>
<td>
<span class="p-column-title">{{'common.modified_at' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
{{user.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{user.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
</td>
<td>
<div class="btn-wrapper">
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil"
(click)="onRowEditInit(dt, user, ri)"></button>
<button *ngIf="!editing" pButton class="btn icon-btn danger-icon-btn" icon="pi pi-trash"
(click)="deleteUser(user)"></button>
<button *ngIf="editing" pButton pSaveEditableRow class="btn icon-btn"
icon="pi pi-check-circle" (click)="onRowEditSave(dt, user, ri)"></button>
<button *ngIf="editing" pButton pCancelEditableRow class="btn icon-btn danger-icon-btn"
icon="pi pi-times-circle" (click)="onRowEditCancel(user, ri)"></button>
</div>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="9">{{'common.no_entries_found' | translate}}</td>
</tr>
</ng-template>
<ng-template pTemplate="paginatorleft">
</ng-template>
</p-table>
</div>
</div>

View File

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

View File

@@ -0,0 +1,341 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { AuthRoles } from "src/app/models/auth/auth-roles.enum";
import { AuthUserDTO } from "src/app/models/auth/auth-user.dto";
import { AuthService } from "src/app/services/auth/auth.service";
import { ConfirmationDialogService } from "src/app/services/confirmation-dialog/confirmation-dialog.service";
import { SpinnerService } from "src/app/services/spinner/spinner.service";
import { ToastService } from "src/app/services/toast/toast.service";
import { Table } from "primeng/table";
import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum";
import { RegisterErrorMessages } from "src/app/models/auth/register-error-messages.enum";
import { ErrorDTO } from "src/app/models/error/error-dto";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
import { AuthUserSelectCriterion } from "src/app/models/selection/auth-user/auth-user-select-criterion.dto";
import { LazyLoadEvent } from "primeng/api";
import { Subject, throwError } from "rxjs";
import { TranslateService } from "@ngx-translate/core";
import { ComponentWithTable } from "../../../../../base/component-with-table";
@Component({
selector: "app-auth-user",
templateUrl: "./auth-user.component.html",
styleUrls: ["./auth-user.component.scss"]
})
export class AuthUserComponent extends ComponentWithTable implements OnInit, OnDestroy {
users!: AuthUserDTO[];
statuses!: any[];
loading = true;
activityValues: number[] = [0, 100];
clonedUsers: { [s: string]: AuthUserDTO; } = {};
isEditingNew: boolean = false;
authRoles = [
{ label: AuthRoles[AuthRoles.Normal].toString(), value: AuthRoles.Normal },
{ label: AuthRoles[AuthRoles.Admin].toString(), value: AuthRoles.Admin }
];
newUserTemplate: AuthUserDTO = {
firstName: "",
lastName: "",
email: "",
password: "",
authRole: AuthRoles.Normal
};
isFirstNameInvalid: boolean = false;
isLastNameInvalid: boolean = false;
isEMailInvalid: boolean = false;
isPasswordInvalid: boolean = false;
loggedInUserEMail: string = "";
filterForm!: FormGroup<{
firstName: FormControl<string | null>,
lastName: FormControl<string | null>,
email: FormControl<string | null>,
authRole: FormControl<string | null>
}>;
searchCriterions!: AuthUserSelectCriterion;
totalRecords!: number;
private unsubscriber = new Subject<void>();
constructor(
private authService: AuthService,
private spinnerService: SpinnerService,
private toastService: ToastService,
private confirmDialog: ConfirmationDialogService,
private fb: FormBuilder,
private translate: TranslateService
) {
super("auth-users", ["first_name", "last_name", "email", "active", "auth_role", "password"]);
}
ngOnInit(): void {
this.loggedInUserEMail = this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken()) ?? "";
this.searchCriterions = {
firstName: null,
lastName: null,
email: null,
authRole: null,
pageIndex: 0,
pageSize: 10,
sortColumn: null,
sortDirection: null
};
this.setFilterForm();
this.loadNextPage();
}
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
setFilterForm() {
this.filterForm = this.fb.group({
firstName: [""],
lastName: [""],
email: [""],
authRole: [""]
});
this.filterForm.valueChanges.pipe(
takeUntil(this.unsubscriber),
debounceTime(600)
).subscribe(changes => {
if (changes.firstName) {
this.searchCriterions.firstName = changes.firstName;
} else {
this.searchCriterions.firstName = null;
}
if (changes.lastName) {
this.searchCriterions.lastName = changes.lastName;
} else {
this.searchCriterions.lastName = null;
}
if (changes.email) {
this.searchCriterions.email = changes.email;
} else {
this.searchCriterions.email = null;
}
if (changes.authRole != null) {
this.searchCriterions.authRole = +changes.authRole;
} else {
this.searchCriterions.authRole = null;
}
if (this.searchCriterions.pageSize)
this.searchCriterions.pageSize = 10;
if (this.searchCriterions.pageSize)
this.searchCriterions.pageIndex = 0;
this.loadNextPage();
});
}
loadNextPage() {
this.authService.getFilteredUsers(this.searchCriterions).pipe(catchError(err => {
this.loading = false;
return throwError(() => err);
})).subscribe(list => {
this.totalRecords = list.totalCount;
this.users = list.users;
this.loading = false;
});
}
nextPage(event: LazyLoadEvent) {
this.searchCriterions.pageSize = event.rows ?? 0;
if (event.first != null && event.rows != null)
this.searchCriterions.pageIndex = event.first / event.rows;
this.searchCriterions.sortColumn = event.sortField ?? null;
this.searchCriterions.sortDirection = event.sortOrder === 1 ? "asc" : event.sortOrder === -1 ? "desc" : "asc";
if (event.filters) {
// + "" => convert to string
this.searchCriterions.firstName = event.filters["firstName"] ? event.filters["firstName"] + "" : null;
this.searchCriterions.lastName = event.filters["lastName"] ? event.filters["lastName"] + "" : null;
this.searchCriterions.email = event.filters["email"] ? event.filters["email"] + "" : null;
this.searchCriterions.authRole = event.filters["authRole"] ? +event.filters["authRole"] : null;
}
this.loadNextPage();
}
resetFilters() {
this.filterForm.reset();
}
initUserList(): void {
this.spinnerService.showSpinner();
this.authService.getAllUsers()
.pipe(catchError(error => {
this.spinnerService.hideSpinner();
return throwError(() => error);
}))
.subscribe(users => {
this.users = users;
this.spinnerService.hideSpinner();
});
}
onRowEditInit(table: Table, user: AuthUserDTO, index: number) {
this.clonedUsers[index] = { ...user };
}
onRowEditSave(table: Table, newUser: AuthUserDTO, index: number) {
const oldUser = this.clonedUsers[index];
delete this.clonedUsers[index];
if (JSON.stringify(oldUser) === JSON.stringify(newUser) && !this.isEditingNew) {
return;
}
if (this.isEditingNew && JSON.stringify(newUser) === JSON.stringify(this.newUserTemplate)) {
this.isEditingNew = false;
this.users.splice(index, 1);
return;
}
this.isFirstNameInvalid = newUser.firstName == "";
this.isLastNameInvalid = newUser.lastName == "";
this.isEMailInvalid = newUser.email == "";
this.isPasswordInvalid = newUser.password == "";
if (
this.isEditingNew && (
newUser.firstName == "" ||
newUser.lastName == "" ||
newUser.email == ""
)
) {
table.initRowEdit(newUser);
return;
}
if (this.isEditingNew) {
this.spinnerService.showSpinner();
this.authService.register(newUser).pipe(catchError(error => {
this.spinnerService.hideSpinner();
if (error.error !== null) {
const err: ErrorDTO = error.error;
if (err.errorCode === ServiceErrorCode.InvalidData && err.message === RegisterErrorMessages.InvalidEMail) {
this.isEMailInvalid = true;
this.toastService.error(this.translate.instant("admin.auth_users.message.invalid_email"), this.translate.instant("admin.auth_users.message.invalid_email_d", { email: newUser.email }));
} else if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === RegisterErrorMessages.UserAlreadyExists) {
this.isEMailInvalid = true;
this.toastService.error(this.translate.instant("admin.auth_users.message.user_already_exists"), this.translate.instant("admin.auth_users.message.user_already_exists_d", { email: newUser.email }));
}
error.error = null;
table.initRowEdit(newUser);
}
this.spinnerService.hideSpinner();
return throwError(() => error);
}))
.subscribe(_ => {
this.initUserList();
this.spinnerService.hideSpinner();
this.toastService.success(this.translate.instant("admin.auth_users.message.user_added"), this.translate.instant("admin.auth_users.message.user_added_d", { email: newUser.email }));
this.isEditingNew = false;
});
this.triggerUserChangeDetection();
return;
}
this.spinnerService.showSpinner();
this.authService.updateUserAsAdmin({
authUserDTO: oldUser,
newAuthUserDTO: newUser,
changePassword: newUser.password != ""
}).pipe(catchError(error => {
this.spinnerService.hideSpinner();
this.toastService.error(this.translate.instant("admin.auth_users.message.user_change_failed"), this.translate.instant("admin.auth_users.message.user_change_failed_d", { email: newUser.email }));
this.initUserList();
return throwError(() => error);
}))
.subscribe(_ => {
this.initUserList();
this.spinnerService.hideSpinner();
this.toastService.success(this.translate.instant("admin.auth_users.message.user_changed"), this.translate.instant("admin.auth_users.message.user_changed_d", { email: newUser.email }));
});
this.triggerUserChangeDetection();
}
onRowEditCancel(user: AuthUserDTO, index: number) {
this.isFirstNameInvalid = false;
this.isLastNameInvalid = false;
this.isEMailInvalid = false;
this.isPasswordInvalid = false;
if (this.isEditingNew) {
this.users.splice(index, 1);
this.triggerUserChangeDetection();
delete this.clonedUsers[index];
this.isEditingNew = false;
return;
}
this.users[index] = this.clonedUsers[index];
this.triggerUserChangeDetection();
delete this.clonedUsers[index];
}
deleteUser(user: AuthUserDTO) {
if (user.email == this.loggedInUserEMail) {
this.toastService.error(this.translate.instant("admin.auth_users.message.cannot_delete_user"), this.translate.instant("admin.auth_users.message.logon_with_another_user"));
return;
}
this.confirmDialog.confirmDialog(
this.translate.instant("admin.auth_users.message.user_delete"), this.translate.instant("admin.auth_users.message.user_delete_q", { email: user.email }),
() => {
this.spinnerService.showSpinner();
this.authService.deleteUserByMail(user.email ?? "")
.pipe(catchError(error => {
this.spinnerService.hideSpinner();
return throwError(() => error);
}))
.subscribe(_ => {
this.initUserList();
this.spinnerService.hideSpinner();
this.toastService.success(this.translate.instant("admin.auth_users.message.user_deleted"), this.translate.instant("admin.auth_users.message.user_deleted_d", { email: user.email }));
});
});
}
addUser(table: Table) {
const newUser = JSON.parse(JSON.stringify(this.newUserTemplate));
newUser.id = this.users.length == 0 ? 1 : Math.max.apply(Math, this.users.map(u => {
return u.id ?? 0;
})) + 1;
this.users.push(newUser);
this.triggerUserChangeDetection();
table.initRowEdit(newUser);
const index = this.users.findIndex(u => u.email == newUser.email);
this.onRowEditInit(table, newUser, index);
this.isEditingNew = true;
}
triggerUserChangeDetection() {
// trigger change detection (https://github.com/primefaces/primeng/issues/2219)
this.users = this.users.slice();
}
}

View File

@@ -0,0 +1,182 @@
<h1>
{{'admin.settings.header' | translate}}
</h1>
<div class="content-wrapper">
<div class="content-header">
<h2>
{{'admin.settings.website.header' | translate}}
</h2>
</div>
<div class="content">
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.website.frontend_version' | translate}}:</div>
<div class="content-data-value">{{data.webVersion}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.website.backend_version' | translate}}:</div>
<div class="content-data-value">{{data.apiVersion}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.website.config_path' | translate}}:</div>
<div class="content-data-value">{{data.configPath}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.website.frontend_base_url' | translate}}:</div>
<div class="content-data-value">{{data.webBaseURL}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.website.backend_base_url' | translate}}:</div>
<div class="content-data-value">{{data.apiBaseURL}}</div>
</div>
</div>
<div class="content-divider"></div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.website.token_expire_time' | translate}}:</div>
<div class="content-data-value">{{data.tokenExpireTime}} {{'general.minutes' | translate}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.website.refresh_token_expire_time' | translate}}:</div>
<div class="content-data-value">{{data.refreshTokenExpireTime}} {{'general.days' | translate}}</div>
</div>
</div>
</div>
</div>
<div class="content-wrapper">
<div class="content-header">
<h2>
{{'admin.settings.email.header' | translate}}
</h2>
</div>
<div class="content">
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.email.user' | translate}}:</div>
<div class="content-data-value">{{data.mailUser}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.email.host' | translate}}:</div>
<div class="content-data-value">{{data.mailHost}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.email.port' | translate}}:</div>
<div class="content-data-value">{{data.mailPort}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.email.transceiver' | translate}}:</div>
<div class="content-data-value">{{data.mailTransceiver}}</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.email.email_address' | translate}}:</div>
<div class="content-data-value">{{data.mailTransceiverAddress}}</div>
</div>
</div>
<div class="content-row">
<form [formGroup]="testMailForm" class="content-column">
<div class="content-data-name">
<div class="input-field content-input-field">
<input type="email" pInputText formControlName="mail" placeholder="{{'common.email' | translate}}" autocomplete="email">
</div>
</div>
<div class="content-data-value">
<div class="login-form-submit">
<button pButton icon="pi pi-send" label="{{'common.email' | translate}}" class="btn login-form-submit-btn"
(click)="testMail()" [disabled]="testMailForm.invalid"></button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="content-wrapper">
<div class="content-header">
<h2>
{{'admin.settings.bot.header' | translate}}
</h2>
</div>
<div class="content">
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.bot.help_url' | translate}}:</div>
<div class="content-data-value">
<input type="text" pInputText [(ngModel)]="config.helpCommandReferenceUrl" placeholder="{{'admin.settings.bot.help_url' | translate}}">
</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.bot.wait_for_restart' | translate}}:</div>
<div class="content-data-value">
<input type="number" pInputText [(ngModel)]="config.waitForRestart" placeholder="{{'admin.settings.bot.wait_for_restart' | translate}}">
</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.bot.wait_for_shutdown' | translate}}:</div>
<div class="content-data-value">
<input type="number" pInputText [(ngModel)]="config.waitForShutdown" placeholder="{{'admin.settings.bot.wait_for_shutdown' | translate}}">
</div>
</div>
</div>
<div class="content-row">
<div class="content-column">
<div class="content-data-name">{{'admin.settings.bot.cache_max_messages' | translate}}:</div>
<div class="content-data-value">
<input type="number" pInputText [(ngModel)]="config.cacheMaxMessages" placeholder="{{'admin.settings.bot.cache_max_messages' | translate}}">
</div>
</div>
</div>
<div class="content-divider"></div>
<app-config-list translationKey="admin.settings.bot.ping_urls" [(data)]="config.pingURLs"></app-config-list>
<app-config-list translationKey="admin.settings.bot.technician_ids" [options]="possibleTechnicians" optionLabel="name" optionValue="id"
[(data)]="config.technicianIds"></app-config-list>
<app-feature-flag-list [(data)]="config.featureFlags"></app-feature-flag-list>
<div class="content-row">
<button pButton icon="pi pi-save" label="{{'common.save' | translate}}" class="btn login-form-submit-btn"
(click)="saveTechnicianConfig()"></button>
</div>
</div>
</div>

View File

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

View File

@@ -0,0 +1,173 @@
import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { TranslateService } from "@ngx-translate/core";
import { catchError } from "rxjs/operators";
import { SettingsDTO } from "src/app/models/config/settings.dto";
import { ErrorDTO } from "src/app/models/error/error-dto";
import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum";
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";
import { ToastService } from "src/app/services/toast/toast.service";
import { forkJoin, throwError } from "rxjs";
import { TechnicianConfig } from "../../../../../models/config/technician-config.model";
import { SingleDiscordQuery, TechnicianConfigQuery } from "../../../../../models/graphql/query.model";
import { Queries } from "../../../../../models/graphql/queries.model";
import { DataService } from "../../../../../services/data/data.service";
import { TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model";
import { Mutations } from "../../../../../models/graphql/mutations.model";
import { AuthService } from "../../../../../services/auth/auth.service";
import { ChannelType, DiscordUser } from "../../../../../models/data/discord.model";
@Component({
selector: "app-settings",
templateUrl: "./settings.component.html",
styleUrls: ["./settings.component.scss"]
})
export class SettingsComponent implements OnInit {
testMailForm!: FormGroup;
data: SettingsDTO = {
webVersion: "",
apiVersion: "",
configPath: "",
webBaseURL: "",
apiBaseURL: "",
tokenExpireTime: 0,
refreshTokenExpireTime: 0,
mailUser: "",
mailPort: 0,
mailHost: "",
mailTransceiver: "",
mailTransceiverAddress: ""
};
config: TechnicianConfig = {
helpCommandReferenceUrl: "",
waitForRestart: 0,
waitForShutdown: 0,
cacheMaxMessages: 0,
featureFlags: [],
pingURLs: [],
technicianIds: []
};
possibleTechnicians?: DiscordUser[];
constructor(
private dataService: DataService,
private settingsService: SettingsService,
private spinnerService: SpinnerService,
private guiService: GuiService,
private formBuilder: FormBuilder,
private toastService: ToastService,
private translate: TranslateService,
private authService: AuthService,
private spinner: SpinnerService
) {
}
ngOnInit(): void {
this.spinnerService.showSpinner();
this.initForms();
forkJoin([
this.guiService.getSettings().pipe(catchError(error => {
this.spinnerService.hideSpinner();
return throwError(() => error);
})),
this.dataService.query<TechnicianConfigQuery>(Queries.technicianConfigQuery),
this.dataService.query<SingleDiscordQuery>(Queries.discordUsersQuery)
]).subscribe(data => {
this.data = data[0];
this.data.webVersion = this.settingsService.getWebVersion()?.getVersionString() ?? "0.0.0";
this.data.apiBaseURL = this.settingsService.getApiURL();
if (!this.data.apiBaseURL.endsWith("/")) {
this.data.apiBaseURL += "/";
}
this.config = data[1].technicianConfig;
this.possibleTechnicians = data[2].discord.users ?? undefined;
this.initForms();
this.spinnerService.hideSpinner();
});
}
initForms(): void {
this.testMailForm = this.formBuilder.group({
mail: [null, [Validators.required, Validators.email]]
});
}
testMail(): void {
this.spinnerService.showSpinner();
const mail = this.testMailForm.value.mail;
if (!mail) {
this.spinnerService.hideSpinner();
return;
}
this.guiService.sendTestMail(mail)
.pipe(catchError(error => {
let header = this.translate.instant("admin.settings.message.error");
let message = this.translate.instant("admin.settings.message.could_not_send_mail");
if (error.error !== null) {
const err: ErrorDTO = error.error;
if (err.errorCode === ServiceErrorCode.ConnectionFailed) {
header = this.translate.instant("admin.settings.message.connection_failed");
message = this.translate.instant("admin.settings.message.connection_to_mail_failed");
error.error = null;
}
if (err.errorCode === ServiceErrorCode.InvalidUser) {
header = this.translate.instant("admin.settings.message.connection_failed");
message = this.translate.instant("admin.settings.message.mail_login_failed");
error.error = null;
}
if (err.errorCode === ServiceErrorCode.MailError) {
header = this.translate.instant("admin.settings.message.send_failed");
message = this.translate.instant("admin.settings.message.test_mail_not_send");
error.error = null;
}
}
this.spinnerService.hideSpinner();
this.toastService.error(header, message);
return throwError(() => error);
}))
.subscribe(res => {
this.spinnerService.hideSpinner();
this.toastService.success(this.translate.instant("admin.settings.message.success"), this.translate.instant("admin.settings.message.send_mail"));
this.testMailForm.reset();
});
}
saveTechnicianConfig() {
this.spinner.showSpinner();
this.dataService.mutation<TechnicianConfigMutationResult>(Mutations.updateTechnicianConfig, {
helpCommandReferenceUrl: this.config.helpCommandReferenceUrl,
waitForRestart: this.config.waitForRestart,
waitForShutdown: this.config.waitForShutdown,
cacheMaxMessages: this.config.cacheMaxMessages,
featureFlags: this.config.featureFlags,
pingURLs: this.config.pingURLs,
technicianIds: this.config.technicianIds
}
).pipe(catchError(err => {
this.spinner.hideSpinner();
return throwError(err);
})).subscribe(result => {
this.spinner.hideSpinner();
this.toastService.success(this.translate.instant("admin.settings.message.technician_config_create"), this.translate.instant("admin.settings.message.technician_config_create_d"));
});
}
}

View File

@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SettingsComponent } from './components/settings/settings.component';
const routes: Routes = [
{path:'', component: SettingsComponent}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SettingsRoutingModule { }

View File

@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SettingsRoutingModule } from './settings-routing.module';
import { SettingsComponent } from './components/settings/settings.component';
import { SharedModule } from '../../shared/shared.module';
@NgModule({
declarations: [
SettingsComponent
],
imports: [
CommonModule,
SettingsRoutingModule,
SharedModule
]
})
export class SettingsModule { }

View File

@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ForgetPasswordComponent } from './components/forget-password/forget-password.component';
import { LoginComponent } from './components/login/login.component';
import { RegistrationComponent } from './components/registration/registration.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegistrationComponent },
{ path: 'register/:id', component: RegistrationComponent },
{ path: 'forgot-password', component: ForgetPasswordComponent },
{ path: 'forgot-password/:id', component: ForgetPasswordComponent },
{ path: 'forgot-password', component: ForgetPasswordComponent },
{ path: 'forgot-password/:id', component: ForgetPasswordComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AuthRoutingModule { }

View File

@@ -0,0 +1,27 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { AuthRoutingModule } from './auth-routing.module';
import { AuthHeaderComponent } from './components/auth-header/auth-header.component';
import { ForgetPasswordComponent } from './components/forget-password/forget-password.component';
import { LoginComponent } from './components/login/login.component';
import { RegistrationComponent } from './components/registration/registration.component';
import { AuthFooterComponent } from "./components/auth-footer/auth-footer.component";
@NgModule({
declarations: [
ForgetPasswordComponent,
LoginComponent,
RegistrationComponent,
AuthHeaderComponent,
AuthFooterComponent
],
imports: [
CommonModule,
AuthRoutingModule,
SharedModule
]
})
export class AuthModule { }

View File

@@ -0,0 +1,7 @@
<div class="auth-footer">
<div class="logo-button-wrapper">
<button pButton label="{{'footer.privacy' | translate}}" class="btn text-btn" (click)="navigateToPrivacy()"></button>
<span> | </span>
<button pButton label="{{'footer.imprint' | translate}}" class="btn text-btn" (click)="navigateToImprint()"></button>
</div>
</div>

View File

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

View File

@@ -0,0 +1,32 @@
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { SettingsService } from "../../../../services/settings/settings.service";
@Component({
selector: "app-auth-footer",
templateUrl: "./auth-footer.component.html",
styleUrls: ["./auth-footer.component.scss"]
})
export class AuthFooterComponent implements OnInit {
private privacy: string = "";
private imprint: string = "";
constructor(
private settings: SettingsService
) {
}
ngOnInit(): void {
this.privacy = this.settings.getPrivacyURL();
this.imprint = this.settings.getImprintURL();
}
public navigateToPrivacy(): void {
window.open(this.settings.getPrivacyURL(), "_blank");
}
public navigateToImprint(): void {
window.open(this.settings.getImprintURL(), "_blank");
}
}

View File

@@ -0,0 +1,10 @@
<div class="auth-header">
<div class="logo-button-wrapper">
<button type="button" pButton icon="pi pi-globe" class="btn icon-btn p-button-text" (click)="authLangMenu.toggle($event)"></button>
<p-menu #authLangMenu [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)="authThemeMenu.toggle($event)"></button>
<p-menu #authThemeMenu [popup]="true" [model]="themeList" class="theme-menu"></p-menu>
</div>
</div>

View File

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

View File

@@ -0,0 +1,80 @@
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { TranslateService } from "@ngx-translate/core";
import { MenuItem, PrimeNGConfig } from "primeng/api";
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-auth-header',
templateUrl: './auth-header.component.html',
styleUrls: ['./auth-header.component.scss']
})
export class AuthHeaderComponent implements OnInit {
public langList: MenuItem[] = [];
public themeList: MenuItem[] = [];
constructor(
private router: Router,
private themeService: ThemeService,
private spinnerService: SpinnerService,
private settings: SettingsService,
private translateService: TranslateService,
private config: PrimeNGConfig
) { }
public ngOnInit(): void {
this.initMenuLists();
this.loadLang();
}
private initMenuLists(): void {
this.langList = [
{
label: 'English', command: () => {
this.translate('en');
this.setLang('en');
},
},
{
label: 'Deutsch', command: () => {
this.translate('de');
this.setLang('de');
},
},
];
this.settings.getThemes()?.forEach(theme => {
this.themeList.push({
label: theme.Label,
command: () => {
this.changeTheme(theme.Name);
}
});
});
}
private changeTheme(name: string): void {
this.themeService.setTheme(name, true);
}
private translate(lang: string) {
this.translateService.use(lang);
this.translateService.get('primeng').subscribe(res => this.config.setTranslation(res));
}
private loadLang(): void {
let lang = localStorage.getItem(`default_lang`);
if (!lang) {
lang = 'en';
this.setLang(lang);
}
this.translate(lang);
}
private setLang(lang: string): void {
localStorage.setItem(`default_lang`, lang);
}
}

View File

@@ -0,0 +1,64 @@
<section class="login-wrapper">
<div class="login-form-wrapper">
<div class="login-form">
<ng-container *ngIf="resetPasswordId === null; else resetPasswordForm">
<form [formGroup]="emailForm">
<h1>{{'auth.header' | translate}}</h1>
<div *ngIf="!ready" class="input-field">
<input type="email" pInputText formControlName="email"
placeholder="{{'common.email' | translate}}" autocomplete="username email">
</div>
<div *ngIf="ready" class="input-field-info-text">
{{'auth.forgot_password.send_confirmation_url' | translate}}
</div>
<div class="login-form-submit">
<button pButton label="{{'auth.forgot_password.reset_password' | translate}}"
class="btn login-form-submit-btn" (click)="forgotPassword()"
[disabled]="emailForm.invalid || ready"></button>
</div>
<div class="login-form-sub-button-wrapper">
<div class="login-form-sub-btn-wrapper">
<button pButton label="{{'auth.forgot_password.login' | translate}}"
class="btn login-form-sub-btn" (click)="login()"></button>
</div>
<div class="login-form-sub-btn-wrapper">
<button pButton label="{{'auth.forgot_password.register' | translate}}"
class="btn login-form-sub-btn" (click)="register()"></button>
</div>
</div>
</form>
</ng-container>
<ng-template #resetPasswordForm>
<form [formGroup]="passwordForm">
<h1>{{'auth.header' | translate}}</h1>
<div class="input-field">
<input type="password" pInputText formControlName="password"
placeholder="{{'auth.forgot_password.password' | translate}}" autocomplete="new-password">
</div>
<div class="input-field">
<input type="password" pInputText formControlName="passwordRepeat"
placeholder="{{'auth.forgot_password.repeat_password' | translate}}"
[ngClass]="{ 'invalid-feedback-input': submitted && repeatErrors.password}">
<div *ngIf="submitted" class="invalid-feedback">
<div *ngIf="repeatErrors.password">{{'auth.forgot_password.passwords_do_not_match' |
translate}}</div>
</div>
</div>
<div class="login-form-submit">
<button pButton label="{{'auth.forgot_password.reset_password' | translate}}"
class="btn login-form-submit-btn" (click)="resetPassword()"
[disabled]="passwordForm.invalid || ready"></button>
</div>
</form>
</ng-template>
</div>
</div>
<app-auth-header></app-auth-header>
<app-auth-footer></app-auth-footer>
</section>

Some files were not shown because too many files have changed in this diff Show More