Handle redirects & First design steps
This commit is contained in:
parent
7b8e818339
commit
99960572ea
@ -1,16 +1,27 @@
|
|||||||
from flask import redirect
|
from flask import redirect, jsonify
|
||||||
|
|
||||||
from api.route import Route
|
from api.route import Route
|
||||||
from core.logger import Logger
|
from core.logger import Logger
|
||||||
from data.schemas.public.short_url import ShortUrl
|
from data.schemas.public.short_url import ShortUrl
|
||||||
from data.schemas.public.short_url_dao import shortUrlDao
|
from data.schemas.public.short_url_dao import shortUrlDao
|
||||||
|
|
||||||
BasePath = f"/"
|
|
||||||
logger = Logger(__name__)
|
logger = Logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@Route.get(f"{BasePath}/<path:path>")
|
@Route.get(f"/api/find/<path:path>")
|
||||||
|
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/<path:path>")
|
||||||
async def handle_short_url(path: str):
|
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})
|
from_db = await shortUrlDao.find_single_by({ShortUrl.short_url: path})
|
||||||
if from_db is None:
|
if from_db is None:
|
||||||
return {"error": "Short URL not found"}, 404
|
return {"error": "Short URL not found"}, 404
|
||||||
|
@ -22,7 +22,7 @@ class FilterABC[T](ABC):
|
|||||||
filter_type: Union[Type["FilterABC"], Type[Union[int, str, bool, datetime]]],
|
filter_type: Union[Type["FilterABC"], Type[Union[int, str, bool, datetime]]],
|
||||||
db_name=None,
|
db_name=None,
|
||||||
):
|
):
|
||||||
if field not in self._obj:
|
if field not in self._obj and db_name not in self._obj:
|
||||||
return
|
return
|
||||||
|
|
||||||
if db_name is None:
|
if db_name is None:
|
||||||
|
@ -4,8 +4,8 @@ from api_graphql.abc.filter.string_filter import StringFilter
|
|||||||
|
|
||||||
class ShortUrlFilter(DbModelFilterABC):
|
class ShortUrlFilter(DbModelFilterABC):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
obj: dict,
|
obj: dict,
|
||||||
):
|
):
|
||||||
DbModelFilterABC.__init__(self, obj)
|
DbModelFilterABC.__init__(self, obj)
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ type Group implements DbModel {
|
|||||||
id: ID
|
id: ID
|
||||||
name: String
|
name: String
|
||||||
|
|
||||||
|
shortUrls: [ShortUrl]
|
||||||
|
|
||||||
deleted: Boolean
|
deleted: Boolean
|
||||||
editor: User
|
editor: User
|
||||||
createdUtc: String
|
createdUtc: String
|
||||||
|
@ -21,7 +21,6 @@ input ShortUrlSort {
|
|||||||
id: SortOrder
|
id: SortOrder
|
||||||
name: SortOrder
|
name: SortOrder
|
||||||
description: SortOrder
|
description: SortOrder
|
||||||
group: GroupSort
|
|
||||||
|
|
||||||
deleted: SortOrder
|
deleted: SortOrder
|
||||||
editorId: SortOrder
|
editorId: SortOrder
|
||||||
@ -33,7 +32,6 @@ input ShortUrlFilter {
|
|||||||
id: IntFilter
|
id: IntFilter
|
||||||
name: StringFilter
|
name: StringFilter
|
||||||
description: StringFilter
|
description: StringFilter
|
||||||
group: GroupFilter
|
|
||||||
|
|
||||||
deleted: BooleanFilter
|
deleted: BooleanFilter
|
||||||
editor: IntFilter
|
editor: IntFilter
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
from api_graphql.abc.db_model_query_abc import DbModelQueryABC
|
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):
|
class GroupQuery(DbModelQueryABC):
|
||||||
@ -6,3 +9,8 @@ class GroupQuery(DbModelQueryABC):
|
|||||||
DbModelQueryABC.__init__(self, "Group")
|
DbModelQueryABC.__init__(self, "Group")
|
||||||
|
|
||||||
self.set_field("name", lambda x, *_: x.name)
|
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})
|
||||||
|
@ -85,7 +85,13 @@ class Query(QueryABC):
|
|||||||
.with_dao(groupDao)
|
.with_dao(groupDao)
|
||||||
.with_filter(GroupFilter)
|
.with_filter(GroupFilter)
|
||||||
.with_sort(Sort[Group])
|
.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
|
# partially public to load redirect if not resolved/redirected by api
|
||||||
self.field(
|
self.field(
|
||||||
|
@ -5,6 +5,7 @@ from async_property import async_property
|
|||||||
|
|
||||||
from core.database.abc.db_model_abc import DbModelABC
|
from core.database.abc.db_model_abc import DbModelABC
|
||||||
from core.typing import SerialId
|
from core.typing import SerialId
|
||||||
|
from data.schemas.public.group import Group
|
||||||
|
|
||||||
|
|
||||||
class ShortUrl(DbModelABC):
|
class ShortUrl(DbModelABC):
|
||||||
@ -59,7 +60,16 @@ class ShortUrl(DbModelABC):
|
|||||||
self._group_id = value
|
self._group_id = value
|
||||||
|
|
||||||
@async_property
|
@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
|
from data.schemas.public.group_dao import groupDao
|
||||||
|
|
||||||
return await groupDao.get_by_id(self._group_id)
|
return await groupDao.get_by_id(self._group_id)
|
||||||
|
|
||||||
|
def to_dto(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"target_url": self.target_url,
|
||||||
|
}
|
||||||
|
@ -1,34 +1,23 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { NotFoundComponent } from 'src/app/components/error/not-found/not-found.component';
|
import { NotFoundComponent } from "src/app/components/error/not-found/not-found.component";
|
||||||
import { AuthGuard } from 'src/app/core/guard/auth.guard';
|
import { AuthGuard } from "src/app/core/guard/auth.guard";
|
||||||
import {HomeComponent} from "src/app/components/home/home.component";
|
import { HomeComponent } from "src/app/components/home/home.component";
|
||||||
|
import { RedirectComponent } from "src/app/components/redirect/redirect.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: "",
|
||||||
redirectTo: 'home',
|
|
||||||
pathMatch: 'full',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'about',
|
|
||||||
redirectTo: 'home',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'home',
|
|
||||||
component: HomeComponent,
|
component: HomeComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'admin',
|
path: "admin",
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('./modules/admin/admin.module').then(m => m.AdminModule),
|
import("./modules/admin/admin.module").then((m) => m.AdminModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
{ path: '404', component: NotFoundComponent },
|
{ path: "404", component: NotFoundComponent },
|
||||||
{
|
{ path: "**", component: RedirectComponent },
|
||||||
path: '**',
|
|
||||||
redirectTo: '404',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -1,32 +1,36 @@
|
|||||||
<main>
|
<main *ngIf="isLoggedIn; else home">
|
||||||
<app-header></app-header>
|
<app-header></app-header>
|
||||||
|
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<aside *ngIf="showSidebar">
|
<aside *ngIf="showSidebar">
|
||||||
<app-sidebar></app-sidebar>
|
<app-sidebar></app-sidebar>
|
||||||
</aside>
|
</aside>
|
||||||
<section class="component">
|
<section class="component">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<app-footer></app-footer>
|
<app-footer></app-footer>
|
||||||
|
|
||||||
<p-toast></p-toast>
|
<p-toast></p-toast>
|
||||||
<p-confirmDialog #cd key="confirmConfirmationDialog" [baseZIndex]="10000">
|
<p-confirmDialog #cd key="confirmConfirmationDialog" [baseZIndex]="10000">
|
||||||
<ng-template pTemplate="footer">
|
<ng-template pTemplate="footer">
|
||||||
<div class="flex gap-2.5 items-center justify-end">
|
<div class="flex gap-2.5 items-center justify-end">
|
||||||
<p-button
|
<p-button
|
||||||
label="{{ 'dialog.abort' | translate }}"
|
label="{{ 'dialog.abort' | translate }}"
|
||||||
class="btn icon-btn danger-icon-btn"
|
class="btn icon-btn danger-icon-btn"
|
||||||
icon="pi pi-times-circle"
|
icon="pi pi-times-circle"
|
||||||
(onClick)="cd.reject()"></p-button>
|
(onClick)="cd.reject()"></p-button>
|
||||||
<p-button
|
<p-button
|
||||||
label="{{ 'dialog.confirm' | translate }}"
|
label="{{ 'dialog.confirm' | translate }}"
|
||||||
class="btn"
|
class="btn"
|
||||||
icon="pi pi-check-circle"
|
icon="pi pi-check-circle"
|
||||||
(onClick)="cd.accept()"></p-button>
|
(onClick)="cd.accept()"></p-button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</p-confirmDialog>
|
</p-confirmDialog>
|
||||||
</main>
|
</main>
|
||||||
<app-spinner></app-spinner>
|
<app-spinner></app-spinner>
|
||||||
|
|
||||||
|
<ng-template #home>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</ng-template>
|
@ -1,22 +1,22 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from "@angular/core/testing";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from "./app.component";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { HomeComponent } from 'src/app/components/home/home.component';
|
import { HomeComponent } from "src/app/components/home/home.component";
|
||||||
import { FooterComponent } from 'src/app/components/footer/footer.component';
|
import { FooterComponent } from "src/app/components/footer/footer.component";
|
||||||
import { HeaderComponent } from 'src/app/components/header/header.component';
|
import { HeaderComponent } from "src/app/components/header/header.component";
|
||||||
import { NotFoundComponent } from 'src/app/components/error/not-found/not-found.component';
|
import { NotFoundComponent } from "src/app/components/error/not-found/not-found.component";
|
||||||
import { SpinnerComponent } from 'src/app/components/spinner/spinner.component';
|
import { SpinnerComponent } from "src/app/components/spinner/spinner.component";
|
||||||
import { SidebarComponent } from 'src/app/components/sidebar/sidebar.component';
|
import { SidebarComponent } from "src/app/components/sidebar/sidebar.component";
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe("AppComponent", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -46,7 +46,7 @@ describe('AppComponent', () => {
|
|||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the app', () => {
|
it("should create the app", () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
const app = fixture.componentInstance;
|
const app = fixture.componentInstance;
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
|
@ -1,27 +1,32 @@
|
|||||||
import { Component, OnDestroy } from '@angular/core';
|
import { Component, OnDestroy } from "@angular/core";
|
||||||
import { SidebarService } from 'src/app/service/sidebar.service';
|
import { SidebarService } from "src/app/service/sidebar.service";
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from "rxjs";
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from "rxjs/operators";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: "app-root",
|
||||||
templateUrl: './app.component.html',
|
templateUrl: "./app.component.html",
|
||||||
styleUrl: './app.component.scss',
|
styleUrl: "./app.component.scss",
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnDestroy {
|
export class AppComponent implements OnDestroy {
|
||||||
showSidebar = false;
|
showSidebar = false;
|
||||||
|
isLoggedIn = false;
|
||||||
unsubscribe$ = new Subject<void>();
|
unsubscribe$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sidebar: SidebarService,
|
private sidebar: SidebarService,
|
||||||
private auth: AuthService
|
private auth: AuthService,
|
||||||
) {
|
) {
|
||||||
this.auth.loadUser();
|
this.auth.loadUser();
|
||||||
|
|
||||||
|
this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe((user) => {
|
||||||
|
this.isLoggedIn = user !== null && user !== undefined;
|
||||||
|
});
|
||||||
|
|
||||||
this.sidebar.visible$
|
this.sidebar.visible$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(visible => {
|
.subscribe((visible) => {
|
||||||
this.showSidebar = visible;
|
this.showSidebar = visible;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,27 @@
|
|||||||
import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core';
|
import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core";
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from "@angular/platform-browser";
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from "./app-routing.module";
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from "./app.component";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { HomeComponent } from './components/home/home.component';
|
import { initializeKeycloak } from "./core/init-keycloak";
|
||||||
import { initializeKeycloak } from './core/init-keycloak';
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { environment } from "../environments/environment";
|
||||||
import { environment } from '../environments/environment';
|
import { FooterComponent } from "src/app/components/footer/footer.component";
|
||||||
import { FooterComponent } from 'src/app/components/footer/footer.component';
|
import { HeaderComponent } from "src/app/components/header/header.component";
|
||||||
import { HeaderComponent } from 'src/app/components/header/header.component';
|
import { NotFoundComponent } from "src/app/components/error/not-found/not-found.component";
|
||||||
import { NotFoundComponent } from 'src/app/components/error/not-found/not-found.component';
|
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
|
||||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
import { Logger } from "src/app/service/logger.service";
|
||||||
import { Logger } from 'src/app/service/logger.service';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SpinnerComponent } from "src/app/components/spinner/spinner.component";
|
||||||
import { SpinnerComponent } from 'src/app/components/spinner/spinner.component';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { DialogService } from "primeng/dynamicdialog";
|
||||||
import { DialogService } from 'primeng/dynamicdialog';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { SidebarComponent } from "./components/sidebar/sidebar.component";
|
||||||
import { SidebarComponent } from './components/sidebar/sidebar.component';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
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) {
|
if (environment.production) {
|
||||||
Logger.enableProductionMode();
|
Logger.enableProductionMode();
|
||||||
@ -35,12 +36,13 @@ export function HttpLoaderFactory(http: HttpClient) {
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HomeComponent,
|
|
||||||
FooterComponent,
|
FooterComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
NotFoundComponent,
|
NotFoundComponent,
|
||||||
SpinnerComponent,
|
SpinnerComponent,
|
||||||
SidebarComponent,
|
SidebarComponent,
|
||||||
|
HomeComponent,
|
||||||
|
RedirectComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -48,7 +50,7 @@ export function HttpLoaderFactory(http: HttpClient) {
|
|||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
defaultLanguage: 'en',
|
defaultLanguage: "en",
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useFactory: HttpLoaderFactory,
|
useFactory: HttpLoaderFactory,
|
||||||
|
@ -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 { NotFoundComponent } from "src/app/components/error/not-found/not-found.component";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
|
|
||||||
describe('NotFoundComponent', () => {
|
describe("NotFoundComponent", () => {
|
||||||
let component: NotFoundComponent;
|
let component: NotFoundComponent;
|
||||||
let fixture: ComponentFixture<NotFoundComponent>;
|
let fixture: ComponentFixture<NotFoundComponent>;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ describe('NotFoundComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-not-found',
|
selector: "app-not-found",
|
||||||
templateUrl: './not-found.component.html',
|
templateUrl: "./not-found.component.html",
|
||||||
styleUrls: ['./not-found.component.scss'],
|
styleUrls: ["./not-found.component.scss"],
|
||||||
})
|
})
|
||||||
export class NotFoundComponent {}
|
export class NotFoundComponent {}
|
||||||
|
@ -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 { FooterComponent } from "src/app/components/footer/footer.component";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from "@angular/platform-browser";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
|
|
||||||
describe('FooterComponent', () => {
|
describe("FooterComponent", () => {
|
||||||
let component: FooterComponent;
|
let component: FooterComponent;
|
||||||
let fixture: ComponentFixture<FooterComponent>;
|
let fixture: ComponentFixture<FooterComponent>;
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ describe('FooterComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { SettingsService } from 'src/app/service/settings.service';
|
import { SettingsService } from "src/app/service/settings.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-footer',
|
selector: "app-footer",
|
||||||
templateUrl: './footer.component.html',
|
templateUrl: "./footer.component.html",
|
||||||
styleUrls: ['./footer.component.scss'],
|
styleUrls: ["./footer.component.scss"],
|
||||||
})
|
})
|
||||||
export class FooterComponent {
|
export class FooterComponent {
|
||||||
constructor(private settings: SettingsService) {}
|
constructor(private settings: SettingsService) {}
|
||||||
|
@ -1,47 +1,53 @@
|
|||||||
<header>
|
<header>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
|
<p-button
|
||||||
|
*ngIf="user"
|
||||||
|
icon="pi pi-bars"
|
||||||
|
class="btn icon-btn p-button-text"
|
||||||
|
(onClick)="toggleSidebar()"
|
||||||
|
></p-button>
|
||||||
|
</div>
|
||||||
|
<div class="logo">
|
||||||
|
<!-- <img src="/assets/images/logo.svg" alt="logo"/>-->
|
||||||
|
</div>
|
||||||
|
<div class="app-name">
|
||||||
|
<h1>Open-redirect</h1>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="logo">
|
|
||||||
<!-- <img src="/assets/images/logo.svg" alt="logo"/>-->
|
|
||||||
</div>
|
|
||||||
<div class="app-name">
|
|
||||||
<h1>Open-redirect</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<p-button
|
<div class="flex items-center justify-center">
|
||||||
type="button"
|
<p-button
|
||||||
icon="pi pi-globe"
|
type="button"
|
||||||
class="btn icon-btn p-button-text"
|
icon="pi pi-globe"
|
||||||
(onClick)="langMenu.toggle($event)"></p-button>
|
class="btn icon-btn p-button-text"
|
||||||
<p-menu
|
(onClick)="langMenu.toggle($event)"></p-button>
|
||||||
#langMenu
|
<p-menu
|
||||||
[popup]="true"
|
#langMenu
|
||||||
[model]="langList"
|
[popup]="true"
|
||||||
class="lang-menu"></p-menu>
|
[model]="langList"
|
||||||
</div>
|
class="lang-menu"></p-menu>
|
||||||
<div class="flex items-center justify-center">
|
</div>
|
||||||
<p-button
|
<div class="flex items-center justify-center">
|
||||||
*ngIf="!user; else loggedIn"
|
<p-button
|
||||||
icon="pi pi-sign-in"
|
*ngIf="!user; else loggedIn"
|
||||||
class="btn icon-btn p-button-text"
|
icon="pi pi-sign-in"
|
||||||
(onClick)="login()"></p-button>
|
class="btn icon-btn p-button-text"
|
||||||
|
(onClick)="login()"></p-button>
|
||||||
|
|
||||||
<ng-template #loggedIn>
|
<ng-template #loggedIn>
|
||||||
<p-button
|
<p-button
|
||||||
type="button"
|
type="button"
|
||||||
icon="pi pi-user"
|
icon="pi pi-user"
|
||||||
class="btn icon-btn p-button-text"
|
class="btn icon-btn p-button-text"
|
||||||
(onClick)="userMenu.toggle($event)"></p-button>
|
(onClick)="userMenu.toggle($event)"></p-button>
|
||||||
<p-menu
|
<p-menu
|
||||||
#userMenu
|
#userMenu
|
||||||
[popup]="true"
|
[popup]="true"
|
||||||
[model]="userMenuList"
|
[model]="userMenuList"
|
||||||
class="user-menu"></p-menu>
|
class="user-menu"></p-menu>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { HeaderComponent } from 'src/app/components/header/header.component';
|
import { HeaderComponent } from "src/app/components/header/header.component";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
|
|
||||||
describe('HeaderComponent', () => {
|
describe("HeaderComponent", () => {
|
||||||
let component: HeaderComponent;
|
let component: HeaderComponent;
|
||||||
let fixture: ComponentFixture<HeaderComponent>;
|
let fixture: ComponentFixture<HeaderComponent>;
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ describe('HeaderComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { MenuItem, PrimeNGConfig } from 'primeng/api';
|
import { MenuItem, PrimeNGConfig } from "primeng/api";
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from "rxjs";
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from "rxjs/operators";
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { GuiService } from 'src/app/service/gui.service';
|
import { GuiService } from "src/app/service/gui.service";
|
||||||
import { User } from 'src/app/model/auth/user';
|
import { User } from "src/app/model/auth/user";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { MenuElement } from 'src/app/model/view/menu-element';
|
import { MenuElement } from "src/app/model/view/menu-element";
|
||||||
|
import { SidebarService } from "src/app/service/sidebar.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-header',
|
selector: "app-header",
|
||||||
templateUrl: './header.component.html',
|
templateUrl: "./header.component.html",
|
||||||
styleUrls: ['./header.component.scss'],
|
styleUrls: ["./header.component.scss"],
|
||||||
})
|
})
|
||||||
export class HeaderComponent implements OnInit, OnDestroy {
|
export class HeaderComponent implements OnInit, OnDestroy {
|
||||||
langList: MenuItem[] = [];
|
langList: MenuItem[] = [];
|
||||||
@ -26,18 +27,24 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private config: PrimeNGConfig,
|
private config: PrimeNGConfig,
|
||||||
private guiService: GuiService,
|
private guiService: GuiService,
|
||||||
private auth: AuthService
|
private auth: AuthService,
|
||||||
|
private sidebarService: SidebarService,
|
||||||
) {
|
) {
|
||||||
this.guiService.isMobile$
|
this.guiService.isMobile$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(isMobile => {
|
.subscribe((isMobile) => {
|
||||||
this.isMobile = isMobile;
|
this.isMobile = isMobile;
|
||||||
|
if (isMobile) {
|
||||||
|
this.sidebarService.hide();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe(async user => {
|
this.auth.user$
|
||||||
this.user = user;
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
await this.initMenuLists();
|
.subscribe(async (user) => {
|
||||||
});
|
this.user = user;
|
||||||
|
await this.initMenuLists();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -55,49 +62,24 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initMenuLists() {
|
async initMenuLists() {
|
||||||
await this.initMenuList();
|
|
||||||
await this.initLangMenuList();
|
await this.initLangMenuList();
|
||||||
await this.initUserMenuList();
|
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() {
|
async initLangMenuList() {
|
||||||
this.langList = [
|
this.langList = [
|
||||||
{
|
{
|
||||||
label: 'English',
|
label: "English",
|
||||||
command: () => {
|
command: () => {
|
||||||
this.translate('en');
|
this.translate("en");
|
||||||
this.setLang('en');
|
this.setLang("en");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Deutsch',
|
label: "Deutsch",
|
||||||
command: () => {
|
command: () => {
|
||||||
this.translate('de');
|
this.translate("de");
|
||||||
this.setLang('de');
|
this.setLang("de");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -114,13 +96,13 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||||||
separator: true,
|
separator: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.translateService.instant('header.logout'),
|
label: this.translateService.instant("header.logout"),
|
||||||
command: () => {
|
command: () => {
|
||||||
this.auth.logout().then(() => {
|
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) {
|
translate(lang: string) {
|
||||||
this.translateService.use(lang);
|
this.translateService.use(lang);
|
||||||
this.translateService
|
this.translateService
|
||||||
.get('primeng')
|
.get("primeng")
|
||||||
.subscribe(res => this.config.setTranslation(res));
|
.subscribe((res) => this.config.setTranslation(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadLang() {
|
async loadLang() {
|
||||||
const lang = 'en';
|
const lang = "en";
|
||||||
this.setLang(lang);
|
this.setLang(lang);
|
||||||
this.translate(lang);
|
this.translate(lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
setLang(lang: string) {
|
setLang(lang: string) {
|
||||||
// this.settings.setSetting(`lang`, lang);
|
// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<p>home works!</p>
|
|
@ -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 component: HomeComponent;
|
||||||
let fixture: ComponentFixture<HomeComponent>;
|
let fixture: ComponentFixture<HomeComponent>;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ describe('HomeComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: "app-home",
|
||||||
templateUrl: './home.component.html',
|
templateUrl: "./home.component.html",
|
||||||
styleUrl: './home.component.scss',
|
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(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1
web/src/app/components/redirect/redirect.component.html
Normal file
1
web/src/app/components/redirect/redirect.component.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>redirect works!</p>
|
22
web/src/app/components/redirect/redirect.component.spec.ts
Normal file
22
web/src/app/components/redirect/redirect.component.spec.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
|
||||||
|
import { RedirectComponent } from "./redirect.component";
|
||||||
|
|
||||||
|
describe("RedirectComponent", () => {
|
||||||
|
let component: RedirectComponent;
|
||||||
|
let fixture: ComponentFixture<RedirectComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [RedirectComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RedirectComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create", () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
47
web/src/app/components/redirect/redirect.component.ts
Normal file
47
web/src/app/components/redirect/redirect.component.ts
Normal file
@ -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<ShortUrlDto | undefined>(`${environment.api.url}/find/${shortUrl}`)
|
||||||
|
.subscribe(async (response) => {
|
||||||
|
if (!response) {
|
||||||
|
await this.router.navigate(["/404"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = `${environment.api.url}/redirect/${shortUrl}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,17 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
|
||||||
import { SidebarComponent } from './sidebar.component';
|
import { SidebarComponent } from "./sidebar.component";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
|
|
||||||
describe('SidebarComponent', () => {
|
describe("SidebarComponent", () => {
|
||||||
let component: SidebarComponent;
|
let component: SidebarComponent;
|
||||||
let fixture: ComponentFixture<SidebarComponent>;
|
let fixture: ComponentFixture<SidebarComponent>;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ describe('SidebarComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
import {Component, OnDestroy} from '@angular/core';
|
import { Component, OnDestroy } from "@angular/core";
|
||||||
import {MenuElement} from 'src/app/model/view/menu-element';
|
import { MenuElement } from "src/app/model/view/menu-element";
|
||||||
import {Subject} from 'rxjs';
|
import { Subject } from "rxjs";
|
||||||
import {SidebarService} from 'src/app/service/sidebar.service';
|
import { SidebarService } from "src/app/service/sidebar.service";
|
||||||
import {takeUntil} from 'rxjs/operators';
|
import { takeUntil } from "rxjs/operators";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sidebar',
|
selector: "app-sidebar",
|
||||||
templateUrl: './sidebar.component.html',
|
templateUrl: "./sidebar.component.html",
|
||||||
styleUrl: './sidebar.component.scss',
|
styleUrl: "./sidebar.component.scss",
|
||||||
})
|
})
|
||||||
export class SidebarComponent implements OnDestroy {
|
export class SidebarComponent implements OnDestroy {
|
||||||
elements: MenuElement[] = [];
|
elements: MenuElement[] = [];
|
||||||
|
|
||||||
unsubscribe$ = new Subject<void>();
|
unsubscribe$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(private sidebar: SidebarService) {
|
constructor(private sidebar: SidebarService) {
|
||||||
this.sidebar.elements$
|
this.sidebar.elements$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(elements => {
|
.subscribe((elements) => {
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.unsubscribe$.next();
|
this.unsubscribe$.next();
|
||||||
this.unsubscribe$.complete();
|
this.unsubscribe$.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 component: SpinnerComponent;
|
||||||
let fixture: ComponentFixture<SpinnerComponent>;
|
let fixture: ComponentFixture<SpinnerComponent>;
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ describe('SpinnerComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-spinner',
|
selector: "app-spinner",
|
||||||
templateUrl: './spinner.component.html',
|
templateUrl: "./spinner.component.html",
|
||||||
styleUrls: ['./spinner.component.scss'],
|
styleUrls: ["./spinner.component.scss"],
|
||||||
})
|
})
|
||||||
export class SpinnerComponent {
|
export class SpinnerComponent {
|
||||||
showSpinnerState = false;
|
showSpinnerState = false;
|
||||||
|
|
||||||
constructor(public spinnerService: SpinnerService) {
|
constructor(public spinnerService: SpinnerService) {
|
||||||
this.spinnerService.showSpinnerState$.subscribe(value => {
|
this.spinnerService.showSpinnerState$.subscribe((value) => {
|
||||||
this.showSpinnerState = value;
|
this.showSpinnerState = value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Directive, inject } from '@angular/core';
|
import { Directive, inject } from "@angular/core";
|
||||||
import { PageDataService } from 'src/app/core/base/page.data.service';
|
import { PageDataService } from "src/app/core/base/page.data.service";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
import { FilterService } from 'src/app/service/filter.service';
|
import { FilterService } from "src/app/service/filter.service";
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export abstract class FormPageBase<
|
export abstract class FormPageBase<
|
||||||
@ -27,7 +27,7 @@ export abstract class FormPageBase<
|
|||||||
protected dataService = inject(PageDataService) as S;
|
protected dataService = inject(PageDataService) as S;
|
||||||
|
|
||||||
protected constructor() {
|
protected constructor() {
|
||||||
const id = this.route.snapshot.params['id'];
|
const id = this.route.snapshot.params["id"];
|
||||||
this.validateRoute(id);
|
this.validateRoute(id);
|
||||||
|
|
||||||
this.buildForm();
|
this.buildForm();
|
||||||
@ -35,18 +35,18 @@ export abstract class FormPageBase<
|
|||||||
|
|
||||||
validateRoute(id: string | undefined) {
|
validateRoute(id: string | undefined) {
|
||||||
const url = this.router.url;
|
const url = this.router.url;
|
||||||
if (url.endsWith('create') && id !== undefined) {
|
if (url.endsWith("create") && id !== undefined) {
|
||||||
throw new Error('Route ends with create but id is defined');
|
throw new Error("Route ends with create but id is defined");
|
||||||
}
|
}
|
||||||
if (url.endsWith('edit') && (id === undefined || isNaN(+id))) {
|
if (url.endsWith("edit") && (id === undefined || isNaN(+id))) {
|
||||||
throw new Error('Route ends with edit but id is not a number');
|
throw new Error("Route ends with edit but id is not a number");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nodeId = id ? +id : undefined;
|
this.nodeId = id ? +id : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
const backRoute = this.nodeId ? '../..' : '..';
|
const backRoute = this.nodeId ? "../.." : "..";
|
||||||
|
|
||||||
this.router.navigate([backRoute], { relativeTo: this.route }).then(() => {
|
this.router.navigate([backRoute], { relativeTo: this.route }).then(() => {
|
||||||
this.filterService.onLoad.emit();
|
this.filterService.onLoad.emit();
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { Directive, inject, OnDestroy } from '@angular/core';
|
import { Directive, inject, OnDestroy } from "@angular/core";
|
||||||
import { PageDataService } from 'src/app/core/base/page.data.service';
|
import { PageDataService } from "src/app/core/base/page.data.service";
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from "rxjs";
|
||||||
import { Logger } from 'src/app/service/logger.service';
|
import { Logger } from "src/app/service/logger.service";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
import { FilterService } from 'src/app/service/filter.service';
|
import { FilterService } from "src/app/service/filter.service";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from "rxjs/operators";
|
||||||
import { PaginatorState } from 'primeng/paginator';
|
import { PaginatorState } from "primeng/paginator";
|
||||||
import { PageColumns } from 'src/app/core/base/page.columns';
|
import { PageColumns } from "src/app/core/base/page.columns";
|
||||||
import {
|
import {
|
||||||
TableColumn,
|
TableColumn,
|
||||||
TableRequireAnyPermissions,
|
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()
|
@Directive()
|
||||||
export abstract class PageBase<
|
export abstract class PageBase<
|
||||||
@ -96,13 +96,13 @@ export abstract class PageBase<
|
|||||||
}
|
}
|
||||||
|
|
||||||
columns: TableColumn<T>[] =
|
columns: TableColumn<T>[] =
|
||||||
'get' in this.columnsService ? this.columnsService.get() : [];
|
"get" in this.columnsService ? this.columnsService.get() : [];
|
||||||
|
|
||||||
protected unsubscribe$ = new Subject<void>();
|
protected unsubscribe$ = new Subject<void>();
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
useQueryParams = false,
|
useQueryParams = false,
|
||||||
permissions?: TableRequireAnyPermissions
|
permissions?: TableRequireAnyPermissions,
|
||||||
) {
|
) {
|
||||||
this.subscribeToFilterService();
|
this.subscribeToFilterService();
|
||||||
this.filterService.reset({
|
this.filterService.reset({
|
||||||
@ -113,7 +113,7 @@ export abstract class PageBase<
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
logger.trace('Destroy component');
|
logger.trace("Destroy component");
|
||||||
this.unsubscribe$.next();
|
this.unsubscribe$.next();
|
||||||
this.unsubscribe$.complete();
|
this.unsubscribe$.complete();
|
||||||
}
|
}
|
||||||
@ -125,26 +125,26 @@ export abstract class PageBase<
|
|||||||
|
|
||||||
this.filterService.filter$
|
this.filterService.filter$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(filter => {
|
.subscribe((filter) => {
|
||||||
this._filter = filter;
|
this._filter = filter;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filterService.sort$
|
this.filterService.sort$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(sort => {
|
.subscribe((sort) => {
|
||||||
this._sort = sort;
|
this._sort = sort;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filterService.skip$
|
this.filterService.skip$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(skip => {
|
.subscribe((skip) => {
|
||||||
this._skip = skip;
|
this._skip = skip;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filterService.take$
|
this.filterService.take$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(take => {
|
.subscribe((take) => {
|
||||||
if (take && Object.prototype.hasOwnProperty.call(take, 'showAll')) {
|
if (take && Object.prototype.hasOwnProperty.call(take, "showAll")) {
|
||||||
this._take = 0;
|
this._take = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,67 +1,67 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { TableColumn } from 'src/app/modules/shared/components/table/table.model';
|
import { TableColumn } from "src/app/modules/shared/components/table/table.model";
|
||||||
import { DbModel } from 'src/app/model/entities/db-model';
|
import { DbModel } from "src/app/model/entities/db-model";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export abstract class PageColumns<T> {
|
export abstract class PageColumns<T> {
|
||||||
abstract get(): TableColumn<T>[];
|
abstract get(): TableColumn<T>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ID_COLUMN = {
|
export const ID_COLUMN = {
|
||||||
name: 'id',
|
name: "id",
|
||||||
label: 'common.id',
|
label: "common.id",
|
||||||
type: 'number',
|
type: "number",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: { id?: number }) => row.id,
|
value: (row: { id?: number }) => row.id,
|
||||||
class: 'max-w-24',
|
class: "max-w-24",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NAME_COLUMN = {
|
export const NAME_COLUMN = {
|
||||||
name: 'name',
|
name: "name",
|
||||||
label: 'common.name',
|
label: "common.name",
|
||||||
type: 'text',
|
type: "text",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: { name?: string }) => row.name,
|
value: (row: { name?: string }) => row.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DESCRIPTION_COLUMN = {
|
export const DESCRIPTION_COLUMN = {
|
||||||
name: 'description',
|
name: "description",
|
||||||
label: 'common.description',
|
label: "common.description",
|
||||||
type: 'text',
|
type: "text",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: { description?: string }) => row.description,
|
value: (row: { description?: string }) => row.description,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DELETED_COLUMN = {
|
export const DELETED_COLUMN = {
|
||||||
name: 'deleted',
|
name: "deleted",
|
||||||
label: 'common.deleted',
|
label: "common.deleted",
|
||||||
type: 'bool',
|
type: "bool",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: DbModel) => row.deleted,
|
value: (row: DbModel) => row.deleted,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EDITOR_COLUMN = {
|
export const EDITOR_COLUMN = {
|
||||||
name: 'editor',
|
name: "editor",
|
||||||
label: 'common.editor',
|
label: "common.editor",
|
||||||
value: (row: DbModel) => row.editor?.username,
|
value: (row: DbModel) => row.editor?.username,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CREATED_UTC_COLUMN = {
|
export const CREATED_UTC_COLUMN = {
|
||||||
name: 'createdUtc',
|
name: "createdUtc",
|
||||||
label: 'common.created',
|
label: "common.created",
|
||||||
type: 'date',
|
type: "date",
|
||||||
value: (row: DbModel) => row.createdUtc,
|
value: (row: DbModel) => row.createdUtc,
|
||||||
class: 'max-w-32',
|
class: "max-w-32",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UPDATED_UTC_COLUMN = {
|
export const UPDATED_UTC_COLUMN = {
|
||||||
name: 'updatedUtc',
|
name: "updatedUtc",
|
||||||
label: 'common.updated',
|
label: "common.updated",
|
||||||
type: 'date',
|
type: "date",
|
||||||
value: (row: DbModel) => row.updatedUtc,
|
value: (row: DbModel) => row.updatedUtc,
|
||||||
class: 'max-w-32',
|
class: "max-w-32",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DB_MODEL_COLUMNS = [
|
export const DB_MODEL_COLUMNS = [
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from "rxjs";
|
||||||
import { MutationResult } from 'apollo-angular';
|
import { MutationResult } from "apollo-angular";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export abstract class PageDataService<T> {
|
export abstract class PageDataService<T> {
|
||||||
abstract load(
|
abstract load(
|
||||||
filter?: Filter[],
|
filter?: Filter[],
|
||||||
sort?: Sort[],
|
sort?: Sort[],
|
||||||
skip?: number,
|
skip?: number,
|
||||||
take?: number
|
take?: number,
|
||||||
): Observable<QueryResult<T>>;
|
): Observable<QueryResult<T>>;
|
||||||
|
|
||||||
abstract loadById(id: number): Observable<T>;
|
abstract loadById(id: number): Observable<T>;
|
||||||
@ -29,12 +29,12 @@ export interface Update<T, U> {
|
|||||||
|
|
||||||
export interface Delete<T> {
|
export interface Delete<T> {
|
||||||
delete(
|
delete(
|
||||||
object: T
|
object: T,
|
||||||
): Observable<T | undefined | boolean> | Observable<MutationResult>;
|
): Observable<T | undefined | boolean> | Observable<MutationResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Restore<T> {
|
export interface Restore<T> {
|
||||||
restore(
|
restore(
|
||||||
object: T
|
object: T,
|
||||||
): Observable<T | undefined | boolean> | Observable<MutationResult>;
|
): Observable<T | undefined | boolean> | Observable<MutationResult>;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { CanActivate } from '@angular/router';
|
import { CanActivate } from "@angular/router";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthGuard implements CanActivate {
|
||||||
constructor(private keycloak: KeycloakService) {}
|
constructor(private keycloak: KeycloakService) {}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { ActivatedRouteSnapshot, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router } from "@angular/router";
|
||||||
import { Logger } from 'src/app/service/logger.service';
|
import { Logger } from "src/app/service/logger.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
|
|
||||||
const log = new Logger('PermissionGuard');
|
const log = new Logger("PermissionGuard");
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class PermissionGuard {
|
export class PermissionGuard {
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private auth: AuthService
|
private auth: AuthService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||||
const permissions = route.data['permissions'] as PermissionsEnum[];
|
const permissions = route.data["permissions"] as PermissionsEnum[];
|
||||||
|
|
||||||
if (!permissions || permissions.length === 0) {
|
if (!permissions || permissions.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
@ -26,11 +26,11 @@ export class PermissionGuard {
|
|||||||
|
|
||||||
const validate = await this.auth.hasAnyPermissionLazy(permissions);
|
const validate = await this.auth.hasAnyPermissionLazy(permissions);
|
||||||
if (!validate) {
|
if (!validate) {
|
||||||
log.debug('Permission denied', permissions);
|
log.debug("Permission denied", permissions);
|
||||||
this.toast.warn('common.warning', 'error.permission_denied');
|
this.toast.warn("common.warning", "error.permission_denied");
|
||||||
this.router.navigate(['/']).then();
|
this.router.navigate(["/"]).then();
|
||||||
}
|
}
|
||||||
log.debug('Permission granted', permissions);
|
log.debug("Permission granted", permissions);
|
||||||
return validate;
|
return validate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import {KeycloakService} from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import {environment} from 'src/environments/environment';
|
import { environment } from "src/environments/environment";
|
||||||
|
|
||||||
export function initializeKeycloak(keycloak: KeycloakService) {
|
export function initializeKeycloak(keycloak: KeycloakService) {
|
||||||
return () =>
|
return () =>
|
||||||
keycloak.init({
|
keycloak.init({
|
||||||
config: {
|
config: {
|
||||||
url: environment.auth.url,
|
url: environment.auth.url,
|
||||||
realm: environment.auth.realm,
|
realm: environment.auth.realm,
|
||||||
clientId: environment.auth.clientId,
|
clientId: environment.auth.clientId,
|
||||||
},
|
},
|
||||||
initOptions: {
|
initOptions: {
|
||||||
onLoad: 'check-sso',
|
onLoad: "check-sso",
|
||||||
},
|
},
|
||||||
enableBearerInterceptor: false,
|
enableBearerInterceptor: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { HttpInterceptorFn } from '@angular/common/http';
|
import { HttpInterceptorFn } from "@angular/common/http";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { inject } from '@angular/core';
|
import { inject } from "@angular/core";
|
||||||
import { from, switchMap } from 'rxjs';
|
import { from, switchMap } from "rxjs";
|
||||||
|
|
||||||
export const tokenInterceptor: HttpInterceptorFn = (req, next) => {
|
export const tokenInterceptor: HttpInterceptorFn = (req, next) => {
|
||||||
const keycloak = inject(KeycloakService);
|
const keycloak = inject(KeycloakService);
|
||||||
@ -15,14 +15,14 @@ export const tokenInterceptor: HttpInterceptorFn = (req, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return from(keycloak.getToken()).pipe(
|
return from(keycloak.getToken()).pipe(
|
||||||
switchMap(token => {
|
switchMap((token) => {
|
||||||
const modifiedReq = token
|
const modifiedReq = token
|
||||||
? req.clone({
|
? req.clone({
|
||||||
headers: req.headers.set('Authorization', `Bearer ${token}`),
|
headers: req.headers.set("Authorization", `Bearer ${token}`),
|
||||||
})
|
})
|
||||||
: req;
|
: req;
|
||||||
|
|
||||||
return next(modifiedReq);
|
return next(modifiedReq);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Role } from 'src/app/model/entities/role';
|
import { Role } from "src/app/model/entities/role";
|
||||||
import { DbModel } from 'src/app/model/entities/db-model';
|
import { DbModel } from "src/app/model/entities/db-model";
|
||||||
|
|
||||||
export interface NotExistingUser {
|
export interface NotExistingUser {
|
||||||
keycloakId: string;
|
keycloakId: string;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Theme } from 'src/app/model/view/theme';
|
import { Theme } from "src/app/model/view/theme";
|
||||||
|
|
||||||
export interface AppSettings {
|
export interface AppSettings {
|
||||||
TermsUrl: string;
|
TermsUrl: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DbModel } from 'src/app/model/entities/db-model';
|
import { DbModel } from "src/app/model/entities/db-model";
|
||||||
import { Permission } from 'src/app/model/entities/role';
|
import { Permission } from "src/app/model/entities/role";
|
||||||
|
|
||||||
export interface ApiKey extends DbModel {
|
export interface ApiKey extends DbModel {
|
||||||
identifier?: string;
|
identifier?: string;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { User } from 'src/app/model/auth/user';
|
import { User } from "src/app/model/auth/user";
|
||||||
|
|
||||||
export interface DbModel {
|
export interface DbModel {
|
||||||
id?: number;
|
id?: number;
|
||||||
|
@ -2,7 +2,7 @@ import { DbModel } from "src/app/model/entities/db-model";
|
|||||||
import { Permission } from "src/app/model/entities/role";
|
import { Permission } from "src/app/model/entities/role";
|
||||||
|
|
||||||
export interface Group extends DbModel {
|
export interface Group extends DbModel {
|
||||||
name?: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupCreateInput {
|
export interface GroupCreateInput {
|
||||||
|
@ -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 {
|
export interface News extends DbModel {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -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 {
|
export interface Role extends DbModel {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -9,6 +9,11 @@ export interface ShortUrl extends DbModel {
|
|||||||
group?: Group;
|
group?: Group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ShortUrlDto {
|
||||||
|
id: number;
|
||||||
|
targetUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ShortUrlCreateInput {
|
export interface ShortUrlCreateInput {
|
||||||
shortUrl: string;
|
shortUrl: string;
|
||||||
targetUrl: string;
|
targetUrl: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { gql } from 'apollo-angular';
|
import { gql } from "apollo-angular";
|
||||||
import { EDITOR_FRAGMENT } from 'src/app/model/graphql/editor.query';
|
import { EDITOR_FRAGMENT } from "src/app/model/graphql/editor.query";
|
||||||
|
|
||||||
export const DB_MODEL_FRAGMENT = gql`
|
export const DB_MODEL_FRAGMENT = gql`
|
||||||
fragment DB_MODEL on DbModel {
|
fragment DB_MODEL on DbModel {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { gql } from 'apollo-angular';
|
import { gql } from "apollo-angular";
|
||||||
|
|
||||||
export const EDITOR_FRAGMENT = gql`
|
export const EDITOR_FRAGMENT = gql`
|
||||||
fragment EDITOR on User {
|
fragment EDITOR on User {
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
export type Sort = { [key: string]: any };
|
export type Sort = { [key: string]: any };
|
||||||
|
|
||||||
export enum SortOrder {
|
export enum SortOrder {
|
||||||
ASC = 'ASC',
|
ASC = "ASC",
|
||||||
DESC = 'DESC',
|
DESC = "DESC",
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export enum Themes {
|
export enum Themes {
|
||||||
Default = 'maxlan-dark-theme',
|
Default = "maxlan-dark-theme",
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from "@angular/common";
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
import { PermissionGuard } from "src/app/core/guard/permission.guard";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'users',
|
path: "users",
|
||||||
title: 'Users | Maxlan',
|
title: "Users | Maxlan",
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('src/app/modules/admin/administration/users/users.module').then(
|
import("src/app/modules/admin/administration/users/users.module").then(
|
||||||
m => m.UsersModule
|
(m) => m.UsersModule,
|
||||||
),
|
),
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: { permissions: [PermissionsEnum.users] },
|
data: { permissions: [PermissionsEnum.users] },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'roles',
|
path: "roles",
|
||||||
title: 'Roles | Maxlan',
|
title: "Roles | Maxlan",
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('src/app/modules/admin/administration/roles/roles.module').then(
|
import("src/app/modules/admin/administration/roles/roles.module").then(
|
||||||
m => m.RolesModule
|
(m) => m.RolesModule,
|
||||||
),
|
),
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: { permissions: [PermissionsEnum.roles] },
|
data: { permissions: [PermissionsEnum.roles] },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'api-keys',
|
path: "api-keys",
|
||||||
title: 'API Key | Maxlan',
|
title: "API Key | Maxlan",
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import(
|
import(
|
||||||
'src/app/modules/admin/administration/api-keys/api-keys.module'
|
"src/app/modules/admin/administration/api-keys/api-keys.module"
|
||||||
).then(m => m.ApiKeysModule),
|
).then((m) => m.ApiKeysModule),
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: { permissions: [PermissionsEnum.apiKeys] },
|
data: { permissions: [PermissionsEnum.apiKeys] },
|
||||||
},
|
},
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import {
|
import {
|
||||||
DB_MODEL_COLUMNS,
|
DB_MODEL_COLUMNS,
|
||||||
ID_COLUMN,
|
ID_COLUMN,
|
||||||
PageColumns,
|
PageColumns,
|
||||||
} from 'src/app/core/base/page.columns';
|
} from "src/app/core/base/page.columns";
|
||||||
import { TableColumn } from 'src/app/modules/shared/components/table/table.model';
|
import { TableColumn } from "src/app/modules/shared/components/table/table.model";
|
||||||
import { ApiKey } from 'src/app/model/entities/api-key';
|
import { ApiKey } from "src/app/model/entities/api-key";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApiKeysColumns extends PageColumns<ApiKey> {
|
export class ApiKeysColumns extends PageColumns<ApiKey> {
|
||||||
@ -13,16 +13,16 @@ export class ApiKeysColumns extends PageColumns<ApiKey> {
|
|||||||
return [
|
return [
|
||||||
ID_COLUMN,
|
ID_COLUMN,
|
||||||
{
|
{
|
||||||
name: 'identifier',
|
name: "identifier",
|
||||||
label: 'common.identifier',
|
label: "common.identifier",
|
||||||
type: 'text',
|
type: "text",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: ApiKey) => row.identifier,
|
value: (row: ApiKey) => row.identifier,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'key',
|
name: "key",
|
||||||
label: 'common.key',
|
label: "common.key",
|
||||||
type: 'password',
|
type: "password",
|
||||||
value: (row: ApiKey) => row.key,
|
value: (row: ApiKey) => row.key,
|
||||||
},
|
},
|
||||||
...DB_MODEL_COLUMNS,
|
...DB_MODEL_COLUMNS,
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from "rxjs";
|
||||||
import {
|
import {
|
||||||
Create,
|
Create,
|
||||||
Delete,
|
Delete,
|
||||||
PageDataService,
|
PageDataService,
|
||||||
Restore,
|
Restore,
|
||||||
Update,
|
Update,
|
||||||
} from 'src/app/core/base/page.data.service';
|
} from "src/app/core/base/page.data.service";
|
||||||
import { Permission } from 'src/app/model/entities/role';
|
import { Permission } from "src/app/model/entities/role";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from "apollo-angular";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query";
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from "rxjs/operators";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
import {
|
import {
|
||||||
ApiKey,
|
ApiKey,
|
||||||
ApiKeyCreateInput,
|
ApiKeyCreateInput,
|
||||||
ApiKeyUpdateInput,
|
ApiKeyUpdateInput,
|
||||||
} from 'src/app/model/entities/api-key';
|
} from "src/app/model/entities/api-key";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApiKeysDataService
|
export class ApiKeysDataService
|
||||||
@ -32,7 +32,7 @@ export class ApiKeysDataService
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private spinner: SpinnerService,
|
private spinner: SpinnerService,
|
||||||
private apollo: Apollo
|
private apollo: Apollo,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ export class ApiKeysDataService
|
|||||||
filter?: Filter[] | undefined,
|
filter?: Filter[] | undefined,
|
||||||
sort?: Sort[] | undefined,
|
sort?: Sort[] | undefined,
|
||||||
skip?: number | undefined,
|
skip?: number | undefined,
|
||||||
take?: number | undefined
|
take?: number | undefined,
|
||||||
): Observable<QueryResult<ApiKey>> {
|
): Observable<QueryResult<ApiKey>> {
|
||||||
return this.apollo
|
return this.apollo
|
||||||
.query<{ apiKeys: QueryResult<ApiKey> }>({
|
.query<{ apiKeys: QueryResult<ApiKey> }>({
|
||||||
@ -78,12 +78,12 @@ export class ApiKeysDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.apiKeys));
|
.pipe(map((result) => result.data.apiKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById(id: number): Observable<ApiKey> {
|
loadById(id: number): Observable<ApiKey> {
|
||||||
@ -109,12 +109,12 @@ export class ApiKeysDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.apiKeys.nodes[0]));
|
.pipe(map((result) => result.data.apiKeys.nodes[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
create(object: ApiKeyCreateInput): Observable<ApiKey | undefined> {
|
create(object: ApiKeyCreateInput): Observable<ApiKey | undefined> {
|
||||||
@ -140,17 +140,17 @@ export class ApiKeysDataService
|
|||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
identifier: object.identifier,
|
identifier: object.identifier,
|
||||||
permissions: object.permissions?.map(x => x.id),
|
permissions: object.permissions?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.apiKey.create));
|
.pipe(map((result) => result.data?.apiKey.create));
|
||||||
}
|
}
|
||||||
|
|
||||||
update(object: ApiKeyUpdateInput): Observable<ApiKey | undefined> {
|
update(object: ApiKeyUpdateInput): Observable<ApiKey | undefined> {
|
||||||
@ -177,17 +177,17 @@ export class ApiKeysDataService
|
|||||||
input: {
|
input: {
|
||||||
id: object.id,
|
id: object.id,
|
||||||
identifier: object.identifier,
|
identifier: object.identifier,
|
||||||
permissions: object.permissions?.map(x => x.id),
|
permissions: object.permissions?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.apiKey.update));
|
.pipe(map((result) => result.data?.apiKey.update));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(object: ApiKey): Observable<boolean> {
|
delete(object: ApiKey): Observable<boolean> {
|
||||||
@ -205,12 +205,12 @@ export class ApiKeysDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.apiKey.delete ?? false));
|
.pipe(map((result) => result.data?.apiKey.delete ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(object: ApiKey): Observable<boolean> {
|
restore(object: ApiKey): Observable<boolean> {
|
||||||
@ -228,12 +228,12 @@ export class ApiKeysDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.apiKey.restore ?? false));
|
.pipe(map((result) => result.data?.apiKey.restore ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllPermissions(): Observable<Permission[]> {
|
getAllPermissions(): Observable<Permission[]> {
|
||||||
@ -251,12 +251,12 @@ export class ApiKeysDataService
|
|||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.permissions.nodes));
|
.pipe(map((result) => result.data.permissions.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static provide(): Provider[] {
|
static provide(): Provider[] {
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from "@angular/common";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
import { PermissionGuard } from "src/app/core/guard/permission.guard";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
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 { 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 { 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 { 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 { ApiKeysColumns } from "src/app/modules/admin/administration/api-keys/api-keys.columns";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: "",
|
||||||
title: 'Admin - ApiKeys | Maxlan',
|
title: "Admin - ApiKeys | Maxlan",
|
||||||
component: ApiKeysPage,
|
component: ApiKeysPage,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: "create",
|
||||||
component: ApiKeyFormPageComponent,
|
component: ApiKeyFormPageComponent,
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: {
|
data: {
|
||||||
@ -24,7 +24,7 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'edit/:id',
|
path: "edit/:id",
|
||||||
component: ApiKeyFormPageComponent,
|
component: ApiKeyFormPageComponent,
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: {
|
data: {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { ApiKeysPage } from 'src/app/modules/admin/administration/api-keys/api-keys.page';
|
import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { PageDataService } from 'src/app/core/base/page.data.service';
|
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 { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||||
|
|
||||||
describe('ApiKeysComponent', () => {
|
describe("ApiKeysComponent", () => {
|
||||||
let component: ApiKeysPage;
|
let component: ApiKeysPage;
|
||||||
let fixture: ComponentFixture<ApiKeysPage>;
|
let fixture: ComponentFixture<ApiKeysPage>;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ describe('ApiKeysComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { PageBase } from 'src/app/core/base/page-base';
|
import { PageBase } from "src/app/core/base/page-base";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service';
|
import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.service";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
import { ApiKey } from 'src/app/model/entities/api-key';
|
import { ApiKey } from "src/app/model/entities/api-key";
|
||||||
import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service';
|
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 { ApiKeysColumns } from "src/app/modules/admin/administration/api-keys/api-keys.columns";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-api-keys',
|
selector: "app-api-keys",
|
||||||
templateUrl: './api-keys.page.html',
|
templateUrl: "./api-keys.page.html",
|
||||||
styleUrl: './api-keys.page.scss',
|
styleUrl: "./api-keys.page.scss",
|
||||||
})
|
})
|
||||||
export class ApiKeysPage extends PageBase<
|
export class ApiKeysPage extends PageBase<
|
||||||
ApiKey,
|
ApiKey,
|
||||||
@ -19,7 +19,7 @@ export class ApiKeysPage extends PageBase<
|
|||||||
> {
|
> {
|
||||||
constructor(
|
constructor(
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private confirmation: ConfirmationDialogService
|
private confirmation: ConfirmationDialogService,
|
||||||
) {
|
) {
|
||||||
super(true, {
|
super(true, {
|
||||||
read: [PermissionsEnum.apiKeys],
|
read: [PermissionsEnum.apiKeys],
|
||||||
@ -34,7 +34,7 @@ export class ApiKeysPage extends PageBase<
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService
|
this.dataService
|
||||||
.load(this.filter, this.sort, this.skip, this.take)
|
.load(this.filter, this.sort, this.skip, this.take)
|
||||||
.subscribe(result => {
|
.subscribe((result) => {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
@ -42,12 +42,12 @@ export class ApiKeysPage extends PageBase<
|
|||||||
|
|
||||||
delete(apiKey: ApiKey): void {
|
delete(apiKey: ApiKey): void {
|
||||||
this.confirmation.confirmDialog({
|
this.confirmation.confirmDialog({
|
||||||
header: 'dialog.delete.header',
|
header: "dialog.delete.header",
|
||||||
message: 'dialog.delete.message',
|
message: "dialog.delete.message",
|
||||||
accept: () => {
|
accept: () => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService.delete(apiKey).subscribe(() => {
|
this.dataService.delete(apiKey).subscribe(() => {
|
||||||
this.toast.success('action.deleted');
|
this.toast.success("action.deleted");
|
||||||
this.load();
|
this.load();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -57,12 +57,12 @@ export class ApiKeysPage extends PageBase<
|
|||||||
|
|
||||||
restore(apiKey: ApiKey): void {
|
restore(apiKey: ApiKey): void {
|
||||||
this.confirmation.confirmDialog({
|
this.confirmation.confirmDialog({
|
||||||
header: 'dialog.restore.header',
|
header: "dialog.restore.header",
|
||||||
message: 'dialog.restore.message',
|
message: "dialog.restore.message",
|
||||||
accept: () => {
|
accept: () => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService.restore(apiKey).subscribe(() => {
|
this.dataService.restore(apiKey).subscribe(() => {
|
||||||
this.toast.success('action.restored');
|
this.toast.success("action.restored");
|
||||||
this.load();
|
this.load();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component';
|
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 { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service';
|
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
describe('ApiKeyFormpageComponent', () => {
|
describe("ApiKeyFormpageComponent", () => {
|
||||||
let component: RoleFormPageComponent;
|
let component: RoleFormPageComponent;
|
||||||
let fixture: ComponentFixture<RoleFormPageComponent>;
|
let fixture: ComponentFixture<RoleFormPageComponent>;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ describe('ApiKeyFormpageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
import { InputSwitchChangeEvent } from 'primeng/inputswitch';
|
import { InputSwitchChangeEvent } from "primeng/inputswitch";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from "rxjs";
|
||||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
import { FormPageBase } from "src/app/core/base/form-page-base";
|
||||||
import { Permission } from 'src/app/model/entities/role';
|
import { Permission } from "src/app/model/entities/role";
|
||||||
import {
|
import {
|
||||||
ApiKey,
|
ApiKey,
|
||||||
ApiKeyCreateInput,
|
ApiKeyCreateInput,
|
||||||
ApiKeyUpdateInput,
|
ApiKeyUpdateInput,
|
||||||
} from 'src/app/model/entities/api-key';
|
} from "src/app/model/entities/api-key";
|
||||||
import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service';
|
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-api-key-form-page',
|
selector: "app-api-key-form-page",
|
||||||
templateUrl: './api-key-form-page.component.html',
|
templateUrl: "./api-key-form-page.component.html",
|
||||||
styleUrl: './api-key-form-page.component.scss',
|
styleUrl: "./api-key-form-page.component.scss",
|
||||||
})
|
})
|
||||||
export class ApiKeyFormPageComponent extends FormPageBase<
|
export class ApiKeyFormPageComponent extends FormPageBase<
|
||||||
ApiKey,
|
ApiKey,
|
||||||
@ -38,7 +38,7 @@ export class ApiKeyFormPageComponent extends FormPageBase<
|
|||||||
|
|
||||||
this.dataService
|
this.dataService
|
||||||
.load([{ id: { equal: this.nodeId } }])
|
.load([{ id: { equal: this.nodeId } }])
|
||||||
.subscribe(apiKey => {
|
.subscribe((apiKey) => {
|
||||||
this.node = apiKey.nodes[0];
|
this.node = apiKey.nodes[0];
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
});
|
});
|
||||||
@ -47,12 +47,12 @@ export class ApiKeyFormPageComponent extends FormPageBase<
|
|||||||
|
|
||||||
async initializePermissions() {
|
async initializePermissions() {
|
||||||
const permissions = await firstValueFrom(
|
const permissions = await firstValueFrom(
|
||||||
this.dataService.getAllPermissions()
|
this.dataService.getAllPermissions(),
|
||||||
);
|
);
|
||||||
this.allPermissions = permissions;
|
this.allPermissions = permissions;
|
||||||
this.permissionGroups = permissions.reduce(
|
this.permissionGroups = permissions.reduce(
|
||||||
(acc, p) => {
|
(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]) {
|
if (!acc[group]) {
|
||||||
acc[group] = [];
|
acc[group] = [];
|
||||||
@ -60,10 +60,10 @@ export class ApiKeyFormPageComponent extends FormPageBase<
|
|||||||
acc[group].push(p);
|
acc[group].push(p);
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{} as { [key: string]: Permission[] }
|
{} as { [key: string]: Permission[] },
|
||||||
);
|
);
|
||||||
|
|
||||||
permissions.forEach(p => {
|
permissions.forEach((p) => {
|
||||||
this.form.addControl(p.name, new FormControl<boolean>(false));
|
this.form.addControl(p.name, new FormControl<boolean>(false));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -77,48 +77,48 @@ export class ApiKeyFormPageComponent extends FormPageBase<
|
|||||||
id: new FormControl<number | undefined>(undefined),
|
id: new FormControl<number | undefined>(undefined),
|
||||||
identifier: new FormControl<string | undefined>(
|
identifier: new FormControl<string | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
Validators.required
|
Validators.required,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
this.form.controls['id'].disable();
|
this.form.controls["id"].disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
setForm(node?: ApiKey) {
|
setForm(node?: ApiKey) {
|
||||||
this.form.controls['id'].setValue(node?.id);
|
this.form.controls["id"].setValue(node?.id);
|
||||||
this.form.controls['identifier'].setValue(node?.identifier);
|
this.form.controls["identifier"].setValue(node?.identifier);
|
||||||
|
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
const permissions = node.permissions ?? [];
|
const permissions = node.permissions ?? [];
|
||||||
|
|
||||||
permissions.forEach(p => {
|
permissions.forEach((p) => {
|
||||||
this.form.controls[p.name].setValue(true);
|
this.form.controls[p.name].setValue(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateInput(): ApiKeyCreateInput {
|
getCreateInput(): ApiKeyCreateInput {
|
||||||
return {
|
return {
|
||||||
identifier: this.form.controls['identifier'].pristine
|
identifier: this.form.controls["identifier"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['identifier'].value ?? undefined),
|
: (this.form.controls["identifier"].value ?? undefined),
|
||||||
permissions: this.allPermissions.filter(
|
permissions: this.allPermissions.filter(
|
||||||
p => this.form.controls[p.name].value
|
(p) => this.form.controls[p.name].value,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateInput(): ApiKeyUpdateInput {
|
getUpdateInput(): ApiKeyUpdateInput {
|
||||||
if (!this.node?.id) {
|
if (!this.node?.id) {
|
||||||
throw new Error('Node id is missing');
|
throw new Error("Node id is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: this.form.controls['id'].value,
|
id: this.form.controls["id"].value,
|
||||||
identifier: this.form.controls['identifier'].pristine
|
identifier: this.form.controls["identifier"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['identifier'].value ?? undefined),
|
: (this.form.controls["identifier"].value ?? undefined),
|
||||||
permissions: this.allPermissions.filter(
|
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 {
|
create(apiKey: ApiKeyCreateInput): void {
|
||||||
this.dataService.create(apiKey).subscribe(() => {
|
this.dataService.create(apiKey).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -134,20 +134,20 @@ export class ApiKeyFormPageComponent extends FormPageBase<
|
|||||||
update(apiKey: ApiKeyUpdateInput): void {
|
update(apiKey: ApiKeyUpdateInput): void {
|
||||||
this.dataService.update(apiKey).subscribe(() => {
|
this.dataService.update(apiKey).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleGroup(event: InputSwitchChangeEvent, group: string) {
|
toggleGroup(event: InputSwitchChangeEvent, group: string) {
|
||||||
this.permissionGroups[group].forEach(p => {
|
this.permissionGroups[group].forEach((p) => {
|
||||||
this.form.controls[p.name].setValue(event.checked);
|
this.form.controls[p.name].setValue(event.checked);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isGroupChecked(group: string) {
|
isGroupChecked(group: string) {
|
||||||
return this.permissionGroups[group].every(
|
return this.permissionGroups[group].every(
|
||||||
p => this.form.controls[p.name].value
|
(p) => this.form.controls[p.name].value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component';
|
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 { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service';
|
import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
describe('RoleFormpageComponent', () => {
|
describe("RoleFormpageComponent", () => {
|
||||||
let component: RoleFormPageComponent;
|
let component: RoleFormPageComponent;
|
||||||
let fixture: ComponentFixture<RoleFormPageComponent>;
|
let fixture: ComponentFixture<RoleFormPageComponent>;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ describe('RoleFormpageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import {
|
import {
|
||||||
Permission,
|
Permission,
|
||||||
Role,
|
Role,
|
||||||
RoleCreateInput,
|
RoleCreateInput,
|
||||||
RoleUpdateInput,
|
RoleUpdateInput,
|
||||||
} from 'src/app/model/entities/role';
|
} from "src/app/model/entities/role";
|
||||||
import { InputSwitchChangeEvent } from 'primeng/inputswitch';
|
import { InputSwitchChangeEvent } from "primeng/inputswitch";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from "rxjs";
|
||||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
import { FormPageBase } from "src/app/core/base/form-page-base";
|
||||||
import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service';
|
import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service";
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-role-form-page',
|
selector: "app-role-form-page",
|
||||||
templateUrl: './role-form-page.component.html',
|
templateUrl: "./role-form-page.component.html",
|
||||||
styleUrl: './role-form-page.component.scss',
|
styleUrl: "./role-form-page.component.scss",
|
||||||
})
|
})
|
||||||
export class RoleFormPageComponent extends FormPageBase<
|
export class RoleFormPageComponent extends FormPageBase<
|
||||||
Role,
|
Role,
|
||||||
@ -36,7 +36,7 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataService.loadById(this.nodeId).subscribe(role => {
|
this.dataService.loadById(this.nodeId).subscribe((role) => {
|
||||||
this.node = role;
|
this.node = role;
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
});
|
});
|
||||||
@ -45,12 +45,12 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
|
|
||||||
async initializePermissions() {
|
async initializePermissions() {
|
||||||
const permissions = await firstValueFrom(
|
const permissions = await firstValueFrom(
|
||||||
this.dataService.getAllPermissions()
|
this.dataService.getAllPermissions(),
|
||||||
);
|
);
|
||||||
this.allPermissions = permissions;
|
this.allPermissions = permissions;
|
||||||
this.permissionGroups = permissions.reduce(
|
this.permissionGroups = permissions.reduce(
|
||||||
(acc, p) => {
|
(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]) {
|
if (!acc[group]) {
|
||||||
acc[group] = [];
|
acc[group] = [];
|
||||||
@ -58,10 +58,10 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
acc[group].push(p);
|
acc[group].push(p);
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{} as { [key: string]: Permission[] }
|
{} as { [key: string]: Permission[] },
|
||||||
);
|
);
|
||||||
|
|
||||||
permissions.forEach(p => {
|
permissions.forEach((p) => {
|
||||||
this.form.addControl(p.name, new FormControl<boolean>(false));
|
this.form.addControl(p.name, new FormControl<boolean>(false));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -76,48 +76,48 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
name: new FormControl<string | undefined>(undefined, Validators.required),
|
name: new FormControl<string | undefined>(undefined, Validators.required),
|
||||||
description: new FormControl<string | undefined>(undefined),
|
description: new FormControl<string | undefined>(undefined),
|
||||||
});
|
});
|
||||||
this.form.controls['id'].disable();
|
this.form.controls["id"].disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
setForm(node?: Role) {
|
setForm(node?: Role) {
|
||||||
this.form.controls['id'].setValue(node?.id);
|
this.form.controls["id"].setValue(node?.id);
|
||||||
this.form.controls['name'].setValue(node?.name);
|
this.form.controls["name"].setValue(node?.name);
|
||||||
this.form.controls['description'].setValue(node?.description);
|
this.form.controls["description"].setValue(node?.description);
|
||||||
|
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
const permissions = node.permissions ?? [];
|
const permissions = node.permissions ?? [];
|
||||||
|
|
||||||
permissions.forEach(p => {
|
permissions.forEach((p) => {
|
||||||
this.form.controls[p.name].setValue(true);
|
this.form.controls[p.name].setValue(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateInput(): RoleCreateInput {
|
getCreateInput(): RoleCreateInput {
|
||||||
return {
|
return {
|
||||||
name: this.form.controls['name'].pristine
|
name: this.form.controls["name"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['name'].value ?? undefined),
|
: (this.form.controls["name"].value ?? undefined),
|
||||||
description: this.form.controls['description'].pristine
|
description: this.form.controls["description"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['description'].value ?? undefined),
|
: (this.form.controls["description"].value ?? undefined),
|
||||||
permissions: this.allPermissions.filter(
|
permissions: this.allPermissions.filter(
|
||||||
p => this.form.controls[p.name].value
|
(p) => this.form.controls[p.name].value,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateInput(): RoleUpdateInput {
|
getUpdateInput(): RoleUpdateInput {
|
||||||
return {
|
return {
|
||||||
id: this.form.controls['id'].value,
|
id: this.form.controls["id"].value,
|
||||||
name: this.form.controls['name'].pristine
|
name: this.form.controls["name"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['name'].value,
|
: this.form.controls["name"].value,
|
||||||
description: this.form.controls['description'].pristine
|
description: this.form.controls["description"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['description'].value,
|
: this.form.controls["description"].value,
|
||||||
permissions: this.allPermissions.filter(
|
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 {
|
create(role: RoleCreateInput): void {
|
||||||
this.dataService.create(role).subscribe(() => {
|
this.dataService.create(role).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -133,20 +133,20 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
update(role: RoleUpdateInput): void {
|
update(role: RoleUpdateInput): void {
|
||||||
this.dataService.update(role).subscribe(() => {
|
this.dataService.update(role).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleGroup(event: InputSwitchChangeEvent, group: string) {
|
toggleGroup(event: InputSwitchChangeEvent, group: string) {
|
||||||
this.permissionGroups[group].forEach(p => {
|
this.permissionGroups[group].forEach((p) => {
|
||||||
this.form.controls[p.name].setValue(event.checked);
|
this.form.controls[p.name].setValue(event.checked);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isGroupChecked(group: string) {
|
isGroupChecked(group: string) {
|
||||||
return this.permissionGroups[group].every(
|
return this.permissionGroups[group].every(
|
||||||
p => this.form.controls[p.name].value
|
(p) => this.form.controls[p.name].value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import { Role } from 'src/app/model/entities/role';
|
import { Role } from "src/app/model/entities/role";
|
||||||
import {
|
import {
|
||||||
DB_MODEL_COLUMNS,
|
DB_MODEL_COLUMNS,
|
||||||
DESCRIPTION_COLUMN,
|
DESCRIPTION_COLUMN,
|
||||||
ID_COLUMN,
|
ID_COLUMN,
|
||||||
NAME_COLUMN,
|
NAME_COLUMN,
|
||||||
PageColumns,
|
PageColumns,
|
||||||
} from 'src/app/core/base/page.columns';
|
} from "src/app/core/base/page.columns";
|
||||||
import { TableColumn } from 'src/app/modules/shared/components/table/table.model';
|
import { TableColumn } from "src/app/modules/shared/components/table/table.model";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RolesColumns extends PageColumns<Role> {
|
export class RolesColumns extends PageColumns<Role> {
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from "rxjs";
|
||||||
import {
|
import {
|
||||||
Create,
|
Create,
|
||||||
Delete,
|
Delete,
|
||||||
PageDataService,
|
PageDataService,
|
||||||
Restore,
|
Restore,
|
||||||
Update,
|
Update,
|
||||||
} from 'src/app/core/base/page.data.service';
|
} from "src/app/core/base/page.data.service";
|
||||||
import {
|
import {
|
||||||
Permission,
|
Permission,
|
||||||
Role,
|
Role,
|
||||||
RoleCreateInput,
|
RoleCreateInput,
|
||||||
RoleUpdateInput,
|
RoleUpdateInput,
|
||||||
} from 'src/app/model/entities/role';
|
} from "src/app/model/entities/role";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from "apollo-angular";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query";
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from "rxjs/operators";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RolesDataService
|
export class RolesDataService
|
||||||
@ -32,7 +32,7 @@ export class RolesDataService
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private spinner: SpinnerService,
|
private spinner: SpinnerService,
|
||||||
private apollo: Apollo
|
private apollo: Apollo,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ export class RolesDataService
|
|||||||
filter?: Filter[] | undefined,
|
filter?: Filter[] | undefined,
|
||||||
sort?: Sort[] | undefined,
|
sort?: Sort[] | undefined,
|
||||||
skip?: number | undefined,
|
skip?: number | undefined,
|
||||||
take?: number | undefined
|
take?: number | undefined,
|
||||||
): Observable<QueryResult<Role>> {
|
): Observable<QueryResult<Role>> {
|
||||||
return this.apollo
|
return this.apollo
|
||||||
.query<{ roles: QueryResult<Role> }>({
|
.query<{ roles: QueryResult<Role> }>({
|
||||||
@ -75,12 +75,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.roles));
|
.pipe(map((result) => result.data.roles));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById(id: number): Observable<Role> {
|
loadById(id: number): Observable<Role> {
|
||||||
@ -112,12 +112,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.roles.nodes[0]));
|
.pipe(map((result) => result.data.roles.nodes[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
create(object: RoleCreateInput): Observable<Role | undefined> {
|
create(object: RoleCreateInput): Observable<Role | undefined> {
|
||||||
@ -145,17 +145,17 @@ export class RolesDataService
|
|||||||
input: {
|
input: {
|
||||||
name: object.name,
|
name: object.name,
|
||||||
description: object.description,
|
description: object.description,
|
||||||
permissions: object.permissions?.map(x => x.id),
|
permissions: object.permissions?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.create));
|
.pipe(map((result) => result.data?.role.create));
|
||||||
}
|
}
|
||||||
|
|
||||||
update(object: RoleUpdateInput): Observable<Role | undefined> {
|
update(object: RoleUpdateInput): Observable<Role | undefined> {
|
||||||
@ -184,17 +184,17 @@ export class RolesDataService
|
|||||||
id: object.id,
|
id: object.id,
|
||||||
name: object.name,
|
name: object.name,
|
||||||
description: object.description,
|
description: object.description,
|
||||||
permissions: object.permissions?.map(x => x.id),
|
permissions: object.permissions?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.update));
|
.pipe(map((result) => result.data?.role.update));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(object: Role): Observable<boolean> {
|
delete(object: Role): Observable<boolean> {
|
||||||
@ -212,12 +212,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.delete ?? false));
|
.pipe(map((result) => result.data?.role.delete ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(object: Role): Observable<boolean> {
|
restore(object: Role): Observable<boolean> {
|
||||||
@ -235,12 +235,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.restore ?? false));
|
.pipe(map((result) => result.data?.role.restore ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllPermissions(): Observable<Permission[]> {
|
getAllPermissions(): Observable<Permission[]> {
|
||||||
@ -258,12 +258,12 @@ export class RolesDataService
|
|||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.permissions.nodes));
|
.pipe(map((result) => result.data.permissions.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static provide(): Provider[] {
|
static provide(): Provider[] {
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from "@angular/common";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
import { PermissionGuard } from "src/app/core/guard/permission.guard";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component';
|
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 { RolesPage } from "src/app/modules/admin/administration/roles/roles.page";
|
||||||
import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service';
|
import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service";
|
||||||
import { RolesColumns } from 'src/app/modules/admin/administration/roles/roles.columns';
|
import { RolesColumns } from "src/app/modules/admin/administration/roles/roles.columns";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: "",
|
||||||
title: 'Admin - Roles | Maxlan',
|
title: "Admin - Roles | Maxlan",
|
||||||
component: RolesPage,
|
component: RolesPage,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: "create",
|
||||||
component: RoleFormPageComponent,
|
component: RoleFormPageComponent,
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: {
|
data: {
|
||||||
@ -24,7 +24,7 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'edit/:id',
|
path: "edit/:id",
|
||||||
component: RoleFormPageComponent,
|
component: RoleFormPageComponent,
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: {
|
data: {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { RolesPage } from 'src/app/modules/admin/administration/roles/roles.page';
|
import { RolesPage } from "src/app/modules/admin/administration/roles/roles.page";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { PageDataService } from 'src/app/core/base/page.data.service';
|
import { PageDataService } from "src/app/core/base/page.data.service";
|
||||||
import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service';
|
import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service";
|
||||||
|
|
||||||
describe('RolesComponent', () => {
|
describe("RolesComponent", () => {
|
||||||
let component: RolesPage;
|
let component: RolesPage;
|
||||||
let fixture: ComponentFixture<RolesPage>;
|
let fixture: ComponentFixture<RolesPage>;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ describe('RolesComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { PageBase } from 'src/app/core/base/page-base';
|
import { PageBase } from "src/app/core/base/page-base";
|
||||||
import { Role } from 'src/app/model/entities/role';
|
import { Role } from "src/app/model/entities/role";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service';
|
import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.service";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
import { RolesColumns } from 'src/app/modules/admin/administration/roles/roles.columns';
|
import { RolesColumns } from "src/app/modules/admin/administration/roles/roles.columns";
|
||||||
import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service';
|
import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-roles',
|
selector: "app-roles",
|
||||||
templateUrl: './roles.page.html',
|
templateUrl: "./roles.page.html",
|
||||||
styleUrl: './roles.page.scss',
|
styleUrl: "./roles.page.scss",
|
||||||
})
|
})
|
||||||
export class RolesPage extends PageBase<Role, RolesDataService, RolesColumns> {
|
export class RolesPage extends PageBase<Role, RolesDataService, RolesColumns> {
|
||||||
constructor(
|
constructor(
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private confirmation: ConfirmationDialogService
|
private confirmation: ConfirmationDialogService,
|
||||||
) {
|
) {
|
||||||
super(true, {
|
super(true, {
|
||||||
read: [PermissionsEnum.roles],
|
read: [PermissionsEnum.roles],
|
||||||
@ -30,7 +30,7 @@ export class RolesPage extends PageBase<Role, RolesDataService, RolesColumns> {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService
|
this.dataService
|
||||||
.load(this.filter, this.sort, this.skip, this.take)
|
.load(this.filter, this.sort, this.skip, this.take)
|
||||||
.subscribe(result => {
|
.subscribe((result) => {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
@ -38,12 +38,12 @@ export class RolesPage extends PageBase<Role, RolesDataService, RolesColumns> {
|
|||||||
|
|
||||||
delete(role: Role): void {
|
delete(role: Role): void {
|
||||||
this.confirmation.confirmDialog({
|
this.confirmation.confirmDialog({
|
||||||
header: 'dialog.delete.header',
|
header: "dialog.delete.header",
|
||||||
message: 'dialog.delete.message',
|
message: "dialog.delete.message",
|
||||||
accept: () => {
|
accept: () => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService.delete(role).subscribe(() => {
|
this.dataService.delete(role).subscribe(() => {
|
||||||
this.toast.success('action.deleted');
|
this.toast.success("action.deleted");
|
||||||
this.load();
|
this.load();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -53,12 +53,12 @@ export class RolesPage extends PageBase<Role, RolesDataService, RolesColumns> {
|
|||||||
|
|
||||||
restore(role: Role): void {
|
restore(role: Role): void {
|
||||||
this.confirmation.confirmDialog({
|
this.confirmation.confirmDialog({
|
||||||
header: 'dialog.restore.header',
|
header: "dialog.restore.header",
|
||||||
message: 'dialog.restore.message',
|
message: "dialog.restore.message",
|
||||||
accept: () => {
|
accept: () => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService.restore(role).subscribe(() => {
|
this.dataService.restore(role).subscribe(() => {
|
||||||
this.toast.success('action.restored');
|
this.toast.success("action.restored");
|
||||||
this.load();
|
this.load();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component';
|
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 { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service';
|
import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
describe('UserFormpageComponent', () => {
|
describe("UserFormpageComponent", () => {
|
||||||
let component: RoleFormPageComponent;
|
let component: RoleFormPageComponent;
|
||||||
let fixture: ComponentFixture<RoleFormPageComponent>;
|
let fixture: ComponentFixture<RoleFormPageComponent>;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ describe('UserFormpageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
import { FormPageBase } from "src/app/core/base/form-page-base";
|
||||||
import {
|
import {
|
||||||
NotExistingUser,
|
NotExistingUser,
|
||||||
User,
|
User,
|
||||||
UserCreateInput,
|
UserCreateInput,
|
||||||
UserUpdateInput,
|
UserUpdateInput,
|
||||||
} from 'src/app/model/auth/user';
|
} from "src/app/model/auth/user";
|
||||||
import { Role } from 'src/app/model/entities/role';
|
import { Role } from "src/app/model/entities/role";
|
||||||
import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service';
|
import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-form-page',
|
selector: "app-user-form-page",
|
||||||
templateUrl: './user-form-page.component.html',
|
templateUrl: "./user-form-page.component.html",
|
||||||
styleUrl: './user-form-page.component.scss',
|
styleUrl: "./user-form-page.component.scss",
|
||||||
})
|
})
|
||||||
export class UserFormPageComponent extends FormPageBase<
|
export class UserFormPageComponent extends FormPageBase<
|
||||||
User,
|
User,
|
||||||
@ -27,12 +27,12 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
|
|
||||||
constructor(private toast: ToastService) {
|
constructor(private toast: ToastService) {
|
||||||
super();
|
super();
|
||||||
this.dataService.getAllRoles().subscribe(roles => {
|
this.dataService.getAllRoles().subscribe((roles) => {
|
||||||
this.roles = roles;
|
this.roles = roles;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.nodeId) {
|
if (!this.nodeId) {
|
||||||
this.dataService.getNotExistingUsersFromKeycloak().subscribe(users => {
|
this.dataService.getNotExistingUsersFromKeycloak().subscribe((users) => {
|
||||||
this.notExistingUsers = users;
|
this.notExistingUsers = users;
|
||||||
this.node = this.new();
|
this.node = this.new();
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
@ -41,7 +41,7 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataService.loadById(this.nodeId).subscribe(user => {
|
this.dataService.loadById(this.nodeId).subscribe((user) => {
|
||||||
this.node = user;
|
this.node = user;
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
});
|
});
|
||||||
@ -59,47 +59,47 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
email: new FormControl<string | undefined>(undefined),
|
email: new FormControl<string | undefined>(undefined),
|
||||||
roles: new FormControl<Role[]>([]),
|
roles: new FormControl<Role[]>([]),
|
||||||
});
|
});
|
||||||
this.form.controls['id'].disable();
|
this.form.controls["id"].disable();
|
||||||
this.form.controls['username'].disable();
|
this.form.controls["username"].disable();
|
||||||
this.form.controls['email'].disable();
|
this.form.controls["email"].disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
setForm(node?: User) {
|
setForm(node?: User) {
|
||||||
this.form.controls['id'].setValue(node?.id);
|
this.form.controls["id"].setValue(node?.id);
|
||||||
this.form.controls['username'].setValue(node?.username);
|
this.form.controls["username"].setValue(node?.username);
|
||||||
this.form.controls['email'].setValue(node?.email);
|
this.form.controls["email"].setValue(node?.email);
|
||||||
this.form.controls['roles'].setValue(node?.roles ?? []);
|
this.form.controls["roles"].setValue(node?.roles ?? []);
|
||||||
|
|
||||||
if (this.notExistingUsers.length > 0) {
|
if (this.notExistingUsers.length > 0) {
|
||||||
this.form.controls['id'].enable();
|
this.form.controls["id"].enable();
|
||||||
this.form.controls['keycloakId'].reset(undefined, { required: true });
|
this.form.controls["keycloakId"].reset(undefined, { required: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateInput(): UserCreateInput {
|
getCreateInput(): UserCreateInput {
|
||||||
return {
|
return {
|
||||||
keycloakId: this.form.controls['keycloakId'].pristine
|
keycloakId: this.form.controls["keycloakId"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['keycloakId'].value,
|
: this.form.controls["keycloakId"].value,
|
||||||
roles: this.form.controls['roles'].pristine
|
roles: this.form.controls["roles"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['roles'].value,
|
: this.form.controls["roles"].value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateInput(): UserUpdateInput {
|
getUpdateInput(): UserUpdateInput {
|
||||||
return {
|
return {
|
||||||
id: this.form.controls['id'].value,
|
id: this.form.controls["id"].value,
|
||||||
roles: this.form.controls['roles'].pristine
|
roles: this.form.controls["roles"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['roles'].value,
|
: this.form.controls["roles"].value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
create(user: UserCreateInput): void {
|
create(user: UserCreateInput): void {
|
||||||
this.dataService.create(user).subscribe(() => {
|
this.dataService.create(user).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
update(user: UserUpdateInput): void {
|
update(user: UserUpdateInput): void {
|
||||||
this.dataService.update(user).subscribe(() => {
|
this.dataService.update(user).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import {
|
import {
|
||||||
DB_MODEL_COLUMNS,
|
DB_MODEL_COLUMNS,
|
||||||
ID_COLUMN,
|
ID_COLUMN,
|
||||||
PageColumns,
|
PageColumns,
|
||||||
} from 'src/app/core/base/page.columns';
|
} from "src/app/core/base/page.columns";
|
||||||
import { TableColumn } from 'src/app/modules/shared/components/table/table.model';
|
import { TableColumn } from "src/app/modules/shared/components/table/table.model";
|
||||||
import { User } from 'src/app/model/auth/user';
|
import { User } from "src/app/model/auth/user";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersColumns extends PageColumns<User> {
|
export class UsersColumns extends PageColumns<User> {
|
||||||
@ -13,15 +13,15 @@ export class UsersColumns extends PageColumns<User> {
|
|||||||
return [
|
return [
|
||||||
ID_COLUMN,
|
ID_COLUMN,
|
||||||
{
|
{
|
||||||
name: 'username',
|
name: "username",
|
||||||
label: 'user.username',
|
label: "user.username",
|
||||||
type: 'text',
|
type: "text",
|
||||||
value: (row: User) => row.username,
|
value: (row: User) => row.username,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'email',
|
name: "email",
|
||||||
label: 'user.email',
|
label: "user.email",
|
||||||
type: 'text',
|
type: "text",
|
||||||
value: (row: User) => row.email,
|
value: (row: User) => row.email,
|
||||||
},
|
},
|
||||||
...DB_MODEL_COLUMNS,
|
...DB_MODEL_COLUMNS,
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from "rxjs";
|
||||||
import {
|
import {
|
||||||
Create,
|
Create,
|
||||||
Delete,
|
Delete,
|
||||||
PageDataService,
|
PageDataService,
|
||||||
Restore,
|
Restore,
|
||||||
Update,
|
Update,
|
||||||
} from 'src/app/core/base/page.data.service';
|
} from "src/app/core/base/page.data.service";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from "apollo-angular";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query";
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from "rxjs/operators";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
import {
|
import {
|
||||||
NotExistingUser,
|
NotExistingUser,
|
||||||
User,
|
User,
|
||||||
UserCreateInput,
|
UserCreateInput,
|
||||||
UserUpdateInput,
|
UserUpdateInput,
|
||||||
} from 'src/app/model/auth/user';
|
} from "src/app/model/auth/user";
|
||||||
import { Role } from 'src/app/model/entities/role';
|
import { Role } from "src/app/model/entities/role";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersDataService
|
export class UsersDataService
|
||||||
@ -33,7 +33,7 @@ export class UsersDataService
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private spinner: SpinnerService,
|
private spinner: SpinnerService,
|
||||||
private apollo: Apollo
|
private apollo: Apollo,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ export class UsersDataService
|
|||||||
filter?: Filter[] | undefined,
|
filter?: Filter[] | undefined,
|
||||||
sort?: Sort[] | undefined,
|
sort?: Sort[] | undefined,
|
||||||
skip?: number | undefined,
|
skip?: number | undefined,
|
||||||
take?: number | undefined
|
take?: number | undefined,
|
||||||
): Observable<QueryResult<User>> {
|
): Observable<QueryResult<User>> {
|
||||||
return this.apollo
|
return this.apollo
|
||||||
.query<{ users: QueryResult<User> }>({
|
.query<{ users: QueryResult<User> }>({
|
||||||
@ -77,12 +77,12 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.users));
|
.pipe(map((result) => result.data.users));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById(id: number): Observable<User> {
|
loadById(id: number): Observable<User> {
|
||||||
@ -115,12 +115,12 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.users.nodes[0]));
|
.pipe(map((result) => result.data.users.nodes[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
create(object: UserCreateInput): Observable<User | undefined> {
|
create(object: UserCreateInput): Observable<User | undefined> {
|
||||||
@ -145,17 +145,17 @@ export class UsersDataService
|
|||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
keycloakId: object.keycloakId,
|
keycloakId: object.keycloakId,
|
||||||
roles: object.roles?.map(x => x.id),
|
roles: object.roles?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.create));
|
.pipe(map((result) => result.data?.user.create));
|
||||||
}
|
}
|
||||||
|
|
||||||
update(object: UserUpdateInput): Observable<User | undefined> {
|
update(object: UserUpdateInput): Observable<User | undefined> {
|
||||||
@ -180,17 +180,17 @@ export class UsersDataService
|
|||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
id: object.id,
|
id: object.id,
|
||||||
roles: object.roles?.map(x => x.id),
|
roles: object.roles?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.update));
|
.pipe(map((result) => result.data?.user.update));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(object: User): Observable<boolean> {
|
delete(object: User): Observable<boolean> {
|
||||||
@ -208,12 +208,12 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.delete ?? false));
|
.pipe(map((result) => result.data?.user.delete ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(object: User): Observable<boolean> {
|
restore(object: User): Observable<boolean> {
|
||||||
@ -231,12 +231,12 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.restore ?? false));
|
.pipe(map((result) => result.data?.user.restore ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllRoles(): Observable<Role[]> {
|
getAllRoles(): Observable<Role[]> {
|
||||||
@ -254,12 +254,12 @@ export class UsersDataService
|
|||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.roles.nodes));
|
.pipe(map((result) => result.data.roles.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
getNotExistingUsersFromKeycloak(): Observable<NotExistingUser[]> {
|
getNotExistingUsersFromKeycloak(): Observable<NotExistingUser[]> {
|
||||||
@ -277,12 +277,12 @@ export class UsersDataService
|
|||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.notExistingUsersFromKeycloak.nodes));
|
.pipe(map((result) => result.data.notExistingUsersFromKeycloak.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static provide(): Provider[] {
|
static provide(): Provider[] {
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from "@angular/common";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
import { PermissionGuard } from "src/app/core/guard/permission.guard";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
import { UserFormPageComponent } from 'src/app/modules/admin/administration/users/form-page/user-form-page.component';
|
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 { UsersPage } from "src/app/modules/admin/administration/users/users.page";
|
||||||
import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service';
|
import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service";
|
||||||
import { UsersColumns } from 'src/app/modules/admin/administration/users/users.columns';
|
import { UsersColumns } from "src/app/modules/admin/administration/users/users.columns";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: "",
|
||||||
title: 'Admin - Users | Maxlan',
|
title: "Admin - Users | Maxlan",
|
||||||
component: UsersPage,
|
component: UsersPage,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: "create",
|
||||||
component: UserFormPageComponent,
|
component: UserFormPageComponent,
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: {
|
data: {
|
||||||
@ -24,7 +24,7 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'edit/:id',
|
path: "edit/:id",
|
||||||
component: UserFormPageComponent,
|
component: UserFormPageComponent,
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: {
|
data: {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { UsersPage } from 'src/app/modules/admin/administration/users/users.page';
|
import { UsersPage } from "src/app/modules/admin/administration/users/users.page";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { PageDataService } from 'src/app/core/base/page.data.service';
|
import { PageDataService } from "src/app/core/base/page.data.service";
|
||||||
import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service';
|
import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service";
|
||||||
|
|
||||||
describe('UsersComponent', () => {
|
describe("UsersComponent", () => {
|
||||||
let component: UsersPage;
|
let component: UsersPage;
|
||||||
let fixture: ComponentFixture<UsersPage>;
|
let fixture: ComponentFixture<UsersPage>;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ describe('UsersComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { PageBase } from 'src/app/core/base/page-base';
|
import { PageBase } from "src/app/core/base/page-base";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service';
|
import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.service";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
import { User } from 'src/app/model/auth/user';
|
import { User } from "src/app/model/auth/user";
|
||||||
import { UsersColumns } from 'src/app/modules/admin/administration/users/users.columns';
|
import { UsersColumns } from "src/app/modules/admin/administration/users/users.columns";
|
||||||
import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service';
|
import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-users',
|
selector: "app-users",
|
||||||
templateUrl: './users.page.html',
|
templateUrl: "./users.page.html",
|
||||||
styleUrl: './users.page.scss',
|
styleUrl: "./users.page.scss",
|
||||||
})
|
})
|
||||||
export class UsersPage extends PageBase<User, UsersDataService, UsersColumns> {
|
export class UsersPage extends PageBase<User, UsersDataService, UsersColumns> {
|
||||||
constructor(
|
constructor(
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private confirmation: ConfirmationDialogService
|
private confirmation: ConfirmationDialogService,
|
||||||
) {
|
) {
|
||||||
super(true, {
|
super(true, {
|
||||||
read: [PermissionsEnum.users],
|
read: [PermissionsEnum.users],
|
||||||
@ -30,7 +30,7 @@ export class UsersPage extends PageBase<User, UsersDataService, UsersColumns> {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService
|
this.dataService
|
||||||
.load(this.filter, this.sort, this.skip, this.take)
|
.load(this.filter, this.sort, this.skip, this.take)
|
||||||
.subscribe(result => {
|
.subscribe((result) => {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
@ -38,12 +38,12 @@ export class UsersPage extends PageBase<User, UsersDataService, UsersColumns> {
|
|||||||
|
|
||||||
delete(user: User): void {
|
delete(user: User): void {
|
||||||
this.confirmation.confirmDialog({
|
this.confirmation.confirmDialog({
|
||||||
header: 'dialog.delete.header',
|
header: "dialog.delete.header",
|
||||||
message: 'dialog.delete.message',
|
message: "dialog.delete.message",
|
||||||
accept: () => {
|
accept: () => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService.delete(user).subscribe(() => {
|
this.dataService.delete(user).subscribe(() => {
|
||||||
this.toast.success('action.deleted');
|
this.toast.success("action.deleted");
|
||||||
this.load();
|
this.load();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -53,12 +53,12 @@ export class UsersPage extends PageBase<User, UsersDataService, UsersColumns> {
|
|||||||
|
|
||||||
restore(user: User): void {
|
restore(user: User): void {
|
||||||
this.confirmation.confirmDialog({
|
this.confirmation.confirmDialog({
|
||||||
header: 'dialog.restore.header',
|
header: "dialog.restore.header",
|
||||||
message: 'dialog.restore.message',
|
message: "dialog.restore.message",
|
||||||
accept: () => {
|
accept: () => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService.restore(user).subscribe(() => {
|
this.dataService.restore(user).subscribe(() => {
|
||||||
this.toast.success('action.restored');
|
this.toast.success("action.restored");
|
||||||
this.load();
|
this.load();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component';
|
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 { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service';
|
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
describe('ApiKeyFormpageComponent', () => {
|
describe("ApiKeyFormpageComponent", () => {
|
||||||
let component: RoleFormPageComponent;
|
let component: RoleFormPageComponent;
|
||||||
let fixture: ComponentFixture<RoleFormPageComponent>;
|
let fixture: ComponentFixture<RoleFormPageComponent>;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ describe('ApiKeyFormpageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { ApiKeysPage } from 'src/app/modules/admin/administration/api-keys/api-keys.page';
|
import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { PageDataService } from 'src/app/core/base/page.data.service';
|
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 { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||||
|
|
||||||
describe('ApiKeysComponent', () => {
|
describe("ApiKeysComponent", () => {
|
||||||
let component: ApiKeysPage;
|
let component: ApiKeysPage;
|
||||||
let fixture: ComponentFixture<ApiKeysPage>;
|
let fixture: ComponentFixture<ApiKeysPage>;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ describe('ApiKeysComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import {CommonModule} from '@angular/common';
|
import { CommonModule } from "@angular/common";
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import {SharedModule} from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [];
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<input pInputText class="value" type="number" formControlName="id"/>
|
<input pInputText class="value" type="number" formControlName="id"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-page-input">
|
<div class="form-page-input">
|
||||||
<p class="label">{{ 'common.short_url' | translate }}</p>
|
<p class="label">{{ 'short_url.short_url' | translate }}</p>
|
||||||
<input
|
<input
|
||||||
pInputText
|
pInputText
|
||||||
class="value"
|
class="value"
|
||||||
@ -28,7 +28,7 @@
|
|||||||
formControlName="shortUrl"/>
|
formControlName="shortUrl"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-page-input">
|
<div class="form-page-input">
|
||||||
<p class="label">{{ 'common.target_url' | translate }}</p>
|
<p class="label">{{ 'short_url.target_url' | translate }}</p>
|
||||||
<input
|
<input
|
||||||
pInputText
|
pInputText
|
||||||
class="value"
|
class="value"
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { RoleFormPageComponent } from 'src/app/modules/admin/administration/roles/form-page/role-form-page.component';
|
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 { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { ApiKeysDataService } from 'src/app/modules/admin/administration/api-keys/api-keys.data.service';
|
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
describe('ApiKeyFormpageComponent', () => {
|
describe("ApiKeyFormpageComponent", () => {
|
||||||
let component: RoleFormPageComponent;
|
let component: RoleFormPageComponent;
|
||||||
let fixture: ComponentFixture<RoleFormPageComponent>;
|
let fixture: ComponentFixture<RoleFormPageComponent>;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ describe('ApiKeyFormpageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -65,9 +65,22 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
|||||||
this.form.controls["id"].disable();
|
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) {
|
setForm(node?: ShortUrl) {
|
||||||
this.form.controls["id"].setValue(node?.id);
|
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["targetUrl"].setValue(node?.targetUrl);
|
||||||
this.form.controls["description"].setValue(node?.description);
|
this.form.controls["description"].setValue(node?.description);
|
||||||
this.form.controls["groupId"].setValue(node?.group?.id);
|
this.form.controls["groupId"].setValue(node?.group?.id);
|
||||||
@ -75,9 +88,7 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
|||||||
|
|
||||||
getCreateInput(): ShortUrlCreateInput {
|
getCreateInput(): ShortUrlCreateInput {
|
||||||
return {
|
return {
|
||||||
shortUrl: this.form.controls["shortUrl"].pristine
|
shortUrl: this.form.controls["shortUrl"].value ?? undefined,
|
||||||
? undefined
|
|
||||||
: (this.form.controls["shortUrl"].value ?? undefined),
|
|
||||||
targetUrl: this.form.controls["targetUrl"].pristine
|
targetUrl: this.form.controls["targetUrl"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls["targetUrl"].value ?? undefined),
|
: (this.form.controls["targetUrl"].value ?? undefined),
|
||||||
|
@ -14,14 +14,14 @@ export class ShortUrlsColumns extends PageColumns<ShortUrl> {
|
|||||||
ID_COLUMN,
|
ID_COLUMN,
|
||||||
{
|
{
|
||||||
name: "short_url",
|
name: "short_url",
|
||||||
label: "common.short_url",
|
label: "short_url.short_url",
|
||||||
type: "text",
|
type: "text",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: ShortUrl) => row.shortUrl,
|
value: (row: ShortUrl) => row.shortUrl,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "target_url",
|
name: "target_url",
|
||||||
label: "common.target_url",
|
label: "short_url.target_url",
|
||||||
type: "text",
|
type: "text",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: ShortUrl) => row.targetUrl,
|
value: (row: ShortUrl) => row.targetUrl,
|
||||||
@ -33,13 +33,6 @@ export class ShortUrlsColumns extends PageColumns<ShortUrl> {
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
value: (row: ShortUrl) => row.description,
|
value: (row: ShortUrl) => row.description,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "group",
|
|
||||||
label: "common.group",
|
|
||||||
type: "text",
|
|
||||||
filterable: true,
|
|
||||||
value: (row: ShortUrl) => row.group?.name,
|
|
||||||
},
|
|
||||||
...DB_MODEL_COLUMNS,
|
...DB_MODEL_COLUMNS,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,107 @@
|
|||||||
<app-table
|
|
||||||
[rows]="result.nodes"
|
|
||||||
[columns]="columns"
|
|
||||||
[rowsPerPageOptions]="rowsPerPageOptions"
|
|
||||||
[totalCount]="result.totalCount"
|
|
||||||
[requireAnyPermissions]="requiredPermissions"
|
|
||||||
countHeaderTranslation="group.count_header"
|
|
||||||
[loading]="loading"
|
|
||||||
[(filter)]="filter"
|
|
||||||
[(sort)]="sort"
|
|
||||||
[(skip)]="skip"
|
|
||||||
[(take)]="take"
|
|
||||||
(load)="load()"
|
|
||||||
[create]="true"
|
|
||||||
[update]="true"
|
|
||||||
(delete)="delete($event)"
|
|
||||||
(restore)="restore($event)"></app-table>
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
|
||||||
|
<ng-template #shortUrl let-url>
|
||||||
|
<div class="flex flex-col gap-2 bg rounded-l p-3">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<h3>{{ url.shortUrl }}</h3>
|
||||||
|
<div class="grid-container">
|
||||||
|
<span class="grid-label font-bold">{{ 'short_url.target_url' | translate }}:</span>
|
||||||
|
<a class="grid-value" [href]="url.targetUrl" target="_blank">{{ url.targetUrl }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid-container">
|
||||||
|
<span class="grid-label font-bold">{{ 'common.description' | translate }}:</span>
|
||||||
|
<span class="grid-value">{{ url.description }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="grid-container">
|
||||||
|
<span class="grid-label font-bold">{{ 'short_url.visits' | translate }}:</span>
|
||||||
|
<span class="grid-value">{{ url.visits }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<p-button
|
||||||
|
class="icon-btn btn"
|
||||||
|
icon="pi pi-clone"
|
||||||
|
(onClick)="copy(url.shortUrl)"></p-button>
|
||||||
|
<p-button
|
||||||
|
class="icon-btn btn"
|
||||||
|
icon="pi pi-external-link"
|
||||||
|
(onClick)="open(url.shortUrl)"></p-button>
|
||||||
|
|
||||||
|
<p-button
|
||||||
|
*ngIf="hasPermissions.update"
|
||||||
|
class="icon-btn btn"
|
||||||
|
icon="pi pi-pencil"
|
||||||
|
tooltipPosition="left"
|
||||||
|
pTooltip="{{ 'table.update' | translate }}"
|
||||||
|
[disabled]="url?.deleted"
|
||||||
|
routerLink="edit/{{ url.id }}"></p-button>
|
||||||
|
<p-button
|
||||||
|
*ngIf="hasPermissions.delete"
|
||||||
|
class="icon-btn btn danger-icon-btn"
|
||||||
|
icon="pi pi-trash"
|
||||||
|
tooltipPosition="left"
|
||||||
|
pTooltip="{{ 'table.delete' | translate }}"
|
||||||
|
[disabled]="url?.deleted"
|
||||||
|
(click)="delete(url)"></p-button>
|
||||||
|
<p-button
|
||||||
|
*ngIf="url?.deleted && hasPermissions.restore"
|
||||||
|
class="icon-btn btn"
|
||||||
|
icon="pi pi-undo"
|
||||||
|
tooltipPosition="left"
|
||||||
|
pTooltip="{{ 'table.restore' | translate }}"
|
||||||
|
(click)="restore(url)"></p-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="flex justify-between gap-1">
|
||||||
|
<div><h1>{{ 'short_url.short_url' | translate }}</h1></div>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<p-button
|
||||||
|
*ngIf="hasPermissions.create"
|
||||||
|
class="icon-btn btn"
|
||||||
|
icon="pi pi-plus"
|
||||||
|
tooltipPosition="left"
|
||||||
|
pTooltip="{{ 'table.create' | translate }}"
|
||||||
|
routerLink="create"></p-button>
|
||||||
|
<p-button
|
||||||
|
class="icon-btn btn"
|
||||||
|
[styleClass]="showDeleted ? 'highlight2' : ''"
|
||||||
|
icon="pi pi-trash"
|
||||||
|
tooltipPosition="left"
|
||||||
|
pTooltip="{{
|
||||||
|
(showDeleted ? 'table.hide_deleted' : 'table.show_deleted')
|
||||||
|
| translate
|
||||||
|
}}"
|
||||||
|
(click)="toggleShowDeleted()"></p-button>
|
||||||
|
<p-button
|
||||||
|
class="icon-btn btn"
|
||||||
|
icon="pi pi-sort-alt-slash"
|
||||||
|
tooltipPosition="left"
|
||||||
|
pTooltip="{{ 'table.reset_sort' | translate }}"
|
||||||
|
(click)="resetSort()"></p-button>
|
||||||
|
<p-button
|
||||||
|
class="icon-btn btn"
|
||||||
|
icon="pi pi-filter-slash"
|
||||||
|
tooltipPosition="left"
|
||||||
|
pTooltip="{{ 'table.reset_filters' | translate }}"
|
||||||
|
(click)="resetFilters()"></p-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="bg2 rounded-xl p-3">
|
||||||
|
<div *ngFor="let url of shortUrlsWithoutGroup">
|
||||||
|
<ng-template [ngTemplateOutlet]="shortUrl" [ngTemplateOutletContext]="{ $implicit: url }"></ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngFor="let group of groupsFromGroupedShortUrls" class="bg2 rounded-xl p-3">
|
||||||
|
<div><h2>{{ group }}</h2></div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<ng-template *ngFor="let url of groupedShortUrls[group]" [ngTemplateOutlet]="shortUrl"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: url }"></ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { ApiKeysPage } from 'src/app/modules/admin/administration/api-keys/api-keys.page';
|
import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { PageDataService } from 'src/app/core/base/page.data.service';
|
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 { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
||||||
|
|
||||||
describe('ApiKeysComponent', () => {
|
describe("ApiKeysComponent", () => {
|
||||||
let component: ApiKeysPage;
|
let component: ApiKeysPage;
|
||||||
let fixture: ComponentFixture<ApiKeysPage>;
|
let fixture: ComponentFixture<ApiKeysPage>;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ describe('ApiKeysComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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 { PageBase } from "src/app/core/base/page-base";
|
||||||
import { ToastService } from "src/app/service/toast.service";
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationDialogService } from "src/app/service/confirmation-dialog.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 { ShortUrl } from "src/app/model/entities/short-url";
|
||||||
import { ShortUrlsDataService } from "src/app/modules/admin/short-urls/short-urls.data.service";
|
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 { 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({
|
@Component({
|
||||||
selector: "app-short-urls",
|
selector: "app-short-urls",
|
||||||
templateUrl: "./short-urls.page.html",
|
templateUrl: "./short-urls.page.html",
|
||||||
styleUrl: "./short-urls.page.scss",
|
styleUrl: "./short-urls.page.scss",
|
||||||
})
|
})
|
||||||
export class ShortUrlsPage extends PageBase<
|
export class ShortUrlsPage
|
||||||
ShortUrl,
|
extends PageBase<ShortUrl, ShortUrlsDataService, ShortUrlsColumns>
|
||||||
ShortUrlsDataService,
|
implements OnInit
|
||||||
ShortUrlsColumns
|
{
|
||||||
> {
|
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(
|
constructor(
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private confirmation: ConfirmationDialogService,
|
private confirmation: ConfirmationDialogService,
|
||||||
|
private auth: AuthService,
|
||||||
) {
|
) {
|
||||||
super(true, {
|
super(true, {
|
||||||
read: [PermissionsEnum.shortUrls],
|
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 {
|
load(): void {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.dataService
|
this.dataService
|
||||||
.load(this.filter, this.sort, this.skip, this.take)
|
.load(this.filter, this.sort, this.skip, this.take)
|
||||||
.subscribe((result) => {
|
.subscribe((result) => {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
|
this.shortUrlsWithoutGroup = this.getShortUrlsWithoutGroup();
|
||||||
|
this.groupedShortUrls = this.getShortUrlsWithGroup();
|
||||||
|
|
||||||
|
console.warn(
|
||||||
|
"result",
|
||||||
|
result,
|
||||||
|
this.shortUrlsWithoutGroup,
|
||||||
|
this.groupedShortUrls,
|
||||||
|
);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -69,4 +122,54 @@ export class ShortUrlsPage extends PageBase<
|
|||||||
messageParams: { entity: group.shortUrl },
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 component: MenuBarComponent;
|
||||||
let fixture: ComponentFixture<MenuBarComponent>;
|
let fixture: ComponentFixture<MenuBarComponent>;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ describe('MenuBarComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from "@angular/core";
|
||||||
import { MenuElement } from 'src/app/model/view/menu-element';
|
import { MenuElement } from "src/app/model/view/menu-element";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-menu-bar',
|
selector: "app-menu-bar",
|
||||||
templateUrl: './menu-bar.component.html',
|
templateUrl: "./menu-bar.component.html",
|
||||||
styleUrl: './menu-bar.component.scss',
|
styleUrl: "./menu-bar.component.scss",
|
||||||
})
|
})
|
||||||
export class MenuBarComponent {
|
export class MenuBarComponent {
|
||||||
@Input() elements: MenuElement[] = [];
|
@Input() elements: MenuElement[] = [];
|
||||||
|
@ -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 component: SideMenuComponent;
|
||||||
let fixture: ComponentFixture<SideMenuComponent>;
|
let fixture: ComponentFixture<SideMenuComponent>;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ describe('SideMenuComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from "@angular/core";
|
||||||
import { MenuElement } from 'src/app/model/view/menu-element';
|
import { MenuElement } from "src/app/model/view/menu-element";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-side-menu',
|
selector: "app-side-menu",
|
||||||
templateUrl: './side-menu.component.html',
|
templateUrl: "./side-menu.component.html",
|
||||||
styleUrl: './side-menu.component.scss',
|
styleUrl: "./side-menu.component.scss",
|
||||||
})
|
})
|
||||||
export class SideMenuComponent {
|
export class SideMenuComponent {
|
||||||
@Input() elements: MenuElement[] = [];
|
@Input() elements: MenuElement[] = [];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<p-sidebar
|
<p-sidebar
|
||||||
[visible]="true"
|
[visible]="true"
|
||||||
position="right"
|
position="right"
|
||||||
[style]="{ width: 'min-content', minWidth: '500px' }"
|
[style]="{ width: 'min-content', minWidth: '350px' }"
|
||||||
(onHide)="onClose.emit()"
|
(onHide)="onClose.emit()"
|
||||||
(visibleChange)="!$event ? onClose.emit() : undefined">
|
(visibleChange)="!$event ? onClose.emit() : undefined">
|
||||||
<ng-template pTemplate="headless">
|
<ng-template pTemplate="headless">
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { FormPageComponent } from 'src/app/modules/shared/components/slidein/form-page.component';
|
import { FormPageComponent } from "src/app/modules/shared/components/slidein/form-page.component";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from "@angular/forms";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
describe('FormpageComponent', () => {
|
describe("FormpageComponent", () => {
|
||||||
let component: FormPageComponent<string>;
|
let component: FormPageComponent<string>;
|
||||||
let fixture: ComponentFixture<FormPageComponent<string>>;
|
let fixture: ComponentFixture<FormPageComponent<string>>;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ describe('FormpageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -5,18 +5,18 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
TemplateRef,
|
TemplateRef,
|
||||||
} from '@angular/core';
|
} from "@angular/core";
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from "@angular/forms";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
import {
|
import {
|
||||||
FormPageContentDirective,
|
FormPageContentDirective,
|
||||||
FormPageHeaderDirective,
|
FormPageHeaderDirective,
|
||||||
} from 'src/app/modules/shared/form';
|
} from "src/app/modules/shared/form";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-form-page',
|
selector: "app-form-page",
|
||||||
templateUrl: './form-page.component.html',
|
templateUrl: "./form-page.component.html",
|
||||||
styleUrl: './form-page.component.scss',
|
styleUrl: "./form-page.component.scss",
|
||||||
})
|
})
|
||||||
export class FormPageComponent<T> {
|
export class FormPageComponent<T> {
|
||||||
@Input({ required: true }) formGroup!: FormGroup;
|
@Input({ required: true }) formGroup!: FormGroup;
|
||||||
|
@ -1,121 +1,121 @@
|
|||||||
<p-table
|
<p-table
|
||||||
[dataKey]="dataKey"
|
[dataKey]="dataKey"
|
||||||
[value]="rows"
|
[value]="rows"
|
||||||
[paginator]="true"
|
[paginator]="true"
|
||||||
[rowsPerPageOptions]="rowsPerPageOptions"
|
[rowsPerPageOptions]="rowsPerPageOptions"
|
||||||
[rows]="take"
|
[rows]="take"
|
||||||
(rowsChange)="takeChange.emit($event)"
|
(rowsChange)="takeChange.emit($event)"
|
||||||
[first]="skip"
|
[first]="skip"
|
||||||
(firstChange)="skipChange.emit($event)"
|
(firstChange)="skipChange.emit($event)"
|
||||||
[totalRecords]="totalCount"
|
[totalRecords]="totalCount"
|
||||||
[lazy]="true"
|
[lazy]="true"
|
||||||
(onLazyLoad)="loadData($event)"
|
(onLazyLoad)="loadData($event)"
|
||||||
[loading]="loading"
|
[loading]="loading"
|
||||||
[resizableColumns]="false"
|
[resizableColumns]="false"
|
||||||
[reorderableColumns]="false"
|
[reorderableColumns]="false"
|
||||||
[responsiveLayout]="responsiveLayout"
|
[responsiveLayout]="responsiveLayout"
|
||||||
columnResizeMode="expand"
|
columnResizeMode="expand"
|
||||||
[breakpoint]="'900px'"
|
[breakpoint]="'900px'"
|
||||||
[rowHover]="true">
|
[rowHover]="true">
|
||||||
<ng-template pTemplate="caption">
|
<ng-template pTemplate="caption">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<div *ngIf="countHeaderTranslation" class="table-caption-table-info">
|
<div *ngIf="countHeaderTranslation" class="table-caption-table-info">
|
||||||
<div class="table-caption-text">
|
<div class="table-caption-text">
|
||||||
<ng-container *ngIf="!loading"
|
<ng-container *ngIf="!loading"
|
||||||
>{{ skip > 0 ? skip : 1 }} {{ 'table.to' | translate }}
|
>{{ skip > 0 ? skip : 1 }} {{ 'table.to' | translate }}
|
||||||
{{ skip + rows.length }} {{ 'table.of' | translate }}
|
{{ skip + rows.length }} {{ 'table.of' | translate }}
|
||||||
{{ totalCount }}
|
{{ totalCount }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
{{ countHeaderTranslation | translate }}
|
{{ countHeaderTranslation | translate }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<p-button
|
<p-button
|
||||||
*ngIf="create && hasPermissions.create"
|
*ngIf="create && hasPermissions.create"
|
||||||
class="icon-btn btn"
|
class="icon-btn btn"
|
||||||
icon="pi pi-plus"
|
icon="pi pi-plus"
|
||||||
tooltipPosition="left"
|
tooltipPosition="left"
|
||||||
pTooltip="{{ 'table.create' | translate }}"
|
pTooltip="{{ 'table.create' | translate }}"
|
||||||
routerLink="{{ updateBaseUrl }}create"></p-button>
|
routerLink="{{ updateBaseUrl }}create"></p-button>
|
||||||
<p-button
|
<p-button
|
||||||
class="icon-btn btn"
|
class="icon-btn btn"
|
||||||
[styleClass]="showDeleted ? 'highlight2' : ''"
|
[styleClass]="showDeleted ? 'highlight2' : ''"
|
||||||
icon="pi pi-trash"
|
icon="pi pi-trash"
|
||||||
tooltipPosition="left"
|
tooltipPosition="left"
|
||||||
pTooltip="{{
|
pTooltip="{{
|
||||||
(showDeleted ? 'table.hide_deleted' : 'table.show_deleted')
|
(showDeleted ? 'table.hide_deleted' : 'table.show_deleted')
|
||||||
| translate
|
| translate
|
||||||
}}"
|
}}"
|
||||||
(click)="toggleShowDeleted()"></p-button>
|
(click)="toggleShowDeleted()"></p-button>
|
||||||
<p-button
|
<p-button
|
||||||
class="icon-btn btn"
|
class="icon-btn btn"
|
||||||
icon="pi pi-sort-alt-slash"
|
icon="pi pi-sort-alt-slash"
|
||||||
tooltipPosition="left"
|
tooltipPosition="left"
|
||||||
pTooltip="{{ 'table.reset_sort' | translate }}"
|
pTooltip="{{ 'table.reset_sort' | translate }}"
|
||||||
(click)="resetSort()"></p-button>
|
(click)="resetSort()"></p-button>
|
||||||
<p-button
|
<p-button
|
||||||
class="icon-btn btn"
|
class="icon-btn btn"
|
||||||
icon="pi pi-filter-slash"
|
icon="pi pi-filter-slash"
|
||||||
tooltipPosition="left"
|
tooltipPosition="left"
|
||||||
pTooltip="{{ 'table.reset_filters' | translate }}"
|
pTooltip="{{ 'table.reset_filters' | translate }}"
|
||||||
(click)="resetFilters()"></p-button>
|
(click)="resetFilters()"></p-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template pTemplate="header">
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
*ngFor="let column of columns"
|
|
||||||
[pSortableColumn]="column.name"
|
|
||||||
[class]="column.class ?? ''">
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<span>{{ column.label | translate }}</span>
|
|
||||||
<p-sortIcon [field]="column.name"></p-sortIcon>
|
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</ng-template>
|
||||||
<th *ngIf="update || delete.observed"></th>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr *ngIf="showFilters">
|
<ng-template pTemplate="header">
|
||||||
<th *ngFor="let column of columns" [class]="column.class ?? ''">
|
<tr>
|
||||||
<form *ngIf="filterForm && column.filterable" [formGroup]="filterForm">
|
<th
|
||||||
<ng-container [ngSwitch]="column.type">
|
*ngFor="let column of columns"
|
||||||
<input
|
[pSortableColumn]="column.name"
|
||||||
*ngSwitchCase="'date'"
|
[class]="column.class ?? ''">
|
||||||
pInputText
|
<div class="flex items-center space-x-2">
|
||||||
type="text"
|
<span>{{ column.label | translate }}</span>
|
||||||
[formControlName]="column.name"
|
<p-sortIcon *ngIf="column.sortable !== false" [field]="column.name"></p-sortIcon>
|
||||||
class="w-full box-border"
|
</div>
|
||||||
placeholder="dd.MM.yyyy HH:mm:ss" />
|
</th>
|
||||||
<p-triStateCheckbox
|
<th *ngIf="update || delete.observed"></th>
|
||||||
*ngSwitchCase="'bool'"
|
</tr>
|
||||||
[formControlName]="column.name"
|
|
||||||
class="w-full box-border"></p-triStateCheckbox>
|
<tr *ngIf="showFilters">
|
||||||
<input
|
<th *ngFor="let column of columns" [class]="column.class ?? ''">
|
||||||
*ngSwitchDefault
|
<form *ngIf="filterForm && column.filterable" [formGroup]="filterForm">
|
||||||
pInputText
|
<ng-container [ngSwitch]="column.type">
|
||||||
[formControlName]="column.name"
|
<input
|
||||||
class="w-full box-border" />
|
*ngSwitchCase="'date'"
|
||||||
</ng-container>
|
pInputText
|
||||||
</form>
|
type="text"
|
||||||
</th>
|
[formControlName]="column.name"
|
||||||
<th *ngIf="update || delete.observed"></th>
|
class="w-full box-border"
|
||||||
</tr>
|
placeholder="dd.MM.yyyy HH:mm:ss"/>
|
||||||
</ng-template>
|
<p-triStateCheckbox
|
||||||
<ng-template pTemplate="body" let-row let-i="rowIndex">
|
*ngSwitchCase="'bool'"
|
||||||
<tr *ngIf="hasPermissions.read && resolvedColumns.length > 0">
|
[formControlName]="column.name"
|
||||||
<ng-container *ngFor="let r of resolvedColumns[i - take * (skip / take)]">
|
class="w-full box-border"></p-triStateCheckbox>
|
||||||
<td
|
<input
|
||||||
[ngClass]="{ deleted: row?.deleted }"
|
*ngSwitchDefault
|
||||||
[class]="r.column.class ?? ''">
|
pInputText
|
||||||
<ng-container [ngSwitch]="r.column.type">
|
[formControlName]="column.name"
|
||||||
|
class="w-full box-border"/>
|
||||||
|
</ng-container>
|
||||||
|
</form>
|
||||||
|
</th>
|
||||||
|
<th *ngIf="update || delete.observed"></th>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template pTemplate="body" let-row let-i="rowIndex">
|
||||||
|
<tr *ngIf="hasPermissions.read && resolvedColumns.length > 0">
|
||||||
|
<ng-container *ngFor="let r of resolvedColumns[i - take * (skip / take)]">
|
||||||
|
<td
|
||||||
|
[ngClass]="{ deleted: row?.deleted }"
|
||||||
|
[class]="r.column.class ?? ''">
|
||||||
|
<ng-container [ngSwitch]="r.column.type">
|
||||||
<span *ngSwitchCase="'date'">{{
|
<span *ngSwitchCase="'date'">{{
|
||||||
r.value | customDate: 'dd.MM.yyyy HH:mm:ss'
|
r.value | customDate: 'dd.MM.yyyy HH:mm:ss'
|
||||||
}}</span>
|
}}</span>
|
||||||
<span *ngSwitchCase="'bool'">
|
<span *ngSwitchCase="'bool'">
|
||||||
<ng-container [ngSwitch]="r.value">
|
<ng-container [ngSwitch]="r.value">
|
||||||
<span *ngSwitchCase="true">
|
<span *ngSwitchCase="true">
|
||||||
<span class="pi pi-check-circle info"></span>
|
<span class="pi pi-check-circle info"></span>
|
||||||
@ -125,51 +125,56 @@
|
|||||||
</span>
|
</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</span>
|
</span>
|
||||||
<span *ngSwitchCase="'password'">
|
<span *ngSwitchCase="'password'">
|
||||||
{{ r.value | protect }}
|
{{ r.value | protect }}
|
||||||
<p-button
|
<p-button
|
||||||
class="btn icon-btn"
|
class="btn icon-btn"
|
||||||
icon="pi pi-copy"
|
icon="pi pi-copy"
|
||||||
(onClick)="copy(r.value)"></p-button>
|
(onClick)="copy(r.value)"></p-button>
|
||||||
</span>
|
</span>
|
||||||
<span *ngSwitchDefault>{{ r.value }}</span>
|
<span *ngSwitchDefault>{{ r.value }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<td class="flex max-w-32">
|
<td class="flex overflow-hidden">
|
||||||
<p-button
|
<ng-container
|
||||||
*ngIf="update && hasPermissions.update"
|
*ngTemplateOutlet="
|
||||||
class="icon-btn btn"
|
customActions;
|
||||||
icon="pi pi-pencil"
|
context: { $implicit: row }
|
||||||
tooltipPosition="left"
|
"></ng-container>
|
||||||
pTooltip="{{ 'table.update' | translate }}"
|
<p-button
|
||||||
[disabled]="row?.deleted"
|
*ngIf="update && hasPermissions.update"
|
||||||
routerLink="{{ updateBaseUrl }}edit/{{ row.id }}"></p-button>
|
class="icon-btn btn"
|
||||||
<p-button
|
icon="pi pi-pencil"
|
||||||
*ngIf="delete.observed && hasPermissions.delete"
|
tooltipPosition="left"
|
||||||
class="icon-btn btn danger-icon-btn"
|
pTooltip="{{ 'table.update' | translate }}"
|
||||||
icon="pi pi-trash"
|
[disabled]="row?.deleted"
|
||||||
tooltipPosition="left"
|
routerLink="{{ updateBaseUrl }}edit/{{ row.id }}"></p-button>
|
||||||
pTooltip="{{ 'table.delete' | translate }}"
|
<p-button
|
||||||
[disabled]="row?.deleted"
|
*ngIf="delete.observed && hasPermissions.delete"
|
||||||
(click)="delete.emit(row)"></p-button>
|
class="icon-btn btn danger-icon-btn"
|
||||||
<p-button
|
icon="pi pi-trash"
|
||||||
*ngIf="restore.observed && row?.deleted && hasPermissions.restore"
|
tooltipPosition="left"
|
||||||
class="icon-btn btn"
|
pTooltip="{{ 'table.delete' | translate }}"
|
||||||
icon="pi pi-undo"
|
[disabled]="row?.deleted"
|
||||||
tooltipPosition="left"
|
(click)="delete.emit(row)"></p-button>
|
||||||
pTooltip="{{ 'table.restore' | translate }}"
|
<p-button
|
||||||
(click)="restore.emit(row)"></p-button>
|
*ngIf="restore.observed && row?.deleted && hasPermissions.restore"
|
||||||
</td>
|
class="icon-btn btn"
|
||||||
</tr>
|
icon="pi pi-undo"
|
||||||
</ng-template>
|
tooltipPosition="left"
|
||||||
<ng-template pTemplate="emptymessage">
|
pTooltip="{{ 'table.restore' | translate }}"
|
||||||
<tr></tr>
|
(click)="restore.emit(row)"></p-button>
|
||||||
<tr>
|
</td>
|
||||||
<td [attr.colspan]="columns.length + 1">
|
</tr>
|
||||||
{{ 'table.no_entries_found' | translate }}
|
</ng-template>
|
||||||
</td>
|
<ng-template pTemplate="emptymessage">
|
||||||
</tr>
|
<tr></tr>
|
||||||
<tr></tr>
|
<tr>
|
||||||
</ng-template>
|
<td [attr.colspan]="columns.length + 1">
|
||||||
|
{{ 'table.no_entries_found' | translate }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr></tr>
|
||||||
|
</ng-template>
|
||||||
</p-table>
|
</p-table>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
|
||||||
import { TableComponent } from './table.component';
|
import { TableComponent } from "./table.component";
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from "src/app/modules/shared/shared.module";
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { ErrorHandlingService } from 'src/app/service/error-handling.service';
|
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from "primeng/api";
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { of } from 'rxjs';
|
import { of } from "rxjs";
|
||||||
|
|
||||||
describe('TableComponent', () => {
|
describe("TableComponent", () => {
|
||||||
let component: TableComponent<string>;
|
let component: TableComponent<string>;
|
||||||
let fixture: ComponentFixture<TableComponent<string>>;
|
let fixture: ComponentFixture<TableComponent<string>>;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ describe('TableComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,26 +1,44 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
|
||||||
import {
|
import {
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
Directive,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
TemplateRef,
|
||||||
|
} from "@angular/core";
|
||||||
|
import {
|
||||||
|
FilterMode,
|
||||||
ResolvedTableColumn,
|
ResolvedTableColumn,
|
||||||
TableColumn,
|
TableColumn,
|
||||||
TableRequireAnyPermissions,
|
TableRequireAnyPermissions,
|
||||||
} from 'src/app/modules/shared/components/table/table.model';
|
} from "src/app/modules/shared/components/table/table.model";
|
||||||
import { TableLazyLoadEvent } from 'primeng/table';
|
import { TableLazyLoadEvent } from "primeng/table";
|
||||||
import { RowsPerPageOption } from 'src/app/service/filter.service';
|
import { RowsPerPageOption } from "src/app/service/filter.service";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort, SortOrder } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort, SortOrder } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
import { debounceTime, Subject } from 'rxjs';
|
import { debounceTime, Subject } from "rxjs";
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from "rxjs/operators";
|
||||||
import { Logger } from 'src/app/service/logger.service';
|
import { Logger } from "src/app/service/logger.service";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { ToastService } from 'src/app/service/toast.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<unknown>) {}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-table',
|
selector: "app-table",
|
||||||
templateUrl: './table.component.html',
|
templateUrl: "./table.component.html",
|
||||||
styleUrl: './table.component.scss',
|
styleUrl: "./table.component.scss",
|
||||||
})
|
})
|
||||||
export class TableComponent<T> implements OnInit {
|
export class TableComponent<T> implements OnInit {
|
||||||
private _rows: T[] = [];
|
private _rows: T[] = [];
|
||||||
@ -57,17 +75,20 @@ export class TableComponent<T> implements OnInit {
|
|||||||
@Output() load = new EventEmitter<void>();
|
@Output() load = new EventEmitter<void>();
|
||||||
@Input() loading = true;
|
@Input() loading = true;
|
||||||
|
|
||||||
@Input() dataKey = 'id';
|
@Input() dataKey = "id";
|
||||||
@Input() responsiveLayout: 'stack' | 'scroll' = 'stack';
|
@Input() responsiveLayout: "stack" | "scroll" = "stack";
|
||||||
|
|
||||||
@Input() create = false;
|
@Input() create = false;
|
||||||
@Input() update = false;
|
@Input() update = false;
|
||||||
@Input() createBaseUrl = '';
|
@Input() createBaseUrl = "";
|
||||||
@Input() updateBaseUrl = '';
|
@Input() updateBaseUrl = "";
|
||||||
|
|
||||||
@Output() delete: EventEmitter<T> = new EventEmitter<T>();
|
@Output() delete: EventEmitter<T> = new EventEmitter<T>();
|
||||||
@Output() restore: EventEmitter<T> = new EventEmitter<T>();
|
@Output() restore: EventEmitter<T> = new EventEmitter<T>();
|
||||||
|
|
||||||
|
@ContentChild(CustomActionDirective, { read: TemplateRef })
|
||||||
|
customActions!: TemplateRef<never>;
|
||||||
|
|
||||||
protected resolvedColumns: ResolvedTableColumn<T>[][] = [];
|
protected resolvedColumns: ResolvedTableColumn<T>[][] = [];
|
||||||
protected filterForm!: FormGroup;
|
protected filterForm!: FormGroup;
|
||||||
protected defaultFilterForm!: FormGroup;
|
protected defaultFilterForm!: FormGroup;
|
||||||
@ -76,12 +97,12 @@ export class TableComponent<T> implements OnInit {
|
|||||||
|
|
||||||
get showDeleted() {
|
get showDeleted() {
|
||||||
return !this.filter.some(
|
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() {
|
get showFilters() {
|
||||||
return this.columns.some(x => x.filterable);
|
return this.columns.some((x) => x.filterable);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unsubscriber$ = new Subject<void>();
|
protected unsubscriber$ = new Subject<void>();
|
||||||
@ -96,25 +117,25 @@ export class TableComponent<T> implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private auth: AuthService,
|
private auth: AuthService,
|
||||||
private toast: ToastService
|
private toast: ToastService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.hasPermissions = {
|
this.hasPermissions = {
|
||||||
read: await this.auth.hasAnyPermissionLazy(
|
read: await this.auth.hasAnyPermissionLazy(
|
||||||
this.requireAnyPermissions.read ?? []
|
this.requireAnyPermissions.read ?? [],
|
||||||
),
|
),
|
||||||
create: await this.auth.hasAnyPermissionLazy(
|
create: await this.auth.hasAnyPermissionLazy(
|
||||||
this.requireAnyPermissions.create ?? []
|
this.requireAnyPermissions.create ?? [],
|
||||||
),
|
),
|
||||||
update: await this.auth.hasAnyPermissionLazy(
|
update: await this.auth.hasAnyPermissionLazy(
|
||||||
this.requireAnyPermissions.update ?? []
|
this.requireAnyPermissions.update ?? [],
|
||||||
),
|
),
|
||||||
delete: await this.auth.hasAnyPermissionLazy(
|
delete: await this.auth.hasAnyPermissionLazy(
|
||||||
this.requireAnyPermissions.delete ?? []
|
this.requireAnyPermissions.delete ?? [],
|
||||||
),
|
),
|
||||||
restore: await this.auth.hasAnyPermissionLazy(
|
restore: await this.auth.hasAnyPermissionLazy(
|
||||||
this.requireAnyPermissions.restore ?? []
|
this.requireAnyPermissions.restore ?? [],
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -131,9 +152,9 @@ export class TableComponent<T> implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resolvedColumns: ResolvedTableColumn<T>[][] = [];
|
const resolvedColumns: ResolvedTableColumn<T>[][] = [];
|
||||||
this.rows.forEach(row => {
|
this.rows.forEach((row) => {
|
||||||
const resolvedRow: ResolvedTableColumn<T>[] = [];
|
const resolvedRow: ResolvedTableColumn<T>[] = [];
|
||||||
this.columns.forEach(column => {
|
this.columns.forEach((column) => {
|
||||||
resolvedRow.push({
|
resolvedRow.push({
|
||||||
value: column.value(row),
|
value: column.value(row),
|
||||||
data: row,
|
data: row,
|
||||||
@ -147,7 +168,7 @@ export class TableComponent<T> implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadData(event: TableLazyLoadEvent) {
|
loadData(event: TableLazyLoadEvent) {
|
||||||
logger.trace('lazy load event', event);
|
logger.trace("lazy load event", event);
|
||||||
|
|
||||||
const { rows, first, sortField, sortOrder } = event;
|
const { rows, first, sortField, sortOrder } = event;
|
||||||
const reload =
|
const reload =
|
||||||
@ -172,9 +193,9 @@ export class TableComponent<T> implements OnInit {
|
|||||||
) {
|
) {
|
||||||
if (sortField instanceof Array) {
|
if (sortField instanceof Array) {
|
||||||
this.sortChange.emit(
|
this.sortChange.emit(
|
||||||
sortField.map(x => ({
|
sortField.map((x) => ({
|
||||||
[x]: sortOrder === 1 ? SortOrder.ASC : SortOrder.DESC,
|
[x]: sortOrder === 1 ? SortOrder.ASC : SortOrder.DESC,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.sortChange.emit([
|
this.sortChange.emit([
|
||||||
@ -211,17 +232,17 @@ export class TableComponent<T> implements OnInit {
|
|||||||
buildDefaultFilterForm() {
|
buildDefaultFilterForm() {
|
||||||
this.defaultFilterForm = new FormGroup({});
|
this.defaultFilterForm = new FormGroup({});
|
||||||
this.columns
|
this.columns
|
||||||
.filter(x => x.filterable)
|
.filter((x) => x.filterable)
|
||||||
.forEach(x => {
|
.forEach((x) => {
|
||||||
let control!: FormControl;
|
let control!: FormControl;
|
||||||
|
|
||||||
if (x.type === 'text') {
|
if (x.type === "text") {
|
||||||
control = new FormControl<string | undefined>(undefined);
|
control = new FormControl<string | undefined>(undefined);
|
||||||
} else if (x.type === 'number') {
|
} else if (x.type === "number") {
|
||||||
control = new FormControl<number | undefined>(undefined);
|
control = new FormControl<number | undefined>(undefined);
|
||||||
} else if (x.type === 'bool') {
|
} else if (x.type === "bool") {
|
||||||
control = new FormControl<boolean | undefined>(undefined);
|
control = new FormControl<boolean | undefined>(undefined);
|
||||||
} else if (x.type === 'date') {
|
} else if (x.type === "date") {
|
||||||
control = new FormControl<Date | undefined>(undefined);
|
control = new FormControl<Date | undefined>(undefined);
|
||||||
} else {
|
} else {
|
||||||
control = new FormControl<unknown | undefined>(undefined);
|
control = new FormControl<unknown | undefined>(undefined);
|
||||||
@ -231,14 +252,14 @@ export class TableComponent<T> implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDefaultFilterForm() {
|
setDefaultFilterForm() {
|
||||||
this.defaultFilter.forEach(x => {
|
this.defaultFilter.forEach((x) => {
|
||||||
Object.keys(x).forEach(key => {
|
Object.keys(x).forEach((key) => {
|
||||||
const value = x[key];
|
const value = x[key];
|
||||||
if (!(key in this.defaultFilterForm.controls)) {
|
if (!(key in this.defaultFilterForm.controls)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof value === 'object' && value !== null) {
|
if (typeof value === "object" && value !== null) {
|
||||||
Object.keys(value).forEach(subKey => {
|
Object.keys(value).forEach((subKey) => {
|
||||||
this.defaultFilterForm.get([key])?.setValue(value[subKey]);
|
this.defaultFilterForm.get([key])?.setValue(value[subKey]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -253,38 +274,41 @@ export class TableComponent<T> implements OnInit {
|
|||||||
|
|
||||||
this.filterForm.valueChanges
|
this.filterForm.valueChanges
|
||||||
.pipe(takeUntil(this.unsubscriber$), debounceTime(200))
|
.pipe(takeUntil(this.unsubscriber$), debounceTime(200))
|
||||||
.subscribe(changes => {
|
.subscribe((changes) => {
|
||||||
logger.trace('Filter input', changes);
|
logger.trace("Filter input", changes);
|
||||||
if (this.filterForm.disabled) {
|
if (this.filterForm.disabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter = Object.keys(changes)
|
const filter = Object.keys(changes)
|
||||||
.filter(
|
.filter(
|
||||||
key =>
|
(key) =>
|
||||||
changes[key] !== undefined &&
|
changes[key] !== undefined &&
|
||||||
changes[key] !== null &&
|
changes[key] !== null &&
|
||||||
changes[key] !== ''
|
changes[key] !== "",
|
||||||
)
|
)
|
||||||
.map(key => {
|
.map((key) => {
|
||||||
const column = this.columns.find(x => x.name === key);
|
const column = this.columns.find((x) => x.name === key);
|
||||||
if (!column || !column.filterable) {
|
if (!column || !column.filterable) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = changes[key];
|
let value = changes[key];
|
||||||
let defaultFilterMode = 'contains';
|
let defaultFilterMode: FilterMode = "contains";
|
||||||
switch (column.type) {
|
switch (column.type) {
|
||||||
case 'number':
|
case "number":
|
||||||
defaultFilterMode = 'equal';
|
defaultFilterMode = "equal";
|
||||||
value = +value;
|
value = +value;
|
||||||
break;
|
break;
|
||||||
case 'bool':
|
case "bool":
|
||||||
defaultFilterMode = 'equal';
|
defaultFilterMode = "equal";
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterMode = column.filterMode || defaultFilterMode;
|
const filterMode = column.filterMode || defaultFilterMode;
|
||||||
|
|
||||||
|
if (column.filterFactory) {
|
||||||
|
return column.filterFactory(key, filterMode, value);
|
||||||
|
}
|
||||||
return { [key]: { [filterMode]: value } };
|
return { [key]: { [filterMode]: value } };
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -300,7 +324,7 @@ export class TableComponent<T> implements OnInit {
|
|||||||
|
|
||||||
copy(val: T | T[keyof T] | string) {
|
copy(val: T | T[keyof T] | string) {
|
||||||
navigator.clipboard.writeText(val as string).then(() => {
|
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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 | T[keyof T] | string;
|
export type TableColumnValue<T> = T | T[keyof T] | string;
|
||||||
|
export type FilterMode = "contains" | "startsWith" | "endsWith" | "equal";
|
||||||
|
|
||||||
export interface TableColumn<T> {
|
export interface TableColumn<T> {
|
||||||
name: string;
|
name: string;
|
||||||
@ -12,7 +14,8 @@ export interface TableColumn<T> {
|
|||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
sortable?: boolean;
|
sortable?: boolean;
|
||||||
filterable?: boolean;
|
filterable?: boolean;
|
||||||
filterMode?: 'contains' | 'startsWith' | 'endsWith' | 'equals';
|
filterMode?: FilterMode;
|
||||||
|
filterFactory?: (key: string, mode: FilterMode, value: T) => Filter;
|
||||||
sort?: (a: T, b: T) => number;
|
sort?: (a: T, b: T) => number;
|
||||||
filter?: (row: T, filter: string) => boolean;
|
filter?: (row: T, filter: string) => boolean;
|
||||||
width?: string;
|
width?: string;
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { addMinutes, format, parseISO } from 'date-fns';
|
import { addMinutes, format, parseISO } from "date-fns";
|
||||||
|
|
||||||
export function formatUTCDateToClientTimezone(date: string) {
|
export function formatUTCDateToClientTimezone(date: string) {
|
||||||
const dateTZ = addMinutes(
|
const dateTZ = addMinutes(
|
||||||
parseISO(date),
|
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) {
|
export function formatUTCDateToGermanLocaleAndTimezone(date: string) {
|
||||||
const dateTZ = addMinutes(
|
const dateTZ = addMinutes(
|
||||||
parseISO(date),
|
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");
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Directive, TemplateRef } from '@angular/core';
|
import { Directive, TemplateRef } from "@angular/core";
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
// eslint-disable-next-line @angular-eslint/directive-selector
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
||||||
selector: '[formPageHeader]',
|
selector: "[formPageHeader]",
|
||||||
})
|
})
|
||||||
export class FormPageHeaderDirective {
|
export class FormPageHeaderDirective {
|
||||||
constructor(public templateRef: TemplateRef<unknown>) {}
|
constructor(public templateRef: TemplateRef<unknown>) {}
|
||||||
@ -10,7 +10,7 @@ export class FormPageHeaderDirective {
|
|||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
// eslint-disable-next-line @angular-eslint/directive-selector
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
||||||
selector: '[formPageContent]',
|
selector: "[formPageContent]",
|
||||||
})
|
})
|
||||||
export class FormPageContentDirective {
|
export class FormPageContentDirective {
|
||||||
constructor(public templateRef: TemplateRef<unknown>) {}
|
constructor(public templateRef: TemplateRef<unknown>) {}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from "@angular/core";
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'bool',
|
name: "bool",
|
||||||
})
|
})
|
||||||
export class BoolPipe implements PipeTransform {
|
export class BoolPipe implements PipeTransform {
|
||||||
constructor(private translate: TranslateService) {}
|
constructor(private translate: TranslateService) {}
|
||||||
|
|
||||||
transform(value: unknown): string {
|
transform(value: unknown): string {
|
||||||
return this.translate.instant(
|
return this.translate.instant(
|
||||||
value === true || value === 'true' ? 'bool.true' : 'bool.false'
|
value === true || value === "true" ? "bool.true" : "bool.false",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from "@angular/core";
|
||||||
import { format } from 'date-fns';
|
import { format } from "date-fns";
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'customDate',
|
name: "customDate",
|
||||||
})
|
})
|
||||||
export class CustomDatePipe implements PipeTransform {
|
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) {
|
if (!value) {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const date =
|
const date =
|
||||||
typeof value === 'string' || typeof value === 'number'
|
typeof value === "string" || typeof value === "number"
|
||||||
? new Date(value)
|
? new Date(value)
|
||||||
: value;
|
: value;
|
||||||
return format(date as Date, dateFormat);
|
return format(date as Date, dateFormat);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error formatting date:', error);
|
console.error("Error formatting date:", error);
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from "@angular/core";
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'protect',
|
name: "protect",
|
||||||
})
|
})
|
||||||
export class ProtectPipe implements PipeTransform {
|
export class ProtectPipe implements PipeTransform {
|
||||||
transform(text?: unknown): string {
|
transform(text?: unknown): string {
|
||||||
const len = 8;
|
const len = 8;
|
||||||
if (text && typeof text === 'string' && text.length) {
|
if (text && typeof text === "string" && text.length) {
|
||||||
return '*'.repeat(text.length);
|
return "*".repeat(text.length);
|
||||||
}
|
}
|
||||||
return '*'.repeat(len);
|
return "*".repeat(len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user