Compare commits

..

9 Commits

Author SHA1 Message Date
Developer 02
c060cd9083 refactor(UserRep): ValidFrom- und ValidTo-Attribute in Entitäten und DTOs nullbar gemacht 2024-11-08 16:34:30 +01:00
Developer 02
371967380b refactor(rep-create-form.component): Erstellung gestoppt, wenn das Formular nicht gültig ist 2024-11-08 16:27:41 +01:00
Developer 02
7316343658 feat: Hinzufügen von Newtonsoft.Json Unterstützung und Entfernen von doppelten Controller-Konfigurationen
- Hinzugefügt: Newtonsoft.Json zur Projektdatei und konfiguriert, um UTC für DateTime-Serialisierung zu verwenden.
- Entfernt: Doppelte Controller-Konfiguration für das Hinzufügen von Conventions.
- Aktualisiert: Projektdatei mit neuen Paketreferenzen für Newtonsoft.Json und Microsoft.AspNetCore.Mvc.NewtonsoftJson.
2024-11-08 16:11:48 +01:00
Developer 02
31c8249516 feat(rep-create-form.component): Datumslokalisierung für Deutschland hinzugefügt 2024-11-08 14:28:22 +01:00
Developer 02
114995d274 feat(rep-create-form.component): Vorlage erstellt.
- Mat-Datumsbereich-Eingabe hinzugefügt.
 - Klick-Ereignis der Erstellungsschaltfläche arrangiert
2024-11-08 13:44:40 +01:00
Developer 02
e8376ccd21 feat(Benutzer-Darstellung): - openCreateSheet-Methoden in repUserOnSelectedRows-Events integriert
- create-Methode zur Komponente rep-create-form hinzugefügt
2024-11-08 11:36:53 +01:00
Developer 02
52e6eac71d feat(rep-create-form): Initialized and add as a pop-up menu in user-representation 2024-11-08 10:34:09 +01:00
Developer 02
36c55b6382 refactor(UserRepCreateDto): Die Eigenschaften validFrom und validTo wurden zu not-null. 2024-11-08 10:17:43 +01:00
Developer 02
c73b299e3e refactor(UserRep): Hinzufügen der Eigenschaften validFrom und validTo zum UserRep-Modell.
- Spalten 'validFrom' und 'validTo' zu Umgebungen hinzugefügt.
2024-11-08 10:12:46 +01:00
14 changed files with 221 additions and 18 deletions

View File

@@ -15,6 +15,7 @@
"@angular/core": "^17.3.0", "@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0", "@angular/forms": "^17.3.0",
"@angular/material": "^17.3.10", "@angular/material": "^17.3.10",
"@angular/material-moment-adapter": "^17.3.10",
"@angular/platform-browser": "^17.3.0", "@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0", "@angular/platform-browser-dynamic": "^17.3.0",
"@angular/platform-server": "^17.3.0", "@angular/platform-server": "^17.3.0",
@@ -542,6 +543,19 @@
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/material-moment-adapter": {
"version": "17.3.10",
"resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-17.3.10.tgz",
"integrity": "sha512-R68ssdGMSmVIfpOGaB9vjW5lBh6zH9GboBuaIAqizC/ZAzdEgpmT7qdr3PBCmRPTLTx8Yx9K3rhgRekO79ympw==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/core": "^17.0.0 || ^18.0.0",
"@angular/material": "17.3.10",
"moment": "^2.18.1"
}
},
"node_modules/@angular/platform-browser": { "node_modules/@angular/platform-browser": {
"version": "17.3.11", "version": "17.3.11",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.11.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.11.tgz",
@@ -9453,6 +9467,15 @@
"mkdirp": "bin/cmd.js" "mkdirp": "bin/cmd.js"
} }
}, },
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"peer": true,
"engines": {
"node": "*"
}
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",

View File

