Compare commits

...

13 Commits

Author SHA1 Message Date
Developer 02
2800392be3 feat(user-update-form): Eingabe des Datumsformats hinzugefügt 2024-10-31 07:45:30 +01:00
Developer 02
b7f4ed7597 feat(user-update-form): Eingabe von Kurznamen hinzugefügt 2024-10-31 07:19:46 +01:00
Developer 02
16cc729373 feat(user-update-form): Kommentareingabe hinzugefügt 2024-10-31 07:13:30 +01:00
Developer 02
4d38cce459 refactor(user-update-form.component.css): Angeordnete CSSS anstatt Bootstrap zu verwenden 2024-10-31 07:03:26 +01:00
Developer 02
80a3f96404 refactor: ClientUser-Repository-Abhängigkeit hinzugefügt und DeleteAsync in UserRepository verbessert 2024-10-30 21:02:59 +01:00
Developer 02
2a4358a7c7 feat: ReadAsync-Methode zur ClientUserRepository mit optionalem userId-Filter hinzugefügt 2024-10-30 20:41:31 +01:00
Developer 02
978d8aaa55 feat(ClientUser): Repository erstellt 2024-10-30 20:36:03 +01:00
Developer 02
ae729198b9 feat(ClientUser): Repository-Schnittstelle erstellt 2024-10-30 20:32:03 +01:00
Developer 02
76f57676b1 feat: ClientUser-Entität mit erforderlichen Attributen und Standardwerten erstellt 2024-10-30 20:26:48 +01:00
Developer 02
97cefb3fea feat: Abhängigkeit von IUserRepRepository hinzugefügt und Unterstützung für zugehörige Löschungen in UserRepository 2024-10-30 16:12:39 +01:00
Developer 02
1273b7ac46 refactor: repUserId-Parameter zur ReadAllAsync-Methode in UserRepRepository hinzugefügt 2024-10-30 16:09:35 +01:00
Developer 02
4c02607409 fix: zusätzlichen Löschfall in der GroupRepository-DeleteAsync-Methode behandeln. 2024-10-30 16:01:21 +01:00
Developer 02
9f99bb0bc9 feat: erweitere GroupRepository mit Unterstützung für das Löschen verwandter Entitäten
- Abhängigkeiten IGroupOfUserRepository und IUserRepRepository zum GroupRepository-Konstruktor hinzugefügt.
- DeleteAsync aktualisiert, um verwandte Entitäten in den GroupOfUser- und UserRep-Tabellen zu löschen, bevor die Gruppe entfernt wird.
2024-10-30 15:45:47 +01:00
13 changed files with 238 additions and 83 deletions

View File

