Compare commits

...

10 Commits

Author SHA1 Message Date
Developer 02
d3c7ab3da3 feat: Edibility Slide-Toggle auf Ctrl + L zugewiesen 2024-08-12 17:50:45 +02:00
Developer 02
6947db1c4e feat: Update-Service auf Edibility Slide-Toggle umgestellt
- Update-Service-Funktionalität auf einen Slide-Toggle-Button übertragen.
- Mat-Icon-Bedingungen hinzugefügt, um `lock` und `lock-open` Icons anzuzeigen.
- Benutzerfreundlichkeit verbessert durch intuitive visuelle Indikatoren für die Bearbeitbarkeit.
2024-08-12 17:20:25 +02:00
Developer 02
a024cf5409 feat: Click-Event zur zeitbasierten Anzeige von Tooltips implementiert
- Click-Event-Handler hinzugefügt, um die Anzeige von Tooltips zu steuern.
- Tooltips erscheinen jetzt für eine bestimmte Dauer, wenn sie durch das Click-Event ausgelöst werden.
- Verbesserung der Benutzerinteraktion durch zeitgesteuertes visuelles Feedback.
2024-08-12 16:55:29 +02:00
Developer 02
d26fd87367 feat: Informationskomponente initialisiert und als Dialog zum Navigations-Button hinzugefügt
- Die Informationskomponente wurde initialisiert.
- Die Informationskomponente wurde dem Navigations-Button für Informationen als Dialog hinzugefügt.
2024-08-12 13:58:34 +02:00
Developer 02
4644407ca3 refactor: Ersetzen von err.message durch statische Fehlermeldung in Swal.fire 2024-08-12 13:42:38 +02:00
Developer 02
45dac8a554 feat: Delete-Service erstellt und Löschprozess optimiert
- Delete-Service hinzugefügt, um den Löschvorgang zu verwalten.
- Löschprozess in `User`- und `Group`-Komponenten im `ngAfterViewInit`-Lebenszyklus implementiert.
- `removeAll`-Methode im Konstruktor der `BaseComponent` aufgerufen, um vorhandene Services zu bereinigen.
2024-08-12 13:37:36 +02:00
Developer 02
42f082996b feat: Fragebutton erstellt und Shortcut-Zuweisungen optimiert
- Fragebutton zur Benutzeroberfläche hinzugefügt.
- Tastenkombination für den Transferprozess von `Ctrl + T` auf `Ctrl + Space` geändert.
- Zuweisungen der Shortcuts in die `app.component.ts` verschoben.
2024-08-12 13:08:51 +02:00
Developer 02
ef99c674e7 feat: Transfer-Service in Benutzervertretungskomponente integriert 2024-08-12 11:35:02 +02:00
Developer 02
d6f909a81b feat: Alle SweetAlert-Titel und -Nachrichten von Englisch auf Deutsch übersetzt. 2024-08-08 14:50:36 +02:00
Developer 02
be1bc2889f chore: Budget für die Produktionsumgebung erhöht. favicon.ico Datei aktualisiert. 2024-08-08 14:38:10 +02:00
20 changed files with 193 additions and 51 deletions

View File

@@ -48,12 +48,12 @@
{
"type": "initial",
"maximumWarning": "1mb",
"maximumError": "2mb"
"maximumError": "3mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
"maximumError": "2.5mb"
}
],
"outputHashing": "all",

View File

@@ -1,6 +1,10 @@
import { Component } from '@angular/core';
import { Component, HostListener, inject } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {NavMenuComponent} from './components/nav-menu/nav-menu.component'
import { TransferService } from './services/button/transfer.service';
import { UpdateService } from './services/button/update.service';
import { RefreshService } from './services/button/refresh.service';
import { DeletionService } from './services/button/deletion.service';
@Component({
selector: 'app-root',
@@ -11,4 +15,38 @@ import {NavMenuComponent} from './components/nav-menu/nav-menu.component'
})
export class AppComponent {
title = 'app';
protected transferService: TransferService = inject(TransferService)
protected updateService: UpdateService = inject(UpdateService)
protected refreshService: RefreshService = inject(RefreshService)
protected deletionService: DeletionService = inject(DeletionService)
@HostListener('window:keydown.control.s', ['$event'])
protected handleCtrlS(event: KeyboardEvent) {
event.preventDefault();
this.updateService.executeAll();
}
@HostListener('window:keydown.control.r', ['$event'])
protected handleCtrlR(event: KeyboardEvent) {
event.preventDefault();
this.refreshService.executeAll();
}
@HostListener('window:keydown.delete', ['$event'])
protected handleDelete(event: KeyboardEvent) {
event.preventDefault();
this.deletionService.executeAll();
}
@HostListener('window:keydown.control.space', ['$event'])
protected handleCtrlSpace(event: KeyboardEvent) {
event.preventDefault();
this.transferService.executeAll();
}
@HostListener('window:keydown.control.l', ['$event'])
protected handleCtrlL(event: KeyboardEvent) {
event.preventDefault();
this.updateService.toggleEditability()
}
}