@@ -18,6 +18,7 @@
"@angular/core": "^17.3.0", "@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0", "@angular/forms": "^17.3.0",
"@angular/material": "^17.3.10", "@angular/material": "^17.3.10",
"@angular/material-moment-adapter": "^17.3.10",
"@angular/platform-browser": "^17.3.0", "@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0", "@angular/platform-browser-dynamic": "^17.3.0",
"@angular/platform-server": "^17.3.0", "@angular/platform-server": "^17.3.0",

View File

@@ -0,0 +1,25 @@
<div class="dd-container">
<div class="dd-row input-row">
<mat-form-field class="w40p">
<mat-label>Geben Sie einen Datumsbereich ein</mat-label>
<mat-date-range-input [formGroup]="range" [rangePicker]="picker">
<input matStartDate formControlName="start" placeholder="Start date">
<input matEndDate formControlName="end" placeholder="End date">
</mat-date-range-input>
<mat-hint>{{dateFormatString()}}</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
@if (range.controls.start.hasError('matStartDateInvalid')) {
<mat-error>Ungültiges Startdatum</mat-error>
}
@if (range.controls.end.hasError('matEndDateInvalid')) {
<mat-error>Ungültiges Enddatum</mat-error>
}
</mat-form-field>
<button mat-fab extended (click)="create()" class="w20p">
<mat-icon>playlist_add</mat-icon>
Erstellen
</button>
</div>
</div>

View File

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

View File

@@ -0,0 +1,95 @@
import { Component, inject, ChangeDetectionStrategy, signal, OnInit, computed } from '@angular/core';
import { UserRep } from '../../../models/user-management.api.models';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { UserRepService } from '../../../services/api/user-representation.service';
import { GroupUpdateFormComponent } from '../group-update-form/group-update-form.component';
import { JsonPipe } from '@angular/common';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DateAdapter, MAT_DATE_LOCALE, provideNativeDateAdapter } from '@angular/material/core';
import { MatDatepickerIntl, MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import Swal from 'sweetalert2';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { provideMomentDateAdapter } from '@angular/material-moment-adapter';
import { MatInputModule } from '@angular/material/input';
import 'moment/locale/de';
@Component({
selector: 'app-rep-create-form',
standalone: true,
providers: [
provideNativeDateAdapter(),
{ provide: MAT_DATE_LOCALE, useValue: 'de-DE' },
provideMomentDateAdapter()
],
imports: [MatFormFieldModule, MatDatepickerModule, FormsModule, ReactiveFormsModule, JsonPipe, MatButtonModule, MatIconModule, MatInputModule],
templateUrl: './rep-create-form.component.html',
styleUrl: './rep-create-form.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RepCreateFormComponent implements OnInit {
readonly userRep: UserRep;
readonly afterCreation: (any: any) => any;
readonly userRepService: UserRepService = inject(UserRepService);
readonly dialogRef: MatDialogRef<GroupUpdateFormComponent> = inject(MatDialogRef<GroupUpdateFormComponent>);
readonly range = new FormGroup({
start: new FormControl<Date | null>(null),
end: new FormControl<Date | null>(null),
});
private readonly _locale = signal(inject<unknown>(MAT_DATE_LOCALE));
private readonly _adapter = inject<DateAdapter<unknown, unknown>>(DateAdapter);
private readonly _intl = inject(MatDatepickerIntl);
constructor() {
const dialogData: { userRep: UserRep, afterCreation: (any: any) => any } = inject(MAT_DIALOG_DATA)
this.userRep = dialogData.userRep;
this.afterCreation = dialogData.afterCreation;
}
ngOnInit(): void {
//update close button label
this._intl.closeCalendarLabel = 'Kalender schließen';
this._intl.changes.next();
//set local
this._locale.set('de');
this._adapter.setLocale(this._locale());
}
readonly dateFormatString = computed(() => {
if (this._locale() === 'de-DE') {
return 'dd.mm.yyyy';
}
return '';
});
create() {
const validFrom = this.range.value.start;
const validTo = this.range.value.end;
if (!validFrom || !validTo) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte geben Sie einen gültigen Datumsbereich ein!",
});
return;
}
this.userRep.validFrom = validFrom!;
this.userRep.validTo = validTo!;
this.userRepService.create(this.userRep).subscribe({
next: (res) => {
this.afterCreation({ successful: res });
this.dialogRef.close();
},
error: (error) => {
this.afterCreation({ error: error });
}
});
}
}