@@ -1,72 +1,70 @@
<div class="container my-3">
<div class="row">
<div [ngClass]="formFieldBSClass">
<mat-form-field>
<mat-label>Benutzername</mat-label>
<input
matInput
[formControl]="username"
(blur)="updateErrorMessage()"
<div class="dd-container">
<!-- username, e-mail -->
<div class="dd-row input-row">
<mat-form-field>
<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-label>E-Mail</mat-label>
<input matInput placeholder="user@example.com" [formControl]="email" (blur)="updateMailErrorMessage()"
required />
@if (email.invalid) {
<mat-error>{{errorMessage()}}</mat-error>
}
</mat-form-field>
</div>
<div [ngClass]="formFieldBSClass">
<mat-form-field>
<mat-label>E-Mail</mat-label>
<input
matInput placeholder="user@example.com"
[formControl]="email"
(blur)="updateMailErrorMessage()"
required />
@if (email.invalid) {
<mat-error>{{mailErrorMessage()}}</mat-error>
}
</mat-form-field>
</div>
<div [ngClass]="formFieldBSClass">
<div [ngClass]="buttonBSClass">
<button mat-fab extended (click)="update()">
<mat-icon>save</mat-icon>
Speichern
</button>
</div>
</div>
@if (email.invalid) {
<mat-error>{{mailErrorMessage()}}</mat-error>
}
</mat-form-field>
</div>
<div class="row">
<div [ngClass]="formFieldBSClass">
<mat-form-field>
<mat-label>Vorname</mat-label>
<input
matInput
[formControl]="name"
(blur)="updateErrorMessage()"
required />
@if (email.invalid) {
<mat-error>{{errorMessage()}}</mat-error>
}
</mat-form-field>
</div>
<div [ngClass]="formFieldBSClass">
<mat-form-field>
<mat-label>Nachname</mat-label>
<input
matInput
[formControl]="surname"
(blur)="updateErrorMessage()"
required />
@if (email.invalid) {
<mat-error>{{errorMessage()}}</mat-error>
}
</mat-form-field>
</div>
<div [ngClass]="formFieldBSClass">
<button mat-fab extended (click)="delete()">
<mat-icon>delete</mat-icon>
Löschen
</button>
</div>
<!-- firstname, surname -->
<div class="dd-row input-row">
<mat-form-field>
<mat-label>Vorname</mat-label>
<input matInput [formControl]="name" (blur)="updateErrorMessage()" required />
@if (email.invalid) {
<mat-error>{{errorMessage()}}</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>Nachname</mat-label>
<input matInput [formControl]="surname" (blur)="updateErrorMessage()" required />
@if (email.invalid) {
<mat-error>{{errorMessage()}}</mat-error>
}
</mat-form-field>
</div>
<!-- shortname -->
<div class="dd-row input-row">
<mat-form-field>
<mat-label>Kürzel</mat-label>
<input matInput [formControl]="shortname" />
</mat-form-field>
<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>
</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" />
</mat-form-field>
</div>
<!-- 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,32 @@
.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

@@ -14,11 +14,12 @@ import { UserGroupDirImportComponent } from "../../user-group-dir-import/user-gr
import { UserService } from '../../../services/api/user.service';
import { RefreshService } from '../../../services/button/refresh.service';
import Swal from 'sweetalert2';
import { MatSelectModule } from '@angular/material/select';
@Component({
selector: 'app-user-update-form',
standalone: true,
imports: [MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule, MatIconModule, MatButtonModule, CommonModule, MatTabsModule, UserGroupDirImportComponent],
imports: [MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule, MatIconModule, MatButtonModule, CommonModule, MatTabsModule, UserGroupDirImportComponent, MatSelectModule],
templateUrl: './user-update-form.component.html',
styleUrl: './user-update-form.component.scss'
})
@@ -29,16 +30,15 @@ export class UserUpdateFormComponent {
readonly user: User = inject(MAT_DIALOG_DATA);
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]);
readonly surname = new FormControl(this.user.name, [Validators.required]);
readonly email = new FormControl(this.user.email, [Validators.required, Validators.email]);
readonly shortname = new FormControl(this.user.shortname);
readonly comment = new FormControl(this.user.comment);
mailErrorMessage = signal('');
errorMessage = signal('');
public readonly formFieldBSClass: string = "col d-flex justify-content-center mx-1 my-2"
public readonly buttonBSClass: string = "d-flex justify-content-center mx-1 my-2"
constructor(private uService: UserService, private rService: RefreshService) {
merge(
this.email.statusChanges, this.email.valueChanges)
@@ -77,7 +77,9 @@ export class UserUpdateFormComponent {
this.user.prename = this.name.value!;
this.user.username = this.username.value!;
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

@@ -35,6 +35,7 @@ namespace DigitalData.UserManager.Application
.AddScoped<IModuleRepository, ModuleRepository<TDbContext>>()
.AddScoped<IModuleOfUserRepository, ModuleOfUserRepository<TDbContext>>()
.AddScoped<IUserRepRepository, UserRepRepository<TDbContext>>()
.AddScoped<IClientUserRepository, ClientUserRepository<TDbContext>>()
.AddScoped<IUserService, UserService>()
.AddScoped<IGroupService, GroupService>()

View File

@@ -0,0 +1,35 @@
using DigitalData.Core.Abstractions;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace DigitalData.UserManager.Domain.Entities
{
[Table("TBDD_CLIENT_USER", Schema = "dbo")]
public class ClientUser : IUnique<int>
{
[Column("GUID")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[Column("USER_ID")]
public int UserId { get; init; }
[Required]
[Column("CLIENT_ID")]
public int ClientId { get; init; }
[Column("COMMENT")]
public string? Comment { get; init; }
[StringLength(50)]
[Column("ADDED_WHO")]
public string? AddedWho { get; set; }
[Column("ADDED_WHEN", TypeName = "datetime")]
[DefaultValue("GETDATE()")]
public DateTime AddedWhen { get; set; } = DateTime.Now;
}
}

View File

@@ -0,0 +1,10 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IClientUserRepository : ICRUDRepository<ClientUser, int>
{
Task<IEnumerable<ClientUser>> ReadAsync(bool readOnly = true, int? userId = null);
}
}

View File

@@ -16,5 +16,7 @@ namespace DigitalData.UserManager.Infrastructure.Contracts
public DbSet<User> Users { get; }
public DbSet<UserRep> UserReps { get; }
public DbSet<ClientUser> ClientUsers { get; }
}
}

View File

@@ -7,6 +7,6 @@ namespace DigitalData.UserManager.Infrastructure.Contracts
{
Task<IEnumerable<UserRep>> ReadAllAsync(
bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false,
int? userId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true);
int? userId = null, int? repUserId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true);
}
}

View File

@@ -0,0 +1,25 @@
using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
namespace DigitalData.UserManager.Infrastructure.Repositories
{
public class ClientUserRepository<TDbContext> : CRUDRepository<ClientUser, int, TDbContext>, IClientUserRepository
where TDbContext : DbContext, IUserManagerDbContext
{
public ClientUserRepository(TDbContext dbContext) : base(dbContext, dbContext.ClientUsers)
{
}
public async Task<IEnumerable<ClientUser>> ReadAsync(bool readOnly = true, int? userId = null)
{
var query = readOnly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (userId is not null)
query = query.Where(cu => cu.UserId == userId);
return await query.ToListAsync();
}
}
}