View File

@@ -55,11 +55,10 @@ export class GroupDirImportComponent implements OnInit {
forkJoin(requests).pipe(
// finalize is executed after all requests are completed or when an error occurs
finalize(() => {
// Show Swal notification after all requests are completed
Swal.fire({
icon: "success",
title: "Completed",
text: `${numAdded} new groups added`,
title: "Abgeschlossen",
text: `${numAdded} neue Gruppen hinzugefügt`,
position: "center",
showConfirmButton: false,
timer: 3000

View File

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

View File

@@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-info',
standalone: true,
imports: [],
templateUrl: './info.component.html',
styleUrl: './info.component.scss'
})
export class InfoComponent {
}

View File

@@ -36,8 +36,8 @@ export class LoginComponent {
this.waitRes = false;
Swal.fire({
icon: "error",
title: "Oops...",
text: err.error.messages.join("\n"),
title: "Ungültiger Benutzername oder Passwort",
text: "Bitte überprüfen Sie Ihre Anmeldedaten und versuchen Sie es erneut.",
});
},
complete: () => this.waitRes = false

View File

@@ -30,19 +30,24 @@
<!-- Right menu -->
<div class="navbar-collapse justify-content-end me-5">
<a class="navbar-brand" [routerLink]="['/']">User Manager Portal</a>
<mat-slide-toggle *ngIf="isLogedIn()" [(ngModel)]="updateService.isEditable" [ngStyle]="{ 'visibility': creationService.isVisible ? 'visible' : 'hidden' }"></mat-slide-toggle>
<button *ngIf="isLogedIn()" class="btn" (click)="updateService.executeAll()" [ngStyle]="{ 'visibility': updateService.isVisible ? 'visible' : 'hidden' }">
<button *ngIf="isLogedIn()" class="btn" (click)="this.updateService.toggleEditability()" [ngStyle]="{ 'visibility': updateService.isVisible ? 'visible' : 'hidden' }" matTooltip="strg + L" matTooltipPosition="below" [matTooltipClass]="tooltipClass" [matTooltipDisabled]="!updateService.isVisible">
<mat-icon class="scale-pulse">{{ updateService.isEditable ? 'lock_open' : 'lock' }}</mat-icon>
</button>
<button *ngIf="isLogedIn()" class="btn" (click)="updateService.executeAll()" [ngStyle]="{ 'visibility': updateService.isVisible ? 'visible' : 'hidden' }" matTooltip="strg + S" matTooltipPosition="below" [matTooltipClass]="tooltipClass" [matTooltipDisabled]="!updateService.isVisible">
<mat-icon class="scale-pulse" [matBadge]="updateActCount === 0 ? '' : updateActCount">save</mat-icon>
</button>
<button *ngIf="isLogedIn()" class="btn" (click)="creationService.openDialog()" [ngStyle]="{ 'visibility': creationService.isVisible ? 'visible' : 'hidden' }">
<button *ngIf="isLogedIn()" class="btn" (click)="creationService.openDialog()" [ngStyle]="{ 'visibility': creationService.isVisible ? 'visible' : 'hidden' }" matTooltip="strg + C" matTooltipPosition="below" [matTooltipClass]="tooltipClass" [matTooltipDisabled]="!creationService.isVisible">
<mat-icon class="scale-pulse">add_to_photos</mat-icon>
</button>
<button *ngIf="isLogedIn()" class="btn" (click)="transferService.executeAll()" [ngStyle]="{ 'visibility': transferService.isVisible ? 'visible' : 'hidden' }">
<button *ngIf="isLogedIn()" class="btn" (click)="transferService.executeAll()" [ngStyle]="{ 'visibility': transferService.isVisible ? 'visible' : 'hidden' }" matTooltip="strg + ␣" matTooltipPosition="below" [matTooltipClass]="tooltipClass" [matTooltipDisabled]="!transferService.isVisible">
<mat-icon class="move-left-right">swap_horiz</mat-icon>
</button>
<button *ngIf="isLogedIn()" class="btn" (click)="refreshService.executeAll()" [ngStyle]="{ 'visibility': refreshService.isVisible ? 'visible' : 'hidden' }">
<button *ngIf="isLogedIn()" class="btn" (click)="refreshService.executeAll()" [ngStyle]="{ 'visibility': refreshService.isVisible ? 'visible' : 'hidden' }" matTooltip="strg + R" matTooltipPosition="below" [matTooltipClass]="tooltipClass" [matTooltipDisabled]="!refreshService.isVisible">
<mat-icon class="turn-360">sync</mat-icon>
</button>
<button *ngIf="isLogedIn()" class="btn" (click)="showInfo()">
<mat-icon class="scale-pulse">contact_support</mat-icon>
</button>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse"
aria-label="Toggle navigation" [attr.aria-expanded]="isExpanded" (click)="toggle()">
<span class="navbar-toggler-icon"></span>

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, QueryList, ViewChildren } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { AuthenticationService, IsLogedIn } from '../../services/api/authentication.service';
import { LoginComponent } from '../login/login.component';
@@ -11,14 +11,15 @@ import { CreationService } from '../../services/button/creation.service';
import { UpdateService, UpdateEvent } from '../../services/button/update.service';
import { TransferService } from '../../services/button/transfer.service';
import { MatBadgeModule } from '@angular/material/badge';
import {
MatSlideToggleModule,
} from '@angular/material/slide-toggle';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { FormsModule } from '@angular/forms';
import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
import { MatButtonModule } from '@angular/material/button';
@Component({
standalone: true,
imports: [RouterModule, CommonModule, ColorModeBttnComponent, MatIconModule, MatBadgeModule, MatSlideToggleModule, FormsModule],
imports: [RouterModule, CommonModule, ColorModeBttnComponent, MatIconModule, MatBadgeModule, MatSlideToggleModule, FormsModule, MatButtonModule, MatTooltipModule],
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
@@ -68,4 +69,29 @@ export class NavMenuComponent {
});
}
}
@ViewChildren(MatTooltip) tooltips: QueryList<MatTooltip> | undefined;
private __tooltip_timeout_set = false;
private __shift_tooltip: boolean = true;
get tooltipClass() {
this.__shift_tooltip = !this.__shift_tooltip;
return this.__shift_tooltip ? "pt-3" : "";
}
showInfo() {
this.tooltips?.forEach(t => {
t.show();
});
if(!this.__tooltip_timeout_set){
this.__tooltip_timeout_set = true;
setTimeout(() => {
this.__tooltip_timeout_set = false;
this.tooltips?.forEach(t => {
t.hide();
});
}, 3000);
}
}
}

View File

@@ -71,8 +71,8 @@ export class UserGroupDirImportComponent implements OnInit, AfterViewInit {
// Show Swal notification after all requests are completed
Swal.fire({
icon: "success",
title: "Completed",
text: `${numAdded} new users added`,
title: "Abgeschlossen",
text: `${numAdded} neue Benutzer hinzugefügt`,
position: "center",
showConfirmButton: false,
timer: 3000

View File

@@ -3,6 +3,7 @@ import { RefreshService } from '../../services/button/refresh.service';
import { CreationService } from '../../services/button/creation.service';
import { UpdateService } from '../../services/button/update.service';
import { TransferService } from '../../services/button/transfer.service';
import { DeletionService } from '../../services/button/deletion.service';
import { ButtonVisibilityService } from '../../services/button/button-visibility.service';
@Component({
@@ -18,6 +19,7 @@ export class BasePageComponent {
protected creationService: CreationService = inject(CreationService)
protected updateService: UpdateService = inject(UpdateService)
protected transferService: TransferService = inject(TransferService)
protected deletionService: DeletionService = inject(DeletionService)
protected buttonVisibilityService: ButtonVisibilityService = inject(ButtonVisibilityService)
constructor() {
@@ -28,30 +30,7 @@ export class BasePageComponent {
}
if(this.transferService.any)
this.transferService.removeAll()
}
@HostListener('window:keydown.control.s', ['$event'])
protected handleCtrlS(event: KeyboardEvent) {
event.preventDefault();
this.updateService.executeAll();
}
@HostListener('window:keydown.control.r', ['$event'])
protected handleCtrlR(event: KeyboardEvent) {
event.preventDefault();
this.refreshService.executeAll();
}
@HostListener('window:keydown.delete', ['$event'])
protected handleDelete(event: KeyboardEvent) {
this.handleDeleteRequest();
}
handleDeleteRequest() { }
@HostListener('window:keydown.control.t', ['$event'])
protected handleCtrlT(event: KeyboardEvent) {
event.preventDefault();
this.transferService.executeAll();
if(this.deletionService.any)
this.deletionService.removeAll()
}
}

View File

@@ -43,6 +43,8 @@ export class GroupComponent extends BasePageComponent implements AfterViewInit {
this.userTable.fetchDataByGroupId(this.sGroupId);
});
this.creationService.component = GroupFormComponent
this.deletionService.add(() => this.handleDeleteRequest());
}
@ViewChild("groupTable") groupTable!: GroupTableComponent;
@@ -56,7 +58,7 @@ export class GroupComponent extends BasePageComponent implements AfterViewInit {
}
}
override handleDeleteRequest() {
handleDeleteRequest() {
const sRows = this.groupTable.selectedRows;
if (sRows.length > 0)
Swal.fire({
@@ -81,7 +83,7 @@ export class GroupComponent extends BasePageComponent implements AfterViewInit {
},
error: err => Swal.fire({
title: "Fehler",
text: `${err.message}`,
text: `Dieser Vorgang ist nicht möglich.`,
icon: "error"
})
});

View File

@@ -37,7 +37,7 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
}
ngAfterViewInit(): void {
this.buttonVisibilityService.setVisibleOnly(this.refreshService)
this.buttonVisibilityService.setVisibleOnly(this.refreshService, this.transferService)
this.refreshService.removeAll();
this.refreshService.add(() => {
this.users.fetchData();
@@ -45,6 +45,11 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
this.repGroups.fetchData();
this.rightGroups.fetchData();
})
this.transferService.add(() => {
this.repUsers.safelyUnselectAll();
this.repGroups.safelyUnselectAll();
this.userReps.safelyUnselectAll();
})
}
@ViewChild("users") users!: UserTableComponent;

View File

@@ -47,6 +47,8 @@ export class UserComponent extends BasePageComponent implements AfterViewInit {
});
this.creationService.component = UserFormComponent;
this.deletionService.add(() => this.handleDeleteRequest());
}
@ViewChild("userTable") userTable!: UserTableComponent
@@ -64,7 +66,7 @@ export class UserComponent extends BasePageComponent implements AfterViewInit {
}
}
override handleDeleteRequest() {
handleDeleteRequest() {
const sRows = this.userTable.selectedRows;
if (sRows.length > 0)
Swal.fire({
@@ -88,7 +90,7 @@ export class UserComponent extends BasePageComponent implements AfterViewInit {
},
error: err => Swal.fire({
title: "Fehler",
text: `${err.message}`,
text: `Dieser Vorgang ist nicht möglich.`,
icon: "error"
})
});

View File

@@ -75,7 +75,7 @@ export class AuthenticationService {
Swal.fire({
icon: "error",
title: "Oops...",
text: "The backend application is not responding.",
text: "Der Server antwortet nicht.",
});
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DeletionService } from './deletion.service';
describe('DeletionService', () => {
let service: DeletionService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DeletionService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { BaseButtonService } from './base-button.service';
@Injectable({
providedIn: 'root'
})
export class DeletionService extends BaseButtonService {
private actions: Array<() => void> = [];
public get count(): number {
return this.actions.length;
}
public get any(): boolean {
return this.count > 0;
}
add(action: () => void): void {
this.actions.push(action);
}
removeAll(): void {
this.actions = [];
}
executeAll(): void {
this.actions.forEach(action => action());
}
}

View File

@@ -84,6 +84,10 @@ export class UpdateService extends BaseButtonService {
localStorage.setItem('editable', value ? "T" : "F")
this._isEditable = value;
}
toggleEditability() {
this.isEditable = !this.isEditable;
}
}
export enum UpdateEvent {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB