Compare commits

...

14 Commits

Author SHA1 Message Date
Developer 02
482a78207e fix: Erhöhte Grenzwerte für maximale Warnung und Fehlergröße in Angular-Konfiguration 2024-07-22 18:22:51 +02:00
Developer 02
2212ebfc45 refactor: Spaltennamen-Injektion durch Environment-Variablen ersetzt 2024-07-22 18:17:49 +02:00
Developer 02
3cbd0523eb feat: Environment-Variablen um Spalteninformationen erweitert 2024-07-22 18:07:50 +02:00
Developer 02
34cae91925 feat: Bootstrap hinzugefügt 2024-07-22 18:03:19 +02:00
Developer 02
2671a2ff59 feat: SVGs hinzugefügt und Authentifizierungs-Check vereinfacht 2024-07-22 17:31:30 +02:00
Developer 02
fb0e719616 fix: Vorübergehend nur Dark Mode erlaubt wegen localStorage Problem 2024-07-22 16:45:03 +02:00
Developer 02
4a1f3f14da refactor: API-Services umgestellt, URLs werden jetzt über den URL-Service bezogen 2024-07-22 16:34:01 +02:00
Developer 02
4d350bf6e8 feat: UrlService um Routen- und URL-Methoden erweitert, die environments verwenden 2024-07-22 15:56:21 +02:00
Developer 02
3a3df2e7f1 refactor: API_URL Token und Injektion entfernt 2024-07-22 14:15:44 +02:00
Developer 02
f211506832 refactor: Umbenennung von "environment" zu "env" und Aktualisierung der Pfade, einschließlich Hinzufügung von "api_url" 2024-07-22 14:13:37 +02:00
Developer 02
8a4dfa7421 fix: SSR deaktiviert, um localStorage-Nutzung zu ermöglichen (von "server.ts" zu false geändert) 2024-07-22 13:52:44 +02:00
Developer 02
e86a9aaf29 feat: Umgebungen hinzugefügt und konfiguriert 2024-07-17 10:59:21 +02:00
Developer 02
8cfe57b30b refactor: Moved URL services 2024-07-17 10:20:49 +02:00
Developer 02
f468f65192 chore: Modulimporte aktualisiert, um CommonModule für ngClass und die richtigen Generic UI Grid-Module einzuschließen. 2024-07-17 10:13:05 +02:00
55 changed files with 721 additions and 196 deletions

View File

@@ -30,23 +30,24 @@
"src/assets"
],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": [],
"scripts": [
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
],
"server": "src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "server.ts"
}
"ssr": false
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
"maximumWarning": "1mb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
@@ -54,7 +55,13 @@
"maximumError": "4kb"
}
],
"outputHashing": "all"
"outputHashing": "all",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"development": {
"optimization": false,
@@ -98,12 +105,15 @@
],
"styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": []
"scripts": [
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
]
}
}
}
}
}
}
}

View File

@@ -23,8 +23,11 @@
"@generic-ui/fabric": "^0.21.0",
"@generic-ui/hermes": "^0.21.0",
"@generic-ui/ngx-grid": "^0.21.0",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"express": "^4.18.2",
"rxjs": "~7.8.0",
"sweetalert2": "^11.12.3",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
},
@@ -4210,6 +4213,16 @@
"node": ">=14"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
@@ -5474,6 +5487,39 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"node_modules/bootstrap": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
],
"peerDependencies": {
"@popperjs/core": "^2.11.8"
}
},
"node_modules/bootstrap-icons": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
]
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -11876,6 +11922,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sweetalert2": {
"version": "11.12.3",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.3.tgz",
"integrity": "sha512-tgGJSgfSV/xK113JEvhjwqKAuBYLXsZf0z+tGlHKQNnsLMi4eSNuzn9tt5hUdvjOVTLSPDgo0Lc+tnjy9Uj7Og==",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/limonte"
}
},
"node_modules/symbol-observable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",

View File

@@ -26,8 +26,11 @@
"@generic-ui/fabric": "^0.21.0",
"@generic-ui/hermes": "^0.21.0",
"@generic-ui/ngx-grid": "^0.21.0",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"express": "^4.18.2",
"rxjs": "~7.8.0",
"sweetalert2": "^11.12.3",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
},

View File

@@ -1,6 +1,4 @@
<body>
<app-nav-menu></app-nav-menu>
<main class="container-fluid">
<router-outlet></router-outlet>
</main>
</body>
<app-nav-menu></app-nav-menu>
<main class="container-fluid">
<router-outlet></router-outlet>
</main>

View File

@@ -5,8 +5,7 @@ import { provideClientHydration } from '@angular/platform-browser';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { APP_BASE_HREF } from '@angular/common';
import { UrlService } from '../services/url.service';
import { API_URL } from './tokens';
import { UrlService } from './services/url.service';
export const appConfig: ApplicationConfig = {
providers: [
@@ -18,11 +17,6 @@ export const appConfig: ApplicationConfig = {
provide: APP_BASE_HREF,
useFactory: (urlService: UrlService) => urlService.getBaseHref(),
deps: [UrlService]
},
{
provide: API_URL,
useFactory: (urlService: UrlService) => urlService.getApiUrl(),
deps: [UrlService]
},
}
]
};

View File