View File

@@ -64,6 +64,8 @@ export interface UserRep {
userId?: number, userId?: number,
repGroupId?: number, repGroupId?: number,
groupId?: number, groupId?: number,
validFrom?: Date,
validTo?: Date,
addedWho?: string, addedWho?: string,
repUser?: User repUser?: User
user?: User, user?: User,

View File

@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ViewChild, inject } from '@angular/core';
import { GuiColumn, GuiSelectedRow } from '@generic-ui/ngx-grid/gui/grid/src/core/api/gui.grid.public-api'; 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 { UserTableComponent } from '../../components/tables/user-table/user-table.component';
import { UserRepTableComponent } from '../../components/tables/user-rep-table/user-rep-table.component'; import { UserRepTableComponent } from '../../components/tables/user-rep-table/user-rep-table.component';
@@ -9,6 +9,8 @@ import { MatTabsModule, MatTabGroup } from '@angular/material/tabs';
import { env } from '../../../environments/environment'; import { env } from '../../../environments/environment';
import { BasePageComponent } from '../base-page/base-page.component'; import { BasePageComponent } from '../base-page/base-page.component';
import { UserRep } from '../../models/user-management.api.models'; import { UserRep } from '../../models/user-management.api.models';
import { RepCreateFormComponent } from '../../components/forms/rep-create-form/rep-create-form.component';
import { MatDialog } from '@angular/material/dialog';
@Component({ @Component({
standalone: true, standalone: true,
@@ -28,6 +30,8 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
slRepGroupId?: number; slRepGroupId?: number;
slRepId?: number; slRepId?: number;
private readonly dialog: MatDialog = inject(MatDialog);
initWithoutData = () => { } initWithoutData = () => { }
constructor(private userRepService: UserRepService) { constructor(private userRepService: UserRepService) {
@@ -100,9 +104,12 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
userId: this.slUserId, userId: this.slUserId,
groupId: this.slGroupId, groupId: this.slGroupId,
repUserId: this.slRepUserId, repUserId: this.slRepUserId,
validFrom: new Date(),
validTo: new Date(new Date().setDate(new Date().getDate() + 7))
} }
this.userRepService.create(newUserRep).subscribe({
next: (response) => { this.openCreateSheet(newUserRep, res => {
if (res.successful) {
this.slRepUserId = undefined; this.slRepUserId = undefined;
this.repUsers.safelyUnselectAll() this.repUsers.safelyUnselectAll()
@@ -111,8 +118,8 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
this.userReps.fetchByUser(this.slUserId) this.userReps.fetchByUser(this.slUserId)
if (this.slGroupId) if (this.slGroupId)
this.userReps.fetchByGroup(this.slGroupId) this.userReps.fetchByGroup(this.slGroupId)
}, }
error: (error) => { else if (res.error) {
Swal.fire({ Swal.fire({
icon: "error", icon: "error",
title: "Oops...", title: "Oops...",
@@ -151,8 +158,9 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
groupId: this.slGroupId, groupId: this.slGroupId,
repGroupId: this.slRepGroupId, repGroupId: this.slRepGroupId,
} }
this.userRepService.create(newUserRep).subscribe({
next: (res) => { this.openCreateSheet(newUserRep, res => {
if (res.successful) {
this.slRepGroupId = undefined; this.slRepGroupId = undefined;
this.repUsers.safelyUnselectAll() this.repUsers.safelyUnselectAll()
this.groups.safelyUnselectAll() this.groups.safelyUnselectAll()
@@ -160,8 +168,8 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
this.userReps.fetchByUser(this.slUserId) this.userReps.fetchByUser(this.slUserId)
if (this.slGroupId) if (this.slGroupId)
this.userReps.fetchByGroup(this.slGroupId) this.userReps.fetchByGroup(this.slGroupId)
}, }
error: (error) => { else if (res.error) {
Swal.fire({ Swal.fire({
icon: "error", icon: "error",
title: "Oops...", title: "Oops...",
@@ -204,4 +212,12 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
this.slRepId = rows[0].source?.id; this.slRepId = rows[0].source?.id;
} }
} }
openCreateSheet(userRep: UserRep, afterCreation: (any: any) => any): void {
this.dialog.open(RepCreateFormComponent, {
width: "50rem",
data: { userRep: userRep, afterCreation: afterCreation }
});
}
} }

View File

@@ -165,6 +165,14 @@ export const env = {
header: "Repr. Benutzer", header: "Repr. Benutzer",
field: (ur: any) => ur.repUser?.username field: (ur: any) => ur.repUser?.username
}, },
{
header: "Gültig von",
field: (ur: any) => new Date(ur.validFrom).toLocaleDateString('de-DE')
},
{
header: "Gültig bis",
field: (ur: any) => new Date(ur.validTo).toLocaleDateString('de-DE')
}
] ]
}, },
config_url: "/assets/config.json", config_url: "/assets/config.json",
@@ -188,4 +196,4 @@ export const env = {
{ value: "en-US", name: "en-US" } { value: "en-US", name: "en-US" }
] ]
} }
}; };

View File

@@ -100,6 +100,6 @@ code {
@for $i from 1 through 20 { @for $i from 1 through 20 {
.w#{$i * 5}p { .w#{$i * 5}p {
width: #{$i * 5 + "%"}; width: #{$i * 5 + "%"} !important;
} }
} }

View File

@@ -40,6 +40,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.API" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.API" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.14" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.14" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.20" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
@@ -48,6 +49,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.3.2" /> <PackageReference Include="NLog" Version="5.3.2" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
@@ -63,7 +65,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="ClientApp\" /> <Folder Include="ClientApp\" />
<Folder Include="Models\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -9,6 +9,7 @@ using DigitalData.Core.API;
using DigitalData.UserManager.API.Controllers; using DigitalData.UserManager.API.Controllers;
using DigitalData.UserManager.Application.Services; using DigitalData.UserManager.Application.Services;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Newtonsoft.Json;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Debug("init main"); logger.Debug("init main");
@@ -25,8 +26,16 @@ try {
builder.Logging.ClearProviders(); builder.Logging.ClearProviders();
builder.Host.UseNLog(); builder.Host.UseNLog();
builder.Services.AddControllers(); builder.Services.AddControllers(opt =>
{
opt.Conventions.Add(new RemoveIfControllerConvention()
.AndIf(c => c.ControllerName == nameof(EncryptionController).Replace("Controller", ""))
.AndIf(c => !config.GetValue<bool>("UseEncryptor")));
}).AddNewtonsoftJson(options =>
{
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
});
if (builder.Configuration.GetValue<bool>("UseSwagger")) if (builder.Configuration.GetValue<bool>("UseSwagger"))
{ {

View File

@@ -10,8 +10,8 @@ namespace DigitalData.UserManager.Application.DTOs.UserRep
int? GroupId, int? GroupId,
int? RepUserId, int? RepUserId,
int? RepGroupId, int? RepGroupId,
DateTime ValidFrom, DateTime? ValidFrom,
DateTime ValidTo, DateTime? ValidTo,
string AddedWho, string AddedWho,
string? ChangedWho, string? ChangedWho,
UserReadDto? User, UserReadDto? User,

View File

@@ -20,11 +20,11 @@ namespace DigitalData.UserManager.Domain.Entities
[Required] [Required]
[Column("VALID_FROM")] [Column("VALID_FROM")]
public required DateTime ValidFrom { get; set; } public required DateTime? ValidFrom { get; set; }
[Required] [Required]
[Column("VALID_TO")] [Column("VALID_TO")]
public required DateTime ValidTo { get; set; } public required DateTime? ValidTo { get; set; }
[ForeignKey("UserId")] [ForeignKey("UserId")]
public virtual User? User { get; set; } public virtual User? User { get; set; }