diff --git a/api/src/api/routes/redirect.py b/api/src/api/routes/redirect.py index c42fdef..44af696 100644 --- a/api/src/api/routes/redirect.py +++ b/api/src/api/routes/redirect.py @@ -1,16 +1,27 @@ -from flask import redirect +from flask import redirect, jsonify from api.route import Route from core.logger import Logger from data.schemas.public.short_url import ShortUrl from data.schemas.public.short_url_dao import shortUrlDao -BasePath = f"/" logger = Logger(__name__) -@Route.get(f"{BasePath}/") +@Route.get(f"/api/find/") +async def find(path: str): + from_db = await shortUrlDao.find_single_by({ShortUrl.short_url: path}) + if from_db is None: + return jsonify(None) + + return jsonify(from_db.to_dto()) + + +@Route.get(f"/api/redirect/") async def handle_short_url(path: str): + if path.startswith("api/"): + path = path.replace("api/", "") + from_db = await shortUrlDao.find_single_by({ShortUrl.short_url: path}) if from_db is None: return {"error": "Short URL not found"}, 404 diff --git a/api/src/api_graphql/abc/filter_abc.py b/api/src/api_graphql/abc/filter_abc.py index 74aa02f..c75f971 100644 --- a/api/src/api_graphql/abc/filter_abc.py +++ b/api/src/api_graphql/abc/filter_abc.py @@ -22,7 +22,7 @@ class FilterABC[T](ABC): filter_type: Union[Type["FilterABC"], Type[Union[int, str, bool, datetime]]], db_name=None, ): - if field not in self._obj: + if field not in self._obj and db_name not in self._obj: return if db_name is None: diff --git a/api/src/api_graphql/filter/short_url_filter.py b/api/src/api_graphql/filter/short_url_filter.py index 8fff9af..4c59402 100644 --- a/api/src/api_graphql/filter/short_url_filter.py +++ b/api/src/api_graphql/filter/short_url_filter.py @@ -4,8 +4,8 @@ from api_graphql.abc.filter.string_filter import StringFilter class ShortUrlFilter(DbModelFilterABC): def __init__( - self, - obj: dict, + self, + obj: dict, ): DbModelFilterABC.__init__(self, obj) diff --git a/api/src/api_graphql/graphql/group.gql b/api/src/api_graphql/graphql/group.gql index 82005fe..4a3880c 100644 --- a/api/src/api_graphql/graphql/group.gql +++ b/api/src/api_graphql/graphql/group.gql @@ -8,6 +8,8 @@ type Group implements DbModel { id: ID name: String + shortUrls: [ShortUrl] + deleted: Boolean editor: User createdUtc: String diff --git a/api/src/api_graphql/graphql/short_url.gql b/api/src/api_graphql/graphql/short_url.gql index 5e120eb..f7204fc 100644 --- a/api/src/api_graphql/graphql/short_url.gql +++ b/api/src/api_graphql/graphql/short_url.gql @@ -21,7 +21,6 @@ input ShortUrlSort { id: SortOrder name: SortOrder description: SortOrder - group: GroupSort deleted: SortOrder editorId: SortOrder @@ -33,7 +32,6 @@ input ShortUrlFilter { id: IntFilter name: StringFilter description: StringFilter - group: GroupFilter deleted: BooleanFilter editor: IntFilter diff --git a/api/src/api_graphql/queries/group_query.py b/api/src/api_graphql/queries/group_query.py index ee70246..6917a2e 100644 --- a/api/src/api_graphql/queries/group_query.py +++ b/api/src/api_graphql/queries/group_query.py @@ -1,4 +1,7 @@ from api_graphql.abc.db_model_query_abc import DbModelQueryABC +from data.schemas.public.group import Group +from data.schemas.public.short_url import ShortUrl +from data.schemas.public.short_url_dao import shortUrlDao class GroupQuery(DbModelQueryABC): @@ -6,3 +9,8 @@ class GroupQuery(DbModelQueryABC): DbModelQueryABC.__init__(self, "Group") self.set_field("name", lambda x, *_: x.name) + self.set_field("shortUrls", self._get_urls) + + @staticmethod + async def _get_urls(group: Group, *_): + return await shortUrlDao.find_by({ShortUrl.group_id: group.id}) diff --git a/api/src/api_graphql/query.py b/api/src/api_graphql/query.py index ca9506c..d80bacb 100644 --- a/api/src/api_graphql/query.py +++ b/api/src/api_graphql/query.py @@ -85,7 +85,13 @@ class Query(QueryABC): .with_dao(groupDao) .with_filter(GroupFilter) .with_sort(Sort[Group]) - .with_require_any_permission([Permissions.groups, Permissions.short_urls_create, Permissions.short_urls_update]) + .with_require_any_permission( + [ + Permissions.groups, + Permissions.short_urls_create, + Permissions.short_urls_update, + ] + ) ) # partially public to load redirect if not resolved/redirected by api self.field( diff --git a/api/src/data/schemas/public/short_url.py b/api/src/data/schemas/public/short_url.py index 99eac98..f87cf4b 100644 --- a/api/src/data/schemas/public/short_url.py +++ b/api/src/data/schemas/public/short_url.py @@ -5,6 +5,7 @@ from async_property import async_property from core.database.abc.db_model_abc import DbModelABC from core.typing import SerialId +from data.schemas.public.group import Group class ShortUrl(DbModelABC): @@ -59,7 +60,16 @@ class ShortUrl(DbModelABC): self._group_id = value @async_property - async def group(self): + async def group(self) -> Optional[Group]: + if self._group_id is None: + return None + from data.schemas.public.group_dao import groupDao return await groupDao.get_by_id(self._group_id) + + def to_dto(self) -> dict: + return { + "id": self.id, + "target_url": self.target_url, + } diff --git a/web/src/app/app-routing.module.ts b/web/src/app/app-routing.module.ts index 2ea1f15..40c40bb 100644 --- a/web/src/app/app-routing.module.ts +++ b/web/src/app/app-routing.module.ts @@ -1,34 +1,23 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { NotFoundComponent } from 'src/app/components/error/not-found/not-found.component'; -import { AuthGuard } from 'src/app/core/guard/auth.guard'; -import {HomeComponent} from "src/app/components/home/home.component"; +import { NgModule } from "@angular/core"; +import { RouterModule, Routes } from "@angular/router"; +import { NotFoundComponent } from "src/app/components/error/not-found/not-found.component"; +import { AuthGuard } from "src/app/core/guard/auth.guard"; +import { HomeComponent } from "src/app/components/home/home.component"; +import { RedirectComponent } from "src/app/components/redirect/redirect.component"; const routes: Routes = [ { - path: '', - redirectTo: 'home', - pathMatch: 'full', - }, - { - path: 'about', - redirectTo: 'home', - }, - { - path: 'home', + path: "", component: HomeComponent, }, { - path: 'admin', + path: "admin", loadChildren: () => - import('./modules/admin/admin.module').then(m => m.AdminModule), + import("./modules/admin/admin.module").then((m) => m.AdminModule), canActivate: [AuthGuard], }, - { path: '404', component: NotFoundComponent }, - { - path: '**', - redirectTo: '404', - }, + { path: "404", component: NotFoundComponent }, + { path: "**", component: RedirectComponent }, ]; @NgModule({ diff --git a/web/src/app/app.component.html b/web/src/app/app.component.html index 9cca78f..5d586fc 100644 --- a/web/src/app/app.component.html +++ b/web/src/app/app.component.html @@ -1,32 +1,36 @@ -
- +
+ -
- -
- -
-
- +
+ +
+ +
+
+ - - - -
- - -
-
-
+ + + +
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/web/src/app/app.component.spec.ts b/web/src/app/app.component.spec.ts index 74aafac..e751f6b 100644 --- a/web/src/app/app.component.spec.ts +++ b/web/src/app/app.component.spec.ts @@ -1,22 +1,22 @@ -import { TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { AppComponent } from './app.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { of } from 'rxjs'; -import { HomeComponent } from 'src/app/components/home/home.component'; -import { FooterComponent } from 'src/app/components/footer/footer.component'; -import { HeaderComponent } from 'src/app/components/header/header.component'; -import { NotFoundComponent } from 'src/app/components/error/not-found/not-found.component'; -import { SpinnerComponent } from 'src/app/components/spinner/spinner.component'; -import { SidebarComponent } from 'src/app/components/sidebar/sidebar.component'; +import { TestBed } from "@angular/core/testing"; +import { ActivatedRoute } from "@angular/router"; +import { AppComponent } from "./app.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { of } from "rxjs"; +import { HomeComponent } from "src/app/components/home/home.component"; +import { FooterComponent } from "src/app/components/footer/footer.component"; +import { HeaderComponent } from "src/app/components/header/header.component"; +import { NotFoundComponent } from "src/app/components/error/not-found/not-found.component"; +import { SpinnerComponent } from "src/app/components/spinner/spinner.component"; +import { SidebarComponent } from "src/app/components/sidebar/sidebar.component"; -describe('AppComponent', () => { +describe("AppComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ @@ -46,7 +46,7 @@ describe('AppComponent', () => { }).compileComponents(); }); - it('should create the app', () => { + it("should create the app", () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); diff --git a/web/src/app/app.component.ts b/web/src/app/app.component.ts index 41d98cc..d27b5ef 100644 --- a/web/src/app/app.component.ts +++ b/web/src/app/app.component.ts @@ -1,27 +1,32 @@ -import { Component, OnDestroy } from '@angular/core'; -import { SidebarService } from 'src/app/service/sidebar.service'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { AuthService } from 'src/app/service/auth.service'; +import { Component, OnDestroy } from "@angular/core"; +import { SidebarService } from "src/app/service/sidebar.service"; +import { Subject } from "rxjs"; +import { takeUntil } from "rxjs/operators"; +import { AuthService } from "src/app/service/auth.service"; @Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrl: './app.component.scss', + selector: "app-root", + templateUrl: "./app.component.html", + styleUrl: "./app.component.scss", }) export class AppComponent implements OnDestroy { showSidebar = false; - + isLoggedIn = false; unsubscribe$ = new Subject(); constructor( private sidebar: SidebarService, - private auth: AuthService + private auth: AuthService, ) { this.auth.loadUser(); + + this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe((user) => { + this.isLoggedIn = user !== null && user !== undefined; + }); + this.sidebar.visible$ .pipe(takeUntil(this.unsubscribe$)) - .subscribe(visible => { + .subscribe((visible) => { this.showSidebar = visible; }); } diff --git a/web/src/app/app.module.ts b/web/src/app/app.module.ts index ea42ccc..73100e6 100644 --- a/web/src/app/app.module.ts +++ b/web/src/app/app.module.ts @@ -1,26 +1,27 @@ -import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core"; +import { BrowserModule } from "@angular/platform-browser"; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { KeycloakService } from 'keycloak-angular'; -import { HomeComponent } from './components/home/home.component'; -import { initializeKeycloak } from './core/init-keycloak'; -import { HttpClient } from '@angular/common/http'; -import { environment } from '../environments/environment'; -import { FooterComponent } from 'src/app/components/footer/footer.component'; -import { HeaderComponent } from 'src/app/components/header/header.component'; -import { NotFoundComponent } from 'src/app/components/error/not-found/not-found.component'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { Logger } from 'src/app/service/logger.service'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { SpinnerComponent } from 'src/app/components/spinner/spinner.component'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { DialogService } from 'primeng/dynamicdialog'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { SidebarComponent } from './components/sidebar/sidebar.component'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; +import { AppRoutingModule } from "./app-routing.module"; +import { AppComponent } from "./app.component"; +import { KeycloakService } from "keycloak-angular"; +import { initializeKeycloak } from "./core/init-keycloak"; +import { HttpClient } from "@angular/common/http"; +import { environment } from "../environments/environment"; +import { FooterComponent } from "src/app/components/footer/footer.component"; +import { HeaderComponent } from "src/app/components/header/header.component"; +import { NotFoundComponent } from "src/app/components/error/not-found/not-found.component"; +import { TranslateLoader, TranslateModule } from "@ngx-translate/core"; +import { TranslateHttpLoader } from "@ngx-translate/http-loader"; +import { Logger } from "src/app/service/logger.service"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { SpinnerComponent } from "src/app/components/spinner/spinner.component"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { DialogService } from "primeng/dynamicdialog"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { SidebarComponent } from "./components/sidebar/sidebar.component"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { HomeComponent } from "./components/home/home.component"; +import { RedirectComponent } from "./components/redirect/redirect.component"; if (environment.production) { Logger.enableProductionMode(); @@ -35,12 +36,13 @@ export function HttpLoaderFactory(http: HttpClient) { @NgModule({ declarations: [ AppComponent, - HomeComponent, FooterComponent, HeaderComponent, NotFoundComponent, SpinnerComponent, SidebarComponent, + HomeComponent, + RedirectComponent, ], imports: [ BrowserModule, @@ -48,7 +50,7 @@ export function HttpLoaderFactory(http: HttpClient) { AppRoutingModule, SharedModule, TranslateModule.forRoot({ - defaultLanguage: 'en', + defaultLanguage: "en", loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, diff --git a/web/src/app/components/error/not-found/not-found.component.spec.ts b/web/src/app/components/error/not-found/not-found.component.spec.ts index 73c2e28..4ebd2e8 100644 --- a/web/src/app/components/error/not-found/not-found.component.spec.ts +++ b/web/src/app/components/error/not-found/not-found.component.spec.ts @@ -1,9 +1,9 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { NotFoundComponent } from 'src/app/components/error/not-found/not-found.component'; -import { TranslateModule } from '@ngx-translate/core'; +import { NotFoundComponent } from "src/app/components/error/not-found/not-found.component"; +import { TranslateModule } from "@ngx-translate/core"; -describe('NotFoundComponent', () => { +describe("NotFoundComponent", () => { let component: NotFoundComponent; let fixture: ComponentFixture; @@ -20,7 +20,7 @@ describe('NotFoundComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/components/error/not-found/not-found.component.ts b/web/src/app/components/error/not-found/not-found.component.ts index d80e028..aaf0dfd 100644 --- a/web/src/app/components/error/not-found/not-found.component.ts +++ b/web/src/app/components/error/not-found/not-found.component.ts @@ -1,8 +1,8 @@ -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; @Component({ - selector: 'app-not-found', - templateUrl: './not-found.component.html', - styleUrls: ['./not-found.component.scss'], + selector: "app-not-found", + templateUrl: "./not-found.component.html", + styleUrls: ["./not-found.component.scss"], }) export class NotFoundComponent {} diff --git a/web/src/app/components/footer/footer.component.spec.ts b/web/src/app/components/footer/footer.component.spec.ts index f9df2ad..60cc87a 100644 --- a/web/src/app/components/footer/footer.component.spec.ts +++ b/web/src/app/components/footer/footer.component.spec.ts @@ -1,12 +1,12 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { FooterComponent } from 'src/app/components/footer/footer.component'; -import { TranslateModule } from '@ngx-translate/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; +import { FooterComponent } from "src/app/components/footer/footer.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { SharedModule } from "src/app/modules/shared/shared.module"; -describe('FooterComponent', () => { +describe("FooterComponent", () => { let component: FooterComponent; let fixture: ComponentFixture; @@ -28,7 +28,7 @@ describe('FooterComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/components/footer/footer.component.ts b/web/src/app/components/footer/footer.component.ts index cb7adce..b922851 100644 --- a/web/src/app/components/footer/footer.component.ts +++ b/web/src/app/components/footer/footer.component.ts @@ -1,10 +1,10 @@ -import { Component } from '@angular/core'; -import { SettingsService } from 'src/app/service/settings.service'; +import { Component } from "@angular/core"; +import { SettingsService } from "src/app/service/settings.service"; @Component({ - selector: 'app-footer', - templateUrl: './footer.component.html', - styleUrls: ['./footer.component.scss'], + selector: "app-footer", + templateUrl: "./footer.component.html", + styleUrls: ["./footer.component.scss"], }) export class FooterComponent { constructor(private settings: SettingsService) {} diff --git a/web/src/app/components/header/header.component.html b/web/src/app/components/header/header.component.html index b298046..d5fed22 100644 --- a/web/src/app/components/header/header.component.html +++ b/web/src/app/components/header/header.component.html @@ -1,47 +1,53 @@
-
-
+
+
+ +
+ +
+

Open-redirect

+
- -
-

Open-redirect

-
-
-
- - -
-
- +
+ + +
+
+ - - - - + + + + +
-
diff --git a/web/src/app/components/header/header.component.spec.ts b/web/src/app/components/header/header.component.spec.ts index cde0df1..00618f5 100644 --- a/web/src/app/components/header/header.component.spec.ts +++ b/web/src/app/components/header/header.component.spec.ts @@ -1,16 +1,16 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { HeaderComponent } from 'src/app/components/header/header.component'; -import { TranslateModule } from '@ngx-translate/core'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { HeaderComponent } from "src/app/components/header/header.component"; +import { TranslateModule } from "@ngx-translate/core"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; -describe('HeaderComponent', () => { +describe("HeaderComponent", () => { let component: HeaderComponent; let fixture: ComponentFixture; @@ -41,7 +41,7 @@ describe('HeaderComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/components/header/header.component.ts b/web/src/app/components/header/header.component.ts index 37110c9..442b5f6 100644 --- a/web/src/app/components/header/header.component.ts +++ b/web/src/app/components/header/header.component.ts @@ -1,17 +1,18 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { MenuItem, PrimeNGConfig } from 'primeng/api'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { TranslateService } from '@ngx-translate/core'; -import { GuiService } from 'src/app/service/gui.service'; -import { User } from 'src/app/model/auth/user'; -import { AuthService } from 'src/app/service/auth.service'; -import { MenuElement } from 'src/app/model/view/menu-element'; +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { MenuItem, PrimeNGConfig } from "primeng/api"; +import { Subject } from "rxjs"; +import { takeUntil } from "rxjs/operators"; +import { TranslateService } from "@ngx-translate/core"; +import { GuiService } from "src/app/service/gui.service"; +import { User } from "src/app/model/auth/user"; +import { AuthService } from "src/app/service/auth.service"; +import { MenuElement } from "src/app/model/view/menu-element"; +import { SidebarService } from "src/app/service/sidebar.service"; @Component({ - selector: 'app-header', - templateUrl: './header.component.html', - styleUrls: ['./header.component.scss'], + selector: "app-header", + templateUrl: "./header.component.html", + styleUrls: ["./header.component.scss"], }) export class HeaderComponent implements OnInit, OnDestroy { langList: MenuItem[] = []; @@ -26,18 +27,24 @@ export class HeaderComponent implements OnInit, OnDestroy { private translateService: TranslateService, private config: PrimeNGConfig, private guiService: GuiService, - private auth: AuthService + private auth: AuthService, + private sidebarService: SidebarService, ) { this.guiService.isMobile$ .pipe(takeUntil(this.unsubscribe$)) - .subscribe(isMobile => { + .subscribe((isMobile) => { this.isMobile = isMobile; + if (isMobile) { + this.sidebarService.hide(); + } }); - this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe(async user => { - this.user = user; - await this.initMenuLists(); - }); + this.auth.user$ + .pipe(takeUntil(this.unsubscribe$)) + .subscribe(async (user) => { + this.user = user; + await this.initMenuLists(); + }); } async ngOnInit() { @@ -55,49 +62,24 @@ export class HeaderComponent implements OnInit, OnDestroy { } async initMenuLists() { - await this.initMenuList(); await this.initLangMenuList(); await this.initUserMenuList(); } - async initMenuList() { - this.menu = [ - { - label: 'common.news', - routerLink: ['/'], - icon: 'pi pi-home', - }, - { - label: 'header.menu.about', - routerLink: ['/about'], - icon: 'pi pi-info', - }, - ]; - - if (this.auth.user$.value) { - this.menu.push({ - label: 'header.menu.admin', - routerLink: ['/admin'], - icon: 'pi pi-cog', - visible: await this.auth.isAdmin(), - }); - } - } - async initLangMenuList() { this.langList = [ { - label: 'English', + label: "English", command: () => { - this.translate('en'); - this.setLang('en'); + this.translate("en"); + this.setLang("en"); }, }, { - label: 'Deutsch', + label: "Deutsch", command: () => { - this.translate('de'); - this.setLang('de'); + this.translate("de"); + this.setLang("de"); }, }, ]; @@ -114,13 +96,13 @@ export class HeaderComponent implements OnInit, OnDestroy { separator: true, }, { - label: this.translateService.instant('header.logout'), + label: this.translateService.instant("header.logout"), command: () => { this.auth.logout().then(() => { - console.log('logout'); + console.log("logout"); }); }, - icon: 'pi pi-sign-out', + icon: "pi pi-sign-out", }, ]; } @@ -128,18 +110,27 @@ export class HeaderComponent implements OnInit, OnDestroy { translate(lang: string) { this.translateService.use(lang); this.translateService - .get('primeng') - .subscribe(res => this.config.setTranslation(res)); + .get("primeng") + .subscribe((res) => this.config.setTranslation(res)); } async loadLang() { - const lang = 'en'; + const lang = "en"; this.setLang(lang); this.translate(lang); } setLang(lang: string) { // this.settings.setSetting(`lang`, lang); - console.log('setLang', lang); + console.log("setLang", lang); + } + + toggleSidebar() { + if (this.sidebarService.visible$.value) { + this.sidebarService.hide(); + return; + } + + this.sidebarService.show(); } } diff --git a/web/src/app/components/home/home.component.html b/web/src/app/components/home/home.component.html index 5f2c53f..e69de29 100644 --- a/web/src/app/components/home/home.component.html +++ b/web/src/app/components/home/home.component.html @@ -1 +0,0 @@ -

home works!

diff --git a/web/src/app/components/home/home.component.spec.ts b/web/src/app/components/home/home.component.spec.ts index 7d04862..bc6a361 100644 --- a/web/src/app/components/home/home.component.spec.ts +++ b/web/src/app/components/home/home.component.spec.ts @@ -1,8 +1,8 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { HomeComponent } from './home.component'; +import { HomeComponent } from "./home.component"; -describe('HomeComponent', () => { +describe("HomeComponent", () => { let component: HomeComponent; let fixture: ComponentFixture; @@ -16,7 +16,7 @@ describe('HomeComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/components/home/home.component.ts b/web/src/app/components/home/home.component.ts index 9c05a54..f9dcd50 100644 --- a/web/src/app/components/home/home.component.ts +++ b/web/src/app/components/home/home.component.ts @@ -1,8 +1,17 @@ -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; +import { AuthService } from "src/app/service/auth.service"; @Component({ - selector: 'app-home', - templateUrl: './home.component.html', - styleUrl: './home.component.scss', + selector: "app-home", + templateUrl: "./home.component.html", + styleUrl: "./home.component.scss", }) -export class HomeComponent {} +export class HomeComponent { + constructor(private auth: AuthService) { + if (this.auth.user$.value !== undefined) { + return; + } + + this.auth.login().then(() => {}); + } +} diff --git a/web/src/app/components/redirect/redirect.component.html b/web/src/app/components/redirect/redirect.component.html new file mode 100644 index 0000000..ce0e301 --- /dev/null +++ b/web/src/app/components/redirect/redirect.component.html @@ -0,0 +1 @@ +

redirect works!

diff --git a/web/src/app/components/redirect/redirect.component.scss b/web/src/app/components/redirect/redirect.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/components/redirect/redirect.component.spec.ts b/web/src/app/components/redirect/redirect.component.spec.ts new file mode 100644 index 0000000..496700d --- /dev/null +++ b/web/src/app/components/redirect/redirect.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { RedirectComponent } from "./redirect.component"; + +describe("RedirectComponent", () => { + let component: RedirectComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [RedirectComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(RedirectComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/components/redirect/redirect.component.ts b/web/src/app/components/redirect/redirect.component.ts new file mode 100644 index 0000000..2575ec7 --- /dev/null +++ b/web/src/app/components/redirect/redirect.component.ts @@ -0,0 +1,47 @@ +import { Component, OnInit } from "@angular/core"; +import { SidebarService } from "src/app/service/sidebar.service"; +import { Router } from "@angular/router"; +import { HttpClient } from "@angular/common/http"; +import { environment } from "src/environments/environment"; +import { ShortUrlDto } from "src/app/model/entities/short-url"; + +@Component({ + selector: "app-redirect", + templateUrl: "./redirect.component.html", + styleUrl: "./redirect.component.scss", +}) +export class RedirectComponent implements OnInit { + constructor( + private sidebar: SidebarService, + private router: Router, + private http: HttpClient, + ) { + this.sidebar.hide(); + } + + ngOnInit() { + this.handleUrl(); + } + + handleUrl() { + let shortUrl = this.router.url; + if (shortUrl === "/") { + return; + } + + if (shortUrl.startsWith("/")) { + shortUrl = shortUrl.substring(1); + } + + this.http + .get(`${environment.api.url}/find/${shortUrl}`) + .subscribe(async (response) => { + if (!response) { + await this.router.navigate(["/404"]); + return; + } + + window.location.href = `${environment.api.url}/redirect/${shortUrl}`; + }); + } +} diff --git a/web/src/app/components/sidebar/sidebar.component.spec.ts b/web/src/app/components/sidebar/sidebar.component.spec.ts index e788098..709b7de 100644 --- a/web/src/app/components/sidebar/sidebar.component.spec.ts +++ b/web/src/app/components/sidebar/sidebar.component.spec.ts @@ -1,17 +1,17 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { SidebarComponent } from './sidebar.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { KeycloakService } from 'keycloak-angular'; +import { SidebarComponent } from "./sidebar.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { KeycloakService } from "keycloak-angular"; -describe('SidebarComponent', () => { +describe("SidebarComponent", () => { let component: SidebarComponent; let fixture: ComponentFixture; @@ -40,7 +40,7 @@ describe('SidebarComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/components/sidebar/sidebar.component.ts b/web/src/app/components/sidebar/sidebar.component.ts index f946d28..b9e46dc 100644 --- a/web/src/app/components/sidebar/sidebar.component.ts +++ b/web/src/app/components/sidebar/sidebar.component.ts @@ -1,29 +1,29 @@ -import {Component, OnDestroy} from '@angular/core'; -import {MenuElement} from 'src/app/model/view/menu-element'; -import {Subject} from 'rxjs'; -import {SidebarService} from 'src/app/service/sidebar.service'; -import {takeUntil} from 'rxjs/operators'; +import { Component, OnDestroy } from "@angular/core"; +import { MenuElement } from "src/app/model/view/menu-element"; +import { Subject } from "rxjs"; +import { SidebarService } from "src/app/service/sidebar.service"; +import { takeUntil } from "rxjs/operators"; @Component({ - selector: 'app-sidebar', - templateUrl: './sidebar.component.html', - styleUrl: './sidebar.component.scss', + selector: "app-sidebar", + templateUrl: "./sidebar.component.html", + styleUrl: "./sidebar.component.scss", }) export class SidebarComponent implements OnDestroy { - elements: MenuElement[] = []; + elements: MenuElement[] = []; - unsubscribe$ = new Subject(); + unsubscribe$ = new Subject(); - constructor(private sidebar: SidebarService) { - this.sidebar.elements$ - .pipe(takeUntil(this.unsubscribe$)) - .subscribe(elements => { - this.elements = elements; - }); - } + constructor(private sidebar: SidebarService) { + this.sidebar.elements$ + .pipe(takeUntil(this.unsubscribe$)) + .subscribe((elements) => { + this.elements = elements; + }); + } - ngOnDestroy() { - this.unsubscribe$.next(); - this.unsubscribe$.complete(); - } + ngOnDestroy() { + this.unsubscribe$.next(); + this.unsubscribe$.complete(); + } } diff --git a/web/src/app/components/spinner/spinner.component.spec.ts b/web/src/app/components/spinner/spinner.component.spec.ts index aa347db..b918e88 100644 --- a/web/src/app/components/spinner/spinner.component.spec.ts +++ b/web/src/app/components/spinner/spinner.component.spec.ts @@ -1,8 +1,8 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { SpinnerComponent } from 'src/app/components/spinner/spinner.component'; +import { SpinnerComponent } from "src/app/components/spinner/spinner.component"; -describe('SpinnerComponent', () => { +describe("SpinnerComponent", () => { let component: SpinnerComponent; let fixture: ComponentFixture; @@ -18,7 +18,7 @@ describe('SpinnerComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/components/spinner/spinner.component.ts b/web/src/app/components/spinner/spinner.component.ts index 0536fcd..b179b80 100644 --- a/web/src/app/components/spinner/spinner.component.ts +++ b/web/src/app/components/spinner/spinner.component.ts @@ -1,16 +1,16 @@ -import { Component } from '@angular/core'; -import { SpinnerService } from 'src/app/service/spinner.service'; +import { Component } from "@angular/core"; +import { SpinnerService } from "src/app/service/spinner.service"; @Component({ - selector: 'app-spinner', - templateUrl: './spinner.component.html', - styleUrls: ['./spinner.component.scss'], + selector: "app-spinner", + templateUrl: "./spinner.component.html", + styleUrls: ["./spinner.component.scss"], }) export class SpinnerComponent { showSpinnerState = false; constructor(public spinnerService: SpinnerService) { - this.spinnerService.showSpinnerState$.subscribe(value => { + this.spinnerService.showSpinnerState$.subscribe((value) => { this.showSpinnerState = value; }); } diff --git a/web/src/app/core/base/form-page-base.ts b/web/src/app/core/base/form-page-base.ts index 3fb3523..ac73c3c 100644 --- a/web/src/app/core/base/form-page-base.ts +++ b/web/src/app/core/base/form-page-base.ts @@ -1,9 +1,9 @@ -import { Directive, inject } from '@angular/core'; -import { PageDataService } from 'src/app/core/base/page.data.service'; -import { SpinnerService } from 'src/app/service/spinner.service'; -import { FilterService } from 'src/app/service/filter.service'; -import { FormGroup } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Directive, inject } from "@angular/core"; +import { PageDataService } from "src/app/core/base/page.data.service"; +import { SpinnerService } from "src/app/service/spinner.service"; +import { FilterService } from "src/app/service/filter.service"; +import { FormGroup } from "@angular/forms"; +import { ActivatedRoute, Router } from "@angular/router"; @Directive() export abstract class FormPageBase< @@ -27,7 +27,7 @@ export abstract class FormPageBase< protected dataService = inject(PageDataService) as S; protected constructor() { - const id = this.route.snapshot.params['id']; + const id = this.route.snapshot.params["id"]; this.validateRoute(id); this.buildForm(); @@ -35,18 +35,18 @@ export abstract class FormPageBase< validateRoute(id: string | undefined) { const url = this.router.url; - if (url.endsWith('create') && id !== undefined) { - throw new Error('Route ends with create but id is defined'); + if (url.endsWith("create") && id !== undefined) { + throw new Error("Route ends with create but id is defined"); } - if (url.endsWith('edit') && (id === undefined || isNaN(+id))) { - throw new Error('Route ends with edit but id is not a number'); + if (url.endsWith("edit") && (id === undefined || isNaN(+id))) { + throw new Error("Route ends with edit but id is not a number"); } this.nodeId = id ? +id : undefined; } close() { - const backRoute = this.nodeId ? '../..' : '..'; + const backRoute = this.nodeId ? "../.." : ".."; this.router.navigate([backRoute], { relativeTo: this.route }).then(() => { this.filterService.onLoad.emit(); diff --git a/web/src/app/core/base/page-base.ts b/web/src/app/core/base/page-base.ts index afc2227..58f70e5 100644 --- a/web/src/app/core/base/page-base.ts +++ b/web/src/app/core/base/page-base.ts @@ -1,21 +1,21 @@ -import { Directive, inject, OnDestroy } from '@angular/core'; -import { PageDataService } from 'src/app/core/base/page.data.service'; -import { Subject } from 'rxjs'; -import { Logger } from 'src/app/service/logger.service'; -import { QueryResult } from 'src/app/model/entities/query-result'; -import { SpinnerService } from 'src/app/service/spinner.service'; -import { FilterService } from 'src/app/service/filter.service'; -import { Filter } from 'src/app/model/graphql/filter/filter.model'; -import { Sort } from 'src/app/model/graphql/filter/sort.model'; -import { takeUntil } from 'rxjs/operators'; -import { PaginatorState } from 'primeng/paginator'; -import { PageColumns } from 'src/app/core/base/page.columns'; +import { Directive, inject, OnDestroy } from "@angular/core"; +import { PageDataService } from "src/app/core/base/page.data.service"; +import { Subject } from "rxjs"; +import { Logger } from "src/app/service/logger.service"; +import { QueryResult } from "src/app/model/entities/query-result"; +import { SpinnerService } from "src/app/service/spinner.service"; +import { FilterService } from "src/app/service/filter.service"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; +import { Sort } from "src/app/model/graphql/filter/sort.model"; +import { takeUntil } from "rxjs/operators"; +import { PaginatorState } from "primeng/paginator"; +import { PageColumns } from "src/app/core/base/page.columns"; import { TableColumn, TableRequireAnyPermissions, -} from 'src/app/modules/shared/components/table/table.model'; +} from "src/app/modules/shared/components/table/table.model"; -const logger = new Logger('PageBase'); +const logger = new Logger("PageBase"); @Directive() export abstract class PageBase< @@ -96,13 +96,13 @@ export abstract class PageBase< } columns: TableColumn[] = - 'get' in this.columnsService ? this.columnsService.get() : []; + "get" in this.columnsService ? this.columnsService.get() : []; protected unsubscribe$ = new Subject(); protected constructor( useQueryParams = false, - permissions?: TableRequireAnyPermissions + permissions?: TableRequireAnyPermissions, ) { this.subscribeToFilterService(); this.filterService.reset({ @@ -113,7 +113,7 @@ export abstract class PageBase< } ngOnDestroy(): void { - logger.trace('Destroy component'); + logger.trace("Destroy component"); this.unsubscribe$.next(); this.unsubscribe$.complete(); } @@ -125,26 +125,26 @@ export abstract class PageBase< this.filterService.filter$ .pipe(takeUntil(this.unsubscribe$)) - .subscribe(filter => { + .subscribe((filter) => { this._filter = filter; }); this.filterService.sort$ .pipe(takeUntil(this.unsubscribe$)) - .subscribe(sort => { + .subscribe((sort) => { this._sort = sort; }); this.filterService.skip$ .pipe(takeUntil(this.unsubscribe$)) - .subscribe(skip => { + .subscribe((skip) => { this._skip = skip; }); this.filterService.take$ .pipe(takeUntil(this.unsubscribe$)) - .subscribe(take => { - if (take && Object.prototype.hasOwnProperty.call(take, 'showAll')) { + .subscribe((take) => { + if (take && Object.prototype.hasOwnProperty.call(take, "showAll")) { this._take = 0; return; } diff --git a/web/src/app/core/base/page.columns.ts b/web/src/app/core/base/page.columns.ts index 29381e5..d193624 100644 --- a/web/src/app/core/base/page.columns.ts +++ b/web/src/app/core/base/page.columns.ts @@ -1,67 +1,67 @@ -import { Injectable } from '@angular/core'; -import { TableColumn } from 'src/app/modules/shared/components/table/table.model'; -import { DbModel } from 'src/app/model/entities/db-model'; +import { Injectable } from "@angular/core"; +import { TableColumn } from "src/app/modules/shared/components/table/table.model"; +import { DbModel } from "src/app/model/entities/db-model"; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export abstract class PageColumns { abstract get(): TableColumn[]; } export const ID_COLUMN = { - name: 'id', - label: 'common.id', - type: 'number', + name: "id", + label: "common.id", + type: "number", filterable: true, value: (row: { id?: number }) => row.id, - class: 'max-w-24', + class: "max-w-24", }; export const NAME_COLUMN = { - name: 'name', - label: 'common.name', - type: 'text', + name: "name", + label: "common.name", + type: "text", filterable: true, value: (row: { name?: string }) => row.name, }; export const DESCRIPTION_COLUMN = { - name: 'description', - label: 'common.description', - type: 'text', + name: "description", + label: "common.description", + type: "text", filterable: true, value: (row: { description?: string }) => row.description, }; export const DELETED_COLUMN = { - name: 'deleted', - label: 'common.deleted', - type: 'bool', + name: "deleted", + label: "common.deleted", + type: "bool", filterable: true, value: (row: DbModel) => row.deleted, }; export const EDITOR_COLUMN = { - name: 'editor', - label: 'common.editor', + name: "editor", + label: "common.editor", value: (row: DbModel) => row.editor?.username, }; export const CREATED_UTC_COLUMN = { - name: 'createdUtc', - label: 'common.created', - type: 'date', + name: "createdUtc", + label: "common.created", + type: "date", value: (row: DbModel) => row.createdUtc, - class: 'max-w-32', + class: "max-w-32", }; export const UPDATED_UTC_COLUMN = { - name: 'updatedUtc', - label: 'common.updated', - type: 'date', + name: "updatedUtc", + label: "common.updated", + type: "date", value: (row: DbModel) => row.updatedUtc, - class: 'max-w-32', + class: "max-w-32", }; export const DB_MODEL_COLUMNS = [ diff --git a/web/src/app/core/base/page.data.service.ts b/web/src/app/core/base/page.data.service.ts index 4fe7180..945d1a5 100644 --- a/web/src/app/core/base/page.data.service.ts +++ b/web/src/app/core/base/page.data.service.ts @@ -1,19 +1,19 @@ -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { MutationResult } from 'apollo-angular'; -import { Filter } from 'src/app/model/graphql/filter/filter.model'; -import { Sort } from 'src/app/model/graphql/filter/sort.model'; -import { QueryResult } from 'src/app/model/entities/query-result'; +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; +import { MutationResult } from "apollo-angular"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; +import { Sort } from "src/app/model/graphql/filter/sort.model"; +import { QueryResult } from "src/app/model/entities/query-result"; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export abstract class PageDataService { abstract load( filter?: Filter[], sort?: Sort[], skip?: number, - take?: number + take?: number, ): Observable>; abstract loadById(id: number): Observable; @@ -29,12 +29,12 @@ export interface Update { export interface Delete { delete( - object: T + object: T, ): Observable | Observable; } export interface Restore { restore( - object: T + object: T, ): Observable | Observable; } diff --git a/web/src/app/core/guard/auth.guard.ts b/web/src/app/core/guard/auth.guard.ts index 909bfcd..a910e5e 100644 --- a/web/src/app/core/guard/auth.guard.ts +++ b/web/src/app/core/guard/auth.guard.ts @@ -1,9 +1,9 @@ -import { Injectable } from '@angular/core'; -import { CanActivate } from '@angular/router'; -import { KeycloakService } from 'keycloak-angular'; +import { Injectable } from "@angular/core"; +import { CanActivate } from "@angular/router"; +import { KeycloakService } from "keycloak-angular"; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class AuthGuard implements CanActivate { constructor(private keycloak: KeycloakService) {} diff --git a/web/src/app/core/guard/permission.guard.ts b/web/src/app/core/guard/permission.guard.ts index fc07bcf..7ce5398 100644 --- a/web/src/app/core/guard/permission.guard.ts +++ b/web/src/app/core/guard/permission.guard.ts @@ -1,24 +1,24 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Logger } from 'src/app/service/logger.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { AuthService } from 'src/app/service/auth.service'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; +import { Injectable } from "@angular/core"; +import { ActivatedRouteSnapshot, Router } from "@angular/router"; +import { Logger } from "src/app/service/logger.service"; +import { ToastService } from "src/app/service/toast.service"; +import { AuthService } from "src/app/service/auth.service"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; -const log = new Logger('PermissionGuard'); +const log = new Logger("PermissionGuard"); @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class PermissionGuard { constructor( private router: Router, private toast: ToastService, - private auth: AuthService + private auth: AuthService, ) {} async canActivate(route: ActivatedRouteSnapshot): Promise { - const permissions = route.data['permissions'] as PermissionsEnum[]; + const permissions = route.data["permissions"] as PermissionsEnum[]; if (!permissions || permissions.length === 0) { return true; @@ -26,11 +26,11 @@ export class PermissionGuard { const validate = await this.auth.hasAnyPermissionLazy(permissions); if (!validate) { - log.debug('Permission denied', permissions); - this.toast.warn('common.warning', 'error.permission_denied'); - this.router.navigate(['/']).then(); + log.debug("Permission denied", permissions); + this.toast.warn("common.warning", "error.permission_denied"); + this.router.navigate(["/"]).then(); } - log.debug('Permission granted', permissions); + log.debug("Permission granted", permissions); return validate; } } diff --git a/web/src/app/core/init-keycloak.ts b/web/src/app/core/init-keycloak.ts index 8d08696..482c9cd 100644 --- a/web/src/app/core/init-keycloak.ts +++ b/web/src/app/core/init-keycloak.ts @@ -1,17 +1,17 @@ -import {KeycloakService} from 'keycloak-angular'; -import {environment} from 'src/environments/environment'; +import { KeycloakService } from "keycloak-angular"; +import { environment } from "src/environments/environment"; export function initializeKeycloak(keycloak: KeycloakService) { - return () => - keycloak.init({ - config: { - url: environment.auth.url, - realm: environment.auth.realm, - clientId: environment.auth.clientId, - }, - initOptions: { - onLoad: 'check-sso', - }, - enableBearerInterceptor: false, - }); + return () => + keycloak.init({ + config: { + url: environment.auth.url, + realm: environment.auth.realm, + clientId: environment.auth.clientId, + }, + initOptions: { + onLoad: "check-sso", + }, + enableBearerInterceptor: false, + }); } diff --git a/web/src/app/core/token.interceptor.ts b/web/src/app/core/token.interceptor.ts index 13c0ead..f927bc4 100644 --- a/web/src/app/core/token.interceptor.ts +++ b/web/src/app/core/token.interceptor.ts @@ -1,7 +1,7 @@ -import { HttpInterceptorFn } from '@angular/common/http'; -import { KeycloakService } from 'keycloak-angular'; -import { inject } from '@angular/core'; -import { from, switchMap } from 'rxjs'; +import { HttpInterceptorFn } from "@angular/common/http"; +import { KeycloakService } from "keycloak-angular"; +import { inject } from "@angular/core"; +import { from, switchMap } from "rxjs"; export const tokenInterceptor: HttpInterceptorFn = (req, next) => { const keycloak = inject(KeycloakService); @@ -15,14 +15,14 @@ export const tokenInterceptor: HttpInterceptorFn = (req, next) => { } return from(keycloak.getToken()).pipe( - switchMap(token => { + switchMap((token) => { const modifiedReq = token ? req.clone({ - headers: req.headers.set('Authorization', `Bearer ${token}`), + headers: req.headers.set("Authorization", `Bearer ${token}`), }) : req; return next(modifiedReq); - }) + }), ); }; diff --git a/web/src/app/model/auth/user.ts b/web/src/app/model/auth/user.ts index 42703d6..363dd48 100644 --- a/web/src/app/model/auth/user.ts +++ b/web/src/app/model/auth/user.ts @@ -1,5 +1,5 @@ -import { Role } from 'src/app/model/entities/role'; -import { DbModel } from 'src/app/model/entities/db-model'; +import { Role } from "src/app/model/entities/role"; +import { DbModel } from "src/app/model/entities/db-model"; export interface NotExistingUser { keycloakId: string; diff --git a/web/src/app/model/config/app-settings.ts b/web/src/app/model/config/app-settings.ts index 627ac9c..8cdedc3 100644 --- a/web/src/app/model/config/app-settings.ts +++ b/web/src/app/model/config/app-settings.ts @@ -1,4 +1,4 @@ -import { Theme } from 'src/app/model/view/theme'; +import { Theme } from "src/app/model/view/theme"; export interface AppSettings { TermsUrl: string; diff --git a/web/src/app/model/entities/api-key.ts b/web/src/app/model/entities/api-key.ts index 3b8c69a..987f5c0 100644 --- a/web/src/app/model/entities/api-key.ts +++ b/web/src/app/model/entities/api-key.ts @@ -1,5 +1,5 @@ -import { DbModel } from 'src/app/model/entities/db-model'; -import { Permission } from 'src/app/model/entities/role'; +import { DbModel } from "src/app/model/entities/db-model"; +import { Permission } from "src/app/model/entities/role"; export interface ApiKey extends DbModel { identifier?: string; diff --git a/web/src/app/model/entities/db-model.ts b/web/src/app/model/entities/db-model.ts index e4aca9d..8d54d41 100644 --- a/web/src/app/model/entities/db-model.ts +++ b/web/src/app/model/entities/db-model.ts @@ -1,4 +1,4 @@ -import { User } from 'src/app/model/auth/user'; +import { User } from "src/app/model/auth/user"; export interface DbModel { id?: number; diff --git a/web/src/app/model/entities/group.ts b/web/src/app/model/entities/group.ts index 23c945c..bb544b9 100644 --- a/web/src/app/model/entities/group.ts +++ b/web/src/app/model/entities/group.ts @@ -2,7 +2,7 @@ import { DbModel } from "src/app/model/entities/db-model"; import { Permission } from "src/app/model/entities/role"; export interface Group extends DbModel { - name?: string; + name: string; } export interface GroupCreateInput { diff --git a/web/src/app/model/entities/news.ts b/web/src/app/model/entities/news.ts index 7796f24..11e0be2 100644 --- a/web/src/app/model/entities/news.ts +++ b/web/src/app/model/entities/news.ts @@ -1,4 +1,4 @@ -import { DbModel } from 'src/app/model/entities/db-model'; +import { DbModel } from "src/app/model/entities/db-model"; export interface News extends DbModel { title: string; diff --git a/web/src/app/model/entities/role.ts b/web/src/app/model/entities/role.ts index 0c565e8..a93b7b1 100644 --- a/web/src/app/model/entities/role.ts +++ b/web/src/app/model/entities/role.ts @@ -1,4 +1,4 @@ -import { DbModel } from 'src/app/model/entities/db-model'; +import { DbModel } from "src/app/model/entities/db-model"; export interface Role extends DbModel { name?: string; diff --git a/web/src/app/model/entities/short-url.ts b/web/src/app/model/entities/short-url.ts index 6eef085..466d881 100644 --- a/web/src/app/model/entities/short-url.ts +++ b/web/src/app/model/entities/short-url.ts @@ -9,6 +9,11 @@ export interface ShortUrl extends DbModel { group?: Group; } +export interface ShortUrlDto { + id: number; + targetUrl: string; +} + export interface ShortUrlCreateInput { shortUrl: string; targetUrl: string; diff --git a/web/src/app/model/graphql/db-model.query.ts b/web/src/app/model/graphql/db-model.query.ts index 606c20b..78d9bb3 100644 --- a/web/src/app/model/graphql/db-model.query.ts +++ b/web/src/app/model/graphql/db-model.query.ts @@ -1,5 +1,5 @@ -import { gql } from 'apollo-angular'; -import { EDITOR_FRAGMENT } from 'src/app/model/graphql/editor.query'; +import { gql } from "apollo-angular"; +import { EDITOR_FRAGMENT } from "src/app/model/graphql/editor.query"; export const DB_MODEL_FRAGMENT = gql` fragment DB_MODEL on DbModel { diff --git a/web/src/app/model/graphql/editor.query.ts b/web/src/app/model/graphql/editor.query.ts index 93abf1f..384b17d 100644 --- a/web/src/app/model/graphql/editor.query.ts +++ b/web/src/app/model/graphql/editor.query.ts @@ -1,4 +1,4 @@ -import { gql } from 'apollo-angular'; +import { gql } from "apollo-angular"; export const EDITOR_FRAGMENT = gql` fragment EDITOR on User { diff --git a/web/src/app/model/graphql/filter/sort.model.ts b/web/src/app/model/graphql/filter/sort.model.ts index c0e10bb..122f199 100644 --- a/web/src/app/model/graphql/filter/sort.model.ts +++ b/web/src/app/model/graphql/filter/sort.model.ts @@ -2,6 +2,6 @@ export type Sort = { [key: string]: any }; export enum SortOrder { - ASC = 'ASC', - DESC = 'DESC', + ASC = "ASC", + DESC = "DESC", } diff --git a/web/src/app/model/view/themes.enum.ts b/web/src/app/model/view/themes.enum.ts index 0cfb5a7..ccacc43 100644 --- a/web/src/app/model/view/themes.enum.ts +++ b/web/src/app/model/view/themes.enum.ts @@ -1,3 +1,3 @@ export enum Themes { - Default = 'maxlan-dark-theme', + Default = "maxlan-dark-theme", } diff --git a/web/src/app/modules/admin/administration/administration.module.ts b/web/src/app/modules/admin/administration/administration.module.ts index ede0054..8201495 100644 --- a/web/src/app/modules/admin/administration/administration.module.ts +++ b/web/src/app/modules/admin/administration/administration.module.ts @@ -1,38 +1,38 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { RouterModule, Routes } from '@angular/router'; -import { PermissionGuard } from 'src/app/core/guard/permission.guard'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { RouterModule, Routes } from "@angular/router"; +import { PermissionGuard } from "src/app/core/guard/permission.guard"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { SharedModule } from "src/app/modules/shared/shared.module"; const routes: Routes = [ { - path: 'users', - title: 'Users | Maxlan', + path: "users", + title: "Users | Maxlan", loadChildren: () => - import('src/app/modules/admin/administration/users/users.module').then( - m => m.UsersModule + import("src/app/modules/admin/administration/users/users.module").then( + (m) => m.UsersModule, ), canActivate: [PermissionGuard], data: { permissions: [PermissionsEnum.users] }, }, { - path: 'roles', - title: 'Roles | Maxlan', + path: "roles", + title: "Roles | Maxlan", loadChildren: () => - import('src/app/modules/admin/administration/roles/roles.module').then( - m => m.RolesModule + import("src/app/modules/admin/administration/roles/roles.module").then( + (m) => m.RolesModule, ), canActivate: [PermissionGuard], data: { permissions: [PermissionsEnum.roles] }, }, { - path: 'api-keys', - title: 'API Key | Maxlan', + path: "api-keys", + title: "API Key | Maxlan", loadChildren: () => import( - 'src/app/modules/admin/administration/api-keys/api-keys.module' - ).then(m => m.ApiKeysModule), + "src/app/modules/admin/administration/api-keys/api-keys.module" + ).then((m) => m.ApiKeysModule), canActivate: [PermissionGuard], data: { permissions: [PermissionsEnum.apiKeys] }, }, diff --git a/web/src/app/modules/admin/administration/api-keys/api-keys.columns.ts b/web/src/app/modules/admin/administration/api-keys/api-keys.columns.ts index 3dbab7a..f08227e 100644 --- a/web/src/app/modules/admin/administration/api-keys/api-keys.columns.ts +++ b/web/src/app/modules/admin/administration/api-keys/api-keys.columns.ts @@ -1,11 +1,11 @@ -import { Injectable, Provider } from '@angular/core'; +import { Injectable, Provider } from "@angular/core"; import { DB_MODEL_COLUMNS, ID_COLUMN, PageColumns, -} from 'src/app/core/base/page.columns'; -import { TableColumn } from 'src/app/modules/shared/components/table/table.model'; -import { ApiKey } from 'src/app/model/entities/api-key'; +} from "src/app/core/base/page.columns"; +import { TableColumn } from "src/app/modules/shared/components/table/table.model"; +import { ApiKey } from "src/app/model/entities/api-key"; @Injectable() export class ApiKeysColumns extends PageColumns { @@ -13,16 +13,16 @@ export class ApiKeysColumns extends PageColumns { return [ ID_COLUMN, { - name: 'identifier', - label: 'common.identifier', - type: 'text', + name: "identifier", + label: "common.identifier", + type: "text", filterable: true, value: (row: ApiKey) => row.identifier, }, { - name: 'key', - label: 'common.key', - type: 'password', + name: "key", + label: "common.key", + type: "password", value: (row: ApiKey) => row.key, }, ...DB_MODEL_COLUMNS, diff --git a/web/src/app/modules/admin/administration/api-keys/api-keys.data.service.ts b/web/src/app/modules/admin/administration/api-keys/api-keys.data.service.ts index 830e7f5..30d1d0a 100644 --- a/web/src/app/modules/admin/administration/api-keys/api-keys.data.service.ts +++ b/web/src/app/modules/admin/administration/api-keys/api-keys.data.service.ts @@ -1,25 +1,25 @@ -import { Injectable, Provider } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Injectable, Provider } from "@angular/core"; +import { Observable } from "rxjs"; import { Create, Delete, PageDataService, Restore, Update, -} from 'src/app/core/base/page.data.service'; -import { Permission } from 'src/app/model/entities/role'; -import { Filter } from 'src/app/model/graphql/filter/filter.model'; -import { Sort } from 'src/app/model/graphql/filter/sort.model'; -import { Apollo, gql } from 'apollo-angular'; -import { QueryResult } from 'src/app/model/entities/query-result'; -import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query'; -import { catchError, map } from 'rxjs/operators'; -import { SpinnerService } from 'src/app/service/spinner.service'; +} from "src/app/core/base/page.data.service"; +import { Permission } from "src/app/model/entities/role"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; +import { Sort } from "src/app/model/graphql/filter/sort.model"; +import { Apollo, gql } from "apollo-angular"; +import { QueryResult } from "src/app/model/entities/query-result"; +import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query"; +import { catchError, map } from "rxjs/operators"; +import { SpinnerService } from "src/app/service/spinner.service"; import { ApiKey, ApiKeyCreateInput, ApiKeyUpdateInput, -} from 'src/app/model/entities/api-key'; +} from "src/app/model/entities/api-key"; @Injectable() export class ApiKeysDataService @@ -32,7 +32,7 @@ export class ApiKeysDataService { constructor( private spinner: SpinnerService, - private apollo: Apollo + private apollo: Apollo, ) { super(); } @@ -41,7 +41,7 @@ export class ApiKeysDataService filter?: Filter[] | undefined, sort?: Sort[] | undefined, skip?: number | undefined, - take?: number | undefined + take?: number | undefined, ): Observable> { return this.apollo .query<{ apiKeys: QueryResult }>({ @@ -78,12 +78,12 @@ export class ApiKeysDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.apiKeys)); + .pipe(map((result) => result.data.apiKeys)); } loadById(id: number): Observable { @@ -109,12 +109,12 @@ export class ApiKeysDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.apiKeys.nodes[0])); + .pipe(map((result) => result.data.apiKeys.nodes[0])); } create(object: ApiKeyCreateInput): Observable { @@ -140,17 +140,17 @@ export class ApiKeysDataService variables: { input: { identifier: object.identifier, - permissions: object.permissions?.map(x => x.id), + permissions: object.permissions?.map((x) => x.id), }, }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.apiKey.create)); + .pipe(map((result) => result.data?.apiKey.create)); } update(object: ApiKeyUpdateInput): Observable { @@ -177,17 +177,17 @@ export class ApiKeysDataService input: { id: object.id, identifier: object.identifier, - permissions: object.permissions?.map(x => x.id), + permissions: object.permissions?.map((x) => x.id), }, }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.apiKey.update)); + .pipe(map((result) => result.data?.apiKey.update)); } delete(object: ApiKey): Observable { @@ -205,12 +205,12 @@ export class ApiKeysDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.apiKey.delete ?? false)); + .pipe(map((result) => result.data?.apiKey.delete ?? false)); } restore(object: ApiKey): Observable { @@ -228,12 +228,12 @@ export class ApiKeysDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.apiKey.restore ?? false)); + .pipe(map((result) => result.data?.apiKey.restore ?? false)); } getAllPermissions(): Observable { @@ -251,12 +251,12 @@ export class ApiKeysDataService `, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.permissions.nodes)); + .pipe(map((result) => result.data.permissions.nodes)); } static provide(): Provider[] { diff --git a/web/src/app/modules/admin/administration/api-keys/api-keys.module.ts b/web/src/app/modules/admin/administration/api-keys/api-keys.module.ts index 93a2678..268b1c7 100644 --- a/web/src/app/modules/admin/administration/api-keys/api-keys.module.ts +++ b/web/src/app/modules/admin/administration/api-keys/api-keys.module.ts @@ -1,22 +1,22 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { RouterModule, Routes } from '@angular/router'; -import { PermissionGuard } from 'src/app/core/guard/permission.guard'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; -import { ApiKeyFormPageComponent } from 'src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component'; -import { ApiKeysPage } from 'src/app/modules/admin/administration/api-keys/api-keys.page'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; -import { ApiKeysColumns } from 'src/app/modules/admin/administration/api-keys/api-keys.columns'; +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { RouterModule, Routes } from "@angular/router"; +import { PermissionGuard } from "src/app/core/guard/permission.guard"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { ApiKeyFormPageComponent } from "src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component"; +import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; +import { ApiKeysColumns } from "src/app/modules/admin/administration/api-keys/api-keys.columns"; const routes: Routes = [ { - path: '', - title: 'Admin - ApiKeys | Maxlan', + path: "", + title: "Admin - ApiKeys | Maxlan", component: ApiKeysPage, children: [ { - path: 'create', + path: "create", component: ApiKeyFormPageComponent, canActivate: [PermissionGuard], data: { @@ -24,7 +24,7 @@ const routes: Routes = [ }, }, { - path: 'edit/:id', + path: "edit/:id", component: ApiKeyFormPageComponent, canActivate: [PermissionGuard], data: { diff --git a/web/src/app/modules/admin/administration/api-keys/api-keys.page.spec.ts b/web/src/app/modules/admin/administration/api-keys/api-keys.page.spec.ts index 98eb39d..f1ba746 100644 --- a/web/src/app/modules/admin/administration/api-keys/api-keys.page.spec.ts +++ b/web/src/app/modules/admin/administration/api-keys/api-keys.page.spec.ts @@ -1,18 +1,18 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ApiKeysPage } from 'src/app/modules/admin/administration/api-keys/api-keys.page'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { PageDataService } from 'src/app/core/base/page.data.service'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { PageDataService } from "src/app/core/base/page.data.service"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; -describe('ApiKeysComponent', () => { +describe("ApiKeysComponent", () => { let component: ApiKeysPage; let fixture: ComponentFixture; @@ -45,7 +45,7 @@ describe('ApiKeysComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/administration/api-keys/api-keys.page.ts b/web/src/app/modules/admin/administration/api-keys/api-keys.page.ts index 15562b0..d946dfc 100644 --- a/web/src/app/modules/admin/administration/api-keys/api-keys.page.ts +++ b/web/src/app/modules/admin/administration/api-keys/api-keys.page.ts @@ -1,16 +1,16 @@ -import { Component } from '@angular/core'; -import { PageBase } from 'src/app/core/base/page-base'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; -import { ApiKey } from 'src/app/model/entities/api-key'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; -import { ApiKeysColumns } from 'src/app/modules/admin/administration/api-keys/api-keys.columns'; +import { Component } from "@angular/core"; +import { PageBase } from "src/app/core/base/page-base"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.service"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { ApiKey } from "src/app/model/entities/api-key"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; +import { ApiKeysColumns } from "src/app/modules/admin/administration/api-keys/api-keys.columns"; @Component({ - selector: 'app-api-keys', - templateUrl: './api-keys.page.html', - styleUrl: './api-keys.page.scss', + selector: "app-api-keys", + templateUrl: "./api-keys.page.html", + styleUrl: "./api-keys.page.scss", }) export class ApiKeysPage extends PageBase< ApiKey, @@ -19,7 +19,7 @@ export class ApiKeysPage extends PageBase< > { constructor( private toast: ToastService, - private confirmation: ConfirmationDialogService + private confirmation: ConfirmationDialogService, ) { super(true, { read: [PermissionsEnum.apiKeys], @@ -34,7 +34,7 @@ export class ApiKeysPage extends PageBase< this.loading = true; this.dataService .load(this.filter, this.sort, this.skip, this.take) - .subscribe(result => { + .subscribe((result) => { this.result = result; this.loading = false; }); @@ -42,12 +42,12 @@ export class ApiKeysPage extends PageBase< delete(apiKey: ApiKey): void { this.confirmation.confirmDialog({ - header: 'dialog.delete.header', - message: 'dialog.delete.message', + header: "dialog.delete.header", + message: "dialog.delete.message", accept: () => { this.loading = true; this.dataService.delete(apiKey).subscribe(() => { - this.toast.success('action.deleted'); + this.toast.success("action.deleted"); this.load(); }); }, @@ -57,12 +57,12 @@ export class ApiKeysPage extends PageBase< restore(apiKey: ApiKey): void { this.confirmation.confirmDialog({ - header: 'dialog.restore.header', - message: 'dialog.restore.message', + header: "dialog.restore.header", + message: "dialog.restore.message", accept: () => { this.loading = true; this.dataService.restore(apiKey).subscribe(() => { - this.toast.success('action.restored'); + this.toast.success("action.restored"); this.load(); }); }, diff --git a/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.spec.ts b/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.spec.ts index a4a6c84..d7bfe12 100644 --- a/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.spec.ts +++ b/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.spec.ts @@ -1,17 +1,17 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RoleFormPageComponent } from "src/app/modules/admin/administration/roles/form-page/role-form-page.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -describe('ApiKeyFormpageComponent', () => { +describe("ApiKeyFormpageComponent", () => { let component: RoleFormPageComponent; let fixture: ComponentFixture; @@ -44,7 +44,7 @@ describe('ApiKeyFormpageComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.ts b/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.ts index e28e2ec..3bd327a 100644 --- a/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.ts +++ b/web/src/app/modules/admin/administration/api-keys/form-page/api-key-form-page.component.ts @@ -1,21 +1,21 @@ -import { Component } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; -import { InputSwitchChangeEvent } from 'primeng/inputswitch'; -import { ToastService } from 'src/app/service/toast.service'; -import { firstValueFrom } from 'rxjs'; -import { FormPageBase } from 'src/app/core/base/form-page-base'; -import { Permission } from 'src/app/model/entities/role'; +import { Component } from "@angular/core"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; +import { InputSwitchChangeEvent } from "primeng/inputswitch"; +import { ToastService } from "src/app/service/toast.service"; +import { firstValueFrom } from "rxjs"; +import { FormPageBase } from "src/app/core/base/form-page-base"; +import { Permission } from "src/app/model/entities/role"; import { ApiKey, ApiKeyCreateInput, ApiKeyUpdateInput, -} from 'src/app/model/entities/api-key'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; +} from "src/app/model/entities/api-key"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; @Component({ - selector: 'app-api-key-form-page', - templateUrl: './api-key-form-page.component.html', - styleUrl: './api-key-form-page.component.scss', + selector: "app-api-key-form-page", + templateUrl: "./api-key-form-page.component.html", + styleUrl: "./api-key-form-page.component.scss", }) export class ApiKeyFormPageComponent extends FormPageBase< ApiKey, @@ -38,7 +38,7 @@ export class ApiKeyFormPageComponent extends FormPageBase< this.dataService .load([{ id: { equal: this.nodeId } }]) - .subscribe(apiKey => { + .subscribe((apiKey) => { this.node = apiKey.nodes[0]; this.setForm(this.node); }); @@ -47,12 +47,12 @@ export class ApiKeyFormPageComponent extends FormPageBase< async initializePermissions() { const permissions = await firstValueFrom( - this.dataService.getAllPermissions() + this.dataService.getAllPermissions(), ); this.allPermissions = permissions; this.permissionGroups = permissions.reduce( (acc, p) => { - const group = p.name.includes('.') ? p.name.split('.')[0] : p.name; + const group = p.name.includes(".") ? p.name.split(".")[0] : p.name; if (!acc[group]) { acc[group] = []; @@ -60,10 +60,10 @@ export class ApiKeyFormPageComponent extends FormPageBase< acc[group].push(p); return acc; }, - {} as { [key: string]: Permission[] } + {} as { [key: string]: Permission[] }, ); - permissions.forEach(p => { + permissions.forEach((p) => { this.form.addControl(p.name, new FormControl(false)); }); } @@ -77,48 +77,48 @@ export class ApiKeyFormPageComponent extends FormPageBase< id: new FormControl(undefined), identifier: new FormControl( undefined, - Validators.required + Validators.required, ), }); - this.form.controls['id'].disable(); + this.form.controls["id"].disable(); } setForm(node?: ApiKey) { - this.form.controls['id'].setValue(node?.id); - this.form.controls['identifier'].setValue(node?.identifier); + this.form.controls["id"].setValue(node?.id); + this.form.controls["identifier"].setValue(node?.identifier); if (!node) return; const permissions = node.permissions ?? []; - permissions.forEach(p => { + permissions.forEach((p) => { this.form.controls[p.name].setValue(true); }); } getCreateInput(): ApiKeyCreateInput { return { - identifier: this.form.controls['identifier'].pristine + identifier: this.form.controls["identifier"].pristine ? undefined - : (this.form.controls['identifier'].value ?? undefined), + : (this.form.controls["identifier"].value ?? undefined), permissions: this.allPermissions.filter( - p => this.form.controls[p.name].value + (p) => this.form.controls[p.name].value, ), }; } getUpdateInput(): ApiKeyUpdateInput { if (!this.node?.id) { - throw new Error('Node id is missing'); + throw new Error("Node id is missing"); } return { - id: this.form.controls['id'].value, - identifier: this.form.controls['identifier'].pristine + id: this.form.controls["id"].value, + identifier: this.form.controls["identifier"].pristine ? undefined - : (this.form.controls['identifier'].value ?? undefined), + : (this.form.controls["identifier"].value ?? undefined), permissions: this.allPermissions.filter( - p => this.form.controls[p.name].value + (p) => this.form.controls[p.name].value, ), }; } @@ -126,7 +126,7 @@ export class ApiKeyFormPageComponent extends FormPageBase< create(apiKey: ApiKeyCreateInput): void { this.dataService.create(apiKey).subscribe(() => { this.spinner.hide(); - this.toast.success('action.created'); + this.toast.success("action.created"); this.close(); }); } @@ -134,20 +134,20 @@ export class ApiKeyFormPageComponent extends FormPageBase< update(apiKey: ApiKeyUpdateInput): void { this.dataService.update(apiKey).subscribe(() => { this.spinner.hide(); - this.toast.success('action.created'); + this.toast.success("action.created"); this.close(); }); } toggleGroup(event: InputSwitchChangeEvent, group: string) { - this.permissionGroups[group].forEach(p => { + this.permissionGroups[group].forEach((p) => { this.form.controls[p.name].setValue(event.checked); }); } isGroupChecked(group: string) { return this.permissionGroups[group].every( - p => this.form.controls[p.name].value + (p) => this.form.controls[p.name].value, ); } diff --git a/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.spec.ts b/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.spec.ts index e83d513..12380a9 100644 --- a/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.spec.ts +++ b/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.spec.ts @@ -1,17 +1,17 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RoleFormPageComponent } from "src/app/modules/admin/administration/roles/form-page/role-form-page.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -describe('RoleFormpageComponent', () => { +describe("RoleFormpageComponent", () => { let component: RoleFormPageComponent; let fixture: ComponentFixture; @@ -44,7 +44,7 @@ describe('RoleFormpageComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.ts b/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.ts index 06618e1..4d950f4 100644 --- a/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.ts +++ b/web/src/app/modules/admin/administration/roles/form-page/role-form-page.component.ts @@ -1,21 +1,21 @@ -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; import { Permission, Role, RoleCreateInput, RoleUpdateInput, -} from 'src/app/model/entities/role'; -import { InputSwitchChangeEvent } from 'primeng/inputswitch'; -import { ToastService } from 'src/app/service/toast.service'; -import { firstValueFrom } from 'rxjs'; -import { FormPageBase } from 'src/app/core/base/form-page-base'; -import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; +} from "src/app/model/entities/role"; +import { InputSwitchChangeEvent } from "primeng/inputswitch"; +import { ToastService } from "src/app/service/toast.service"; +import { firstValueFrom } from "rxjs"; +import { FormPageBase } from "src/app/core/base/form-page-base"; +import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; @Component({ - selector: 'app-role-form-page', - templateUrl: './role-form-page.component.html', - styleUrl: './role-form-page.component.scss', + selector: "app-role-form-page", + templateUrl: "./role-form-page.component.html", + styleUrl: "./role-form-page.component.scss", }) export class RoleFormPageComponent extends FormPageBase< Role, @@ -36,7 +36,7 @@ export class RoleFormPageComponent extends FormPageBase< return; } - this.dataService.loadById(this.nodeId).subscribe(role => { + this.dataService.loadById(this.nodeId).subscribe((role) => { this.node = role; this.setForm(this.node); }); @@ -45,12 +45,12 @@ export class RoleFormPageComponent extends FormPageBase< async initializePermissions() { const permissions = await firstValueFrom( - this.dataService.getAllPermissions() + this.dataService.getAllPermissions(), ); this.allPermissions = permissions; this.permissionGroups = permissions.reduce( (acc, p) => { - const group = p.name.includes('.') ? p.name.split('.')[0] : p.name; + const group = p.name.includes(".") ? p.name.split(".")[0] : p.name; if (!acc[group]) { acc[group] = []; @@ -58,10 +58,10 @@ export class RoleFormPageComponent extends FormPageBase< acc[group].push(p); return acc; }, - {} as { [key: string]: Permission[] } + {} as { [key: string]: Permission[] }, ); - permissions.forEach(p => { + permissions.forEach((p) => { this.form.addControl(p.name, new FormControl(false)); }); } @@ -76,48 +76,48 @@ export class RoleFormPageComponent extends FormPageBase< name: new FormControl(undefined, Validators.required), description: new FormControl(undefined), }); - this.form.controls['id'].disable(); + this.form.controls["id"].disable(); } setForm(node?: Role) { - this.form.controls['id'].setValue(node?.id); - this.form.controls['name'].setValue(node?.name); - this.form.controls['description'].setValue(node?.description); + this.form.controls["id"].setValue(node?.id); + this.form.controls["name"].setValue(node?.name); + this.form.controls["description"].setValue(node?.description); if (!node) return; const permissions = node.permissions ?? []; - permissions.forEach(p => { + permissions.forEach((p) => { this.form.controls[p.name].setValue(true); }); } getCreateInput(): RoleCreateInput { return { - name: this.form.controls['name'].pristine + name: this.form.controls["name"].pristine ? undefined - : (this.form.controls['name'].value ?? undefined), - description: this.form.controls['description'].pristine + : (this.form.controls["name"].value ?? undefined), + description: this.form.controls["description"].pristine ? undefined - : (this.form.controls['description'].value ?? undefined), + : (this.form.controls["description"].value ?? undefined), permissions: this.allPermissions.filter( - p => this.form.controls[p.name].value + (p) => this.form.controls[p.name].value, ), }; } getUpdateInput(): RoleUpdateInput { return { - id: this.form.controls['id'].value, - name: this.form.controls['name'].pristine + id: this.form.controls["id"].value, + name: this.form.controls["name"].pristine ? undefined - : this.form.controls['name'].value, - description: this.form.controls['description'].pristine + : this.form.controls["name"].value, + description: this.form.controls["description"].pristine ? undefined - : this.form.controls['description'].value, + : this.form.controls["description"].value, permissions: this.allPermissions.filter( - p => this.form.controls[p.name].value + (p) => this.form.controls[p.name].value, ), }; } @@ -125,7 +125,7 @@ export class RoleFormPageComponent extends FormPageBase< create(role: RoleCreateInput): void { this.dataService.create(role).subscribe(() => { this.spinner.hide(); - this.toast.success('action.created'); + this.toast.success("action.created"); this.close(); }); } @@ -133,20 +133,20 @@ export class RoleFormPageComponent extends FormPageBase< update(role: RoleUpdateInput): void { this.dataService.update(role).subscribe(() => { this.spinner.hide(); - this.toast.success('action.created'); + this.toast.success("action.created"); this.close(); }); } toggleGroup(event: InputSwitchChangeEvent, group: string) { - this.permissionGroups[group].forEach(p => { + this.permissionGroups[group].forEach((p) => { this.form.controls[p.name].setValue(event.checked); }); } isGroupChecked(group: string) { return this.permissionGroups[group].every( - p => this.form.controls[p.name].value + (p) => this.form.controls[p.name].value, ); } diff --git a/web/src/app/modules/admin/administration/roles/roles.columns.ts b/web/src/app/modules/admin/administration/roles/roles.columns.ts index f9b9e51..e59dd7b 100644 --- a/web/src/app/modules/admin/administration/roles/roles.columns.ts +++ b/web/src/app/modules/admin/administration/roles/roles.columns.ts @@ -1,13 +1,13 @@ -import { Injectable, Provider } from '@angular/core'; -import { Role } from 'src/app/model/entities/role'; +import { Injectable, Provider } from "@angular/core"; +import { Role } from "src/app/model/entities/role"; import { DB_MODEL_COLUMNS, DESCRIPTION_COLUMN, ID_COLUMN, NAME_COLUMN, PageColumns, -} from 'src/app/core/base/page.columns'; -import { TableColumn } from 'src/app/modules/shared/components/table/table.model'; +} from "src/app/core/base/page.columns"; +import { TableColumn } from "src/app/modules/shared/components/table/table.model"; @Injectable() export class RolesColumns extends PageColumns { diff --git a/web/src/app/modules/admin/administration/roles/roles.data.service.ts b/web/src/app/modules/admin/administration/roles/roles.data.service.ts index 41050f0..909d969 100644 --- a/web/src/app/modules/admin/administration/roles/roles.data.service.ts +++ b/web/src/app/modules/admin/administration/roles/roles.data.service.ts @@ -1,25 +1,25 @@ -import { Injectable, Provider } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Injectable, Provider } from "@angular/core"; +import { Observable } from "rxjs"; import { Create, Delete, PageDataService, Restore, Update, -} from 'src/app/core/base/page.data.service'; +} from "src/app/core/base/page.data.service"; import { Permission, Role, RoleCreateInput, RoleUpdateInput, -} from 'src/app/model/entities/role'; -import { Filter } from 'src/app/model/graphql/filter/filter.model'; -import { Sort } from 'src/app/model/graphql/filter/sort.model'; -import { Apollo, gql } from 'apollo-angular'; -import { QueryResult } from 'src/app/model/entities/query-result'; -import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query'; -import { catchError, map } from 'rxjs/operators'; -import { SpinnerService } from 'src/app/service/spinner.service'; +} from "src/app/model/entities/role"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; +import { Sort } from "src/app/model/graphql/filter/sort.model"; +import { Apollo, gql } from "apollo-angular"; +import { QueryResult } from "src/app/model/entities/query-result"; +import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query"; +import { catchError, map } from "rxjs/operators"; +import { SpinnerService } from "src/app/service/spinner.service"; @Injectable() export class RolesDataService @@ -32,7 +32,7 @@ export class RolesDataService { constructor( private spinner: SpinnerService, - private apollo: Apollo + private apollo: Apollo, ) { super(); } @@ -41,7 +41,7 @@ export class RolesDataService filter?: Filter[] | undefined, sort?: Sort[] | undefined, skip?: number | undefined, - take?: number | undefined + take?: number | undefined, ): Observable> { return this.apollo .query<{ roles: QueryResult }>({ @@ -75,12 +75,12 @@ export class RolesDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.roles)); + .pipe(map((result) => result.data.roles)); } loadById(id: number): Observable { @@ -112,12 +112,12 @@ export class RolesDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.roles.nodes[0])); + .pipe(map((result) => result.data.roles.nodes[0])); } create(object: RoleCreateInput): Observable { @@ -145,17 +145,17 @@ export class RolesDataService input: { name: object.name, description: object.description, - permissions: object.permissions?.map(x => x.id), + permissions: object.permissions?.map((x) => x.id), }, }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.role.create)); + .pipe(map((result) => result.data?.role.create)); } update(object: RoleUpdateInput): Observable { @@ -184,17 +184,17 @@ export class RolesDataService id: object.id, name: object.name, description: object.description, - permissions: object.permissions?.map(x => x.id), + permissions: object.permissions?.map((x) => x.id), }, }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.role.update)); + .pipe(map((result) => result.data?.role.update)); } delete(object: Role): Observable { @@ -212,12 +212,12 @@ export class RolesDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.role.delete ?? false)); + .pipe(map((result) => result.data?.role.delete ?? false)); } restore(object: Role): Observable { @@ -235,12 +235,12 @@ export class RolesDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.role.restore ?? false)); + .pipe(map((result) => result.data?.role.restore ?? false)); } getAllPermissions(): Observable { @@ -258,12 +258,12 @@ export class RolesDataService `, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.permissions.nodes)); + .pipe(map((result) => result.data.permissions.nodes)); } static provide(): Provider[] { diff --git a/web/src/app/modules/admin/administration/roles/roles.module.ts b/web/src/app/modules/admin/administration/roles/roles.module.ts index 7eebe04..87cf3cb 100644 --- a/web/src/app/modules/admin/administration/roles/roles.module.ts +++ b/web/src/app/modules/admin/administration/roles/roles.module.ts @@ -1,22 +1,22 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { RouterModule, Routes } from '@angular/router'; -import { PermissionGuard } from 'src/app/core/guard/permission.guard'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; -import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component'; -import { RolesPage } from 'src/app/modules/admin/administration/roles/roles.page'; -import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service'; -import { RolesColumns } from 'src/app/modules/admin/administration/roles/roles.columns'; +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { RouterModule, Routes } from "@angular/router"; +import { PermissionGuard } from "src/app/core/guard/permission.guard"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { RoleFormPageComponent } from "src/app/modules/admin/administration/roles/form-page/role-form-page.component"; +import { RolesPage } from "src/app/modules/admin/administration/roles/roles.page"; +import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service"; +import { RolesColumns } from "src/app/modules/admin/administration/roles/roles.columns"; const routes: Routes = [ { - path: '', - title: 'Admin - Roles | Maxlan', + path: "", + title: "Admin - Roles | Maxlan", component: RolesPage, children: [ { - path: 'create', + path: "create", component: RoleFormPageComponent, canActivate: [PermissionGuard], data: { @@ -24,7 +24,7 @@ const routes: Routes = [ }, }, { - path: 'edit/:id', + path: "edit/:id", component: RoleFormPageComponent, canActivate: [PermissionGuard], data: { diff --git a/web/src/app/modules/admin/administration/roles/roles.page.spec.ts b/web/src/app/modules/admin/administration/roles/roles.page.spec.ts index 86eaa98..b818f66 100644 --- a/web/src/app/modules/admin/administration/roles/roles.page.spec.ts +++ b/web/src/app/modules/admin/administration/roles/roles.page.spec.ts @@ -1,18 +1,18 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RolesPage } from 'src/app/modules/admin/administration/roles/roles.page'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { PageDataService } from 'src/app/core/base/page.data.service'; -import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RolesPage } from "src/app/modules/admin/administration/roles/roles.page"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { PageDataService } from "src/app/core/base/page.data.service"; +import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service"; -describe('RolesComponent', () => { +describe("RolesComponent", () => { let component: RolesPage; let fixture: ComponentFixture; @@ -45,7 +45,7 @@ describe('RolesComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/administration/roles/roles.page.ts b/web/src/app/modules/admin/administration/roles/roles.page.ts index 6e1384f..afe28b8 100644 --- a/web/src/app/modules/admin/administration/roles/roles.page.ts +++ b/web/src/app/modules/admin/administration/roles/roles.page.ts @@ -1,21 +1,21 @@ -import { Component } from '@angular/core'; -import { PageBase } from 'src/app/core/base/page-base'; -import { Role } from 'src/app/model/entities/role'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; -import { RolesColumns } from 'src/app/modules/admin/administration/roles/roles.columns'; -import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service'; +import { Component } from "@angular/core"; +import { PageBase } from "src/app/core/base/page-base"; +import { Role } from "src/app/model/entities/role"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.service"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { RolesColumns } from "src/app/modules/admin/administration/roles/roles.columns"; +import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service"; @Component({ - selector: 'app-roles', - templateUrl: './roles.page.html', - styleUrl: './roles.page.scss', + selector: "app-roles", + templateUrl: "./roles.page.html", + styleUrl: "./roles.page.scss", }) export class RolesPage extends PageBase { constructor( private toast: ToastService, - private confirmation: ConfirmationDialogService + private confirmation: ConfirmationDialogService, ) { super(true, { read: [PermissionsEnum.roles], @@ -30,7 +30,7 @@ export class RolesPage extends PageBase { this.loading = true; this.dataService .load(this.filter, this.sort, this.skip, this.take) - .subscribe(result => { + .subscribe((result) => { this.result = result; this.loading = false; }); @@ -38,12 +38,12 @@ export class RolesPage extends PageBase { delete(role: Role): void { this.confirmation.confirmDialog({ - header: 'dialog.delete.header', - message: 'dialog.delete.message', + header: "dialog.delete.header", + message: "dialog.delete.message", accept: () => { this.loading = true; this.dataService.delete(role).subscribe(() => { - this.toast.success('action.deleted'); + this.toast.success("action.deleted"); this.load(); }); }, @@ -53,12 +53,12 @@ export class RolesPage extends PageBase { restore(role: Role): void { this.confirmation.confirmDialog({ - header: 'dialog.restore.header', - message: 'dialog.restore.message', + header: "dialog.restore.header", + message: "dialog.restore.message", accept: () => { this.loading = true; this.dataService.restore(role).subscribe(() => { - this.toast.success('action.restored'); + this.toast.success("action.restored"); this.load(); }); }, diff --git a/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.spec.ts b/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.spec.ts index c1d221d..cd7ab63 100644 --- a/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.spec.ts +++ b/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.spec.ts @@ -1,17 +1,17 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RoleFormPageComponent } from "src/app/modules/admin/administration/roles/form-page/role-form-page.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -describe('UserFormpageComponent', () => { +describe("UserFormpageComponent", () => { let component: RoleFormPageComponent; let fixture: ComponentFixture; @@ -44,7 +44,7 @@ describe('UserFormpageComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.ts b/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.ts index e7bf22a..ba8a1b2 100644 --- a/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.ts +++ b/web/src/app/modules/admin/administration/users/form-page/user-form-page.component.ts @@ -1,20 +1,20 @@ -import { Component } from '@angular/core'; -import { FormControl, FormGroup } from '@angular/forms'; -import { ToastService } from 'src/app/service/toast.service'; -import { FormPageBase } from 'src/app/core/base/form-page-base'; +import { Component } from "@angular/core"; +import { FormControl, FormGroup } from "@angular/forms"; +import { ToastService } from "src/app/service/toast.service"; +import { FormPageBase } from "src/app/core/base/form-page-base"; import { NotExistingUser, User, UserCreateInput, UserUpdateInput, -} from 'src/app/model/auth/user'; -import { Role } from 'src/app/model/entities/role'; -import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service'; +} from "src/app/model/auth/user"; +import { Role } from "src/app/model/entities/role"; +import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service"; @Component({ - selector: 'app-user-form-page', - templateUrl: './user-form-page.component.html', - styleUrl: './user-form-page.component.scss', + selector: "app-user-form-page", + templateUrl: "./user-form-page.component.html", + styleUrl: "./user-form-page.component.scss", }) export class UserFormPageComponent extends FormPageBase< User, @@ -27,12 +27,12 @@ export class UserFormPageComponent extends FormPageBase< constructor(private toast: ToastService) { super(); - this.dataService.getAllRoles().subscribe(roles => { + this.dataService.getAllRoles().subscribe((roles) => { this.roles = roles; }); if (!this.nodeId) { - this.dataService.getNotExistingUsersFromKeycloak().subscribe(users => { + this.dataService.getNotExistingUsersFromKeycloak().subscribe((users) => { this.notExistingUsers = users; this.node = this.new(); this.setForm(this.node); @@ -41,7 +41,7 @@ export class UserFormPageComponent extends FormPageBase< return; } - this.dataService.loadById(this.nodeId).subscribe(user => { + this.dataService.loadById(this.nodeId).subscribe((user) => { this.node = user; this.setForm(this.node); }); @@ -59,47 +59,47 @@ export class UserFormPageComponent extends FormPageBase< email: new FormControl(undefined), roles: new FormControl([]), }); - this.form.controls['id'].disable(); - this.form.controls['username'].disable(); - this.form.controls['email'].disable(); + this.form.controls["id"].disable(); + this.form.controls["username"].disable(); + this.form.controls["email"].disable(); } setForm(node?: User) { - this.form.controls['id'].setValue(node?.id); - this.form.controls['username'].setValue(node?.username); - this.form.controls['email'].setValue(node?.email); - this.form.controls['roles'].setValue(node?.roles ?? []); + this.form.controls["id"].setValue(node?.id); + this.form.controls["username"].setValue(node?.username); + this.form.controls["email"].setValue(node?.email); + this.form.controls["roles"].setValue(node?.roles ?? []); if (this.notExistingUsers.length > 0) { - this.form.controls['id'].enable(); - this.form.controls['keycloakId'].reset(undefined, { required: true }); + this.form.controls["id"].enable(); + this.form.controls["keycloakId"].reset(undefined, { required: true }); } } getCreateInput(): UserCreateInput { return { - keycloakId: this.form.controls['keycloakId'].pristine + keycloakId: this.form.controls["keycloakId"].pristine ? undefined - : this.form.controls['keycloakId'].value, - roles: this.form.controls['roles'].pristine + : this.form.controls["keycloakId"].value, + roles: this.form.controls["roles"].pristine ? undefined - : this.form.controls['roles'].value, + : this.form.controls["roles"].value, }; } getUpdateInput(): UserUpdateInput { return { - id: this.form.controls['id'].value, - roles: this.form.controls['roles'].pristine + id: this.form.controls["id"].value, + roles: this.form.controls["roles"].pristine ? undefined - : this.form.controls['roles'].value, + : this.form.controls["roles"].value, }; } create(user: UserCreateInput): void { this.dataService.create(user).subscribe(() => { this.spinner.hide(); - this.toast.success('action.created'); + this.toast.success("action.created"); this.close(); }); } @@ -107,7 +107,7 @@ export class UserFormPageComponent extends FormPageBase< update(user: UserUpdateInput): void { this.dataService.update(user).subscribe(() => { this.spinner.hide(); - this.toast.success('action.created'); + this.toast.success("action.created"); this.close(); }); } diff --git a/web/src/app/modules/admin/administration/users/users.columns.ts b/web/src/app/modules/admin/administration/users/users.columns.ts index 52debf6..bdb4cc3 100644 --- a/web/src/app/modules/admin/administration/users/users.columns.ts +++ b/web/src/app/modules/admin/administration/users/users.columns.ts @@ -1,11 +1,11 @@ -import { Injectable, Provider } from '@angular/core'; +import { Injectable, Provider } from "@angular/core"; import { DB_MODEL_COLUMNS, ID_COLUMN, PageColumns, -} from 'src/app/core/base/page.columns'; -import { TableColumn } from 'src/app/modules/shared/components/table/table.model'; -import { User } from 'src/app/model/auth/user'; +} from "src/app/core/base/page.columns"; +import { TableColumn } from "src/app/modules/shared/components/table/table.model"; +import { User } from "src/app/model/auth/user"; @Injectable() export class UsersColumns extends PageColumns { @@ -13,15 +13,15 @@ export class UsersColumns extends PageColumns { return [ ID_COLUMN, { - name: 'username', - label: 'user.username', - type: 'text', + name: "username", + label: "user.username", + type: "text", value: (row: User) => row.username, }, { - name: 'email', - label: 'user.email', - type: 'text', + name: "email", + label: "user.email", + type: "text", value: (row: User) => row.email, }, ...DB_MODEL_COLUMNS, diff --git a/web/src/app/modules/admin/administration/users/users.data.service.ts b/web/src/app/modules/admin/administration/users/users.data.service.ts index bb08cec..f11005d 100644 --- a/web/src/app/modules/admin/administration/users/users.data.service.ts +++ b/web/src/app/modules/admin/administration/users/users.data.service.ts @@ -1,26 +1,26 @@ -import { Injectable, Provider } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Injectable, Provider } from "@angular/core"; +import { Observable } from "rxjs"; import { Create, Delete, PageDataService, Restore, Update, -} from 'src/app/core/base/page.data.service'; -import { Filter } from 'src/app/model/graphql/filter/filter.model'; -import { Sort } from 'src/app/model/graphql/filter/sort.model'; -import { Apollo, gql } from 'apollo-angular'; -import { QueryResult } from 'src/app/model/entities/query-result'; -import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query'; -import { catchError, map } from 'rxjs/operators'; -import { SpinnerService } from 'src/app/service/spinner.service'; +} from "src/app/core/base/page.data.service"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; +import { Sort } from "src/app/model/graphql/filter/sort.model"; +import { Apollo, gql } from "apollo-angular"; +import { QueryResult } from "src/app/model/entities/query-result"; +import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query"; +import { catchError, map } from "rxjs/operators"; +import { SpinnerService } from "src/app/service/spinner.service"; import { NotExistingUser, User, UserCreateInput, UserUpdateInput, -} from 'src/app/model/auth/user'; -import { Role } from 'src/app/model/entities/role'; +} from "src/app/model/auth/user"; +import { Role } from "src/app/model/entities/role"; @Injectable() export class UsersDataService @@ -33,7 +33,7 @@ export class UsersDataService { constructor( private spinner: SpinnerService, - private apollo: Apollo + private apollo: Apollo, ) { super(); } @@ -42,7 +42,7 @@ export class UsersDataService filter?: Filter[] | undefined, sort?: Sort[] | undefined, skip?: number | undefined, - take?: number | undefined + take?: number | undefined, ): Observable> { return this.apollo .query<{ users: QueryResult }>({ @@ -77,12 +77,12 @@ export class UsersDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.users)); + .pipe(map((result) => result.data.users)); } loadById(id: number): Observable { @@ -115,12 +115,12 @@ export class UsersDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.users.nodes[0])); + .pipe(map((result) => result.data.users.nodes[0])); } create(object: UserCreateInput): Observable { @@ -145,17 +145,17 @@ export class UsersDataService variables: { input: { keycloakId: object.keycloakId, - roles: object.roles?.map(x => x.id), + roles: object.roles?.map((x) => x.id), }, }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.user.create)); + .pipe(map((result) => result.data?.user.create)); } update(object: UserUpdateInput): Observable { @@ -180,17 +180,17 @@ export class UsersDataService variables: { input: { id: object.id, - roles: object.roles?.map(x => x.id), + roles: object.roles?.map((x) => x.id), }, }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.user.update)); + .pipe(map((result) => result.data?.user.update)); } delete(object: User): Observable { @@ -208,12 +208,12 @@ export class UsersDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.user.delete ?? false)); + .pipe(map((result) => result.data?.user.delete ?? false)); } restore(object: User): Observable { @@ -231,12 +231,12 @@ export class UsersDataService }, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data?.user.restore ?? false)); + .pipe(map((result) => result.data?.user.restore ?? false)); } getAllRoles(): Observable { @@ -254,12 +254,12 @@ export class UsersDataService `, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.roles.nodes)); + .pipe(map((result) => result.data.roles.nodes)); } getNotExistingUsersFromKeycloak(): Observable { @@ -277,12 +277,12 @@ export class UsersDataService `, }) .pipe( - catchError(err => { + catchError((err) => { this.spinner.hide(); throw err; - }) + }), ) - .pipe(map(result => result.data.notExistingUsersFromKeycloak.nodes)); + .pipe(map((result) => result.data.notExistingUsersFromKeycloak.nodes)); } static provide(): Provider[] { diff --git a/web/src/app/modules/admin/administration/users/users.module.ts b/web/src/app/modules/admin/administration/users/users.module.ts index 7546fd4..b54596f 100644 --- a/web/src/app/modules/admin/administration/users/users.module.ts +++ b/web/src/app/modules/admin/administration/users/users.module.ts @@ -1,22 +1,22 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { RouterModule, Routes } from '@angular/router'; -import { PermissionGuard } from 'src/app/core/guard/permission.guard'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; -import { UserFormPageComponent } from 'src/app/modules/admin/administration/users/form-page/user-form-page.component'; -import { UsersPage } from 'src/app/modules/admin/administration/users/users.page'; -import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service'; -import { UsersColumns } from 'src/app/modules/admin/administration/users/users.columns'; +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { RouterModule, Routes } from "@angular/router"; +import { PermissionGuard } from "src/app/core/guard/permission.guard"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { UserFormPageComponent } from "src/app/modules/admin/administration/users/form-page/user-form-page.component"; +import { UsersPage } from "src/app/modules/admin/administration/users/users.page"; +import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service"; +import { UsersColumns } from "src/app/modules/admin/administration/users/users.columns"; const routes: Routes = [ { - path: '', - title: 'Admin - Users | Maxlan', + path: "", + title: "Admin - Users | Maxlan", component: UsersPage, children: [ { - path: 'create', + path: "create", component: UserFormPageComponent, canActivate: [PermissionGuard], data: { @@ -24,7 +24,7 @@ const routes: Routes = [ }, }, { - path: 'edit/:id', + path: "edit/:id", component: UserFormPageComponent, canActivate: [PermissionGuard], data: { diff --git a/web/src/app/modules/admin/administration/users/users.page.spec.ts b/web/src/app/modules/admin/administration/users/users.page.spec.ts index 0e7be0d..2a5e730 100644 --- a/web/src/app/modules/admin/administration/users/users.page.spec.ts +++ b/web/src/app/modules/admin/administration/users/users.page.spec.ts @@ -1,18 +1,18 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { UsersPage } from 'src/app/modules/admin/administration/users/users.page'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { PageDataService } from 'src/app/core/base/page.data.service'; -import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { UsersPage } from "src/app/modules/admin/administration/users/users.page"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { PageDataService } from "src/app/core/base/page.data.service"; +import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service"; -describe('UsersComponent', () => { +describe("UsersComponent", () => { let component: UsersPage; let fixture: ComponentFixture; @@ -45,7 +45,7 @@ describe('UsersComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/administration/users/users.page.ts b/web/src/app/modules/admin/administration/users/users.page.ts index e5f813e..f2aa1fc 100644 --- a/web/src/app/modules/admin/administration/users/users.page.ts +++ b/web/src/app/modules/admin/administration/users/users.page.ts @@ -1,21 +1,21 @@ -import { Component } from '@angular/core'; -import { PageBase } from 'src/app/core/base/page-base'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service'; -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; -import { User } from 'src/app/model/auth/user'; -import { UsersColumns } from 'src/app/modules/admin/administration/users/users.columns'; -import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service'; +import { Component } from "@angular/core"; +import { PageBase } from "src/app/core/base/page-base"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.service"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { User } from "src/app/model/auth/user"; +import { UsersColumns } from "src/app/modules/admin/administration/users/users.columns"; +import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service"; @Component({ - selector: 'app-users', - templateUrl: './users.page.html', - styleUrl: './users.page.scss', + selector: "app-users", + templateUrl: "./users.page.html", + styleUrl: "./users.page.scss", }) export class UsersPage extends PageBase { constructor( private toast: ToastService, - private confirmation: ConfirmationDialogService + private confirmation: ConfirmationDialogService, ) { super(true, { read: [PermissionsEnum.users], @@ -30,7 +30,7 @@ export class UsersPage extends PageBase { this.loading = true; this.dataService .load(this.filter, this.sort, this.skip, this.take) - .subscribe(result => { + .subscribe((result) => { this.result = result; this.loading = false; }); @@ -38,12 +38,12 @@ export class UsersPage extends PageBase { delete(user: User): void { this.confirmation.confirmDialog({ - header: 'dialog.delete.header', - message: 'dialog.delete.message', + header: "dialog.delete.header", + message: "dialog.delete.message", accept: () => { this.loading = true; this.dataService.delete(user).subscribe(() => { - this.toast.success('action.deleted'); + this.toast.success("action.deleted"); this.load(); }); }, @@ -53,12 +53,12 @@ export class UsersPage extends PageBase { restore(user: User): void { this.confirmation.confirmDialog({ - header: 'dialog.restore.header', - message: 'dialog.restore.message', + header: "dialog.restore.header", + message: "dialog.restore.message", accept: () => { this.loading = true; this.dataService.restore(user).subscribe(() => { - this.toast.success('action.restored'); + this.toast.success("action.restored"); this.load(); }); }, diff --git a/web/src/app/modules/admin/groups/form-page/group-form-page.component.spec.ts b/web/src/app/modules/admin/groups/form-page/group-form-page.component.spec.ts index a4a6c84..d7bfe12 100644 --- a/web/src/app/modules/admin/groups/form-page/group-form-page.component.spec.ts +++ b/web/src/app/modules/admin/groups/form-page/group-form-page.component.spec.ts @@ -1,17 +1,17 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RoleFormPageComponent } from "src/app/modules/admin/administration/roles/form-page/role-form-page.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -describe('ApiKeyFormpageComponent', () => { +describe("ApiKeyFormpageComponent", () => { let component: RoleFormPageComponent; let fixture: ComponentFixture; @@ -44,7 +44,7 @@ describe('ApiKeyFormpageComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/groups/groups.page.spec.ts b/web/src/app/modules/admin/groups/groups.page.spec.ts index 98eb39d..f1ba746 100644 --- a/web/src/app/modules/admin/groups/groups.page.spec.ts +++ b/web/src/app/modules/admin/groups/groups.page.spec.ts @@ -1,18 +1,18 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ApiKeysPage } from 'src/app/modules/admin/administration/api-keys/api-keys.page'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { PageDataService } from 'src/app/core/base/page.data.service'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { PageDataService } from "src/app/core/base/page.data.service"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; -describe('ApiKeysComponent', () => { +describe("ApiKeysComponent", () => { let component: ApiKeysPage; let fixture: ComponentFixture; @@ -45,7 +45,7 @@ describe('ApiKeysComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/public/public.module.ts b/web/src/app/modules/admin/public/public.module.ts index 31a74c0..3b80e02 100644 --- a/web/src/app/modules/admin/public/public.module.ts +++ b/web/src/app/modules/admin/public/public.module.ts @@ -1,10 +1,9 @@ -import {NgModule} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {RouterModule, Routes} from '@angular/router'; -import {SharedModule} from 'src/app/modules/shared/shared.module'; +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { RouterModule, Routes } from "@angular/router"; +import { SharedModule } from "src/app/modules/shared/shared.module"; -const routes: Routes = [ -]; +const routes: Routes = []; @NgModule({ declarations: [], diff --git a/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.html b/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.html index 3652646..fd10e36 100644 --- a/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.html +++ b/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.html @@ -20,7 +20,7 @@
-

{{ 'common.short_url' | translate }}

+

{{ 'short_url.short_url' | translate }}

-

{{ 'common.target_url' | translate }}

+

{{ 'short_url.target_url' | translate }}

{ +describe("ApiKeyFormpageComponent", () => { let component: RoleFormPageComponent; let fixture: ComponentFixture; @@ -44,7 +44,7 @@ describe('ApiKeyFormpageComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts b/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts index 9bc7d87..50d2077 100644 --- a/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts +++ b/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts @@ -65,9 +65,22 @@ export class ShortUrlFormPageComponent extends FormPageBase< this.form.controls["id"].disable(); } + private generateRandomString(length: number): string { + const characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let result = ""; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } + setForm(node?: ShortUrl) { this.form.controls["id"].setValue(node?.id); - this.form.controls["shortUrl"].setValue(node?.shortUrl); + this.form.controls["shortUrl"].setValue( + node?.shortUrl ?? this.generateRandomString(8), + ); this.form.controls["targetUrl"].setValue(node?.targetUrl); this.form.controls["description"].setValue(node?.description); this.form.controls["groupId"].setValue(node?.group?.id); @@ -75,9 +88,7 @@ export class ShortUrlFormPageComponent extends FormPageBase< getCreateInput(): ShortUrlCreateInput { return { - shortUrl: this.form.controls["shortUrl"].pristine - ? undefined - : (this.form.controls["shortUrl"].value ?? undefined), + shortUrl: this.form.controls["shortUrl"].value ?? undefined, targetUrl: this.form.controls["targetUrl"].pristine ? undefined : (this.form.controls["targetUrl"].value ?? undefined), diff --git a/web/src/app/modules/admin/short-urls/short-urls.columns.ts b/web/src/app/modules/admin/short-urls/short-urls.columns.ts index 6852d25..56f5431 100644 --- a/web/src/app/modules/admin/short-urls/short-urls.columns.ts +++ b/web/src/app/modules/admin/short-urls/short-urls.columns.ts @@ -14,14 +14,14 @@ export class ShortUrlsColumns extends PageColumns { ID_COLUMN, { name: "short_url", - label: "common.short_url", + label: "short_url.short_url", type: "text", filterable: true, value: (row: ShortUrl) => row.shortUrl, }, { name: "target_url", - label: "common.target_url", + label: "short_url.target_url", type: "text", filterable: true, value: (row: ShortUrl) => row.targetUrl, @@ -33,13 +33,6 @@ export class ShortUrlsColumns extends PageColumns { filterable: true, value: (row: ShortUrl) => row.description, }, - { - name: "group", - label: "common.group", - type: "text", - filterable: true, - value: (row: ShortUrl) => row.group?.name, - }, ...DB_MODEL_COLUMNS, ]; } diff --git a/web/src/app/modules/admin/short-urls/short-urls.page.html b/web/src/app/modules/admin/short-urls/short-urls.page.html index d9a1c72..3aa48dc 100644 --- a/web/src/app/modules/admin/short-urls/short-urls.page.html +++ b/web/src/app/modules/admin/short-urls/short-urls.page.html @@ -1,19 +1,107 @@ - - + + +
+
+

{{ url.shortUrl }}

+
+ {{ 'short_url.target_url' | translate }}: + {{ url.targetUrl }} +
+
+ {{ 'common.description' | translate }}: + {{ url.description }} +
+
+ {{ 'short_url.visits' | translate }}: + {{ url.visits }} +
+
+
+ + + + + + +
+
+
+ +
+
+

{{ 'short_url.short_url' | translate }}

+
+ + + + +
+
+ +
+
+
+ +
+
+ +
+

{{ group }}

+
+ +
+
+
\ No newline at end of file diff --git a/web/src/app/modules/admin/short-urls/short-urls.page.scss b/web/src/app/modules/admin/short-urls/short-urls.page.scss index e69de29..6f065a8 100644 --- a/web/src/app/modules/admin/short-urls/short-urls.page.scss +++ b/web/src/app/modules/admin/short-urls/short-urls.page.scss @@ -0,0 +1,14 @@ +.grid-container { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 10px; + + .grid-label { + grid-column: 1; + } + + .grid-value { + grid-column: 2; + word-break: break-all; + } +} \ No newline at end of file diff --git a/web/src/app/modules/admin/short-urls/short-urls.page.spec.ts b/web/src/app/modules/admin/short-urls/short-urls.page.spec.ts index 98eb39d..f1ba746 100644 --- a/web/src/app/modules/admin/short-urls/short-urls.page.spec.ts +++ b/web/src/app/modules/admin/short-urls/short-urls.page.spec.ts @@ -1,18 +1,18 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ApiKeysPage } from 'src/app/modules/admin/administration/api-keys/api-keys.page'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { PageDataService } from 'src/app/core/base/page.data.service'; -import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { PageDataService } from "src/app/core/base/page.data.service"; +import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service"; -describe('ApiKeysComponent', () => { +describe("ApiKeysComponent", () => { let component: ApiKeysPage; let fixture: ComponentFixture; @@ -45,7 +45,7 @@ describe('ApiKeysComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/admin/short-urls/short-urls.page.ts b/web/src/app/modules/admin/short-urls/short-urls.page.ts index dff86d7..882c1ad 100644 --- a/web/src/app/modules/admin/short-urls/short-urls.page.ts +++ b/web/src/app/modules/admin/short-urls/short-urls.page.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { PageBase } from "src/app/core/base/page-base"; import { ToastService } from "src/app/service/toast.service"; import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.service"; @@ -6,20 +6,44 @@ import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; import { ShortUrl } from "src/app/model/entities/short-url"; import { ShortUrlsDataService } from "src/app/modules/admin/short-urls/short-urls.data.service"; import { ShortUrlsColumns } from "src/app/modules/admin/short-urls/short-urls.columns"; +import { AuthService } from "src/app/service/auth.service"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; @Component({ selector: "app-short-urls", templateUrl: "./short-urls.page.html", styleUrl: "./short-urls.page.scss", }) -export class ShortUrlsPage extends PageBase< - ShortUrl, - ShortUrlsDataService, - ShortUrlsColumns -> { +export class ShortUrlsPage + extends PageBase + implements OnInit +{ + shortUrlsWithoutGroup: ShortUrl[] = []; + groupedShortUrls: { [key: string]: ShortUrl[] } = {}; + + get groupsFromGroupedShortUrls() { + return Object.keys(this.groupedShortUrls); + } + + private hide_deleted_filter: Filter = { deleted: { equal: false } }; + get showDeleted() { + return !this.filter.some( + (f) => JSON.stringify(f) === JSON.stringify(this.hide_deleted_filter), + ); + } + + protected hasPermissions = { + read: false, + create: false, + update: false, + delete: false, + restore: false, + }; + constructor( private toast: ToastService, private confirmation: ConfirmationDialogService, + private auth: AuthService, ) { super(true, { read: [PermissionsEnum.shortUrls], @@ -30,12 +54,41 @@ export class ShortUrlsPage extends PageBase< }); } + async ngOnInit() { + this.hasPermissions = { + read: await this.auth.hasAnyPermissionLazy( + this.requiredPermissions.read ?? [], + ), + create: await this.auth.hasAnyPermissionLazy( + this.requiredPermissions.create ?? [], + ), + update: await this.auth.hasAnyPermissionLazy( + this.requiredPermissions.update ?? [], + ), + delete: await this.auth.hasAnyPermissionLazy( + this.requiredPermissions.delete ?? [], + ), + restore: await this.auth.hasAnyPermissionLazy( + this.requiredPermissions.restore ?? [], + ), + }; + } + load(): void { this.loading = true; this.dataService .load(this.filter, this.sort, this.skip, this.take) .subscribe((result) => { this.result = result; + this.shortUrlsWithoutGroup = this.getShortUrlsWithoutGroup(); + this.groupedShortUrls = this.getShortUrlsWithGroup(); + + console.warn( + "result", + result, + this.shortUrlsWithoutGroup, + this.groupedShortUrls, + ); this.loading = false; }); } @@ -69,4 +122,54 @@ export class ShortUrlsPage extends PageBase< messageParams: { entity: group.shortUrl }, }); } + + toggleShowDeleted() { + if (!this.showDeleted) { + this.filter.splice(this.filter.indexOf(this.hide_deleted_filter), 1); + } else { + this.filter.push(this.hide_deleted_filter); + } + + this.load(); + } + + resetSort() { + this.sort = []; + this.load(); + } + + resetFilters() { + this.filter = []; + this.load(); + } + + open(url: string) { + window.open(url, "_blank"); + } + + copy(val: string) { + navigator.clipboard.writeText(`${window.origin}/${val}`).then(() => { + this.toast.info("common.copied", "common.copied_to_clipboard"); + }); + } + + getShortUrlsWithoutGroup(): ShortUrl[] { + return this.result.nodes.filter((shortUrl) => !shortUrl.group); + } + + getShortUrlsWithGroup() { + const groupedShortUrls: { [key: string]: ShortUrl[] } = {}; + + this.result.nodes.forEach((shortUrl) => { + if (!shortUrl.group) return; + + const groupName = shortUrl.group.name; + if (!groupedShortUrls[groupName]) { + groupedShortUrls[groupName] = []; + } + groupedShortUrls[groupName].push(shortUrl); + }); + + return groupedShortUrls; + } } diff --git a/web/src/app/modules/shared/components/menu-bar/menu-bar.component.spec.ts b/web/src/app/modules/shared/components/menu-bar/menu-bar.component.spec.ts index 4c2b9ab..a2ebabc 100644 --- a/web/src/app/modules/shared/components/menu-bar/menu-bar.component.spec.ts +++ b/web/src/app/modules/shared/components/menu-bar/menu-bar.component.spec.ts @@ -1,8 +1,8 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { MenuBarComponent } from 'src/app/modules/shared/components/menu-bar/menu-bar.component'; +import { MenuBarComponent } from "src/app/modules/shared/components/menu-bar/menu-bar.component"; -describe('MenuBarComponent', () => { +describe("MenuBarComponent", () => { let component: MenuBarComponent; let fixture: ComponentFixture; @@ -16,7 +16,7 @@ describe('MenuBarComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/shared/components/menu-bar/menu-bar.component.ts b/web/src/app/modules/shared/components/menu-bar/menu-bar.component.ts index 79865e7..98a3110 100644 --- a/web/src/app/modules/shared/components/menu-bar/menu-bar.component.ts +++ b/web/src/app/modules/shared/components/menu-bar/menu-bar.component.ts @@ -1,10 +1,10 @@ -import { Component, Input } from '@angular/core'; -import { MenuElement } from 'src/app/model/view/menu-element'; +import { Component, Input } from "@angular/core"; +import { MenuElement } from "src/app/model/view/menu-element"; @Component({ - selector: 'app-menu-bar', - templateUrl: './menu-bar.component.html', - styleUrl: './menu-bar.component.scss', + selector: "app-menu-bar", + templateUrl: "./menu-bar.component.html", + styleUrl: "./menu-bar.component.scss", }) export class MenuBarComponent { @Input() elements: MenuElement[] = []; diff --git a/web/src/app/modules/shared/components/side-menu/side-menu.component.spec.ts b/web/src/app/modules/shared/components/side-menu/side-menu.component.spec.ts index 762db12..688776f 100644 --- a/web/src/app/modules/shared/components/side-menu/side-menu.component.spec.ts +++ b/web/src/app/modules/shared/components/side-menu/side-menu.component.spec.ts @@ -1,8 +1,8 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { SideMenuComponent } from './side-menu.component'; +import { SideMenuComponent } from "./side-menu.component"; -describe('SideMenuComponent', () => { +describe("SideMenuComponent", () => { let component: SideMenuComponent; let fixture: ComponentFixture; @@ -16,7 +16,7 @@ describe('SideMenuComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/shared/components/side-menu/side-menu.component.ts b/web/src/app/modules/shared/components/side-menu/side-menu.component.ts index edd0dec..a649e9a 100644 --- a/web/src/app/modules/shared/components/side-menu/side-menu.component.ts +++ b/web/src/app/modules/shared/components/side-menu/side-menu.component.ts @@ -1,10 +1,10 @@ -import { Component, Input } from '@angular/core'; -import { MenuElement } from 'src/app/model/view/menu-element'; +import { Component, Input } from "@angular/core"; +import { MenuElement } from "src/app/model/view/menu-element"; @Component({ - selector: 'app-side-menu', - templateUrl: './side-menu.component.html', - styleUrl: './side-menu.component.scss', + selector: "app-side-menu", + templateUrl: "./side-menu.component.html", + styleUrl: "./side-menu.component.scss", }) export class SideMenuComponent { @Input() elements: MenuElement[] = []; diff --git a/web/src/app/modules/shared/components/slidein/form-page.component.html b/web/src/app/modules/shared/components/slidein/form-page.component.html index 702b510..8c235b0 100644 --- a/web/src/app/modules/shared/components/slidein/form-page.component.html +++ b/web/src/app/modules/shared/components/slidein/form-page.component.html @@ -1,7 +1,7 @@ diff --git a/web/src/app/modules/shared/components/slidein/form-page.component.spec.ts b/web/src/app/modules/shared/components/slidein/form-page.component.spec.ts index 653ce63..bcf016d 100644 --- a/web/src/app/modules/shared/components/slidein/form-page.component.spec.ts +++ b/web/src/app/modules/shared/components/slidein/form-page.component.spec.ts @@ -1,18 +1,18 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormPageComponent } from 'src/app/modules/shared/components/slidein/form-page.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; -import { FormGroup } from '@angular/forms'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { FormPageComponent } from "src/app/modules/shared/components/slidein/form-page.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; +import { FormGroup } from "@angular/forms"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -describe('FormpageComponent', () => { +describe("FormpageComponent", () => { let component: FormPageComponent; let fixture: ComponentFixture>; @@ -46,7 +46,7 @@ describe('FormpageComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/shared/components/slidein/form-page.component.ts b/web/src/app/modules/shared/components/slidein/form-page.component.ts index 233ddbe..9476233 100644 --- a/web/src/app/modules/shared/components/slidein/form-page.component.ts +++ b/web/src/app/modules/shared/components/slidein/form-page.component.ts @@ -5,18 +5,18 @@ import { Input, Output, TemplateRef, -} from '@angular/core'; -import { FormGroup } from '@angular/forms'; -import { SpinnerService } from 'src/app/service/spinner.service'; +} from "@angular/core"; +import { FormGroup } from "@angular/forms"; +import { SpinnerService } from "src/app/service/spinner.service"; import { FormPageContentDirective, FormPageHeaderDirective, -} from 'src/app/modules/shared/form'; +} from "src/app/modules/shared/form"; @Component({ - selector: 'app-form-page', - templateUrl: './form-page.component.html', - styleUrl: './form-page.component.scss', + selector: "app-form-page", + templateUrl: "./form-page.component.html", + styleUrl: "./form-page.component.scss", }) export class FormPageComponent { @Input({ required: true }) formGroup!: FormGroup; diff --git a/web/src/app/modules/shared/components/table/table.component.html b/web/src/app/modules/shared/components/table/table.component.html index 1d9e9f3..02fa31f 100644 --- a/web/src/app/modules/shared/components/table/table.component.html +++ b/web/src/app/modules/shared/components/table/table.component.html @@ -1,121 +1,121 @@ - -
-
-
-
- {{ skip > 0 ? skip : 1 }} {{ 'table.to' | translate }} - {{ skip + rows.length }} {{ 'table.of' | translate }} - {{ totalCount }} - - {{ countHeaderTranslation | translate }} -
-
-
-
- - + +
+
+
+
+ {{ skip > 0 ? skip : 1 }} {{ 'table.to' | translate }} + {{ skip + rows.length }} {{ 'table.of' | translate }} + {{ totalCount }} + + {{ countHeaderTranslation | translate }} +
+
+
+
+ + - - -
-
-
- - - - -
- {{ column.label | translate }} - + (click)="toggleShowDeleted()"> + + +
- - - + - - -
- - - - - -
- - - - - - - - - + + + +
+ {{ column.label | translate }} + +
+ + + + + + +
+ + + + + +
+ + + +
+ + + + + {{ - r.value | customDate: 'dd.MM.yyyy HH:mm:ss' - }} - + r.value | customDate: 'dd.MM.yyyy HH:mm:ss' + }} + @@ -125,51 +125,56 @@ - + {{ r.value | protect }} - + - {{ r.value }} - - - - - - - - - - - - - - - {{ 'table.no_entries_found' | translate }} - - - - + {{ r.value }} +
+ +
+ + + + + + + +
+ + + + + {{ 'table.no_entries_found' | translate }} + + + + diff --git a/web/src/app/modules/shared/components/table/table.component.spec.ts b/web/src/app/modules/shared/components/table/table.component.spec.ts index daf61e1..cd31ef8 100644 --- a/web/src/app/modules/shared/components/table/table.component.spec.ts +++ b/web/src/app/modules/shared/components/table/table.component.spec.ts @@ -1,17 +1,17 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { TableComponent } from './table.component'; -import { SharedModule } from 'src/app/modules/shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { AuthService } from 'src/app/service/auth.service'; -import { KeycloakService } from 'keycloak-angular'; -import { ErrorHandlingService } from 'src/app/service/error-handling.service'; -import { ToastService } from 'src/app/service/toast.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; +import { TableComponent } from "./table.component"; +import { SharedModule } from "src/app/modules/shared/shared.module"; +import { TranslateModule } from "@ngx-translate/core"; +import { AuthService } from "src/app/service/auth.service"; +import { KeycloakService } from "keycloak-angular"; +import { ErrorHandlingService } from "src/app/service/error-handling.service"; +import { ToastService } from "src/app/service/toast.service"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { ActivatedRoute } from "@angular/router"; +import { of } from "rxjs"; -describe('TableComponent', () => { +describe("TableComponent", () => { let component: TableComponent; let fixture: ComponentFixture>; @@ -40,7 +40,7 @@ describe('TableComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); }); diff --git a/web/src/app/modules/shared/components/table/table.component.ts b/web/src/app/modules/shared/components/table/table.component.ts index 402bfe0..11623d0 100644 --- a/web/src/app/modules/shared/components/table/table.component.ts +++ b/web/src/app/modules/shared/components/table/table.component.ts @@ -1,26 +1,44 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { + Component, + ContentChild, + Directive, + EventEmitter, + Input, + OnInit, + Output, + TemplateRef, +} from "@angular/core"; +import { + FilterMode, ResolvedTableColumn, TableColumn, TableRequireAnyPermissions, -} from 'src/app/modules/shared/components/table/table.model'; -import { TableLazyLoadEvent } from 'primeng/table'; -import { RowsPerPageOption } from 'src/app/service/filter.service'; -import { Filter } from 'src/app/model/graphql/filter/filter.model'; -import { Sort, SortOrder } from 'src/app/model/graphql/filter/sort.model'; -import { FormControl, FormGroup } from '@angular/forms'; -import { debounceTime, Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { Logger } from 'src/app/service/logger.service'; -import { AuthService } from 'src/app/service/auth.service'; -import { ToastService } from 'src/app/service/toast.service'; +} from "src/app/modules/shared/components/table/table.model"; +import { TableLazyLoadEvent } from "primeng/table"; +import { RowsPerPageOption } from "src/app/service/filter.service"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; +import { Sort, SortOrder } from "src/app/model/graphql/filter/sort.model"; +import { FormControl, FormGroup } from "@angular/forms"; +import { debounceTime, Subject } from "rxjs"; +import { takeUntil } from "rxjs/operators"; +import { Logger } from "src/app/service/logger.service"; +import { AuthService } from "src/app/service/auth.service"; +import { ToastService } from "src/app/service/toast.service"; -const logger = new Logger('TableComponent'); +const logger = new Logger("TableComponent"); + +@Directive({ + // eslint-disable-next-line @angular-eslint/directive-selector + selector: "[customAction]", +}) +export class CustomActionDirective { + constructor(public templateRef: TemplateRef) {} +} @Component({ - selector: 'app-table', - templateUrl: './table.component.html', - styleUrl: './table.component.scss', + selector: "app-table", + templateUrl: "./table.component.html", + styleUrl: "./table.component.scss", }) export class TableComponent implements OnInit { private _rows: T[] = []; @@ -57,17 +75,20 @@ export class TableComponent implements OnInit { @Output() load = new EventEmitter(); @Input() loading = true; - @Input() dataKey = 'id'; - @Input() responsiveLayout: 'stack' | 'scroll' = 'stack'; + @Input() dataKey = "id"; + @Input() responsiveLayout: "stack" | "scroll" = "stack"; @Input() create = false; @Input() update = false; - @Input() createBaseUrl = ''; - @Input() updateBaseUrl = ''; + @Input() createBaseUrl = ""; + @Input() updateBaseUrl = ""; @Output() delete: EventEmitter = new EventEmitter(); @Output() restore: EventEmitter = new EventEmitter(); + @ContentChild(CustomActionDirective, { read: TemplateRef }) + customActions!: TemplateRef; + protected resolvedColumns: ResolvedTableColumn[][] = []; protected filterForm!: FormGroup; protected defaultFilterForm!: FormGroup; @@ -76,12 +97,12 @@ export class TableComponent implements OnInit { get showDeleted() { return !this.filter.some( - f => JSON.stringify(f) === JSON.stringify(this.hide_deleted_filter) + (f) => JSON.stringify(f) === JSON.stringify(this.hide_deleted_filter), ); } get showFilters() { - return this.columns.some(x => x.filterable); + return this.columns.some((x) => x.filterable); } protected unsubscriber$ = new Subject(); @@ -96,25 +117,25 @@ export class TableComponent implements OnInit { constructor( private auth: AuthService, - private toast: ToastService + private toast: ToastService, ) {} async ngOnInit() { this.hasPermissions = { read: await this.auth.hasAnyPermissionLazy( - this.requireAnyPermissions.read ?? [] + this.requireAnyPermissions.read ?? [], ), create: await this.auth.hasAnyPermissionLazy( - this.requireAnyPermissions.create ?? [] + this.requireAnyPermissions.create ?? [], ), update: await this.auth.hasAnyPermissionLazy( - this.requireAnyPermissions.update ?? [] + this.requireAnyPermissions.update ?? [], ), delete: await this.auth.hasAnyPermissionLazy( - this.requireAnyPermissions.delete ?? [] + this.requireAnyPermissions.delete ?? [], ), restore: await this.auth.hasAnyPermissionLazy( - this.requireAnyPermissions.restore ?? [] + this.requireAnyPermissions.restore ?? [], ), }; @@ -131,9 +152,9 @@ export class TableComponent implements OnInit { } const resolvedColumns: ResolvedTableColumn[][] = []; - this.rows.forEach(row => { + this.rows.forEach((row) => { const resolvedRow: ResolvedTableColumn[] = []; - this.columns.forEach(column => { + this.columns.forEach((column) => { resolvedRow.push({ value: column.value(row), data: row, @@ -147,7 +168,7 @@ export class TableComponent implements OnInit { } loadData(event: TableLazyLoadEvent) { - logger.trace('lazy load event', event); + logger.trace("lazy load event", event); const { rows, first, sortField, sortOrder } = event; const reload = @@ -172,9 +193,9 @@ export class TableComponent implements OnInit { ) { if (sortField instanceof Array) { this.sortChange.emit( - sortField.map(x => ({ + sortField.map((x) => ({ [x]: sortOrder === 1 ? SortOrder.ASC : SortOrder.DESC, - })) + })), ); } else { this.sortChange.emit([ @@ -211,17 +232,17 @@ export class TableComponent implements OnInit { buildDefaultFilterForm() { this.defaultFilterForm = new FormGroup({}); this.columns - .filter(x => x.filterable) - .forEach(x => { + .filter((x) => x.filterable) + .forEach((x) => { let control!: FormControl; - if (x.type === 'text') { + if (x.type === "text") { control = new FormControl(undefined); - } else if (x.type === 'number') { + } else if (x.type === "number") { control = new FormControl(undefined); - } else if (x.type === 'bool') { + } else if (x.type === "bool") { control = new FormControl(undefined); - } else if (x.type === 'date') { + } else if (x.type === "date") { control = new FormControl(undefined); } else { control = new FormControl(undefined); @@ -231,14 +252,14 @@ export class TableComponent implements OnInit { } setDefaultFilterForm() { - this.defaultFilter.forEach(x => { - Object.keys(x).forEach(key => { + this.defaultFilter.forEach((x) => { + Object.keys(x).forEach((key) => { const value = x[key]; if (!(key in this.defaultFilterForm.controls)) { return; } - if (typeof value === 'object' && value !== null) { - Object.keys(value).forEach(subKey => { + if (typeof value === "object" && value !== null) { + Object.keys(value).forEach((subKey) => { this.defaultFilterForm.get([key])?.setValue(value[subKey]); }); } else { @@ -253,38 +274,41 @@ export class TableComponent implements OnInit { this.filterForm.valueChanges .pipe(takeUntil(this.unsubscriber$), debounceTime(200)) - .subscribe(changes => { - logger.trace('Filter input', changes); + .subscribe((changes) => { + logger.trace("Filter input", changes); if (this.filterForm.disabled) { return; } const filter = Object.keys(changes) .filter( - key => + (key) => changes[key] !== undefined && changes[key] !== null && - changes[key] !== '' + changes[key] !== "", ) - .map(key => { - const column = this.columns.find(x => x.name === key); + .map((key) => { + const column = this.columns.find((x) => x.name === key); if (!column || !column.filterable) { return {}; } let value = changes[key]; - let defaultFilterMode = 'contains'; + let defaultFilterMode: FilterMode = "contains"; switch (column.type) { - case 'number': - defaultFilterMode = 'equal'; + case "number": + defaultFilterMode = "equal"; value = +value; break; - case 'bool': - defaultFilterMode = 'equal'; + case "bool": + defaultFilterMode = "equal"; } const filterMode = column.filterMode || defaultFilterMode; + if (column.filterFactory) { + return column.filterFactory(key, filterMode, value); + } return { [key]: { [filterMode]: value } }; }); @@ -300,7 +324,7 @@ export class TableComponent implements OnInit { copy(val: T | T[keyof T] | string) { navigator.clipboard.writeText(val as string).then(() => { - this.toast.info('common.copied', 'common.copied_to_clipboard'); + this.toast.info("common.copied", "common.copied_to_clipboard"); }); } } diff --git a/web/src/app/modules/shared/components/table/table.model.ts b/web/src/app/modules/shared/components/table/table.model.ts index 8777592..d9d3459 100644 --- a/web/src/app/modules/shared/components/table/table.model.ts +++ b/web/src/app/modules/shared/components/table/table.model.ts @@ -1,6 +1,8 @@ -import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; export type TableColumnValue = T | T[keyof T] | string; +export type FilterMode = "contains" | "startsWith" | "endsWith" | "equal"; export interface TableColumn { name: string; @@ -12,7 +14,8 @@ export interface TableColumn { hidden?: boolean; sortable?: boolean; filterable?: boolean; - filterMode?: 'contains' | 'startsWith' | 'endsWith' | 'equals'; + filterMode?: FilterMode; + filterFactory?: (key: string, mode: FilterMode, value: T) => Filter; sort?: (a: T, b: T) => number; filter?: (row: T, filter: string) => boolean; width?: string; diff --git a/web/src/app/modules/shared/date.ts b/web/src/app/modules/shared/date.ts index f685d65..f82943a 100644 --- a/web/src/app/modules/shared/date.ts +++ b/web/src/app/modules/shared/date.ts @@ -1,17 +1,17 @@ -import { addMinutes, format, parseISO } from 'date-fns'; +import { addMinutes, format, parseISO } from "date-fns"; export function formatUTCDateToClientTimezone(date: string) { const dateTZ = addMinutes( parseISO(date), - new Date().getTimezoneOffset() * -1 + new Date().getTimezoneOffset() * -1, ); - return format(dateTZ, 'yyyy-MM-dd HH:mm:ss'); + return format(dateTZ, "yyyy-MM-dd HH:mm:ss"); } export function formatUTCDateToGermanLocaleAndTimezone(date: string) { const dateTZ = addMinutes( parseISO(date), - new Date().getTimezoneOffset() * -1 + new Date().getTimezoneOffset() * -1, ); - return format(dateTZ, 'dd.MM.yy HH:mm:ss'); + return format(dateTZ, "dd.MM.yy HH:mm:ss"); } diff --git a/web/src/app/modules/shared/form.ts b/web/src/app/modules/shared/form.ts index 1a8c933..038fb65 100644 --- a/web/src/app/modules/shared/form.ts +++ b/web/src/app/modules/shared/form.ts @@ -1,8 +1,8 @@ -import { Directive, TemplateRef } from '@angular/core'; +import { Directive, TemplateRef } from "@angular/core"; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector - selector: '[formPageHeader]', + selector: "[formPageHeader]", }) export class FormPageHeaderDirective { constructor(public templateRef: TemplateRef) {} @@ -10,7 +10,7 @@ export class FormPageHeaderDirective { @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector - selector: '[formPageContent]', + selector: "[formPageContent]", }) export class FormPageContentDirective { constructor(public templateRef: TemplateRef) {} diff --git a/web/src/app/modules/shared/pipes/bool.pipe.ts b/web/src/app/modules/shared/pipes/bool.pipe.ts index 33cb9b0..2a51c23 100644 --- a/web/src/app/modules/shared/pipes/bool.pipe.ts +++ b/web/src/app/modules/shared/pipes/bool.pipe.ts @@ -1,15 +1,15 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; +import { Pipe, PipeTransform } from "@angular/core"; +import { TranslateService } from "@ngx-translate/core"; @Pipe({ - name: 'bool', + name: "bool", }) export class BoolPipe implements PipeTransform { constructor(private translate: TranslateService) {} transform(value: unknown): string { return this.translate.instant( - value === true || value === 'true' ? 'bool.true' : 'bool.false' + value === true || value === "true" ? "bool.true" : "bool.false", ); } } diff --git a/web/src/app/modules/shared/pipes/customDate.pipe.ts b/web/src/app/modules/shared/pipes/customDate.pipe.ts index 7ff9f29..69f9173 100644 --- a/web/src/app/modules/shared/pipes/customDate.pipe.ts +++ b/web/src/app/modules/shared/pipes/customDate.pipe.ts @@ -1,23 +1,23 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { format } from 'date-fns'; +import { Pipe, PipeTransform } from "@angular/core"; +import { format } from "date-fns"; @Pipe({ - name: 'customDate', + name: "customDate", }) export class CustomDatePipe implements PipeTransform { - transform(value: unknown, dateFormat: string = 'yyyy-MM-dd'): string { + transform(value: unknown, dateFormat: string = "yyyy-MM-dd"): string { if (!value) { - return ''; + return ""; } try { const date = - typeof value === 'string' || typeof value === 'number' + typeof value === "string" || typeof value === "number" ? new Date(value) : value; return format(date as Date, dateFormat); } catch (error) { - console.error('Error formatting date:', error); - return ''; + console.error("Error formatting date:", error); + return ""; } } } diff --git a/web/src/app/modules/shared/pipes/protect.pipe.ts b/web/src/app/modules/shared/pipes/protect.pipe.ts index 1a28249..5ed3ea0 100644 --- a/web/src/app/modules/shared/pipes/protect.pipe.ts +++ b/web/src/app/modules/shared/pipes/protect.pipe.ts @@ -1,14 +1,14 @@ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'protect', + name: "protect", }) export class ProtectPipe implements PipeTransform { transform(text?: unknown): string { const len = 8; - if (text && typeof text === 'string' && text.length) { - return '*'.repeat(text.length); + if (text && typeof text === "string" && text.length) { + return "*".repeat(text.length); } - return '*'.repeat(len); + return "*".repeat(len); } } diff --git a/web/src/app/modules/shared/shared.module.ts b/web/src/app/modules/shared/shared.module.ts index 20cd112..f2d2877 100644 --- a/web/src/app/modules/shared/shared.module.ts +++ b/web/src/app/modules/shared/shared.module.ts @@ -1,63 +1,66 @@ -import { inject, NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { StepsModule } from 'primeng/steps'; -import { DialogModule } from 'primeng/dialog'; -import { ToastModule } from 'primeng/toast'; -import { ProgressSpinnerModule } from 'primeng/progressspinner'; -import { ButtonModule } from 'primeng/button'; -import { ButtonGroupModule } from 'primeng/buttongroup'; -import { ConfirmDialogModule } from 'primeng/confirmdialog'; -import { TableModule } from 'primeng/table'; -import { InputTextModule } from 'primeng/inputtext'; -import { CheckboxModule } from 'primeng/checkbox'; -import { DropdownModule } from 'primeng/dropdown'; -import { TranslateModule } from '@ngx-translate/core'; -import { ImageModule } from 'primeng/image'; -import { TabMenuModule } from 'primeng/tabmenu'; -import { MenubarModule } from 'primeng/menubar'; -import { PanelMenuModule } from 'primeng/panelmenu'; -import { CardModule } from 'primeng/card'; -import { MultiSelectModule } from 'primeng/multiselect'; -import { SplitButtonModule } from 'primeng/splitbutton'; -import { CalendarModule } from 'primeng/calendar'; -import { PaginatorModule } from 'primeng/paginator'; -import { DataViewModule } from 'primeng/dataview'; -import { TagModule } from 'primeng/tag'; -import { SidebarModule } from 'primeng/sidebar'; -import { FileUploadModule } from 'primeng/fileupload'; -import { MenuModule } from 'primeng/menu'; -import { ChipModule } from 'primeng/chip'; -import { BadgeModule } from 'primeng/badge'; -import { ContextMenuModule } from 'primeng/contextmenu'; -import { DragDropModule } from 'primeng/dragdrop'; -import { InputSwitchModule } from 'primeng/inputswitch'; -import { SelectButtonModule } from 'primeng/selectbutton'; -import { BreadcrumbModule } from 'primeng/breadcrumb'; -import { OverlayPanelModule } from 'primeng/overlaypanel'; -import { TerminalModule } from 'primeng/terminal'; -import { RatingModule } from 'primeng/rating'; -import { FocusTrapModule } from 'primeng/focustrap'; -import { InputMaskModule } from 'primeng/inputmask'; -import { TriStateCheckboxModule } from 'primeng/tristatecheckbox'; -import { InputTextareaModule } from 'primeng/inputtextarea'; -import { MenuBarComponent } from 'src/app/modules/shared/components/menu-bar/menu-bar.component'; -import { SideMenuComponent } from './components/side-menu/side-menu.component'; -import { TableComponent } from './components/table/table.component'; -import { BoolPipe } from 'src/app/modules/shared/pipes/bool.pipe'; -import { CustomDatePipe } from 'src/app/modules/shared/pipes/customDate.pipe'; -import { FormPageComponent } from 'src/app/modules/shared/components/slidein/form-page.component'; +import { inject, NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { StepsModule } from "primeng/steps"; +import { DialogModule } from "primeng/dialog"; +import { ToastModule } from "primeng/toast"; +import { ProgressSpinnerModule } from "primeng/progressspinner"; +import { ButtonModule } from "primeng/button"; +import { ButtonGroupModule } from "primeng/buttongroup"; +import { ConfirmDialogModule } from "primeng/confirmdialog"; +import { TableModule } from "primeng/table"; +import { InputTextModule } from "primeng/inputtext"; +import { CheckboxModule } from "primeng/checkbox"; +import { DropdownModule } from "primeng/dropdown"; +import { TranslateModule } from "@ngx-translate/core"; +import { ImageModule } from "primeng/image"; +import { TabMenuModule } from "primeng/tabmenu"; +import { MenubarModule } from "primeng/menubar"; +import { PanelMenuModule } from "primeng/panelmenu"; +import { CardModule } from "primeng/card"; +import { MultiSelectModule } from "primeng/multiselect"; +import { SplitButtonModule } from "primeng/splitbutton"; +import { CalendarModule } from "primeng/calendar"; +import { PaginatorModule } from "primeng/paginator"; +import { DataViewModule } from "primeng/dataview"; +import { TagModule } from "primeng/tag"; +import { SidebarModule } from "primeng/sidebar"; +import { FileUploadModule } from "primeng/fileupload"; +import { MenuModule } from "primeng/menu"; +import { ChipModule } from "primeng/chip"; +import { BadgeModule } from "primeng/badge"; +import { ContextMenuModule } from "primeng/contextmenu"; +import { DragDropModule } from "primeng/dragdrop"; +import { InputSwitchModule } from "primeng/inputswitch"; +import { SelectButtonModule } from "primeng/selectbutton"; +import { BreadcrumbModule } from "primeng/breadcrumb"; +import { OverlayPanelModule } from "primeng/overlaypanel"; +import { TerminalModule } from "primeng/terminal"; +import { RatingModule } from "primeng/rating"; +import { FocusTrapModule } from "primeng/focustrap"; +import { InputMaskModule } from "primeng/inputmask"; +import { TriStateCheckboxModule } from "primeng/tristatecheckbox"; +import { InputTextareaModule } from "primeng/inputtextarea"; +import { MenuBarComponent } from "src/app/modules/shared/components/menu-bar/menu-bar.component"; +import { SideMenuComponent } from "./components/side-menu/side-menu.component"; +import { + CustomActionDirective, + TableComponent, +} from "./components/table/table.component"; +import { BoolPipe } from "src/app/modules/shared/pipes/bool.pipe"; +import { CustomDatePipe } from "src/app/modules/shared/pipes/customDate.pipe"; +import { FormPageComponent } from "src/app/modules/shared/components/slidein/form-page.component"; import { FormPageContentDirective, FormPageHeaderDirective, -} from 'src/app/modules/shared/form'; -import { ProtectPipe } from './pipes/protect.pipe'; -import { provideApollo } from 'apollo-angular'; -import { HttpLink } from 'apollo-angular/http'; -import { environment } from 'src/environments/environment'; -import { InMemoryCache } from '@apollo/client/core'; -import { provideHttpClient, withInterceptors } from '@angular/common/http'; -import { tokenInterceptor } from 'src/app/core/token.interceptor'; +} from "src/app/modules/shared/form"; +import { ProtectPipe } from "./pipes/protect.pipe"; +import { provideApollo } from "apollo-angular"; +import { HttpLink } from "apollo-angular/http"; +import { environment } from "src/environments/environment"; +import { InMemoryCache } from "@apollo/client/core"; +import { provideHttpClient, withInterceptors } from "@angular/common/http"; +import { tokenInterceptor } from "src/app/core/token.interceptor"; const sharedModules = [ StepsModule, @@ -114,6 +117,7 @@ const sharedComponents = [ FormPageComponent, FormPageHeaderDirective, FormPageContentDirective, + CustomActionDirective, ]; @NgModule({ @@ -131,13 +135,13 @@ const sharedComponents = [ defaultOptions: { watchQuery: { - fetchPolicy: 'no-cache', + fetchPolicy: "no-cache", }, query: { - fetchPolicy: 'no-cache', + fetchPolicy: "no-cache", }, mutate: { - fetchPolicy: 'no-cache', + fetchPolicy: "no-cache", }, }, }; diff --git a/web/src/app/service/confirmation-dialog.service.ts b/web/src/app/service/confirmation-dialog.service.ts index 968ac67..37839e4 100644 --- a/web/src/app/service/confirmation-dialog.service.ts +++ b/web/src/app/service/confirmation-dialog.service.ts @@ -1,7 +1,7 @@ -import { Injectable } from '@angular/core'; -import { ConfirmationService } from 'primeng/api'; -import { TranslateService } from '@ngx-translate/core'; -import { ConfirmationDialog } from 'src/app/model/utils/confirmation-dialog'; +import { Injectable } from "@angular/core"; +import { ConfirmationService } from "primeng/api"; +import { TranslateService } from "@ngx-translate/core"; +import { ConfirmationDialog } from "src/app/model/utils/confirmation-dialog"; export interface ConfirmationDialogOptions { header: string; @@ -13,12 +13,12 @@ export interface ConfirmationDialogOptions { } @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class ConfirmationDialogService { constructor( private translate: TranslateService, - private confirmationService: ConfirmationService + private confirmationService: ConfirmationService, ) {} confirmDialog({ @@ -30,7 +30,7 @@ export class ConfirmationDialogService { messageParams = undefined, }: ConfirmationDialogOptions) { const options: ConfirmationDialog = { - key: 'confirmConfirmationDialog', + key: "confirmConfirmationDialog", header: this.translate.instant(header, headerParams), message: this.translate.instant(message, messageParams), }; diff --git a/web/src/app/service/error-handling.service.ts b/web/src/app/service/error-handling.service.ts index 2fce807..1688cd8 100644 --- a/web/src/app/service/error-handling.service.ts +++ b/web/src/app/service/error-handling.service.ts @@ -1,22 +1,22 @@ -import { TranslateService } from '@ngx-translate/core'; -import { Logger } from 'src/app/service/logger.service'; -import { ErrorHandler, Injectable } from '@angular/core'; -import { ToastService } from 'src/app/service/toast.service'; -import { HttpErrorResponse } from '@angular/common/http'; -import { ApolloError } from '@apollo/client/errors'; -import { MissingPermissionException } from 'src/app/model/utils/error'; -import { GraphQLError } from 'graphql/error/GraphQLError'; -import { ToastOptions } from 'src/app/model/utils/toast-options'; +import { TranslateService } from "@ngx-translate/core"; +import { Logger } from "src/app/service/logger.service"; +import { ErrorHandler, Injectable } from "@angular/core"; +import { ToastService } from "src/app/service/toast.service"; +import { HttpErrorResponse } from "@angular/common/http"; +import { ApolloError } from "@apollo/client/errors"; +import { MissingPermissionException } from "src/app/model/utils/error"; +import { GraphQLError } from "graphql/error/GraphQLError"; +import { ToastOptions } from "src/app/model/utils/toast-options"; -const logger = new Logger('ErrorHandler'); +const logger = new Logger("ErrorHandler"); @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class ErrorHandlingService implements ErrorHandler { constructor( private t: TranslateService, - private toast: ToastService + private toast: ToastService, ) {} handleError(error: HttpErrorResponse | ApolloError) { @@ -40,14 +40,14 @@ export class ErrorHandlingService implements ErrorHandler { error?: string; options?: ToastOptions; } = { - summary: this.t.instant('common.error'), + summary: this.t.instant("common.error"), detail: e.message, }; if (e.status === 401) { toast = { - summary: this.t.instant('common.error'), - detail: this.t.instant('error.unauthorized'), + summary: this.t.instant("common.error"), + detail: this.t.instant("error.unauthorized"), }; } @@ -57,15 +57,15 @@ export class ErrorHandlingService implements ErrorHandler { .permissions; toast = { - summary: this.t.instant('common.error'), - detail: this.t.instant('error.missing_permissions', { - permissions: missingPermissions.join(', '), + summary: this.t.instant("common.error"), + detail: this.t.instant("error.missing_permissions", { + permissions: missingPermissions.join(", "), }), }; } else { toast = { - summary: this.t.instant('common.error'), - detail: this.t.instant('error.permission_denied'), + summary: this.t.instant("common.error"), + detail: this.t.instant("error.permission_denied"), }; } } @@ -73,7 +73,7 @@ export class ErrorHandlingService implements ErrorHandler { if (e.status === 500 && e.error && e.error.errors) { if (e.error.errors.length > 0) { toast = { - summary: this.t.instant('common.api_error'), + summary: this.t.instant("common.api_error"), detail: e.error.errors[0].message, }; } @@ -86,8 +86,8 @@ export class ErrorHandlingService implements ErrorHandler { private handleGraphQlErrors(errors: GraphQLError[]) { errors.forEach((e: GraphQLError) => { this.toast.error( - this.t.instant('common.api_error'), - `${e.message}${e.path ? ' ' + e.path.join('.') : ''}` + this.t.instant("common.api_error"), + `${e.message}${e.path ? " " + e.path.join(".") : ""}`, ); }); } diff --git a/web/src/app/service/file.service.ts b/web/src/app/service/file.service.ts index 949c188..430cb7c 100644 --- a/web/src/app/service/file.service.ts +++ b/web/src/app/service/file.service.ts @@ -1,16 +1,16 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { environment } from 'src/environments/environment'; +import { Injectable } from "@angular/core"; +import { HttpClient } from "@angular/common/http"; +import { environment } from "src/environments/environment"; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class FileService { constructor(private http: HttpClient) {} getFile(name: string) { return this.http.get(`${environment.api.url}/files/${name}`, { - responseType: 'blob', + responseType: "blob", }); } } diff --git a/web/src/app/service/filter.service.ts b/web/src/app/service/filter.service.ts index 5512bff..21e71a6 100644 --- a/web/src/app/service/filter.service.ts +++ b/web/src/app/service/filter.service.ts @@ -1,23 +1,23 @@ -import { Logger } from 'src/app/service/logger.service'; -import { EventEmitter, Injectable } from '@angular/core'; -import { BehaviorSubject, take } from 'rxjs'; -import { Filter } from 'src/app/model/graphql/filter/filter.model'; -import { Sort } from 'src/app/model/graphql/filter/sort.model'; -import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; +import { Logger } from "src/app/service/logger.service"; +import { EventEmitter, Injectable } from "@angular/core"; +import { BehaviorSubject, take } from "rxjs"; +import { Filter } from "src/app/model/graphql/filter/filter.model"; +import { Sort } from "src/app/model/graphql/filter/sort.model"; +import { ActivatedRoute, Router } from "@angular/router"; +import { TranslateService } from "@ngx-translate/core"; -const log = new Logger('filterService'); +const log = new Logger("filterService"); export type RowsPerPageOption = number | { showAll: string }; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class FilterService { protected useQueryParams = false; get rowsPerPageOptions() { - return [10, 25, 50, { showAll: this.translate.instant('common.all') }]; + return [10, 25, 50, { showAll: this.translate.instant("common.all") }]; } filter$ = new BehaviorSubject([]); @@ -29,13 +29,13 @@ export class FilterService { constructor( private route: ActivatedRoute, private router: Router, - private translate: TranslateService + private translate: TranslateService, ) { this.skip$.subscribe(() => { this.setQueryParams(); }); - this.take$.subscribe(x => { - if (x && Object.prototype.hasOwnProperty.call(x, 'showAll')) { + this.take$.subscribe((x) => { + if (x && Object.prototype.hasOwnProperty.call(x, "showAll")) { this.take$.next(0); return; } @@ -54,7 +54,7 @@ export class FilterService { } reset(args?: { withQueryParams: boolean; withHideDeleted?: boolean }) { - log.debug('Reset', args); + log.debug("Reset", args); this.useQueryParams = args?.withQueryParams ?? false; if (!this.useQueryParams) { this.clearQueryParams(); @@ -77,7 +77,7 @@ export class FilterService { return; } - this.skip$.next(this.getQueryValueFromParams('skip') ?? 0); + this.skip$.next(this.getQueryValueFromParams("skip") ?? 0); } private resetTake() { @@ -87,13 +87,13 @@ export class FilterService { } this.take$.next( - this.getQueryValueFromParams('take') ?? this.rowsPerPageOptions[0] + this.getQueryValueFromParams("take") ?? this.rowsPerPageOptions[0], ); } private resetSortAndFilter() { - this.sort$.next(this.getQueryValueFromParams('sort') ?? []); - this.filter$.next(this.getQueryValueFromParams('filter') ?? []); + this.sort$.next(this.getQueryValueFromParams("sort") ?? []); + this.filter$.next(this.getQueryValueFromParams("filter") ?? []); } protected getQueryValueFromParams(value: string) { @@ -112,7 +112,7 @@ export class FilterService { sort: undefined, filter: undefined, }, - queryParamsHandling: 'merge', // remove to replace all query params by provided + queryParamsHandling: "merge", // remove to replace all query params by provided replaceUrl: true, }) .then(); @@ -120,17 +120,17 @@ export class FilterService { protected setQueryParams() { if (!this.useQueryParams) return; - log.debug('SetQueryParams'); - log.trace('skip: ', this.skip$.value); - log.trace('take: ', this.take$.value); - log.trace('sort: ', this.sort$.value); - log.trace('filter: ', this.filter$.value); - this.route.paramMap.pipe(take(1)).subscribe(params => { + log.debug("SetQueryParams"); + log.trace("skip: ", this.skip$.value); + log.trace("take: ", this.take$.value); + log.trace("sort: ", this.sort$.value); + log.trace("filter: ", this.filter$.value); + this.route.paramMap.pipe(take(1)).subscribe((params) => { if ( - params.get('skip') !== this.skip$.value.toString() || - params.get('take') !== this.take$.value.toString() || - params.get('sort') !== JSON.stringify(this.sort$.value) || - params.get('filter') !== JSON.stringify(this.filter$.value) + params.get("skip") !== this.skip$.value.toString() || + params.get("take") !== this.take$.value.toString() || + params.get("sort") !== JSON.stringify(this.sort$.value) || + params.get("filter") !== JSON.stringify(this.filter$.value) ) this.router .navigate([], { @@ -147,7 +147,7 @@ export class FilterService { ? JSON.stringify(this.filter$.value) : undefined, }, - queryParamsHandling: 'merge', // remove to replace all query params by provided + queryParamsHandling: "merge", // remove to replace all query params by provided replaceUrl: true, }) .then(); diff --git a/web/src/app/service/gui.service.ts b/web/src/app/service/gui.service.ts index 0203a44..d4a2772 100644 --- a/web/src/app/service/gui.service.ts +++ b/web/src/app/service/gui.service.ts @@ -1,17 +1,35 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { Logger } from 'src/app/service/logger.service'; +import { HostListener, Injectable } from "@angular/core"; +import { BehaviorSubject, filter } from "rxjs"; +import { Logger } from "src/app/service/logger.service"; +import { NavigationEnd, Router } from "@angular/router"; +import { SidebarService } from "src/app/service/sidebar.service"; -const logger = new Logger('GuiService'); +const logger = new Logger("GuiService"); @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class GuiService { isMobile$ = new BehaviorSubject(this.isMobileByWindowWith()); isTablet$ = new BehaviorSubject(this.isTabletByWindowWith()); - constructor() {} + constructor( + private router: Router, + private sidebarService: SidebarService, + ) { + this.router.events + .pipe(filter((event) => event instanceof NavigationEnd)) + .subscribe(() => { + if (this.isMobile$.value) { + this.sidebarService.hide(); + } + }); + } + + @HostListener("window:resize", ["$event"]) + onResize(event: any) { + this.checkWindowSize(); + } private isMobileByWindowWith(): boolean { return window.innerWidth <= 600; @@ -22,7 +40,7 @@ export class GuiService { } public checkWindowSize() { - logger.debug('check window size', { + logger.debug("check window size", { mobile: this.isMobileByWindowWith(), tablet: this.isTabletByWindowWith(), size: window.innerWidth, @@ -44,9 +62,9 @@ export class GuiService { b = parseInt(hex.slice(5, 7), 16); if (alpha) { - return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')'; + return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"; } else { - return 'rgb(' + r + ', ' + g + ', ' + b + ')'; + return "rgb(" + r + ", " + g + ", " + b + ")"; } } } diff --git a/web/src/app/service/logger.service.ts b/web/src/app/service/logger.service.ts index 474f265..5abdf3e 100644 --- a/web/src/app/service/logger.service.ts +++ b/web/src/app/service/logger.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@angular/core'; +import { Inject, Injectable } from "@angular/core"; export enum LogLevel { Off = 0, @@ -17,7 +17,7 @@ export type LogOutput = ( ) => void; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class Logger { static level = LogLevel.Info; @@ -57,11 +57,11 @@ export class Logger { private log(func: any, level: LogLevel, objects: any[]) { if (level <= Logger.level) { const log = this.source - ? ['[' + this.source + ']'].concat(objects) + ? ["[" + this.source + "]"].concat(objects) : objects; func.apply(console, log); - Logger.outputs.forEach(output => - output.apply(output, [this.source, level, ...objects]) + Logger.outputs.forEach((output) => + output.apply(output, [this.source, level, ...objects]), ); } } diff --git a/web/src/app/service/settings.service.ts b/web/src/app/service/settings.service.ts index 60261fd..95a3eff 100644 --- a/web/src/app/service/settings.service.ts +++ b/web/src/app/service/settings.service.ts @@ -1,11 +1,11 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { throwError } from 'rxjs'; -import { catchError } from 'rxjs/operators'; -import { AppSettings } from 'src/app/model/config/app-settings'; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { throwError } from "rxjs"; +import { catchError } from "rxjs/operators"; +import { AppSettings } from "src/app/model/config/app-settings"; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class SettingsService { appSettings!: AppSettings; @@ -15,14 +15,14 @@ export class SettingsService { loadSettings(): Promise { return new Promise((resolve, reject) => { this.http - .get('/assets/config.json') + .get("/assets/config.json") .pipe( - catchError(error => { + catchError((error) => { reject(error); return throwError(() => error); - }) + }), ) - .subscribe(settings => { + .subscribe((settings) => { this.appSettings = settings; }); }); @@ -30,21 +30,21 @@ export class SettingsService { public getTermsURL(): string { if (!this.appSettings || !this.appSettings.TermsUrl) { - return '/404'; + return "/404"; } return this.appSettings.TermsUrl; } public getPrivacyURL(): string { if (!this.appSettings || !this.appSettings.PrivacyURL) { - return '/404'; + return "/404"; } return this.appSettings.PrivacyURL; } public getImprintURL(): string { if (!this.appSettings || !this.appSettings.ImprintURL) { - return '/404'; + return "/404"; } return this.appSettings.ImprintURL; } diff --git a/web/src/app/service/sidebar.service.ts b/web/src/app/service/sidebar.service.ts index 60b9310..61ddac4 100644 --- a/web/src/app/service/sidebar.service.ts +++ b/web/src/app/service/sidebar.service.ts @@ -1,83 +1,81 @@ -import {Injectable} from '@angular/core'; -import {BehaviorSubject} from 'rxjs'; -import {MenuElement} from 'src/app/model/view/menu-element'; -import {AuthService} from 'src/app/service/auth.service'; -import {PermissionsEnum} from 'src/app/model/auth/permissionsEnum'; +import { Injectable } from "@angular/core"; +import { BehaviorSubject } from "rxjs"; +import { MenuElement } from "src/app/model/view/menu-element"; +import { AuthService } from "src/app/service/auth.service"; +import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class SidebarService { - visible$ = new BehaviorSubject(true); - elements$ = new BehaviorSubject([]); + visible$ = new BehaviorSubject(true); + elements$ = new BehaviorSubject([]); - constructor( - private auth: AuthService - ) { - this.auth.user$.subscribe(user => { - if (user) { - this.setElements().then(); - } - }); - } + constructor(private auth: AuthService) { + this.auth.user$.subscribe((user) => { + if (user) { + this.setElements().then(); + } + }); + } - show() { - this.visible$.next(true); - } + show() { + this.visible$.next(true); + } - hide() { - this.visible$.next(false); - } + hide() { + this.visible$.next(false); + } - // trust me, you'll need this async - async setElements() { - const elements: MenuElement[] = [ - { - label: 'common.groups', - icon: 'pi pi-tags', - routerLink: ['/admin/groups'], - }, - { - label: 'common.urls', - icon: 'pi pi-tag', - routerLink: ['/admin/urls'], - }, - await this.groupAdministration(), - ]; - this.elements$.next(elements); - } + // trust me, you'll need this async + async setElements() { + const elements: MenuElement[] = [ + { + label: "common.groups", + icon: "pi pi-tags", + routerLink: ["/admin/groups"], + }, + { + label: "common.urls", + icon: "pi pi-tag", + routerLink: ["/admin/urls"], + }, + await this.groupAdministration(), + ]; + this.elements$.next(elements); + } - async groupAdministration() { - return { - label: 'sidebar.administration', - icon: 'pi pi-wrench', - expanded: true, - items: [ - { - label: 'sidebar.users', - icon: 'pi pi-user', - routerLink: ['/admin/administration/users'], - visible: await this.auth.hasAnyPermissionLazy([ - PermissionsEnum.users, - ]), - }, - { - label: 'sidebar.roles', - icon: 'pi pi-user-edit', - routerLink: ['/admin/administration/roles'], - visible: await this.auth.hasAnyPermissionLazy([ - PermissionsEnum.roles, - ]), - }, - { - label: 'sidebar.api_keys', - icon: 'pi pi-key', - routerLink: ['/admin/administration/api-keys'], - visible: await this.auth.hasAnyPermissionLazy([ - PermissionsEnum.apiKeys, - ]), - }, - ], - }; - } + async groupAdministration() { + return { + label: "sidebar.administration", + icon: "pi pi-wrench", + expanded: true, + items: [ + { + label: "sidebar.users", + icon: "pi pi-user", + routerLink: ["/admin/administration/users"], + visible: await this.auth.hasAnyPermissionLazy([ + PermissionsEnum.users, + ]), + }, + { + label: "sidebar.roles", + icon: "pi pi-user-edit", + routerLink: ["/admin/administration/roles"], + visible: await this.auth.hasAnyPermissionLazy([ + PermissionsEnum.roles, + ]), + }, + { + label: "sidebar.api_keys", + icon: "pi pi-key", + routerLink: ["/admin/administration/api-keys"], + visible: await this.auth.hasAnyPermissionLazy([ + PermissionsEnum.apiKeys, + ]), + }, + ], + }; + } } diff --git a/web/src/app/service/spinner.service.ts b/web/src/app/service/spinner.service.ts index 2c55e9c..20c0f2c 100644 --- a/web/src/app/service/spinner.service.ts +++ b/web/src/app/service/spinner.service.ts @@ -1,23 +1,23 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { Logger } from 'src/app/service/logger.service'; +import { Injectable } from "@angular/core"; +import { BehaviorSubject } from "rxjs"; +import { Logger } from "src/app/service/logger.service"; -const log = new Logger('SpinnerService'); +const log = new Logger("SpinnerService"); @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class SpinnerService { showSpinnerState$ = new BehaviorSubject(false); show() { - log.trace('Show spinner'); + log.trace("Show spinner"); this.showSpinnerState$.next(true); this.toggleMain(); } hide() { - log.trace('Hide spinner'); + log.trace("Hide spinner"); this.showSpinnerState$.next(false); this.toggleMain(); } @@ -28,20 +28,20 @@ export class SpinnerService { private toggleMain() { // main can be disabled because the spinner is modal. When the spinner is active the menu does not work anyway. - const elements = document.getElementsByTagName('main'); + const elements = document.getElementsByTagName("main"); if (!elements || elements.length == 0) { return; } const element = elements[0]; if ( !this.showSpinnerState$.value && - element.classList.contains('disabled') + element.classList.contains("disabled") ) { - element.classList.remove('disabled'); + element.classList.remove("disabled"); return; } if (this.showSpinnerState$.value) { - element.classList.add('disabled'); + element.classList.add("disabled"); } } } diff --git a/web/src/app/service/toast.service.ts b/web/src/app/service/toast.service.ts index b07eac2..693f51c 100644 --- a/web/src/app/service/toast.service.ts +++ b/web/src/app/service/toast.service.ts @@ -1,15 +1,15 @@ -import { Injectable } from '@angular/core'; -import { MessageService } from 'primeng/api'; -import { TranslateService } from '@ngx-translate/core'; -import { ToastOptions } from 'src/app/model/utils/toast-options'; +import { Injectable } from "@angular/core"; +import { MessageService } from "primeng/api"; +import { TranslateService } from "@ngx-translate/core"; +import { ToastOptions } from "src/app/model/utils/toast-options"; @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class ToastService { constructor( private translate: TranslateService, - private messageService: MessageService + private messageService: MessageService, ) {} private toast( @@ -17,37 +17,37 @@ export class ToastService { summary: string, detail?: string, additionalDetail?: string, - options?: ToastOptions + options?: ToastOptions, ) { this.messageService.add({ severity: type, summary: summary ? this.translate.instant(summary) : undefined, - detail: `${detail ? this.translate.instant(detail) : ''} ${additionalDetail ?? ''}`, + detail: `${detail ? this.translate.instant(detail) : ""} ${additionalDetail ?? ""}`, life: options?.life, sticky: options?.sticky, closable: options?.closable, - contentStyleClass: 'whitespace-pre-wrap', + contentStyleClass: "whitespace-pre-wrap", }); } success(summary: string, detail?: string, options?: ToastOptions) { - this.toast('success', summary, detail, undefined, options); + this.toast("success", summary, detail, undefined, options); } info(summary: string, detail?: string, options?: ToastOptions) { - this.toast('info', summary, detail, undefined, options); + this.toast("info", summary, detail, undefined, options); } warn(summary: string, detail?: string, options?: ToastOptions) { - this.toast('warn', summary, detail, undefined, options); + this.toast("warn", summary, detail, undefined, options); } error( summary: string, detail?: string, error?: string, - options?: ToastOptions + options?: ToastOptions, ) { - this.toast('error', summary, detail, error, options); + this.toast("error", summary, detail, error, options); } } diff --git a/web/src/assets/i18n/de.json b/web/src/assets/i18n/de.json index c2519f8..09be44b 100644 --- a/web/src/assets/i18n/de.json +++ b/web/src/assets/i18n/de.json @@ -26,6 +26,8 @@ "edited_at": "Bearbeitet am", "editor": "Bearbeiter", "error": "Fehler", + "group": "Gruppe", + "groups": "Gruppen", "id": "Id", "identifier": "Identifier", "key": "Schlüssel", @@ -34,6 +36,7 @@ "role": "Rolle", "save": "Speichern", "updated": "Bearbeitet", + "urls": "URLs", "warning": "Warnung" }, "dialog": { @@ -67,6 +70,9 @@ "privacy": "Datenschutzerklärung", "terms": "ABGs" }, + "group": { + "count_header": "Gruppe(n)" + }, "header": { "logout": "Ausloggen", "menu": { @@ -99,6 +105,12 @@ "role": { "count_header": "Rolle(n)" }, + "short_url": { + "count_header": "Url(s)", + "short_url": "URL", + "target_url": "Ziel", + "visits": "Aufrufe" + }, "sidebar": { "administration": "Administration", "api_keys": "API Keys", @@ -127,4 +139,4 @@ "count_header": "Benutzer", "user": "Benutzer" } -} +} \ No newline at end of file diff --git a/web/src/assets/i18n/en.json b/web/src/assets/i18n/en.json index 94d1ee2..6676e80 100644 --- a/web/src/assets/i18n/en.json +++ b/web/src/assets/i18n/en.json @@ -26,6 +26,8 @@ "edited_at": "Edited at", "editor": "Editor", "error": "Fehler", + "group": "Group", + "groups": "Groups", "id": "Id", "identifier": "Identifier", "key": "Key", @@ -34,6 +36,7 @@ "role": "Role", "save": "Save", "updated": "Updated", + "urls": "URLs", "warning": "Warning" }, "dialog": { @@ -67,6 +70,9 @@ "privacy": "Privacy", "terms": "Terms" }, + "group": { + "count_header": "Group(s)" + }, "header": { "logout": "Logout", "menu": { @@ -99,6 +105,12 @@ "role": { "count_header": "Role(s)" }, + "short_url": { + "count_header": "Url(s)", + "short_url": "URL", + "target_url": "Target", + "visits": "Visits" + }, "sidebar": { "administration": "Administration", "api_keys": "API Keys", @@ -127,4 +139,4 @@ "count_header": "User(s)", "user": "User" } -} +} \ No newline at end of file diff --git a/web/src/environments/environment.development.ts b/web/src/environments/environment.development.ts index 3aab3f3..0907fe7 100644 --- a/web/src/environments/environment.development.ts +++ b/web/src/environments/environment.development.ts @@ -5,11 +5,11 @@ export const environment = { production: false, auth: { - url: 'https://auth.sh-edraft.de', - realm: 'dev', - clientId: 'open-redirect-client', + url: "https://auth.sh-edraft.de", + realm: "dev", + clientId: "open-redirect-client", }, api: { - url: 'https://dev.api.open-redirect.sh-edraft.de', + url: "https://dev.api.open-redirect.sh-edraft.de", }, }; diff --git a/web/src/environments/environment.local.ts b/web/src/environments/environment.local.ts index b5e169f..0130024 100644 --- a/web/src/environments/environment.local.ts +++ b/web/src/environments/environment.local.ts @@ -10,11 +10,11 @@ export const environment = { // clientId: 'open-redirect-client', // }, auth: { - url: 'https://keycloak.maxlan.de', - realm: 'develop', - clientId: 'lan-maestro-client-local', + url: "https://keycloak.maxlan.de", + realm: "develop", + clientId: "lan-maestro-client-local", }, api: { - url: 'http://localhost:5000/api', + url: "http://localhost:5000/api", }, }; diff --git a/web/src/environments/environment.production.ts b/web/src/environments/environment.production.ts index 6d68459..9a3e85c 100644 --- a/web/src/environments/environment.production.ts +++ b/web/src/environments/environment.production.ts @@ -5,11 +5,11 @@ export const environment = { production: false, auth: { - url: 'https://auth.sh-edraft.de', - realm: 'maxlan', - clientId: 'open-redirect', + url: "https://auth.sh-edraft.de", + realm: "maxlan", + clientId: "open-redirect", }, api: { - url: 'https://api.open-redirect.sh-edraft.de', + url: "https://api.open-redirect.sh-edraft.de", }, }; diff --git a/web/src/environments/environment.staging.ts b/web/src/environments/environment.staging.ts index 0041851..0599523 100644 --- a/web/src/environments/environment.staging.ts +++ b/web/src/environments/environment.staging.ts @@ -5,11 +5,11 @@ export const environment = { production: false, auth: { - url: 'https://auth.sh-edraft.de', - realm: 'develop', - clientId: 'open-redirect-client-test', + url: "https://auth.sh-edraft.de", + realm: "develop", + clientId: "open-redirect-client-test", }, api: { - url: 'https://test.api.open-redirect.sh-edraft.de', + url: "https://test.api.open-redirect.sh-edraft.de", }, }; diff --git a/web/src/environments/environment.ts b/web/src/environments/environment.ts index 72cde5a..ead5e1f 100644 --- a/web/src/environments/environment.ts +++ b/web/src/environments/environment.ts @@ -5,12 +5,12 @@ export const environment = { production: false, auth: { - url: '', - realm: '', - clientId: '', + url: "", + realm: "", + clientId: "", }, api: { - url: '', + url: "", }, }; diff --git a/web/src/main.ts b/web/src/main.ts index df5bba9..60e8a78 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -1,9 +1,9 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; -import { AppModule } from './app/app.module'; +import { AppModule } from "./app/app.module"; platformBrowserDynamic() .bootstrapModule(AppModule, { ngZoneEventCoalescing: true, }) - .catch(err => console.error(err)); + .catch((err) => console.error(err)); diff --git a/web/src/styles/mobile.scss b/web/src/styles/mobile.scss index 288041e..a199418 100644 --- a/web/src/styles/mobile.scss +++ b/web/src/styles/mobile.scss @@ -1 +1,13 @@ -@media only screen and (max-width: 600px) {} +@media only screen and (max-width: 600px) { + .form-page-input { + grid-template-columns: 1fr !important; + + .label { + grid-column: 1; + } + + .value { + grid-column: 1 !important; + } + } +}