@@ -15,25 +15,24 @@ export class AuthGuard implements CanActivate {
private router: Router
) {}
canActivate(
async canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return new Observable(observer => {
this.authService.isAuthenticated().subscribe({
next: (res) => {
if(!res)
this.openLogin();
observer.next(res)
},
error: (error) => {
observer.next(false)
},
complete: () => observer.complete()
});
});
): Promise<boolean> {
try {
const isAuthenticated = await this.authService.isAuthenticated();
if (!isAuthenticated) {
this.openLogin();
}
return isAuthenticated;
} catch (error) {
// Hata durumunda false döndür
return false;
}
}
openLogin(): MatDialogRef<LoginComponent, any> {
const dialogRef = this.dialog.open(LoginComponent, {

View File

@@ -1,13 +1,16 @@
import { Component, OnInit } from '@angular/core';
import { ColorModeService, GetLocalTheme, Theme } from 'src/app/services/color-mode.service';
import { ColorModeService, GetLocalTheme, Theme } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common'
@Component({
standalone: true,
imports: [CommonModule],
selector: 'app-color-mode-bttn',
templateUrl: './color-mode-bttn.component.html',
styleUrl: './color-mode-bttn.component.css'
})
export class ColorModeBttnComponent implements OnInit {
constructor(private cModeService: ColorModeService) {
constructor(private cModeService: ColorModeService) {
this.theme = GetLocalTheme();
}
@@ -17,10 +20,10 @@ export class ColorModeBttnComponent implements OnInit {
readonly Themes = Theme;
theme : Theme;
theme: Theme;
onClick(theme: Theme) {
this.theme = theme;
let theTheme:Theme = theme;
let theTheme: Theme = theme;
this.cModeService.setTheme(theTheme);
}
}

View File

@@ -9,6 +9,7 @@ import { DirGroup } from '../../models/user-management.api.models';
@Component({
standalone: true,
imports: [DirGroupTableComponent],
selector: 'app-group-dir-import',
templateUrl: './group-dir-import.component.html',
styleUrl: './group-dir-import.component.css'

View File

@@ -1,7 +1,9 @@
import { Component } from '@angular/core';
import { GroupTableComponent } from '../../components/tables/group-table/group-table.component';
@Component({
standalone: true,
imports: [GroupTableComponent],
selector: 'app-group',
templateUrl: './group.component.html',
styleUrl: './group.component.css'

View File

@@ -1,7 +1,9 @@
import { Component } from '@angular/core';
import { ModuleTableComponent } from '../../components/tables/module-table/module-table.component';
@Component({
standalone: true,
imports: [ModuleTableComponent],
selector: 'app-module',
templateUrl: './module.component.html',
styleUrl: './module.component.css'

View File

@@ -1,11 +1,14 @@
import { AfterViewInit, Component, Inject, Input, OnDestroy, OnInit, ViewChild, input } from '@angular/core';
import { Component, Inject, Input, OnDestroy, OnInit, ViewChild, input } from '@angular/core';
import { ApiService } from '../../../services/user-management.api.service';
import { GuiColumn, GuiColumnMenu, GuiSorting, GuiRowDetail, GuiPaging, GuiPagingDisplay, GuiSearching, GuiCellEdit, GuiInfoPanel, GuiTitlePanel, GuiRowSelection, GuiSelectedRow, GuiGridComponent, GuiGridApi, GuiTheme } from '@generic-ui/ngx-grid';
import { GuiGridModule, GuiColumn, GuiColumnMenu, GuiSorting, GuiRowDetail, GuiPaging, GuiPagingDisplay, GuiSearching, GuiCellEdit, GuiInfoPanel, GuiTitlePanel, GuiRowSelection, GuiSelectedRow, GuiGridComponent, GuiGridApi, GuiTheme } from '@generic-ui/ngx-grid';
import { Subscription } from 'rxjs/internal/Subscription';
import { ColorModeService, Theme } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule ],
selector: 'app-base-table',
templateUrl: './base-table.component.html',
styleUrl: './base-table.component.css'
@@ -62,7 +65,7 @@ export class BaseTableComponent<TModel, TApiService extends ApiService<TModel>>
}
};
theme: GuiTheme = localStorage.getItem('theme') === 'dark' ? GuiTheme.DARK : GuiTheme.FABRIC;
theme: GuiTheme = GuiTheme.DARK;
private themeSubscription: Subscription = new Subscription();

View File

@@ -2,17 +2,21 @@ import { Component, Inject } from '@angular/core';
import { BaseTableComponent } from '../base-table/base-table.component';
import { DirGroupService } from '../../../services/dir-group.service';
import { DirGroup } from '../../../models/user-management.api.models';
import { GuiColumn } from '@generic-ui/ngx-grid';
import { GuiGridModule } from '@generic-ui/ngx-grid';
import { ColorModeService } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { env } from '../../../../environments/environment';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule ],
selector: 'app-dir-group-table',
templateUrl: '../base-table/base-table.component.html',
styleUrl: './dir-group-table.component.css'
})
export class DirGroupTableComponent extends BaseTableComponent<DirGroup, DirGroupService> {
constructor(service: DirGroupService, @Inject('DIR_GROUP_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
super(service, columns, cModeService)
constructor(service: DirGroupService, cModeService: ColorModeService) {
super(service, env.columnNames.dirGroup, cModeService)
}
}

View File

@@ -2,18 +2,22 @@ import { Component, Inject } from '@angular/core';
import { DirUser } from '../../../models/user-management.api.models';
import { DirUserService } from '../../../services/dir-user.service';
import { BaseTableComponent } from '../base-table/base-table.component';
import { GuiColumn } from '@generic-ui/ngx-grid/gui/grid/src/core/api/gui.grid.public-api';
import { GuiGridModule, GuiColumn } from '@generic-ui/ngx-grid';
import { ColorModeService } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { env } from '../../../../environments/environment';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule],
selector: 'app-dir-user-table',
templateUrl: '../base-table/base-table.component.html',
styleUrl: './dir-user-table.component.css'
})
export class DirUserTableComponent extends BaseTableComponent<DirUser, DirUserService> {
constructor(service: DirUserService, @Inject('DIR_USER_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
super(service, columns, cModeService)
constructor(service: DirUserService, cModeService: ColorModeService) {
super(service, env.columnNames.dirUser, cModeService)
}
fetchDataByGroupName(groupName: string): void {
@@ -22,7 +26,7 @@ export class DirUserTableComponent extends BaseTableComponent<DirUser, DirUserSe
this.source = response;
this.loading = false;
},
error: (error: any) => {}
error: (error: any) => { }
})
}
}

View File

@@ -1,19 +1,23 @@
import { Component, Inject } from '@angular/core';
import { GroupService } from '../../../services/group.service';
import { Group } from '../../../models/user-management.api.models';
import { GuiColumn } from '@generic-ui/ngx-grid';
import { GuiGridModule, GuiColumn } from '@generic-ui/ngx-grid';
import { BaseTableComponent } from '../base-table/base-table.component';
import { ColorModeService } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { env } from '../../../../environments/environment';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule],
selector: 'app-group-table',
templateUrl: '../base-table/base-table.component.html',
styleUrl: './group-table.component.css'
})
export class GroupTableComponent extends BaseTableComponent<Group, GroupService> {
constructor(
service: GroupService, @Inject('GROUP_COMPLETE_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
super(service, columns, cModeService)
service: GroupService, cModeService: ColorModeService) {
super(service, env.columnNames.group.complete, cModeService)
}
}

View File

@@ -1,19 +1,23 @@
import { Component, Inject } from '@angular/core';
import { GroupOfUserService } from '../../../services/group-of-user.service';
import { GroupOfUser } from '../../../models/user-management.api.models';
import { GuiColumn } from '@generic-ui/ngx-grid';
import { GuiGridModule, GuiColumn } from '@generic-ui/ngx-grid';
import { BaseTableComponent } from '../base-table/base-table.component';
import { ColorModeService } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { env } from '../../../../environments/environment';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule ],
selector: 'app-group-user-table',
templateUrl: '../base-table/base-table.component.html',
styleUrl: './group-user-table.component.css'
})
export class GroupUserTableComponent extends BaseTableComponent<GroupOfUser, GroupOfUserService> {
constructor(service: GroupOfUserService, @Inject('GROUP_USER_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
super(service, columns, cModeService)
constructor(service: GroupOfUserService, cModeService: ColorModeService) {
super(service, env.columnNames.groupOfUser, cModeService)
this.initData = () => this.fetchDataWith(true,true);
this.loading = false;
}

View File

@@ -1,19 +1,23 @@
import { Component, Inject } from '@angular/core';
import { Component } from '@angular/core';
import { Module } from '../../../models/user-management.api.models';
import { GuiColumn } from '@generic-ui/ngx-grid';
import { GuiGridModule } from '@generic-ui/ngx-grid';
import { BaseTableComponent } from '../base-table/base-table.component';
import { ModuleService } from '../../../services/module.service'
import { ColorModeService } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { env } from '../../../../environments/environment';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule],
selector: 'app-module-table',
templateUrl: '../base-table/base-table.component.html',
styleUrl: './module-table.component.css'
})
export class ModuleTableComponent extends BaseTableComponent<Module, ModuleService> {
constructor(
service: ModuleService, @Inject('MODULE_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
super(service, columns, cModeService)
service: ModuleService, cModeService: ColorModeService) {
super(service, env.columnNames.module, cModeService)
}
}

View File

@@ -2,19 +2,23 @@ import { Component, Inject } from '@angular/core';
import { UserRep } from '../../../models/user-management.api.models';
import { UserRepService } from '../../../services/user-representation.service';
import { BaseTableComponent } from '../base-table/base-table.component';
import { GuiColumn } from '@generic-ui/ngx-grid';
import { GuiGridModule, GuiColumn } from '@generic-ui/ngx-grid';
import { ColorModeService } from '../../../services/color-mode.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { env } from '../../../../environments/environment';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule],
selector: 'app-user-rep-table',
templateUrl: '../base-table/base-table.component.html',
styleUrl: './user-rep-table.component.css'
})
export class UserRepTableComponent extends BaseTableComponent<UserRep, UserRepService> {
constructor(service: UserRepService, @Inject('USER_REP_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
super(service, columns, cModeService)
constructor(service: UserRepService, cModeService: ColorModeService) {
super(service, env.columnNames.userRep, cModeService)
this.loading = false
}

View File

@@ -3,12 +3,16 @@ import { UserService } from '../../../services/user.service';
import { ModuleOfUserService } from '../../../services/module-of-user.service';
import { GroupOfUserService } from '../../../services/group-of-user.service';
import { User } from '../../../models/user-management.api.models';
import { GuiColumn } from '@generic-ui/ngx-grid';
import { GuiGridModule, GuiColumn } from '@generic-ui/ngx-grid';
import { BaseTableComponent } from '../base-table/base-table.component';
import { ColorModeService } from '../../../services/color-mode.service'
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { env } from '../../../../environments/environment';
@Component({
standalone: true,
imports: [CommonModule, FormsModule, GuiGridModule],
selector: 'app-user-table',
templateUrl: '../base-table/base-table.component.html',
styleUrl: './user-table.component.css'
@@ -21,9 +25,8 @@ export class UserTableComponent extends BaseTableComponent<User, UserService> {
constructor(mosService: ModuleOfUserService,
gosService: GroupOfUserService,
service: UserService,
@Inject('USER_TABLE_COLUMNS') columns: Array<GuiColumn>,
cModeService: ColorModeService) {
super(service, columns, cModeService)
super(service, env.columnNames.user, cModeService)
this.mosService = mosService;
this.gosService = gosService;
}

View File

@@ -5,17 +5,18 @@ import { UserTableComponent } from '../tables/user-table/user-table.component';
import { ModuleTableComponent } from '../tables/module-table/module-table.component';
import { GroupTableComponent } from '../tables/group-table/group-table.component';
import { User } from '../../models/user-management.api.models';
import { MatTabsModule, MatTabGroup } from '@angular/material/tabs';
@Component({
standalone: true,
standalone: true,
imports: [MatTabsModule, MatTabGroup, UserTableComponent, ModuleTableComponent, GroupTableComponent],
selector: 'app-user-assignment',
templateUrl: './user-assignment.component.html',
styleUrl: './user-assignment.component.scss'
})
export class UserAssignmentComponent implements OnInit, AfterViewInit {
initWithoutData = () => {}
initWithoutData = () => { }
userInLabel: string = "Benutzer in"
@@ -29,7 +30,7 @@ export class UserAssignmentComponent implements OnInit, AfterViewInit {
title: 'Groups'
}
]
@Input() activeTabTitle: string | null = null;
@Input() activeIndex: number = 0;
@ViewChild("unassignedUsers") unassignedUsers!: UserTableComponent;
@@ -43,7 +44,7 @@ export class UserAssignmentComponent implements OnInit, AfterViewInit {
mode: GuiRowSelectionMode.MULTIPLE
}
ngOnInit(): void {}
ngOnInit(): void { }
ngAfterViewInit(): void {
//this.unassignedUsers.loading = false;

View File

@@ -7,9 +7,11 @@ import { catchError, finalize } from 'rxjs/operators';
import { DirGroupTableComponent } from '../tables/dir-group-table/dir-group-table.component';
import { DirUserTableComponent } from '../tables/dir-user-table/dir-user-table.component';
import { UserService } from '../../services/user.service';
import {User} from '../../models/user-management.api.models'
import { User } from '../../models/user-management.api.models'
@Component({
standalone: true,
imports: [DirGroupTableComponent, DirUserTableComponent],
selector: 'app-user-group-dir-import',
templateUrl: './user-group-dir-import.component.html',
styleUrl: './user-group-dir-import.component.css'

View File

@@ -5,9 +5,11 @@ import { UserRepTableComponent } from '../tables/user-rep-table/user-rep-table.c
import { GroupTableComponent } from '../tables/group-table/group-table.component';
import { UserRepService } from '../../services/user-representation.service';
import Swal from 'sweetalert2';
import { MatTabsModule, MatTabGroup } from '@angular/material/tabs';
@Component({
standalone: true,
imports: [MatTabsModule, MatTabGroup, UserTableComponent, GroupTableComponent, UserRepTableComponent],
selector: 'app-user-representation',
templateUrl: './user-representation.component.html',
styleUrl: './user-representation.component.css'
@@ -57,21 +59,21 @@ export class UserRepresentationComponent {
repUserOnSelectedRows = (rows: GuiSelectedRow[]) => {
if (rows.length == 0 && this.slRepUserId) {
if(!this.slUserId){
if (!this.slUserId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie den Benutzer!",
});
}
else if(!this.slRepUserId){
else if (!this.slRepUserId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie die repräsentative Benutzer!",
});
}
else if(!this.slRightGroupId){
else if (!this.slRightGroupId) {
Swal.fire({
icon: "error",
title: "Oops...",
@@ -89,7 +91,7 @@ export class UserRepresentationComponent {
next: (response) => {
this.slRepUserId = null;
this.repUsers.safelyUnselectAll()
if(this.slUserId != null)
if (this.slUserId != null)
this.userReps.fetchData(this.slUserId)
},
error: (error) => {
@@ -105,28 +107,28 @@ export class UserRepresentationComponent {
this.slRepUserId = null;
}
else if(rows.length > 0) {
else if (rows.length > 0) {
this.slRepUserId = rows[0].source?.id;
}
}
repGroupOnSelectedRows = (rows: GuiSelectedRow[]) => {
if (rows.length == 0 && this.slRepGroupId) {
if(!this.slUserId){
if (!this.slUserId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie den Benutzer!",
});
}
else if(!this.slRepGroupId){
else if (!this.slRepGroupId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie die repräsentative Gruppe!",
});
}
else if(!this.slRightGroupId){
else if (!this.slRightGroupId) {
Swal.fire({
icon: "error",
title: "Oops...",
@@ -144,7 +146,7 @@ export class UserRepresentationComponent {
next: (res) => {
this.slRepGroupId = null;
this.repUsers.safelyUnselectAll()
if(this.slUserId != null)
if (this.slUserId != null)
this.userReps.fetchData(this.slUserId)
},
error: (error) => {
@@ -160,7 +162,7 @@ export class UserRepresentationComponent {
this.slRepGroupId = null;
}
else if(rows.length > 0) {
else if (rows.length > 0) {
this.slRepGroupId = rows[0].source?.id;
}
}
@@ -171,7 +173,7 @@ export class UserRepresentationComponent {
next: (res) => {
this.slUserRepId = null;
this.userReps.safelyUnselectAll();
if(this.slUserId != null)
if (this.slUserId != null)
this.userReps.fetchData(this.slUserId)
},
error: (err) => {
@@ -186,7 +188,7 @@ export class UserRepresentationComponent {
}
})
}
else if(rows.length > 0) {
else if (rows.length > 0) {
this.slUserRepId = rows[0].source?.id;
}
}

View File

@@ -1 +1 @@
<app-user-table #userTable [cellEditing] = "cellEditing"></app-user-table>
<app-user-table #userTable [cellEditing]="cellEditing"></app-user-table>

View File

@@ -4,6 +4,7 @@ import { UserTableComponent } from '../tables/user-table/user-table.component';
@Component({
standalone: true,
imports: [UserTableComponent],
selector: 'app-user',
templateUrl: './user.component.html',
styleUrl: './user.component.css'
@@ -22,5 +23,5 @@ export class UserComponent {
@ViewChild("userTable") userTable! : UserTableComponent
@ViewChild("userTable") userTable!: UserTableComponent
}

View File

@@ -2,9 +2,12 @@ import { Component, Inject, Input } from '@angular/core';
import { AuthenticationService } from '../services/authentication.service';
import Swal from 'sweetalert2';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
standalone: true,
imports: [CommonModule, FormsModule],
selector: 'app-login',
templateUrl: './login.component.html',
styleUrl: './login.component.css'
@@ -14,14 +17,13 @@ export class LoginComponent {
username: string = '';
password: string = '';
waitRes:boolean = false;
waitRes: boolean = false;
IsPwdHidden: boolean = true;
constructor(private authService: AuthenticationService, @Inject(MAT_DIALOG_DATA) public data: any) {
//localStorage.getItem('theme') === 'dark'
if(typeof(this.afterLogin) == typeof(data.afterLogin))
this.afterLogin = data.afterLogin;
if (typeof (this.afterLogin) == typeof (data.afterLogin))
this.afterLogin = data.afterLogin;
}
@Input() afterLogin: () => void = () => { }

View File

@@ -4,25 +4,29 @@ import { UserGroupDirImportComponent } from '../components/user-group-dir-import
import { GroupDirImportComponent } from '../components/group-dir-import/group-dir-import.component';
import { AuthenticationService, IsLogedIn } from '../services/authentication.service';
import { LoginComponent } from '../login/login.component';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ColorModeBttnComponent } from '../components/common/color-mode-bttn/color-mode-bttn.component';
@Component({
standalone:true,
standalone: true,
imports: [RouterModule, CommonModule, ColorModeBttnComponent],
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
isLogedIn() {
return IsLogedIn();
return IsLogedIn();
}
isExpanded = false;
constructor(public dialog: MatDialog, private authService: AuthenticationService) {
this.authService.isAuthenticated().subscribe();
this.authService.isAuthenticated().then().catch()
}
get isDarkTheme(): boolean {
return localStorage.getItem('theme') === 'dark'
return true;
}
collapse() {
@@ -44,24 +48,19 @@ export class NavMenuComponent {
});
}
auth() {
this.authService.isAuthenticated().subscribe({
next: (res) => {
if (res)
this.authService.logout().subscribe();
else {
const dialogRef = this.dialog.open(LoginComponent, {
width: "35vw",
data: {
afterLogin: () => {
dialogRef.close();
}
}
});
async auth() {
const isLoggedIn = await this.authService.isAuthenticated();
if (isLoggedIn)
this.authService.logout().subscribe();
else {
const dialogRef = this.dialog.open(LoginComponent, {
width: "35vw",
data: {
afterLogin: () => {
dialogRef.close();
}
}
},
error: (err) => {
}
})
});
}
}
}

View File

@@ -1,35 +1,38 @@
import { Inject, Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Observable, firstValueFrom } from 'rxjs';
import { Router } from '@angular/router';
import Swal from 'sweetalert2';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root',
})
export class AuthenticationService {
private loginUrl: string;
private logoutUrl: string;
private checkUrl: string;
constructor(
private router: Router,
private http: HttpClient,
@Inject('LOGIN_URL') private loginUrl: string,
@Inject('LOGOUT_URL') private logoutUrl: string,
@Inject('LOGIN_CHECK_URL') private checkUrl: string) { }
urlService : UrlService)
{
this.loginUrl = urlService.apiRoute.login;
this.logoutUrl = urlService.apiRoute.logout;
this.checkUrl = urlService.apiRoute.loginCheck;
}
isAuthenticated(): Observable<boolean> {
return new Observable(observer => {
this.http.get<boolean>(this.checkUrl, { withCredentials: true })
.subscribe({
next: (response) => {
_isLogedIn = response;
observer.next(response)
},
error: (error) => {
this.showErrorAlert()
observer.error(error)
},
complete: () => observer.complete()
});
});
async isAuthenticated(): Promise<boolean> {
try {
const response = await firstValueFrom(this.http.get<boolean>(this.checkUrl, { withCredentials: true }));
_isLogedIn = response;
return response;
} catch (error) {
this.showErrorAlert();
return false;
}
}
login(username: string, password: string): Observable<any> {

View File

@@ -17,7 +17,6 @@ export class ColorModeService {
}
setTheme(theme: Theme) {
localStorage.setItem('theme', theme)
this.updateTheme();
}
@@ -76,7 +75,7 @@ export const ParseTheme: (value: string) => Theme | undefined = (value: string)
}
export function GetLocalTheme(): Theme {
let sTheme: string | null = localStorage.getItem('theme');
let sTheme: string | null = 'dark';
if (sTheme === null)
return Theme.Dark;

View File

@@ -4,13 +4,14 @@ import { DirGroup, } from '../models/user-management.api.models';
import { ApiService } from './user-management.api.service';
import { Observable } from 'rxjs/internal/Observable';
import Swal from 'sweetalert2';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class DirGroupService extends ApiService<DirGroup> {
constructor(http: HttpClient, @Inject('DIR_GROUP_URL') private userUri: string) {
super(http, userUri);
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.dirGroup);
}
//TODO: Swal.fire

View File

@@ -3,13 +3,14 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { DirUser } from '../models/user-management.api.models';
import { ApiService } from './user-management.api.service';
import { Observable } from 'rxjs/internal/Observable';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class DirUserService extends ApiService<DirUser> {
constructor(http: HttpClient, @Inject('DIR_USER_URL') private userUri: string) {
super(http, userUri);
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.dirUser);
}
override getAll(groupName?: string): Observable<DirUser[]> {

View File

@@ -2,13 +2,16 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Inject, Injectable } from '@angular/core';
import { DirUser } from '../models/user-management.api.models';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class DirService {
constructor(private http: HttpClient, @Inject('DIR_URL') private baseUrl: string) {
private baseUrl: string
constructor(private http: HttpClient, urlService : UrlService) {
this.http = http;
this.baseUrl = urlService.apiRoute.directory;
}
getUser(groupName: string): Observable<DirUser[]> {

View File

@@ -3,13 +3,14 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { GroupOfUser } from '../models/user-management.api.models';
import { ApiService } from './user-management.api.service';
import { Observable } from 'rxjs';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class GroupOfUserService extends ApiService<GroupOfUser> {
constructor(http: HttpClient, @Inject('GROUP_OF_USER_URL') private userUri: string) {
super(http, userUri);
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.groupOfUser);
}
deleteByGroupUserId(groupId: number, userId: number): Observable<any> {

View File

@@ -3,13 +3,14 @@ import { HttpClient } from '@angular/common/http';
import { DirGroup, Group, } from '../models/user-management.api.models';
import { ApiService } from './user-management.api.service';
import { Observable } from 'rxjs/internal/Observable';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class GroupService extends ApiService<Group> {
constructor(http: HttpClient, @Inject('GROUP_URL') private userUri: string) {
super(http, userUri);
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.group);
}
createByDir(createModel: DirGroup): Observable<DirGroup> {

View File

@@ -3,13 +3,14 @@ import { HttpClient } from '@angular/common/http';
import { ModuleOfUser } from '../models/user-management.api.models';
import { ApiService } from './user-management.api.service';
import { Observable } from 'rxjs';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class ModuleOfUserService extends ApiService<ModuleOfUser> {
constructor(http: HttpClient, @Inject('MODULE_OF_USER_URL') private userUri: string) {
super(http, userUri);
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.moduleOfUser);
}
deleteByModuleGroupId(moduleId: number, userId: number): Observable<any> {

View File

@@ -2,12 +2,13 @@ import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ApiService } from './user-management.api.service';
import { Module } from '../models/user-management.api.models';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class ModuleService extends ApiService<Module> {
constructor(http: HttpClient, @Inject('MODULE_URL') private userUri: string) {
super(http, userUri);
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.module);
}
}

View File

@@ -0,0 +1,41 @@
import { Injectable, Inject, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Meta } from '@angular/platform-browser';
import { env } from '../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class UrlService {
document: Document;
meta: Meta;
constructor() {
this.document = inject(DOCUMENT)
this.meta = inject(Meta)
}
getBaseHref(): string {
const baseElement = this.document.querySelector('base');
return baseElement?.getAttribute('href') || '/';
}
getApiUrl(route: string = ""): string {
return env.api_url + route;
}
readonly apiRoute = {
user: this.getApiUrl(env.routes.user),
group: this.getApiUrl(env.routes.group),
module: this.getApiUrl(env.routes.module),
moduleOfUser: this.getApiUrl(env.routes.moduleOfUser),
groupOfUser: this.getApiUrl(env.routes.groupOfUser),
userRep: this.getApiUrl(env.routes.userRep),
dirGroup: this.getApiUrl(env.routes.dirGroup),
dirUser: this.getApiUrl(env.routes.dirUser),
directory: this.getApiUrl(env.routes.directory),
login: this.getApiUrl(env.routes.login),
logout: this.getApiUrl(env.routes.logout),
loginCheck: this.getApiUrl(env.routes.loginCheck)
};
}

View File

@@ -1,12 +1,8 @@
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Inject, Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ApiService<Model> {
constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
constructor(http: HttpClient, baseUrl: string) {
this.http = http;
this.baseUrl = baseUrl;
}

View File

@@ -3,13 +3,14 @@ import { UserRep } from "../models/user-management.api.models";
import { ApiService } from "./user-management.api.service";
import { HttpClient, HttpParams } from "@angular/common/http";
import { Observable } from "rxjs";
import { UrlService } from "./url.service";
@Injectable({
providedIn: "root"
})
export class UserRepService extends ApiService<UserRep> {
constructor(http: HttpClient, @Inject('USER_REP_URL') private userRepUri: string) {
super(http, userRepUri)
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.userRep)
}
override getAll(withUser: boolean = false, withRepGroup: boolean = false, withRightGroup: boolean = false, withRepUser: boolean = false, userId?: number): Observable<UserRep[]> {

View File

@@ -3,13 +3,14 @@ import { HttpClient } from '@angular/common/http';
import { DirUser, User } from '../models/user-management.api.models';
import { ApiService } from './user-management.api.service';
import { Observable } from 'rxjs';
import { UrlService } from './url.service';
@Injectable({
providedIn: 'root'
})
export class UserService extends ApiService<User> {
constructor(http: HttpClient, @Inject('USER_URL') private userUri: string) {
super(http, userUri);
constructor(http: HttpClient, urlService : UrlService) {
super(http, urlService.apiRoute.user);
}
getByModuleId(moduleId: number, assigned: boolean = true): Observable<User[]> {

View File

@@ -1,3 +1 @@
import { InjectionToken } from '@angular/core';
export const API_URL = new InjectionToken<string>('API_URL');
import { InjectionToken } from '@angular/core';

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Ebene_1" viewBox="0 0 850 121.32">
<defs>
<style>.cls-1{fill:#fff;}</style>
</defs>
<path class="cls-1" d="m809.77,76.62h-8.94l3.51-28.73c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.39.8,9.58l3.51,28.73h0Zm40.23,41.51l-28.73-114.94h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12Zm-81.41-86.2l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52V31.93h19.16Zm-104.07,44.7h-8.94l3.51-28.73c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.39.8,9.58l3.51,28.73h0Zm40.23,41.51l-28.73-114.94h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12Zm-123.69-57.47c0,19.16-1.6,28.73-15.96,28.73V31.93c14.37,0,15.96,12.77,15.96,28.73m33.52,0c0-35.12-12.77-57.47-41.51-57.47h-41.51v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47m-121.32,28.73h-25.54V3.19h-33.52v114.94h55.87l3.19-28.73h0Zm-105.68-12.77h-8.94l3.51-28.73c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.39.8,9.58l3.51,28.73h0Zm40.23,41.51L399.07,3.19h-31.93l-28.74,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12Zm-81.41-86.2l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52V31.93h19.16ZM268.18,3.19h-33.52v114.94h33.52V3.19h0Zm-44.7,94.19v-46.29h-39.91l-1.6,23.95h12.77v9.58c-3.19,4.79-6.39,7.98-12.77,7.98-7.98,0-12.77-11.17-12.77-31.93s4.79-31.93,12.77-31.93c6.86,0,11.33,5.75,12.77,14.37l28.73-6.39C218.7,15.96,207.52,0,181.98,0c-33.52,0-46.3,28.73-46.3,60.66s12.77,60.66,46.3,60.66c17.56,0,31.93-7.98,41.5-23.94M126.11,3.19h-33.52v114.94h33.52V3.19h0ZM49.49,60.66c0,19.16-1.6,28.73-15.96,28.73V31.93c14.37,0,15.96,12.77,15.96,28.73m33.52,0c0-35.12-12.77-57.47-41.51-57.47H0v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 850 121.32">
<defs>
<style>
.cls-1 {
fill: #a52431;
stroke-width: 0px;
}
</style>
</defs>
<path class="cls-1" d="M83.01,60.66c0-35.12-12.77-57.47-41.51-57.47H0v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47M49.49,60.66c0,19.16-1.6,28.73-15.96,28.73V31.93c14.37,0,15.96,12.77,15.96,28.73M126.11,3.19h-33.52v114.94h33.52V3.19ZM223.49,97.38v-46.29h-39.91l-1.6,23.95h12.77v9.58c-3.19,4.79-6.39,7.98-12.77,7.98-7.98,0-12.77-11.17-12.77-31.93s4.79-31.93,12.77-31.93c6.86,0,11.33,5.75,12.77,14.37l28.73-6.39C218.7,15.96,207.52,0,181.98,0c-33.52,0-46.29,28.73-46.29,60.66s12.77,60.66,46.29,60.66c17.56,0,31.93-7.98,41.51-23.94M268.18,3.19h-33.52v114.94h33.52V3.19ZM346.4,31.93l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52V31.93h19.16ZM427.81,118.13L399.07,3.19h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM387.58,76.62h-8.94l3.51-28.74c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.38.8,9.58l3.51,28.74ZM493.26,89.4h-25.54V3.19h-33.52v114.94h55.87l3.19-28.74ZM614.58,60.66c0-35.12-12.77-57.47-41.51-57.47h-41.51v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47M581.05,60.66c0,19.16-1.6,28.73-15.96,28.73V31.93c14.37,0,15.96,12.77,15.96,28.73M704.75,118.13l-28.73-114.94h-31.93l-28.74,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM664.52,76.62h-8.94l3.51-28.74c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.38.8,9.58l3.51,28.74ZM768.59,31.93l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52V31.93h19.16ZM850,118.13l-28.73-114.94h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM809.77,76.62h-8.94l3.51-28.74c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.38.8,9.58l3.51,28.74Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 200">
<defs>
<style>
.cls-1 {
fill: #a52431;
}
.cls-1, .cls-2 {
stroke-width: 0px;
}
.cls-2 {
fill: #fff;
}
</style>
</defs>
<rect class="cls-1" width="1100" height="200"/>
<path class="cls-2" d="M929.81,115.71h-8.94l3.51-28.74c.32-3.19.64-6.39.8-9.58h.32c.16,3.19.48,6.39.8,9.58l3.51,28.74ZM970.04,157.22l-28.73-114.94h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM888.63,71.02l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52v-86.2h19.16ZM784.56,115.71h-8.94l3.51-28.74c.32-3.19.64-6.39.8-9.58h.32c.16,3.19.48,6.39.8,9.58l3.51,28.74ZM824.79,157.22l-28.73-114.94h-31.93l-28.74,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM701.09,99.75c0,19.16-1.6,28.73-15.96,28.73v-57.47c14.37,0,15.96,12.77,15.96,28.73M734.62,99.75c0-35.12-12.77-57.47-41.51-57.47h-41.51v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47M613.3,128.48h-25.54V42.28h-33.52v114.94h55.87l3.19-28.74ZM507.62,115.71h-8.94l3.51-28.74c.32-3.19.64-6.39.8-9.58h.32c.16,3.19.48,6.39.8,9.58l3.51,28.74ZM547.85,157.22l-28.73-114.94h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM466.44,71.02l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52v-86.2h19.16ZM388.22,42.28h-33.52v114.94h33.52V42.28ZM343.53,136.47v-46.29h-39.91l-1.6,23.94h12.77v9.58c-3.19,4.79-6.38,7.98-12.77,7.98-7.98,0-12.77-11.17-12.77-31.93s4.79-31.93,12.77-31.93c6.86,0,11.33,5.75,12.77,14.37l28.74-6.39c-4.79-20.75-15.96-36.72-41.51-36.72-33.52,0-46.29,28.73-46.29,60.66s12.77,60.66,46.29,60.66c17.56,0,31.93-7.98,41.51-23.94M246.15,42.28h-33.52v114.94h33.52V42.28ZM169.53,99.75c0,19.16-1.6,28.73-15.96,28.73v-57.47c14.37,0,15.96,12.77,15.96,28.73M203.05,99.75c0-35.12-12.77-57.47-41.51-57.47h-41.5v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" class="bi" width="3em" height="2.5em" viewBox="0 0 488.6 488.6" stroke="#a9a8ad">
<style>
vertical-align: -.125em;
fill: currentColor;
</style>
<path opacity="0.4"
d="M480.9,333.2c-27.2-22.3-56.5-37.1-62.4-40c-0.7-0.3-1.1-1-1.1-1.8v-42.3c5.3-3.5,8.8-9.6,8.8-16.5v-43.9
c0-21.8-17.7-39.5-39.5-39.5H382h-4.7c-21.8,0-39.5,17.7-39.5,39.5v43.9c0,6.9,3.5,12.9,8.8,16.5v42.3c0,0.3-0.1,0.5-0.1,0.7
c8.3,5.7,17,12.1,25.5,19.1c9.9,8.2,15.6,20.2,15.6,33.2v35.3h101v-30.1C488.6,343.3,485.8,337.2,480.9,333.2z" />
<path opacity="0.5" d="M142,291.4v-42.3c5.3-3.5,8.8-9.6,8.8-16.5v-43.9c0-21.8-17.7-39.5-39.5-39.5h-4.7h-4.7c-21.8,0-39.5,17.7-39.5,39.5v43.9
c0,6.9,3.5,12.9,8.8,16.5v42.3c0,0.7-0.4,1.4-1.1,1.8c-6,2.9-35.3,17.7-62.4,40c-4.9,4-7.7,10.1-7.7,16.4v30.1h101v-35.3
c0-12.9,5.7-25,15.6-33.2c8.5-7,17.2-13.4,25.5-19.1C142.1,291.9,142,291.7,142,291.4z" />
<path opacity="0.5" d="M360.5,325.1c-31.9-26.2-66.3-43.6-73.4-47.1c-0.8-0.4-1.3-1.2-1.3-2.1v-49.7c6.2-4.2,10.4-11.3,10.4-19.3v-51.6
c0-25.6-20.8-46.4-46.4-46.4h-5.5h-5.5c-25.6,0-46.4,20.8-46.4,46.4v51.5c0,8.1,4.1,15.2,10.4,19.3v49.7c0,0.9-0.5,1.7-1.3,2.1
c-7,3.4-41.4,20.8-73.4,47.1c-5.8,4.7-9.1,11.8-9.1,19.3v35.3h108.9l10.8-49.3c-21.7-30.3,1.6-31.8,5.7-31.8l0,0l0,0
c4.1,0,27.4,1.5,5.7,31.8l10.8,49.3h108.9v-35.3C369.6,336.9,366.3,329.8,360.5,325.1z" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="3em" height="2.5em" viewBox="0 0 24 24" style="stroke: #a9a8ad;">
<path opacity="0.5" d="M15.9998 2L14.9998 2C12.1714 2 10.7576 2.00023 9.87891 2.87891C9.00023 3.75759 9.00023 5.1718 9.00023 8.00023L9.00023 16.0002C9.00023 18.8287 9.00023 20.2429 9.87891 21.1215C10.7574 22 12.1706 22 14.9976 22L14.9998 22L15.9998 22C18.8282 22 20.2424 22 21.1211 21.1213C21.9998 20.2426 21.9998 18.8284 21.9998 16L21.9998 8L21.9998 7.99998C21.9998 5.17157 21.9998 3.75736 21.1211 2.87868C20.2424 2 18.8282 2 15.9998 2Z" fill="#1C274C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25098 11.999C1.25098 11.5848 1.58676 11.249 2.00098 11.249L13.9735 11.249L12.0129 9.56845C11.6984 9.29889 11.662 8.82541 11.9315 8.51092C12.2011 8.19642 12.6746 8.16 12.9891 8.42957L16.4891 11.4296C16.6553 11.5721 16.751 11.7801 16.751 11.999C16.751 12.218 16.6553 12.426 16.4891 12.5685L12.9891 15.5685C12.6746 15.838 12.2011 15.8016 11.9315 15.4871C11.662 15.1726 11.6984 14.6991 12.0129 14.4296L13.9735 12.749L2.00098 12.749C1.58676 12.749 1.25098 12.4132 1.25098 11.999Z" fill="#1C274C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" />
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
<path
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" />
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
<path
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,5 @@
<svg width="em" height="2em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 6C16 8.20914 14.2091 10 12 10C9.79086 10 8 8.20914 8 6C8 3.79086 9.79086 2 12 2C14.2091 2 16 3.79086 16 6Z" fill="#1C274C"/>
<path opacity="0.5" d="M14.4774 21.9208C13.7513 21.9728 12.9296 22 12 22C4 22 4 19.9853 4 17.5C4 15.0147 7.58172 13 12 13C14.8806 13 17.4056 13.8564 18.8142 15.1412C18.298 15 17.5737 15 16.5 15C14.8501 15 14.0251 15 13.5126 15.5126C13 16.0251 13 16.8501 13 18.5C13 20.1499 13 20.9749 13.5126 21.4874C13.7501 21.725 14.0547 21.8524 14.4774 21.9208Z" fill="#1C274C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.5 22C14.8501 22 14.0251 22 13.5126 21.4874C13 20.9749 13 20.1499 13 18.5C13 16.8501 13 16.0251 13.5126 15.5126C14.0251 15 14.8501 15 16.5 15C18.1499 15 18.9749 15 19.4874 15.5126C20 16.0251 20 16.8501 20 18.5C20 20.1499 20 20.9749 19.4874 21.4874C18.9749 22 18.1499 22 16.5 22ZM17.0833 16.9444C17.0833 16.6223 16.8222 16.3611 16.5 16.3611C16.1778 16.3611 15.9167 16.6223 15.9167 16.9444V17.9167H14.9444C14.6223 17.9167 14.3611 18.1778 14.3611 18.5C14.3611 18.8222 14.6223 19.0833 14.9444 19.0833H15.9167V20.0556C15.9167 20.3777 16.1778 20.6389 16.5 20.6389C16.8222 20.6389 17.0833 20.3777 17.0833 20.0556V19.0833H18.0556C18.3777 19.0833 18.6389 18.8222 18.6389 18.5C18.6389 18.1778 18.3777 17.9167 18.0556 17.9167H17.0833V16.9444Z" fill="#1C274C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,12 @@
<svg class="bi my-1 theme-icon-active" width="3em" height="2em" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" style="stroke: #a9a8ad;">
<path
d="M16 6C16 8.20914 14.2091 10 12 10C9.79086 10 8 8.20914 8 6C8 3.79086 9.79086 2 12 2C14.2091 2 16 3.79086 16 6Z"
fill="#1C274C" />
<path opacity="0.5"
d="M14.4774 21.9208C13.7513 21.9728 12.9296 22 12 22C4 22 4 19.9853 4 17.5C4 15.0147 7.58172 13 12 13C14.8806 13 17.4056 13.8564 18.8142 15.1412C18.298 15 17.5737 15 16.5 15C14.8501 15 14.0251 15 13.5126 15.5126C13 16.0251 13 16.8501 13 18.5C13 20.1499 13 20.9749 13.5126 21.4874C13.7501 21.725 14.0547 21.8524 14.4774 21.9208Z"
fill="#1C274C" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M16.5 22C14.8501 22 14.0251 22 13.5126 21.4874C13 20.9749 13 20.1499 13 18.5C13 16.8501 13 16.0251 13.5126 15.5126C14.0251 15 14.8501 15 16.5 15C18.1499 15 18.9749 15 19.4874 15.5126C20 16.0251 20 16.8501 20 18.5C20 20.1499 20 20.9749 19.4874 21.4874C18.9749 22 18.1499 22 16.5 22ZM17.0833 16.9444C17.0833 16.6223 16.8222 16.3611 16.5 16.3611C16.1778 16.3611 15.9167 16.6223 15.9167 16.9444V17.9167H14.9444C14.6223 17.9167 14.3611 18.1778 14.3611 18.5C14.3611 18.8222 14.6223 19.0833 14.9444 19.0833H15.9167V20.0556C15.9167 20.3777 16.1778 20.6389 16.5 20.6389C16.8222 20.6389 17.0833 20.3777 17.0833 20.0556V19.0833H18.0556C18.3777 19.0833 18.6389 18.8222 18.6389 18.5C18.6389 18.1778 18.3777 17.9167 18.0556 17.9167H17.0833V16.9444Z"
fill="#1C274C" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,119 @@
export const env = {
production: false,
api_url: "/api",
routes: {
user: "/user",
group: "/group",
module: "/module",
moduleOfUser: "/moduleOfUser",
groupOfUser: "/groupOfUser",
userRep: "/userRep",
dirGroup: "/directory/Group?propName=samaccountname",
dirUser: "/directory/user",
directory: "/directory",
login: "/auth/login",
logout: "/auth/logout",
loginCheck: "/auth/check"
},
columnNames: {
user: [
{
header: 'Benutzername',
field: 'username'
},
{
header: 'Vorname',
field: 'prename'
},
{
header: 'Name',
field: 'name'
},
{
header: 'E-email',
field: 'email'
}],
group: {
complete: [
{
header: "Gruppe",
field: "name"
},
{
header: "Kommentar",
field: "comment"
}
],
representative: [
{
header: "Repräsentation",
field: "name"
}
],
right: [
{
header: "Richtiger",
field: "name"
}
]
},
dirGroup: [
{
header: "SAM-Account-Name",
field: (dirGroup: any) => dirGroup?.samaccountname[0]
}
],
dirUser: [
{
header: 'Benutzername',
field: (dirUser: any) => dirUser?.samaccountname?.[0] ?? ""
},
{
header: 'Vorname',
field: (dirUser: any) => dirUser?.givenname?.[0] ?? ""
},
{
header: 'Name',
field: (dirUser: any) => dirUser?.sn?.[0] ?? ""
},
{
header: 'E-email',
field: (dirUser: any) => dirUser?.mail?.[0] ?? ""
}
],
module: [
{
header: "Produkt",
field: "name"
},
{
header: "Kurzname",
field: "shortName"
}
],
groupOfUser: [
{
header: "Benutzername",
field: (gou: any) => gou.user.username
},
{
header: "Gruppen",
field: (gou: any) => gou.group.name
},
],
userRep: [
{
header: "Repr. Gruppen",
field: (ur: any) => ur.repGroup?.name
},
{
header: "Rechte Gruppen",
field: (ur: any) => ur.rightGroup?.name
},
{
header: "Repr. Benutzer",
field: (ur: any) => ur.repUser?.username
},
]
}
};

View File

@@ -0,0 +1,119 @@
export const env = {
production: false,
api_url: "/api",
routes: {
user: "/user",
group: "/group",
module: "/module",
moduleOfUser: "/moduleOfUser",
groupOfUser: "/groupOfUser",
userRep: "/userRep",
dirGroup: "/directory/Group?propName=samaccountname",
dirUser: "/directory/user",
directory: "/directory",
login: "/auth/login",
logout: "/auth/logout",
loginCheck: "/auth/check"
},
columnNames: {
user: [
{
header: 'Benutzername',
field: 'username'
},
{
header: 'Vorname',
field: 'prename'
},
{
header: 'Name',
field: 'name'
},
{
header: 'E-email',
field: 'email'
}],
group: {
complete: [
{
header: "Gruppe",
field: "name"
},
{
header: "Kommentar",
field: "comment"
}
],
representative: [
{
header: "Repräsentation",
field: "name"
}
],
right: [
{
header: "Richtiger",
field: "name"
}
]
},
dirGroup: [
{
header: "SAM-Account-Name",
field: (dirGroup: any) => dirGroup?.samaccountname[0]
}
],
dirUser: [
{
header: 'Benutzername',
field: (dirUser: any) => dirUser?.samaccountname?.[0] ?? ""
},
{
header: 'Vorname',
field: (dirUser: any) => dirUser?.givenname?.[0] ?? ""
},
{
header: 'Name',
field: (dirUser: any) => dirUser?.sn?.[0] ?? ""
},
{
header: 'E-email',
field: (dirUser: any) => dirUser?.mail?.[0] ?? ""
}
],
module: [
{
header: "Produkt",
field: "name"
},
{
header: "Kurzname",
field: "shortName"
}
],
groupOfUser: [
{
header: "Benutzername",
field: (gou: any) => gou.user.username
},
{
header: "Gruppen",
field: (gou: any) => gou.group.name
},
],
userRep: [
{
header: "Repr. Gruppen",
field: (ur: any) => ur.repGroup?.name
},
{
header: "Rechte Gruppen",
field: (ur: any) => ur.rightGroup?.name
},
{
header: "Repr. Benutzer",
field: (ur: any) => ur.repUser?.username
},
]
}
};

View File

@@ -2,15 +2,15 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>UserManagerUi</title>
<title>User Manager Portal</title>
<base href="/">
<meta name="api-url" content="/api">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-typography">
<app-root></app-root>
</body>
</html>
</html>

View File

@@ -1,26 +0,0 @@
import { Injectable, Inject, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Meta } from '@angular/platform-browser';
@Injectable({
providedIn: 'root'
})
export class UrlService {
document: Document;
meta: Meta;
constructor() {
this.document = inject(DOCUMENT)
this.meta = inject(Meta)
}
getBaseHref(): string {
const baseElement = this.document.querySelector('base');
return baseElement?.getAttribute('href') || '/';
}
getApiUrl(): string | null {
const apiMetaTag = this.meta.getTag('name="api-url"');
return apiMetaTag ? apiMetaTag.content : null;
}
}

View File

@@ -1,4 +1,50 @@
/* You can add global styles to this file, and also import other style files */
@import "../node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css";
@import '../node_modules/@angular/cdk/overlay-prebuilt.css';
@import "../node_modules/bootstrap-icons/font/bootstrap-icons.css";
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
a {
color: #0366d6;
}
.btn:focus,
.btn:active:focus,
.btn-link.nav-link:focus,
.form-control:focus,
.form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
code {
color: #e01a76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.assignment-row {
height: 40vh;
}
.user-row {
height: 45vh;
}
.flex-col {
display: flex;
flex-direction: column;
}
.flex-col .card {
flex: 1;
}
.mat-color-scheme-light .mat-mdc-tab:not(.mdc-tab--active) .mdc-tab__text-label {
color: rgb(100, 95, 95) !important;
}
.mdc-tab__text-label{
font-size: medium;
}