Usersettings
This commit is contained in:
@@ -20,7 +20,7 @@ export class UserRepository extends Repository<User> {
|
||||
}
|
||||
|
||||
findById(id: string): Promise<User> {
|
||||
return this.findOne({ where: { id }, relations: ['external'] });
|
||||
return this.findOne({ where: { id }, relations: ['external', 'settings'] });
|
||||
}
|
||||
|
||||
async createUser(createUserDto: CreateUserDto): Promise<User> {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { IUser } from 'src/model/interface';
|
||||
import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface';
|
||||
import { HttpErrorByCode } from '@nestjs/common/utils/http-error-by-code.util';
|
||||
import { HttpStatusCode } from 'axios';
|
||||
import { UserSettings } from 'src/model/entitites/user/user.settings.entity';
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Controller('user')
|
||||
@@ -39,4 +40,14 @@ export class UserController {
|
||||
}
|
||||
return this.userService.deleteUserById(id);
|
||||
}
|
||||
|
||||
@Get('settings')
|
||||
getUserSettings(@Req() req: AuthenticatedRequest) {
|
||||
return req.user.settings
|
||||
}
|
||||
|
||||
@Post('settings')
|
||||
updateUserSettings(@Req() req: AuthenticatedRequest, @Body() settings: UserSettings) {
|
||||
return this.userService.updateSettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { User } from 'src/model/entitites';
|
||||
import { UserSettings } from 'src/model/entitites/user/user.settings.entity';
|
||||
import { IUser } from 'src/model/interface';
|
||||
import { ActivityRepository, KeySystemRepository, RoleRepository, UserRepository, UserSettingsRepository } from 'src/model/repositories';
|
||||
import { HelperService } from 'src/shared/service/system.helper.service';
|
||||
@@ -62,4 +63,8 @@ export class UserService {
|
||||
});
|
||||
return activities;
|
||||
}
|
||||
|
||||
updateSettings(settings: UserSettings) {
|
||||
return this.userSettingsRepository.save(settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
@if (isAdmin) {
|
||||
<button mat-button routerLink="/users" routerLinkActive="mat-elevation-z1">Alle User</button>
|
||||
}
|
||||
<button mat-button (click)="openSidebar()">Einstellungen</button>
|
||||
|
||||
</mat-drawer>
|
||||
|
||||
|
||||
@@ -30,4 +32,6 @@
|
||||
</button>
|
||||
</div> -->
|
||||
|
||||
</mat-drawer-container>
|
||||
</mat-drawer-container>
|
||||
|
||||
<app-settings #settings/>
|
||||
@@ -1,20 +1,27 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, inject, ViewChild } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { SettingsComponent } from '../../modules/settings/settings.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout',
|
||||
standalone: true,
|
||||
imports: [MatButtonModule, MatIconModule, MatSidenavModule, RouterModule, MatToolbarModule],
|
||||
imports: [MatButtonModule, MatIconModule, MatSidenavModule, RouterModule, MatToolbarModule, SettingsComponent],
|
||||
templateUrl: './layout.component.html',
|
||||
styleUrl: './layout.component.scss'
|
||||
})
|
||||
export class LayoutComponent {
|
||||
private authService: AuthService = inject(AuthService);
|
||||
@ViewChild('settings') settings!: SettingsComponent;
|
||||
|
||||
openSidebar() {
|
||||
console.log(this.settings)
|
||||
this.settings.open();
|
||||
}
|
||||
|
||||
logout(){
|
||||
this.authService.logout();
|
||||
|
||||
@@ -2,7 +2,7 @@ export interface IUser {
|
||||
username: string;
|
||||
id: string;
|
||||
lastName: string;
|
||||
firstName: String;
|
||||
firstName: string;
|
||||
refreshToken: string;
|
||||
accessToken: string;
|
||||
role: string;
|
||||
|
||||
53
client/src/app/modules/settings/settings.component.html
Normal file
53
client/src/app/modules/settings/settings.component.html
Normal file
@@ -0,0 +1,53 @@
|
||||
@if(isOpen) {
|
||||
<div class="overlay" (click)="closeSidebar()"></div>
|
||||
}
|
||||
<div class="sidebar" [class.open]="isOpen">
|
||||
<div class="sidebar-header">
|
||||
<button class="close-btn" (click)="closeSidebar()">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="content" style="flex: 1 1 auto" >
|
||||
|
||||
<h4>Einstellungen</h4>
|
||||
|
||||
<form [formGroup]="userData" class="flex flex-col p-4">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Vorname</mat-label>
|
||||
<input type="text" matInput formControlName="firstName">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Nachname</mat-label>
|
||||
<input type="text" matInput formControlName="lastName">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Email</mat-label>
|
||||
<input type="text" matInput formControlName="userName">
|
||||
</mat-form-field>
|
||||
<button mat-button [disabled]="userData.invalid" (click)="saveUser()">Speichern</button>
|
||||
</form>
|
||||
|
||||
|
||||
<div class=" px-4">
|
||||
<div class="text-2xl">Emailbenachrichtigungen</div>
|
||||
<div>Sende Emails bei: </div>
|
||||
</div>
|
||||
<form [formGroup]="userSettings" class="flex flex-col p-4 gap-3">
|
||||
<mat-slide-toggle formControlName="sendSystemAccessMails" (change)="save()">
|
||||
Zugriff auf Schließanlage
|
||||
</mat-slide-toggle>
|
||||
<mat-slide-toggle formControlName="sendSystemUpdateMails" (change)="save()">
|
||||
Änderung an Schlueßanlage
|
||||
</mat-slide-toggle>
|
||||
<mat-slide-toggle formControlName="sendUserDisabledMails" (change)="save()">
|
||||
Deaktivierung meines Users
|
||||
</mat-slide-toggle>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@if (isLoading) {
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
</div>
|
||||
46
client/src/app/modules/settings/settings.component.scss
Normal file
46
client/src/app/modules/settings/settings.component.scss
Normal file
@@ -0,0 +1,46 @@
|
||||
/* Overlay */
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 4px;
|
||||
right: -400px; /* Start außerhalb des Bildschirms */
|
||||
width: 400px;
|
||||
height: calc(100% - 8px);
|
||||
|
||||
background-color: #fff;
|
||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
transition: right 0.3s ease-in-out;
|
||||
border-radius: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.sidebar.open {
|
||||
right: 4px; /* Zeige Sidebar */
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
34
client/src/app/modules/settings/settings.component.spec.ts
Normal file
34
client/src/app/modules/settings/settings.component.spec.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SettingsComponent } from './settings.component';
|
||||
import { ApiService } from '../../shared/api.service';
|
||||
import { MockApiService } from '../../../../mocks/services/mock.api.service';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { MockAuthService } from '../../../../mocks/services/mock.auth.service';
|
||||
import { HotToastService } from '@ngxpert/hot-toast';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
let component: SettingsComponent;
|
||||
let fixture: ComponentFixture<SettingsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SettingsComponent, NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: ApiService, useClass: MockApiService },
|
||||
{ provide: AuthService, useClass: MockAuthService },
|
||||
HotToastService
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
103
client/src/app/modules/settings/settings.component.ts
Normal file
103
client/src/app/modules/settings/settings.component.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { ApiService } from '../../shared/api.service';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
import { HotToastService } from '@ngxpert/hot-toast';
|
||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
standalone: true,
|
||||
imports: [MatProgressBarModule, MatFormFieldModule, MatInputModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatSlideToggleModule],
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrl: './settings.component.scss'
|
||||
})
|
||||
export class SettingsComponent {
|
||||
@Input() isOpen = false;
|
||||
@Output() close = new EventEmitter<void>();
|
||||
|
||||
private authService: AuthService = inject(AuthService);
|
||||
private api: ApiService = inject(ApiService);
|
||||
private toast: HotToastService = inject(HotToastService);
|
||||
|
||||
public isLoading = false;
|
||||
|
||||
userData = new FormGroup({
|
||||
firstName: new FormControl(this.firstName, Validators.required),
|
||||
lastName: new FormControl(this.lastName, Validators.required),
|
||||
userName: new FormControl(this.username, [Validators.required, Validators.email])
|
||||
});
|
||||
|
||||
userSettings = new FormGroup({
|
||||
id: new FormControl(),
|
||||
sendSystemAccessMails: new FormControl(false),
|
||||
sendSystemUpdateMails: new FormControl(false),
|
||||
sendUserDisabledMails: new FormControl(false),
|
||||
})
|
||||
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
this.loadSettings();
|
||||
}
|
||||
|
||||
closeSidebar() {
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
get $userData() {
|
||||
return this.userData.controls;
|
||||
}
|
||||
|
||||
get username(): string {
|
||||
return this.authService.user.username;
|
||||
}
|
||||
|
||||
get firstName(): string {
|
||||
return this.authService.user.firstName;
|
||||
}
|
||||
|
||||
get lastName(): string {
|
||||
return this.authService.user.lastName;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
|
||||
loadSettings() {
|
||||
this.isLoading = true;
|
||||
this.api.getSettings().subscribe({
|
||||
next: (r: any) => {
|
||||
this.userSettings.patchValue(r)
|
||||
this.isLoading = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
save() {
|
||||
this.isLoading = true;
|
||||
this.api.updateSettings(this.userSettings.value).subscribe({
|
||||
next: () => {
|
||||
this.toast.success('Gespeichert')
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveUser() {
|
||||
const user = this.authService.user;
|
||||
user.firstName = this.$userData.firstName.value!;
|
||||
user.lastName = this.$userData.lastName.value!;
|
||||
user.username = this.$userData.userName.value!;
|
||||
|
||||
this.api.saveUser(user).subscribe({
|
||||
next: () => {
|
||||
this.toast.success('Gespeichert')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -119,4 +119,12 @@ export class ApiService {
|
||||
createCylinder(data: any) {
|
||||
return this.http.post<ICylinder>('api/cylinder', data);
|
||||
}
|
||||
|
||||
getSettings(): Observable<any> {
|
||||
return this.http.get('api/user/settings')
|
||||
}
|
||||
|
||||
updateSettings(settings: any): Observable<any> {
|
||||
return this.http.post('api/user/settings', settings)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,4 +148,9 @@ div.ag-row {
|
||||
|
||||
text-decoration: line-through !important;
|
||||
}
|
||||
}
|
||||
|
||||
.mdc-notched-outline__notch
|
||||
{
|
||||
border-right: none;
|
||||
}
|
||||
Reference in New Issue
Block a user