Compare commits

..

17 Commits

Author SHA1 Message Date
Developer 02
b3131637ab chore: npm-Build-Skript aktualisiert 2024-10-31 16:03:30 +01:00
Developer 02
4d9a766864 chore: Aktualisiert auf 3.0.0. 2024-10-31 15:57:57 +01:00
Developer 02
01bc5a4425 fix(user-rep.component): geordnete Assgiment-Ereignisse und -Anfragen 2024-10-31 15:54:53 +01:00
Developer 02
be07f16d63 feat(UserRep): UserId in DTOs und Entitäten löschbar gemacht 2024-10-31 14:59:01 +01:00
Developer 02
671500b3a5 feat(AuthController): aktualisiert, um zu prüfen, ob der eingeloggte Benutzer in der erlaubten Gruppe in appsettings ist. 2024-10-31 14:44:39 +01:00
Developer 02
1be71a42e5 refactor: vereinfache env-Spaltennamen für Gruppen
- Ersetzte das `complete`-Spaltenset der Gruppe durch ein vereinfachtes `detailed`-Set.
- Wesentliche Felder für die `detailed`-Ansicht beibehalten, Entfernen der Schalter für die Status 'Aktiv', 'AD Sync' und 'Intern'.
- Vereinfachte Anzeige durch Fokussierung auf Kernfelder, für effizientes Rendering und leichtere Wartung.
2024-10-31 13:28:44 +01:00
Developer 02
6319d22ba9 feat: aktualisiere das Template von GroupUpdateFormComponent mit Formularfeldern und Steuerungen
- Platzhaltertext durch eine strukturierte Formularanordnung ersetzt, einschließlich Felder für 'Id', 'Gruppe' und 'Kommentar' mit mat-form-field-Komponenten.
- Slide-Toggles für die Statussteuerungen 'Aktiv', 'Interne Gruppe' und 'Mit Active Directory' hinzugefügt.
- Schreibgeschützte Felder für 'Hinzugefügt wer', 'Hinzugefügt wann', 'Geändert wer' und 'Geändert wann' mit Datumsformatierung hinzugefügt.
- Speichern- und Löschen-Schaltflächen mit Icons für verbesserte Benutzerinteraktion integriert.
- Formularbenutzerfreundlichkeit und visuelles Layout für eine konsistente Benutzererfahrung verbessert.
2024-10-31 13:22:43 +01:00
Developer 02
5551610dff refactor(user-update-form.component.css): Nach style.scss verschoben, um es global zu machen 2024-10-31 12:41:43 +01:00
Developer 02
b997ea4cce feat: erweitere GroupUpdateFormComponent um Dialogintegration und Formularkontrollen
- Abhängigkeiten für Angular Material-Komponenten, Formularsteuerung und RxJS-Utilities hinzugefügt.
- MatDialogRef und MAT_DIALOG_DATA für die Dialoginteraktion innerhalb der Komponente injiziert.
- FormControl-Instanzen zur Verwaltung der Felder 'name' und 'comment' des Group-Modells integriert.
- Update-Funktionalität implementiert, die Änderungen über GroupService übermittelt und bei Erfolg ein Refresh auslöst.
- Löschfunktion mit Bestätigungsaufforderung über SweetAlert2 hinzugefügt, mit Verarbeitung erfolgreicher Löschungen und Fehlerfällen.
- Verbesserte Fehlerbehandlung mit aussagekräftigen Meldungen für eine bessere Benutzererfahrung.
2024-10-31 12:38:29 +01:00
Developer 02
1b5fa1f52c feat(group-update-form.component): Initialisiert
- Zur Gruppenkomponente als Popup-Seite nach Doppelklick auf Zeilenereignis hinzugefügt
2024-10-31 11:43:56 +01:00
Developer 02
5f4a8e373c refactor: Ändere die Variable sGroupId zu sGroup und passe die Methode groupsOnSelectedRows an, um das sGroup-Objekt zu verarbeiten. 2024-10-31 11:37:15 +01:00
Developer 02
91b78f4b59 refactor(user.component): Verwendung von einfachen Benutzer-Spalten-Namen anstelle von detaillierten Benutzer-Spalten-Namen 2024-10-31 11:02:12 +01:00
Developer 02
3749b5ee97 feat(user-update): readonly Eingabe für addedWho, addedWhen, changedWho und changedWhen hinzugefügt 2024-10-31 10:49:02 +01:00
Developer 02
ab6c843248 feat: aktualisiere UserReadDto zur Durchsetzung erforderlicher Eigenschaften
- Die Eigenschaften 'Username', 'Language', 'DateFormat', 'ChangedWho', 'ChangedWhen' und 'AddedWhen' wurden als erforderlich festgelegt, um sicherzustellen, dass notwendige Daten bereitgestellt werden.
- 'AddedWho' wurde zu einer nur-initialisierbaren Eigenschaft aktualisiert, um die Unveränderlichkeit nach der Objekterstellung zu fördern.
- Diese Änderung verbessert die Datenintegrität und Validierung innerhalb der UserReadDto-Klasse.
2024-10-31 10:39:50 +01:00
Developer 02
b6bc97df07 feat(user-update): Readonly-Eingabe für Id hinzugefügt
- Responsive CSS-Klassen für prozentuale Breiten hinzugefügt
2024-10-31 10:20:30 +01:00
Developer 02
44a017ad9e feat(user-update): Konstante Variablen erstellt, um die erlaubten Sprachen in den Umgebungen zu bestimmen.
- Combobox mit erlaubten Sprachen als Optionen mit Angular for-Schleife erstellt
2024-10-31 09:15:20 +01:00
Developer 02
faac31b3bb feat(user-update): Konstante Variablen zur Bestimmung der zulässigen Datumsformate in den Umgebungen erstellt.
- Zugelassene Datumsformate in Combobox als Optionen mit Angular for-Schleife hinzugefügt
2024-10-31 09:14:41 +01:00
31 changed files with 472 additions and 191 deletions