View File

@@ -8,8 +8,32 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
public class GroupRepository<TDbContext> : CRUDRepository<Group, int, TDbContext>, IGroupRepository
where TDbContext : DbContext, IUserManagerDbContext
{
public GroupRepository(TDbContext dbContext) : base(dbContext, dbContext.Groups)
private readonly IGroupOfUserRepository _gouRepo;
private readonly IUserRepRepository _uRepRepo;
public GroupRepository(TDbContext dbContext, IGroupOfUserRepository gouRepo, IUserRepRepository userRepRepository) : base(dbContext, dbContext.Groups)
{
_gouRepo = gouRepo;
_uRepRepo = userRepRepository;
}
//TODO: instead of this implmenet .OnDelete(DeleteBehavior.ClientCascade) in DbContext
public override async Task<bool> DeleteAsync(Group group)
{
var gou_list = await _gouRepo.ReadAsync(readOnly: false, groupId: group.Id);
if (gou_list.Any())
_dbContext.RemoveRange(gou_list);
var uRep_list = await _uRepRepo.ReadAllAsync(readOnly: false, groupId: group.Id);
if (uRep_list.Any())
_dbContext.RemoveRange(uRep_list);
uRep_list = await _uRepRepo.ReadAllAsync(readOnly: false, repGroupId: group.Id);
if (uRep_list.Any())
_dbContext.RemoveRange(uRep_list);
return await base.DeleteAsync(group);
}
}
}

View File

@@ -14,7 +14,7 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
public async Task<IEnumerable<UserRep>> ReadAllAsync(
bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false,
int? userId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true)
int? userId = null, int? repUserId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true)
{
var query = readOnly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
@@ -30,9 +30,12 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
if (withRepUser)
query = query.Include(ur => ur.RepUser);
if(userId is not null)
if (userId is not null)
query = query.Where(ur => ur.UserId == userId);
if (repUserId is not null)
query = query.Where(ur => ur.RepUserId == repUserId);
if (groupId is not null)
query = query.Where(ur => ur.GroupId == groupId);

View File

@@ -9,11 +9,19 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
where TDbContext : DbContext, IUserManagerDbContext
{
private readonly IModuleOfUserRepository _moduleOfUserRepo;
private readonly IGroupOfUserRepository _groupOfUserRepo;
public UserRepository(TDbContext dbContext, IModuleOfUserRepository moduleOfUserRepo, IGroupOfUserRepository groupOfUserRepo) : base(dbContext, dbContext.Users)
private readonly IUserRepRepository _uRepRepo;
private readonly IClientUserRepository _cUserRepo;
public UserRepository(TDbContext dbContext, IModuleOfUserRepository moduleOfUserRepo, IGroupOfUserRepository groupOfUserRepo, IUserRepRepository userRepRepository, IClientUserRepository clientUserRepository) : base(dbContext, dbContext.Users)
{
_moduleOfUserRepo = moduleOfUserRepo;
_groupOfUserRepo = groupOfUserRepo;
_uRepRepo = userRepRepository;
_cUserRepo = clientUserRepository;
}
public async Task<IEnumerable<User>> ReadByModuleIdAsync(int moduleId)
@@ -48,14 +56,26 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
//TODO: instead of this implmenet .OnDelete(DeleteBehavior.ClientCascade) in DbContext
public override async Task<bool> DeleteAsync(User user)
{
IEnumerable<ModuleOfUser> mou = await _moduleOfUserRepo.ReadByUserAsync(user.Username);
if(mou.Any())
var mou = await _moduleOfUserRepo.ReadByUserAsync(user.Username);
if (mou.Any())
_dbContext.RemoveRange(mou);
IEnumerable<GroupOfUser> gou = await _groupOfUserRepo.ReadByUsernameAsync(user.Username);
if(gou.Any())
var gou = await _groupOfUserRepo.ReadByUsernameAsync(user.Username);
if (gou.Any())
_dbContext.RemoveRange(gou);
var uRep_list = await _uRepRepo.ReadAllAsync(readOnly: false, userId: user.Id);
if (uRep_list.Any())
_dbContext.RemoveRange(uRep_list);
uRep_list = await _uRepRepo.ReadAllAsync(readOnly: false, repUserId: user.Id);
if (uRep_list.Any())
_dbContext.RemoveRange(uRep_list);
var cu_list = await _cUserRepo.ReadAsync(readOnly: false, userId: user.Id);
if (cu_list.Any())
_dbContext.RemoveRange(cu_list);
return await base.DeleteAsync(user);
}
}

View File

@@ -18,6 +18,8 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
public DbSet<UserRep> UserReps { get; set; }
public DbSet<ClientUser> ClientUsers { get; set; }
public UserManagerDbContext(DbContextOptions<UserManagerDbContext> options) : base(options)
{
GroupOfUsers = Set<GroupOfUser>();
@@ -26,6 +28,7 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
Modules = Set<Module>();
Users = Set<User>();
UserReps = Set<UserRep>();
ClientUsers = Set<ClientUser>();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)