Moved folders #405
This commit is contained in:
		
							
								
								
									
										23
									
								
								web/src/app/app-routing.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/app/app-routing.module.ts
									
									
									
									
									
										Normal 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 { } | ||||
							
								
								
									
										37
									
								
								web/src/app/app.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								web/src/app/app.component.html
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										0
									
								
								web/src/app/app.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								web/src/app/app.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										16
									
								
								web/src/app/app.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								web/src/app/app.component.spec.ts
									
									
									
									
									
										Normal 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(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										83
									
								
								web/src/app/app.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								web/src/app/app.component.ts
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										83
									
								
								web/src/app/app.module.ts
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										7
									
								
								web/src/app/base/component-with-table.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/src/app/base/component-with-table.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import { ComponentWithTable } from './component-with-table'; | ||||
|  | ||||
| describe('ComponentWithTable', () => { | ||||
|   it('should create an instance', () => { | ||||
|     expect(new ComponentWithTable()).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										44
									
								
								web/src/app/base/component-with-table.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								web/src/app/base/component-with-table.ts
									
									
									
									
									
										Normal 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)); | ||||
|   } | ||||
| } | ||||
| @@ -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> | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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 { | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										28
									
								
								web/src/app/components/footer/footer.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								web/src/app/components/footer/footer.component.html
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										0
									
								
								web/src/app/components/footer/footer.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								web/src/app/components/footer/footer.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										25
									
								
								web/src/app/components/footer/footer.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/src/app/components/footer/footer.component.spec.ts
									
									
									
									
									
										Normal 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(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										56
									
								
								web/src/app/components/footer/footer.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								web/src/app/components/footer/footer.component.ts
									
									
									
									
									
										Normal 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"); | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										25
									
								
								web/src/app/components/header/header.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/src/app/components/header/header.component.html
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										0
									
								
								web/src/app/components/header/header.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								web/src/app/components/header/header.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										25
									
								
								web/src/app/components/header/header.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/src/app/components/header/header.component.spec.ts
									
									
									
									
									
										Normal 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(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										169
									
								
								web/src/app/components/header/header.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								web/src/app/components/header/header.component.ts
									
									
									
									
									
										Normal 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); | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										3
									
								
								web/src/app/components/sidebar/sidebar.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/src/app/components/sidebar/sidebar.component.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| <div class="menu"> | ||||
|     <p-panelMenu [model]="menuItems"></p-panelMenu> | ||||
| </div> | ||||
							
								
								
									
										25
									
								
								web/src/app/components/sidebar/sidebar.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/src/app/components/sidebar/sidebar.component.spec.ts
									
									
									
									
									
										Normal 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(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										42
									
								
								web/src/app/components/sidebar/sidebar.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								web/src/app/components/sidebar/sidebar.component.ts
									
									
									
									
									
										Normal 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(); | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										7
									
								
								web/src/app/components/spinner/spinner.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/src/app/components/spinner/spinner.component.html
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										25
									
								
								web/src/app/components/spinner/spinner.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/src/app/components/spinner/spinner.component.spec.ts
									
									
									
									
									
										Normal 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(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										24
									
								
								web/src/app/components/spinner/spinner.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								web/src/app/components/spinner/spinner.component.ts
									
									
									
									
									
										Normal 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 { | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										7
									
								
								web/src/app/models/auth/admin-update-user.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/src/app/models/auth/admin-update-user.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import { AuthUserDTO } from "./auth-user.dto"; | ||||
|  | ||||
| export interface AdminUpdateUserDTO { | ||||
|     authUserDTO: AuthUserDTO; | ||||
|     newAuthUserDTO: AuthUserDTO; | ||||
|     changePassword: boolean; | ||||
| } | ||||
							
								
								
									
										7
									
								
								web/src/app/models/auth/auth-error-messages.enum.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/src/app/models/auth/auth-error-messages.enum.ts
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										4
									
								
								web/src/app/models/auth/auth-roles.enum.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/app/models/auth/auth-roles.enum.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export enum AuthRoles { | ||||
|     Normal = 0, | ||||
|     Admin = 1 | ||||
| } | ||||
							
								
								
									
										12
									
								
								web/src/app/models/auth/auth-user-atr-errors.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								web/src/app/models/auth/auth-user-atr-errors.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										33
									
								
								web/src/app/models/auth/auth-user.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								web/src/app/models/auth/auth-user.dto.ts
									
									
									
									
									
										Normal 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, | ||||
| } | ||||
							
								
								
									
										3
									
								
								web/src/app/models/auth/discord-auth-url.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/src/app/models/auth/discord-auth-url.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| export interface DiscordAuthURL { | ||||
|   loginUrl: string; | ||||
| } | ||||
							
								
								
									
										3
									
								
								web/src/app/models/auth/email-string.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/src/app/models/auth/email-string.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| export interface EMailStringDTO { | ||||
|     email: string; | ||||
| } | ||||
							
								
								
									
										6
									
								
								web/src/app/models/auth/oauth.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/src/app/models/auth/oauth.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import { AuthUserDTO } from "./auth-user.dto"; | ||||
|  | ||||
| export interface OAuthDTO { | ||||
|   user: AuthUserDTO; | ||||
|     oAuthId: string; | ||||
| } | ||||
							
								
								
									
										4
									
								
								web/src/app/models/auth/register-error-messages.enum.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/app/models/auth/register-error-messages.enum.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export enum RegisterErrorMessages { | ||||
|     InvalidEMail = "Invalid E-Mail", | ||||
|     UserAlreadyExists = "User already exists", | ||||
| } | ||||
							
								
								
									
										6
									
								
								web/src/app/models/auth/reset-password.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/src/app/models/auth/reset-password.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import { AuthUserDTO } from "./auth-user.dto"; | ||||
|  | ||||
| export interface ResetPasswordDTO { | ||||
|     id: string; | ||||
|     password: string; | ||||
| } | ||||
							
								
								
									
										5
									
								
								web/src/app/models/auth/token.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								web/src/app/models/auth/token.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export interface TokenDTO { | ||||
|     token: string; | ||||
|     refreshToken: string; | ||||
|     firstLogin?: boolean; | ||||
| } | ||||
							
								
								
									
										6
									
								
								web/src/app/models/auth/update-user.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/src/app/models/auth/update-user.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import { AuthUserDTO } from "./auth-user.dto"; | ||||
|  | ||||
| export interface UpdateUserDTO { | ||||
|     authUserDTO: AuthUserDTO; | ||||
|     newAuthUserDTO: AuthUserDTO; | ||||
| } | ||||
							
								
								
									
										5
									
								
								web/src/app/models/config/api-version.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								web/src/app/models/config/api-version.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export interface ApiVersion { | ||||
|     Major: string; | ||||
|     Minor: string; | ||||
|     Micro: string; | ||||
| } | ||||
							
								
								
									
										21
									
								
								web/src/app/models/config/app-settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/src/app/models/config/app-settings.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										4
									
								
								web/src/app/models/config/feature-flags.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/app/models/config/feature-flags.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export interface FeatureFlag { | ||||
|   key: string; | ||||
|   value: boolean; | ||||
| } | ||||
							
								
								
									
										27
									
								
								web/src/app/models/config/server-config.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								web/src/app/models/config/server-config.model.ts
									
									
									
									
									
										Normal 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[]; | ||||
| } | ||||
							
								
								
									
										16
									
								
								web/src/app/models/config/settings.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								web/src/app/models/config/settings.dto.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										5
									
								
								web/src/app/models/config/software-version.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								web/src/app/models/config/software-version.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export interface SoftwareVersionDTO { | ||||
|     major: string; | ||||
|     minor: string; | ||||
|     micro: string | ||||
| } | ||||
							
								
								
									
										19
									
								
								web/src/app/models/config/software-version.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								web/src/app/models/config/software-version.ts
									
									
									
									
									
										Normal 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}`; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										13
									
								
								web/src/app/models/config/technician-config.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								web/src/app/models/config/technician-config.model.ts
									
									
									
									
									
										Normal 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[]; | ||||
| } | ||||
							
								
								
									
										29
									
								
								web/src/app/models/data/achievement.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								web/src/app/models/data/achievement.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										36
									
								
								web/src/app/models/data/auto_role.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/app/models/data/auto_role.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										16
									
								
								web/src/app/models/data/client.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								web/src/app/models/data/client.model.ts
									
									
									
									
									
										Normal 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; | ||||
|  | ||||
| } | ||||
							
								
								
									
										17
									
								
								web/src/app/models/data/data.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								web/src/app/models/data/data.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										42
									
								
								web/src/app/models/data/discord.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								web/src/app/models/data/discord.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										17
									
								
								web/src/app/models/data/level.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								web/src/app/models/data/level.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										36
									
								
								web/src/app/models/data/server.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/app/models/data/server.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										20
									
								
								web/src/app/models/data/short_role_name.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								web/src/app/models/data/short_role_name.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										48
									
								
								web/src/app/models/data/user.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								web/src/app/models/data/user.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										11
									
								
								web/src/app/models/data/user_joined_game_server.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								web/src/app/models/data/user_joined_game_server.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										9
									
								
								web/src/app/models/data/user_joined_server.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								web/src/app/models/data/user_joined_server.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										12
									
								
								web/src/app/models/data/user_joined_voice_channel.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								web/src/app/models/data/user_joined_voice_channel.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										16
									
								
								web/src/app/models/data/user_warning.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								web/src/app/models/data/user_warning.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										8
									
								
								web/src/app/models/error/error-dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								web/src/app/models/error/error-dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import { ServiceErrorCode } from "./service-error-code.enum"; | ||||
|  | ||||
|  | ||||
| export class ErrorDTO { | ||||
|     errorCode!: ServiceErrorCode; | ||||
|     message!: string; | ||||
|  | ||||
| } | ||||
							
								
								
									
										17
									
								
								web/src/app/models/error/service-error-code.enum.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								web/src/app/models/error/service-error-code.enum.ts
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										4
									
								
								web/src/app/models/graphql/filter/page.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/app/models/graphql/filter/page.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export interface Page { | ||||
|   pageIndex?: number; | ||||
|   pageSize?: number; | ||||
| } | ||||
							
								
								
									
										9
									
								
								web/src/app/models/graphql/filter/sort.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								web/src/app/models/graphql/filter/sort.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| export interface Sort { | ||||
|   sortColumn?: string; | ||||
|   sortDirection?: SortDirection; | ||||
| } | ||||
|  | ||||
| export enum SortDirection { | ||||
|   ASC = "ASC", | ||||
|   DESC = "DESC", | ||||
| } | ||||
							
								
								
									
										376
									
								
								web/src/app/models/graphql/mutations.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										376
									
								
								web/src/app/models/graphql/mutations.model.ts
									
									
									
									
									
										Normal 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 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
| } | ||||
							
								
								
									
										550
									
								
								web/src/app/models/graphql/queries.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										550
									
								
								web/src/app/models/graphql/queries.model.ts
									
									
									
									
									
										Normal 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 | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
| } | ||||
							
								
								
									
										87
									
								
								web/src/app/models/graphql/query.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								web/src/app/models/graphql/query.model.ts
									
									
									
									
									
										Normal 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[]; | ||||
| } | ||||
|  | ||||
							
								
								
									
										88
									
								
								web/src/app/models/graphql/result.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								web/src/app/models/graphql/result.model.ts
									
									
									
									
									
										Normal 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 | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										9
									
								
								web/src/app/models/graphql/variables.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								web/src/app/models/graphql/variables.model.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
| @@ -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; | ||||
| } | ||||
| @@ -0,0 +1,6 @@ | ||||
| import { AuthUserDTO } from "../../auth/auth-user.dto"; | ||||
|  | ||||
| export interface GetFilteredAuthUsersResultDTO { | ||||
|     users: AuthUserDTO[]; | ||||
|     totalCount: number; | ||||
| } | ||||
							
								
								
									
										6
									
								
								web/src/app/models/selection/select-criterion.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/src/app/models/selection/select-criterion.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| export interface SelectCriterion { | ||||
|     pageIndex: number; | ||||
|     pageSize: number; | ||||
|     sortDirection: string | null; | ||||
|     sortColumn: string | null; | ||||
| } | ||||
							
								
								
									
										7
									
								
								web/src/app/models/utils/confirmation-dialog.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/src/app/models/utils/confirmation-dialog.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| export interface ConfirmationDialog { | ||||
|     key?: string; | ||||
|     header: string; | ||||
|     message: string; | ||||
|     accept?: () => void; | ||||
|     reject?: () => void; | ||||
| } | ||||
							
								
								
									
										5
									
								
								web/src/app/models/utils/toast-options.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								web/src/app/models/utils/toast-options.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export interface ToastOptions { | ||||
|     life?: number; | ||||
|     sticky?: boolean; | ||||
|     closable?: boolean; | ||||
| } | ||||
							
								
								
									
										4
									
								
								web/src/app/models/view/theme.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/app/models/view/theme.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export interface Theme { | ||||
|     Label: string; | ||||
|     Name: string; | ||||
| } | ||||
							
								
								
									
										7
									
								
								web/src/app/models/view/themes.enum.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/src/app/models/view/themes.enum.ts
									
									
									
									
									
										Normal 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", | ||||
| } | ||||
| @@ -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 { } | ||||
							
								
								
									
										18
									
								
								web/src/app/modules/admin/auth-users/auth-user.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								web/src/app/modules/admin/auth-users/auth-user.module.ts
									
									
									
									
									
										Normal 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 { } | ||||
| @@ -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> | ||||
|  | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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(); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -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> | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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")); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -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 { } | ||||
							
								
								
									
										19
									
								
								web/src/app/modules/admin/settings/settings.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								web/src/app/modules/admin/settings/settings.module.ts
									
									
									
									
									
										Normal 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 { } | ||||
							
								
								
									
										21
									
								
								web/src/app/modules/auth/auth-routing.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/src/app/modules/auth/auth-routing.module.ts
									
									
									
									
									
										Normal 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 { } | ||||
							
								
								
									
										27
									
								
								web/src/app/modules/auth/auth.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								web/src/app/modules/auth/auth.module.ts
									
									
									
									
									
										Normal 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 { } | ||||
| @@ -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> | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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"); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -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> | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -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
		Reference in New Issue
	
	Block a user