View File

@@ -4,7 +4,7 @@
"scripts": {
"ng": "ng",
"start": "ng serve --ssl -o",
"build": "ng build",
"build": "ng build --configuration production",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"serve:ssr:user_manager_ui": "node dist/user_manager_ui/server/server.mjs"

View File

@@ -0,0 +1,64 @@
<div class="dd-container">
<!-- id, name -->
<div class="dd-row input-row">
<mat-form-field class="w20p">
<mat-label>Id</mat-label>
<input matInput readonly [value]="group.id" />
</mat-form-field>
<mat-form-field class="w80p">
<mat-label>Gruppe</mat-label>
<input matInput [formControl]="name" />
</mat-form-field>
</div>
<!-- comment -->
<div class="dd-row input-row">
<mat-form-field>
<mat-label>Kommentar</mat-label>
<textarea matInput [formControl]="comment"></textarea>
</mat-form-field>
</div>
<mat-divider></mat-divider>
<!-- active, internal, async -->
<div class="dd-row input-row">
<mat-slide-toggle [(ngModel)]="group.active">
Aktiv
</mat-slide-toggle>
<mat-slide-toggle [(ngModel)]="group.internal" disabled>
Interne Gruppe
</mat-slide-toggle>
<mat-slide-toggle [(ngModel)]="group.adSync" disabled>
Mit Active Directory
</mat-slide-toggle>
</div>
<!-- addedWho, addedWhen, changedWho and changedWhen -->
<div class="dd-row input-row">
<mat-form-field>
<mat-label>Hinzugefügt wer</mat-label>
<input matInput readonly [value]="group.addedWho" />
</mat-form-field>
<mat-form-field>
<mat-label>Hinzugefügt wann</mat-label>
<input matInput readonly [value]="group.addedWhen | date:'dd.MM.yyyy'" />
</mat-form-field>
<mat-form-field>
<mat-label>Geändert wer</mat-label>
<input matInput readonly [value]="group.changedWho" />
</mat-form-field>
<mat-form-field>
<mat-label>Geändert wann</mat-label>
<input matInput readonly [value]="group.changedWhen | date:'dd.MM.yyyy'" />
</mat-form-field>
</div>
<mat-divider></mat-divider>
<!-- save-button, delete-button -->
<div class="dd-row button-row">
<button mat-fab extended (click)="update()">
<mat-icon>save</mat-icon>
Speichern
</button>
<button mat-fab extended (click)="delete()">
<mat-icon>delete</mat-icon>
Löschen
</button>
</div>
</div>

View File

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

View File

@@ -0,0 +1,103 @@
import { Component, inject, signal } from '@angular/core';
import { Group } from '../../../models/user-management.api.models';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { CommonModule } from '@angular/common';
import { MatTabsModule } from '@angular/material/tabs';
import { GroupService } from '../../../services/api/group.service';
import { RefreshService } from '../../../services/button/refresh.service';
import Swal from 'sweetalert2';
import { MatSelectModule } from '@angular/material/select';
import { env } from '../../../../environments/environment'
import { MatDividerModule } from '@angular/material/divider';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
@Component({
selector: 'app-group-update-form',
standalone: true,
imports: [MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule, MatIconModule, MatButtonModule, CommonModule, MatTabsModule, MatSelectModule, MatDividerModule, MatCheckboxModule, MatSlideToggleModule],
templateUrl: './group-update-form.component.html',
styleUrl: './group-update-form.component.scss'
})
export class GroupUpdateFormComponent {
readonly dialogRef: MatDialogRef<GroupUpdateFormComponent> = inject(MatDialogRef<GroupUpdateFormComponent>);
readonly group: Group = inject(MAT_DIALOG_DATA);
get allowedDateFormats(): Array<{ value: string, name: string }> {
return env.constants.date_formats;
}
get allowedLanguages(): Array<{ value: string, name: string }> {
return env.constants.languages;
}
readonly name = new FormControl(this.group.name);
readonly comment = new FormControl(this.group.comment);
mailErrorMessage = signal('');
errorMessage = signal('');
constructor(private uService: GroupService, private rService: RefreshService) {
}
update() {
this.group.name = this.name.value!;
this.group.comment = this.comment.value!;
this.uService.update(this.group).subscribe({
next: () => {
this.rService.executeAll();
},
error: err => {
console.error(err)
Swal.fire({
title: "Interner Dienstfehler",
text: "Bitte wenden Sie sich an das IT-Team, um den Fehler zu beheben.",
icon: "error"
});
}
})
}
delete() {
Swal.fire({
text: "Sind Sie sicher, dass Sie diesen Datensatz löschen wollen?",
icon: "question",
showDenyButton: true,
confirmButtonText: "Ja",
denyButtonText: `Nein`
}).then((result) => {
if (result.isConfirmed) {
if (this.group.id) {
this.uService.delete(this.group.id).subscribe({
next: () => {
this.rService.executeAll();
this.dialogRef.close();
},
error: err => {
Swal.fire({
title: "Interner Dienstfehler",
text: "Bitte wenden Sie sich an das IT-Team, um den Fehler zu beheben.",
icon: "error"
});
},
})
}
else
Swal.fire({
title: "Ein unerwarteter Fehler",
text: "Die Benutzer-ID existiert nicht (Nullwert).",
icon: "error"
});
}
});
}
}

View File

@@ -1,14 +1,18 @@
<div class="dd-container">
<!-- username, e-mail -->
<!-- id, username, e-mail -->
<div class="dd-row input-row">
<mat-form-field>
<mat-form-field class="w10p">
<mat-label>Id</mat-label>
<input matInput readonly [value]="user.id" />
</mat-form-field>
<mat-form-field class="w30p">
<mat-label>Benutzername</mat-label>
<input matInput [formControl]="username" (blur)="updateErrorMessage()" required />
@if (email.invalid) {
<mat-error>{{errorMessage()}}</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-form-field class="w60p">
<mat-label>E-Mail</mat-label>
<input matInput placeholder="user@example.com" [formControl]="email" (blur)="updateMailErrorMessage()"
required />
@@ -43,19 +47,48 @@
<mat-form-field>
<mat-label>Datumsformat</mat-label>
<mat-select [(value)]="user.dateFormat" [(ngModel)]="user.dateFormat">
<mat-option value="dd.MM.yyyy">dd.MM.yyyy</mat-option>
<mat-option value="MM.dd.yyyy">MM.dd.yyyy</mat-option>
<mat-option value="yyyy-MM-dd">yyyy-MM-dd</mat-option>
@for (format of allowedDateFormats; track format) {
<mat-option [value]="format.value">{{format.name}}</mat-option>
}
</mat-select>
</mat-form-field>
</mat-form-field>
<mat-form-field>
<mat-label>Sprache</mat-label>
<mat-select [(value)]="user.language" [(ngModel)]="user.language">
@for (language of allowedLanguages; track language) {
<mat-option [value]="language.value">{{language.name}}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<!-- comment -->
<div class="dd-row input-row">
<mat-form-field>
<mat-label>Kommentar</mat-label>
<input matInput [formControl]="comment" />
<textarea matInput [formControl]="comment"></textarea>
</mat-form-field>
</div>
<mat-divider></mat-divider>
<!-- addedWho, addedWhen, changedWho and changedWhen -->
<div class="dd-row input-row">
<mat-form-field>
<mat-label>Hinzugefügt wer</mat-label>
<input matInput readonly [value]="user.addedWho" />
</mat-form-field>
<mat-form-field>
<mat-label>Hinzugefügt wann</mat-label>
<input matInput readonly [value]="user.addedWhen | date:'dd.MM.yyyy'" />
</mat-form-field>
<mat-form-field>
<mat-label>Geändert wer</mat-label>
<input matInput readonly [value]="user.changedWho" />
</mat-form-field>
<mat-form-field>
<mat-label>Geändert wann</mat-label>
<input matInput readonly [value]="user.changedWhen | date:'dd.MM.yyyy'" />
</mat-form-field>
</div>
<mat-divider></mat-divider>
<!-- save-button, delete-button -->
<div class="dd-row button-row">
<button mat-fab extended (click)="update()">

View File

@@ -1,32 +0,0 @@
.dd-container {
display: flex;
flex-direction: column;
padding: 0;
margin: 1rem 0 1rem 0;
justify-content: space-evenly;
}
.dd-row {
display: flex;
flex-direction: row;
padding: 0;
margin: 0;
}
.input-row {
justify-content: space-evenly;
}
.button-row {
align-items: center;
justify-content: center;
}
mat-form-field {
width: 100%;
margin: 0 1rem 0 1rem;
}
button {
margin: 0 1rem 0 1rem;
}

View File

@@ -10,16 +10,17 @@ import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { CommonModule } from '@angular/common';
import { MatTabsModule } from '@angular/material/tabs';
import { UserGroupDirImportComponent } from "../../user-group-dir-import/user-group-dir-import.component";
import { UserService } from '../../../services/api/user.service';
import { RefreshService } from '../../../services/button/refresh.service';
import Swal from 'sweetalert2';
import { MatSelectModule } from '@angular/material/select';
import { env } from '../../../../environments/environment'
import {MatDividerModule} from '@angular/material/divider';
@Component({
selector: 'app-user-update-form',
standalone: true,
imports: [MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule, MatIconModule, MatButtonModule, CommonModule, MatTabsModule, UserGroupDirImportComponent, MatSelectModule],
imports: [MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule, MatIconModule, MatButtonModule, CommonModule, MatTabsModule, MatSelectModule, MatDividerModule],
templateUrl: './user-update-form.component.html',
styleUrl: './user-update-form.component.scss'
})
@@ -29,6 +30,14 @@ export class UserUpdateFormComponent {
readonly user: User = inject(MAT_DIALOG_DATA);
get allowedDateFormats(): Array<{ value: string, name: string }> {
return env.constants.date_formats;
}
get allowedLanguages(): Array<{ value: string, name: string }> {
return env.constants.languages;
}
readonly username = new FormControl(this.user.username, [Validators.required]);
readonly email = new FormControl(this.user.email, [Validators.required, Validators.email]);
readonly name = new FormControl(this.user.prename, [Validators.required]);
@@ -79,7 +88,7 @@ export class UserUpdateFormComponent {
this.user.name = this.surname.value!;
this.user.shortname = this.shortname.value!;
this.user.comment = this.comment.value!;
this.uService.update(this.user).subscribe({
next: () => {
this.rService.executeAll();

View File

@@ -10,7 +10,9 @@ export interface User {
deleted?: boolean;
dateFormat?: string;
addedWho?: string;
addedWhen?: Date;
changedWho?: string;
changedWhen?: Date;
active?: boolean;
}
@@ -23,7 +25,9 @@ export interface Group {
active?: boolean;
comment?: string;
addedWho?: string;
addedWhen?: Date;
changedWho?: string;
changedWhen?: Date;
}
export interface Module {
@@ -56,15 +60,15 @@ export interface GroupOfUser {
export interface UserRep {
id?: number,
userId: number,
repUserId?: number,
userId?: number,
repGroupId?: number,
groupId?: number,
addedWho: string,
repUserId?: number,
repUser?: User
user?: User,
repGroup?: Group,
group?: Group,
repUser?: User
group?: Group,
}
export interface DirGroup {

View File

@@ -3,7 +3,7 @@
<div class="col-7">
<mat-tab-group>
<mat-tab label="Gruppen">
<app-group-table #groupTable [onSelectedRows]="groupsOnSelectedRows" [cellEditing]="cellEditing" [columns]="complete_group_cols"></app-group-table>
<app-group-table #groupTable [onSelectedRows]="groupsOnSelectedRows" [cellEditing]="cellEditing" [columns]="detailed_group_cols"></app-group-table>
</mat-tab>
</mat-tab-group>
</div>

View File

@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { AfterViewInit, Component, ViewChild, inject } from '@angular/core';
import { GroupTableComponent } from '../../components/tables/group-table/group-table.component';
import { UserTableComponent } from '../../components/tables/user-table/user-table.component';
import { MatTabsModule } from '@angular/material/tabs';
@@ -9,6 +9,8 @@ import { Group } from '../../models/user-management.api.models';
import { firstValueFrom, forkJoin } from 'rxjs';
import Swal from 'sweetalert2';
import { env } from '../../../environments/environment';
import { MatDialog } from '@angular/material/dialog';
import { GroupUpdateFormComponent } from '../../components/forms/group-update-form/group-update-form.component';
@Component({
standalone: true,
@@ -33,15 +35,17 @@ export class GroupComponent extends BasePageComponent implements AfterViewInit {
}
}
private sGroupId = null;
private sGroup: Group | null = null;
private readonly dialog: MatDialog = inject(MatDialog);
ngAfterViewInit(): void {
this.buttonVisibilityService.setVisibleOnly(this.refreshService, this.creationService, this.updateService)
this.refreshService.removeAll()
this.refreshService.add(() => {
this.groupTable.fetchData();
if (this.sGroupId)
this.userTable.fetchDataByGroupId(this.sGroupId);
if (this.sGroup)
this.userTable.fetchDataByGroupId(this.sGroup.id!);
});
this.creationService.component = GroupFormComponent
@@ -53,9 +57,12 @@ export class GroupComponent extends BasePageComponent implements AfterViewInit {
groupsOnSelectedRows = (rows: GuiSelectedRow[]) => {
if (rows.length > 0) {
this.sGroupId = rows[0].source.id;
if (this.sGroupId)
this.userTable.fetchDataByGroupId(this.sGroupId);
this.sGroup = rows[0].source;
if (this.sGroup)
this.userTable.fetchDataByGroupId(this.sGroup.id!);
}
else if (rows.length == 0 && this.sGroup?.name != null) {
this.openUpdateSheet(this.sGroup);
}
}
@@ -92,7 +99,14 @@ export class GroupComponent extends BasePageComponent implements AfterViewInit {
});
}
get complete_group_cols() {
return env.columnNames.group.complete
get detailed_group_cols() {
return env.columnNames.group.detailed
}
openUpdateSheet(group: Group): void {
this.dialog.open(GroupUpdateFormComponent, {
width: "50rem",
data: group
});
}
}

View File

@@ -8,7 +8,7 @@
<app-user-table #users [onSelectedRows]="userOnSelectedRows"></app-user-table>
</mat-tab>
<mat-tab label="Gruppe">
<app-group-table #rightGroups [columns]="groupColumns" [onSelectedRows]="groupOnSelectedRows"></app-group-table>
<app-group-table #groups [columns]="groupColumns" [onSelectedRows]="groupOnSelectedRows"></app-group-table>
</mat-tab>
</mat-tab-group>
</div>

View File

@@ -1,4 +1,4 @@
import { AfterViewInit, Component, Inject, ViewChild } from '@angular/core';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { GuiColumn, GuiSelectedRow } from '@generic-ui/ngx-grid/gui/grid/src/core/api/gui.grid.public-api';
import { UserTableComponent } from '../../components/tables/user-table/user-table.component';
import { UserRepTableComponent } from '../../components/tables/user-rep-table/user-rep-table.component';
@@ -22,11 +22,11 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
useRepLabel: string = "";
groupColumns: Array<GuiColumn>;
groupRepCols: Array<GuiColumn>;
slUserId: null | number = null;
slRepUserId: null | number = null;
slRepGroupId: null | number = null;
slRightGroupId: null | number = null;
slUserRepId: null | number = null;
slUserId?: number;
slGroupId?: number;
slRepUserId?: number;
slRepGroupId?: number;
slUserRepId?: number;
initWithoutData = () => { }
@@ -61,17 +61,19 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
userGroupOnSelectedRows = (rows: GuiSelectedRow[], isUser: boolean = true) => {
if (rows.length > 0) {
this.users.safelyUnselectAll();
if (isUser) {
this.useRepLabel = `Vertretungen von ${rows[0].source?.username}`
this.users.safelyUnselectAll();
this.userReps.fetchData(rows[0].source?.id)
this.slGroupId = undefined;
this.slUserId = rows[0].source?.id
}
else {
this.useRepLabel = `Vertretungen von ${rows[0].source?.name}`
this.groups.safelyUnselectAll();
this.userReps.fetchData(undefined, rows[0].source?.id)
this.slUserId = rows[0].source?.id
this.slUserId = undefined;
this.slGroupId = rows[0].source?.id
}
}
}
@@ -86,11 +88,11 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
repUserOnSelectedRows = (rows: GuiSelectedRow[]) => {
if (rows.length == 0 && this.slRepUserId) {
if (!this.slUserId) {
if (!this.slUserId && !this.slGroupId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie den Benutzer!",
text: "Bitte wählen Sie den Benutzer oder die Gruppe!",
});
}
else if (!this.slRepUserId) {
@@ -103,15 +105,20 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
else {
var newUserRep: UserRep = {
userId: this.slUserId,
groupId: this.slGroupId,
repUserId: this.slRepUserId,
addedWho: 'DEFAULT'
}
this.userRepService.create(newUserRep).subscribe({
next: (response) => {
this.slRepUserId = null;
this.slRepUserId = undefined;
this.repUsers.safelyUnselectAll()
if (this.slUserId != null)
this.repGroups.safelyUnselectAll()
if (this.slUserId != undefined)
this.userReps.fetchData(this.slUserId)
if (this.slGroupId != undefined)
this.userReps.fetchData(this.slGroupId)
},
error: (error) => {
Swal.fire({
@@ -123,7 +130,7 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
});
}
this.slRepUserId = null;
this.slRepUserId = undefined;
}
else if (rows.length > 0) {
this.slRepUserId = rows[0].source?.id;
@@ -132,11 +139,11 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
repGroupOnSelectedRows = (rows: GuiSelectedRow[]) => {
if (rows.length == 0 && this.slRepGroupId) {
if (!this.slUserId) {
if (!this.slUserId && !this.slGroupId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie den Benutzer!",
text: "Bitte wählen Sie den Benutzer oder die Gruppe!",
});
}
else if (!this.slRepGroupId) {
@@ -149,15 +156,19 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
else {
var newUserRep = {
userId: this.slUserId,
groupId: this.slGroupId,
repGroupId: this.slRepGroupId,
addedWho: 'DEFAULT'
}
this.userRepService.create(newUserRep).subscribe({
next: (res) => {
this.slRepGroupId = null;
this.slRepGroupId = undefined;
this.repUsers.safelyUnselectAll()
if (this.slUserId != null)
this.groups.safelyUnselectAll()
if (this.slUserId != undefined)
this.userReps.fetchData(this.slUserId)
if (this.slGroupId != undefined)
this.userReps.fetchData(undefined, this.slGroupId)
},
error: (error) => {
Swal.fire({
@@ -169,7 +180,7 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
});
}
this.slRepGroupId = null;
this.slRepGroupId = undefined;
}
else if (rows.length > 0) {
this.slRepGroupId = rows[0].source?.id;
@@ -180,13 +191,15 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
if (rows.length == 0 && this.slUserRepId) {
this.userRepService.delete(this.slUserRepId).subscribe({
next: (res) => {
this.slUserRepId = null;
this.slUserRepId = undefined;
this.userReps.safelyUnselectAll();
if (this.slUserId != null)
if (this.slUserId != undefined)
this.userReps.fetchData(this.slUserId)
if (this.slGroupId != undefined)
this.userReps.fetchData(undefined, this.slGroupId)
},
error: (err) => {
this.slUserRepId = null;
this.slUserRepId = undefined;
this.repUsers.safelyUnselectAll()
Swal.fire({
icon: "error",

View File

@@ -4,7 +4,7 @@
<mat-tab-group>
<mat-tab label="Benutzer">
<app-user-table #userTable [onSelectedRows]="usersOnSelectedRows"
[cellEditing]="cellEditing" [columns]="detailed_user_columns"></app-user-table>
[cellEditing]="cellEditing" [columns]="basic_user_columns"></app-user-table>
</mat-tab>
</mat-tab-group>
</div>

View File

@@ -10,7 +10,7 @@ import { User } from '../../models/user-management.api.models';
import { firstValueFrom, forkJoin } from 'rxjs';
import Swal from 'sweetalert2';
import { env } from '../../../environments/environment'
import { MatBottomSheet, MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { UserUpdateFormComponent } from '../../components/forms/user-update-form/user-update-form.component';
@@ -40,8 +40,6 @@ export class UserComponent extends BasePageComponent implements AfterViewInit {
private sUser: any = null;
private _bottomSheet = inject(MatBottomSheet);
private readonly dialog: MatDialog = inject(MatDialog);
ngAfterViewInit(): void {
@@ -107,8 +105,8 @@ export class UserComponent extends BasePageComponent implements AfterViewInit {
});
}
public get detailed_user_columns() {
return env.columnNames.user.detailed
public get basic_user_columns() {
return env.columnNames.user.basic
}
openUpdateSheet(user: User): void {

View File

@@ -53,7 +53,7 @@ export const env = {
field: 'email'
},
{
header:'Kommentar',
header: 'Kommentar',
field: 'comment'
},
{
@@ -199,5 +199,25 @@ export const env = {
},
]
},
config_url: "/assets/config.json"
config_url: "/assets/config.json",
constants: {
date_formats: [
{
value: "dd.MM.yyyy",
name: "dd.MM.yyyy",
},
{
value: "MM.dd.yyyy",
name: "MM.dd.yyyy",
},
{
value: "yyyy-MM-dd",
name: "yyyy-MM-dd",
}
],
languages: [
{ value: "de-DE", name: "de-DE" },
{ value: "en-US", name: "en-US" }
]
}
};

View File

@@ -53,7 +53,7 @@ export const env = {
field: 'email'
},
{
header:'Kommentar',
header: 'Kommentar',
field: 'comment'
},
{
@@ -89,7 +89,7 @@ export const env = {
field: "name"
}
],
complete: [
detailed: [
{
header: "Gruppe",
field: "name"
@@ -97,34 +97,6 @@ export const env = {
{
header: "Kommentar",
field: "comment"
},
{
header: "Active",
field: (group: any) => group.active ? "✓" : ""
},
{
header: "AD Sync",
field: (group: any) => group.adSync ? "✓" : ""
},
{
header: "Internal",
field: (group: any) => group.internal ? "✓" : ""
},
{
header: 'Hinzugefügt<br>wer',
field: (g: any) => g.addedWho
},
{
header: 'Hinzugefügt<br>wann',
field: (g: any) => new Date(g.addedWhen).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit' }).replace(',', '')
},
{
header: 'Geändert<br>wer',
field: 'changedWho'
},
{
header: 'Geändert<br>wann',
field: (g: any) => new Date(g.changedWhen).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit' }).replace(',', '')
}
],
representative: [
@@ -195,5 +167,25 @@ export const env = {
},
]
},
config_url: "/assets/config.json"
};
config_url: "/assets/config.json",
constants: {
date_formats: [
{
value: "dd.MM.yyyy",
name: "dd.MM.yyyy",
},
{
value: "MM.dd.yyyy",
name: "MM.dd.yyyy",
},
{
value: "yyyy-MM-dd",
name: "yyyy-MM-dd",
}
],
languages: [
{ value: "de-DE", name: "de-DE" },
{ value: "en-US", name: "en-US" }
]
}
};

View File

@@ -45,6 +45,61 @@ code {
color: rgb(100, 95, 95) !important;
}
.mdc-tab__text-label{
.mdc-tab__text-label {
font-size: medium;
}
//Layout
.dd-container {
display: flex;
flex-direction: column;
padding: 0;
margin: 1rem 0 1rem 0;
justify-content: space-evenly;
}
.dd-row {
display: flex;
flex-direction: row;
padding: 0;
margin: 0;
}
.input-row {
justify-content: space-evenly;
}
.button-row {
align-items: center;
justify-content: center;
}
.dd-row mat-form-field {
width: 100%;
margin: 0 1rem 0 1rem;
}
.dd-row mat-checkbox {
width: 100%;
margin: 0 1rem 1rem 1rem;
}
.dd-row mat-slide-toggle {
width: 100%;
margin: 0 1rem 1rem 1rem;
}
.dd-row button {
margin: 0 1rem 0 1rem;
}
.dd-container mat-divider {
margin: 0 0 1rem 0;
padding: 0;
}
@for $i from 1 through 20 {
.w#{$i * 5}p {
width: #{$i * 5 + "%"};
}
}

View File

@@ -21,15 +21,16 @@ namespace DigitalData.UserManager.API.Controllers
private readonly IDirectorySearchService _dirSearchService;
private readonly IStringLocalizer<Resource> _localizer;
private readonly ILogger<AuthController> _logger;
public AuthController(IUserService userService, IGroupOfUserService gouService, IDirectorySearchService directorySearchService, IStringLocalizer<Resource> localizer, ILogger<AuthController> logger)
private readonly IConfiguration _config;
public AuthController(IUserService userService, IGroupOfUserService gouService, IDirectorySearchService directorySearchService, IStringLocalizer<Resource> localizer, ILogger<AuthController> logger, IConfiguration configuration)
{
_userService = userService;
_gouService = gouService;
_dirSearchService = directorySearchService;
_localizer = localizer;
_logger = logger;
}
_config = configuration;
}
[AllowAnonymous]
[HttpGet("check")]
@@ -57,10 +58,13 @@ namespace DigitalData.UserManager.API.Controllers
if (!isValid)
return Unauthorized(Result.Fail().Message(_localizer[Key.UserNotFound]));
var gouMsg = await _gouService.HasGroup(login.Username, "PM_USER", caseSensitive: false);
var allowedGroupName = _config.GetSection("AllowedGroupName").Get<string>()
?? throw new InvalidOperationException("Allowed group names configuration is missing.");
var gouMsg = await _gouService.HasGroup(login.Username, allowedGroupName, caseSensitive: false);
if (!gouMsg.IsSuccess)
return Unauthorized(Result.Fail().Message(_localizer[Key.UnauthorizedUser]));
//find the user
var uRes = await _userService.ReadByUsernameAsync(login.Username);
if (!uRes.IsSuccess || uRes.Data is null)

View File

@@ -1,27 +0,0 @@
using DigitalData.UserManager.API.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace DigitalData.UserManager.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ConstantsController : ControllerBase
{
private readonly Constants _constants;
public ConstantsController(IOptions<Constants> constantsOptions)
{
_constants = constantsOptions.Value;
}
[HttpGet]
public IActionResult GetConstant(string? name = null)
{
if(name is null)
return Ok(_constants);
return Ok(_constants[name]);
}
}
}

View File

@@ -4,7 +4,9 @@
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>2.0.0.0</Version>
<Version>3.0.0.0</Version>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>
@@ -61,6 +63,7 @@
<ItemGroup>
<Folder Include="ClientApp\" />
<Folder Include="Models\" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,13 +0,0 @@
using System.Reflection;
namespace DigitalData.UserManager.API.Models
{
public class Constants
{
public IEnumerable<string> UserLanguages { get; init; } = Array.Empty<string>();
public object? this[string propertyName] => GetType()
.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)?
.GetValue(this, null);
}
}

View File

@@ -9,8 +9,6 @@ using DigitalData.Core.API;
using DigitalData.UserManager.API.Controllers;
using DigitalData.UserManager.Application.Services;
using Microsoft.Data.SqlClient;
using System.Reflection.Metadata;
using DigitalData.UserManager.API.Models;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Debug("init main");
@@ -80,8 +78,6 @@ try {
builder.Services.AddCookieBasedLocalizer();
builder.ConfigureBySection<Constants>();
var app = builder.Build();
cnn_str = new(() =>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>07ccd651-647c-49f7-9715-30cebc13710d</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\DD-UserManager\Web\${Version}\$(Version).zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>UserManager.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
</PropertyGroup>
</Project>

View File

@@ -71,13 +71,8 @@
"Key": "JGPwHVD0BQmC7upi5OV11PzzIk47ugTJoqBV/et5w40=",
"IV": "gMuetIjlPvJnSzu+i7I3xg=="
},
"AllowedGroupName": "UM_ADMINS",
// Delete below in production
"UseEncryptor": true,
"UseSwagger": true,
"Constants": {
"UserLanguages": [
"de-DE",
"en-US"
]
}
"UseSwagger": true
}

View File

@@ -2,5 +2,6 @@
namespace DigitalData.UserManager.Application.DTOs.Base
{
// TODO: use getter - setter methods for a simple inheritance.
public record BaseReadDto(int Id, string? AddedWho, DateTime? AddedWhen, string? ChangedWho, DateTime? ChangedWhen) : BaseDTO<int>(Id);
}

View File

@@ -4,6 +4,7 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
namespace DigitalData.UserManager.Application.DTOs.User
{
//TODO: inherit from base read dto
public record UserReadDto()
{
public int Id { get; set; }
@@ -12,23 +13,29 @@ namespace DigitalData.UserManager.Application.DTOs.User
public string? Name { get; set; }
public string Username { get; set; }
public required string Username { get; set; }
public string? Shortname { get; set; }
public string? Email { get; set; }
public string Language { get; set; }
public required string Language { get; set; }
public string? Comment { get; set; }
public bool Deleted { get; set; }
public string DateFormat { get; set; }
public required string DateFormat { get; set; }
public string AddedWho { get; set; }
public string? ChangedWho { get; set; }
public bool Active { get; set; }
public required DateTime? ChangedWhen { get; set; }
public string? AddedWho { get; init; }
public required DateTime AddedWhen { get; init; }
public bool Active { get; set; }
[JsonIgnore]
[NotMapped]

View File

@@ -3,7 +3,7 @@
namespace DigitalData.UserManager.Application.DTOs.UserRep
{
public record UserRepCreateDto(
int UserId,
int? UserId,
int? RepGroupId,
int? GroupId,
int RepUserId

View File

@@ -6,12 +6,12 @@ namespace DigitalData.UserManager.Application.DTOs.UserRep
{
public record UserRepReadDto(
int Id,
int UserId,
int? RepGroupId,
int? UserId,
int? GroupId,
string AddedWho,
string ChangedWho,
int? RepUserId,
int? RepGroupId,
string AddedWho,
string ChangedWho,
UserReadDto? User,
GroupReadDto? RepGroup,
GroupReadDto? Group,

View File

@@ -3,7 +3,7 @@
namespace DigitalData.UserManager.Application.DTOs.UserRep
{
public record UserRepUpdateDto(
int UserId,
int? UserId,
int? RepGroupId,
int? GroupId,
int RepUserId

View File

@@ -8,7 +8,7 @@ namespace DigitalData.UserManager.Domain.Entities
{
[Required]
[Column("USER_ID")]
public int UserId { get; set; }
public int? UserId { get; set; }
[Column("REPR_GROUP")]
public int? RepGroupId { get; set; }