1
0
Fork 0
mirror of https://codeberg.org/beerbrawl/beerbrawl.git synced 2024-09-23 05:40:51 +02:00

bug/(#63): bugs fixes, Confirm Action dialog

This commit is contained in:
rafael 2024-05-17 21:20:51 +02:00
parent e77aecad36
commit be6d9cfdae
21 changed files with 280 additions and 115 deletions

View file

@ -2,8 +2,12 @@ package at.ac.tuwien.sepr.groupphase.backend.endpoint;
import at.ac.tuwien.sepr.groupphase.backend.endpoint.dto.UserDetailDto;
import at.ac.tuwien.sepr.groupphase.backend.endpoint.dto.UserLoginDto;
import at.ac.tuwien.sepr.groupphase.backend.entity.Tournament;
import at.ac.tuwien.sepr.groupphase.backend.exception.UserAlreadyExistsException;
import at.ac.tuwien.sepr.groupphase.backend.service.TournamentService;
import at.ac.tuwien.sepr.groupphase.backend.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import org.slf4j.Logger;
@ -11,6 +15,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -25,6 +30,10 @@ import org.springframework.web.bind.annotation.RestController;
import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@RestController
@RequestMapping(value = UserEndpoint.BASE_ENDPOINT)
@ -33,10 +42,12 @@ public class UserEndpoint {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final UserService userService;
private final TournamentService tournamentService;
@Autowired
public UserEndpoint(UserService userService) {
public UserEndpoint(UserService userService, TournamentService tournamentService) {
this.userService = userService;
this.tournamentService = tournamentService;
}
/**
@ -73,11 +84,12 @@ public class UserEndpoint {
*/
@DeleteMapping("{username}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("isAuthenticated()") //see JwtAuthFilter UsernamePasswordAuthenticationToken
@PreAuthorize("isAuthenticated()")
@Operation(summary = "Delete user and all data belonging to them(Tournaments, Teams, etc) from the database.", security = @SecurityRequirement(name = "apiKey"))
public ResponseEntity<?> delete(@PathVariable(value = "username") String username, Authentication authentication) {
LOG.info("DELETE {}/{}", BASE_ENDPOINT, username);
if (!username.equals(authentication.getPrincipal())) {
return new ResponseEntity<>("Forbidden", HttpStatus.FORBIDDEN); // ToDo: Check why PreAUth("#username == authentication.principal") not working (string == object)
throw new AccessDeniedException("Username does not match.");
}
userService.deleteUser(username);
@ -93,35 +105,53 @@ public class UserEndpoint {
@PutMapping("{username}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("isAuthenticated()")
@Operation(summary = "Update username and password for user.", security = @SecurityRequirement(name = "apiKey"))
public ResponseEntity<?> update(@PathVariable(value = "username") String username, @Valid @RequestBody UserLoginDto userUpdate, Authentication authentication) {
LOG.info("UPDATE {}/{}", BASE_ENDPOINT, username);
if (!username.equals(authentication.getPrincipal())) {
return new ResponseEntity<>("Forbidden", HttpStatus.FORBIDDEN);
throw new AccessDeniedException("Username does not match.");
}
userService.updateUser(userUpdate, username);
try {
userService.updateUser(userUpdate, username);
} catch (UserAlreadyExistsException e) {
HttpStatus status = HttpStatus.CONFLICT;
logClientError(status, "User already exists", e);
return new ResponseEntity<>(e.getMessage(), status);
}
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
/**
* Get Username and password for user. ToDo: Add futher info like Num of Tournaments
* Get Username and password for user.
* The target user is retrieved through the JWT
*/
@GetMapping("/detail/{username}")
@ResponseStatus(HttpStatus.OK)
@PreAuthorize("isAuthenticated()")
@Operation(summary = "Get detailed information about user and their tournaments.", security = @SecurityRequirement(name = "apiKey"))
public ResponseEntity<UserDetailDto> details(@PathVariable(value = "username") String username, Authentication authentication) {
LOG.info("GET-DETAILS {}/detail/{}", BASE_ENDPOINT, username);
if (!username.equals(authentication.getPrincipal())) {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
throw new AccessDeniedException("Username does not match.");
}
var user = userService.findApplicationUserByUsername(username);
var tournaments = tournamentService.findAllByOrganizer(username);
//Todo: Move filtering to tournamentService?
var tournamentNames = tournaments.stream().map(Tournament::getName).toList();
Predicate<Tournament> isPast = t -> t.getRegistrationEnd().isBefore(LocalDateTime.now());
Map<Boolean, Long> counts = tournaments.stream()
.collect(Collectors.partitioningBy(isPast, Collectors.counting()));
if (user != null) {
var dto = UserDetailDto.UserDetailDtoBuilder
.anUserDetailDto()
.withUsername(user.getUsername())
.withPassword(user.getPassword())
.withRole(user.getAdmin() ? "ADMIN" : "USER")
.withTournaments(tournamentNames)
.withOpen(counts.get(false).intValue())
.withClosed(counts.get(true).intValue())
.build();
return ResponseEntity.ok(dto);
}

View file

@ -3,6 +3,7 @@ package at.ac.tuwien.sepr.groupphase.backend.endpoint.dto;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.List;
import java.util.Objects;
public class UserDetailDto {
@ -11,75 +12,75 @@ public class UserDetailDto {
@Size(min = 3, max = 256)
private String username;
//Todo: Discuss possible security concerns
@NotNull(message = "Password must not be null")
@Size(min = 8, max = 1024)
private String password;
@NotNull
private String role;
private List<String> tournaments;
@NotNull
private int closedTournaments;
@NotNull
private int openTournaments;
public List<String> getTournaments() {
return tournaments;
}
public void setTournaments(List<String> tournaments) {
this.tournaments = tournaments;
}
public int getClosedTournaments() {
return closedTournaments;
}
public void setClosedTournaments(int closedTournaments) {
this.closedTournaments = closedTournaments;
}
public int getOpenTournaments() {
return openTournaments;
}
public void setOpenTournaments(int openTournaments) {
this.openTournaments = openTournaments;
}
// Getters
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getRole() {
return role;
}
// Setters
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setRole(String role) {
this.role = role;
}
// hashCode
@Override
public int hashCode() {
return Objects.hash(username, password, role);
}
// equals
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UserDetailDto user = (UserDetailDto) o;
return Objects.equals(username, user.username)
&& Objects.equals(password, user.password)
&& Objects.equals(role, user.role);
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserDetailDto that = (UserDetailDto) o;
return closedTournaments == that.closedTournaments && openTournaments == that.openTournaments && Objects.equals(username, that.username);
}
@Override
public int hashCode() {
return Objects.hash(username, tournaments, closedTournaments, openTournaments);
}
// toString
@Override
public String toString() {
return "User{"
+ "username='" + username + '\''
+ ", password='[PROTECTED]'"
+ ", role='" + role + '\''
+ '}';
return "UserDetailDto{" +
"username='" + username + '\'' +
", TournamentCount=" + tournaments.size() +
", closedTournaments=" + closedTournaments +
", openTournaments=" + openTournaments +
'}';
}
public static final class UserDetailDtoBuilder {
private String username;
private String password;
private String role;
private List<String> tournaments;
private int closedTournaments;
private int openTournaments;
private UserDetailDtoBuilder() {
}
@ -88,26 +89,33 @@ public class UserDetailDto {
return new UserDetailDto.UserDetailDtoBuilder();
}
public UserDetailDto.UserDetailDtoBuilder withTournaments(List<String> tournaments) {
this.tournaments = tournaments;
return this;
}
public UserDetailDto.UserDetailDtoBuilder withClosed(int closedTournaments) {
this.closedTournaments = closedTournaments;
return this;
}
public UserDetailDto.UserDetailDtoBuilder withOpen(int openTournaments) {
this.openTournaments = openTournaments;
return this;
}
public UserDetailDto.UserDetailDtoBuilder withUsername(String username) {
this.username = username;
return this;
}
public UserDetailDto.UserDetailDtoBuilder withPassword(String password) {
this.password = password;
return this;
}
public UserDetailDto.UserDetailDtoBuilder withRole(String role) {
this.role = role;
return this;
}
public UserDetailDto build() {
UserDetailDto userDetailDto = new UserDetailDto();
userDetailDto.setUsername(username);
userDetailDto.setPassword(password);
userDetailDto.setRole(role);
userDetailDto.setTournaments(tournaments);
userDetailDto.setClosedTournaments(closedTournaments);
userDetailDto.setOpenTournaments(openTournaments);
return userDetailDto;
}
}

View file

@ -26,9 +26,13 @@ public class ApplicationUser {
public ApplicationUser() {
}
public ApplicationUser(String username, String password, Boolean admin) {
public ApplicationUser(String username, String password) {
this.username = username;
this.password = password;
}
public ApplicationUser(String username, String password, Boolean admin) {
this(username, password);
this.admin = admin;
}

View file

@ -6,6 +6,7 @@ import at.ac.tuwien.sepr.groupphase.backend.entity.ApplicationUser;
public interface UserRepository extends JpaRepository<ApplicationUser, Long> {
ApplicationUser findByUsername(String username);
boolean existsByUsername(String username);
void deleteByUsername(String username);
}

View file

@ -64,5 +64,5 @@ public interface UserService extends UserDetailsService {
* @return updated user
* @throws NotFoundException if the user to update is not found by the username
*/
ApplicationUser updateUser(UserLoginDto user, String username) throws NotFoundException;
ApplicationUser updateUser(UserLoginDto user, String username) throws NotFoundException, UserAlreadyExistsException;
}

View file

@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Objects;
@Service
public class CustomUserDetailService implements UserService {
@ -92,20 +93,17 @@ public class CustomUserDetailService implements UserService {
public ApplicationUser register(UserLoginDto dto) throws UserAlreadyExistsException {
LOGGER.debug("Trying to register user '{}'", dto.getUsername());
try {
loadUserByUsername(dto.getUsername());
} catch (UsernameNotFoundException e) {
var newUser = new ApplicationUser(
dto.getUsername(),
passwordEncoder.encode(dto.getPassword()),
false
if (userRepository.existsByUsername(dto.getUsername())) {
throw new UserAlreadyExistsException(
"User with username '%s' already exists".formatted(dto.getUsername())
);
return userRepository.save(newUser);
}
throw new UserAlreadyExistsException(
"User with username '%s' already exists".formatted(dto.getUsername())
var newUser = new ApplicationUser(
dto.getUsername(),
passwordEncoder.encode(dto.getPassword()),
false
);
return userRepository.save(newUser);
}
@Override
@ -123,9 +121,13 @@ public class CustomUserDetailService implements UserService {
@Override
@Transactional
public ApplicationUser updateUser(UserLoginDto user, String username) throws NotFoundException {
public ApplicationUser updateUser(UserLoginDto user, String username) throws NotFoundException, UserAlreadyExistsException {
LOGGER.debug("Updating registered user '{}'", user.getUsername());
if (!Objects.equals(username, user.getUsername()) && userRepository.existsByUsername(user.getUsername())) {
throw new UserAlreadyExistsException("User with username '%s' already exists.".formatted(user.getUsername()));
}
var targetUser = userRepository.findByUsername(username);
if (targetUser != null) {
targetUser.setPassword(passwordEncoder.encode(user.getPassword()));

View file

@ -9,6 +9,7 @@ import at.ac.tuwien.sepr.groupphase.backend.entity.QualificationParticipation;
import at.ac.tuwien.sepr.groupphase.backend.entity.Team;
import at.ac.tuwien.sepr.groupphase.backend.entity.Tournament;
import at.ac.tuwien.sepr.groupphase.backend.exception.NotFoundException;
import at.ac.tuwien.sepr.groupphase.backend.exception.UserAlreadyExistsException;
import at.ac.tuwien.sepr.groupphase.backend.repository.BeerPongTableRepository;
import at.ac.tuwien.sepr.groupphase.backend.repository.QualificationMatchRepository;
import at.ac.tuwien.sepr.groupphase.backend.repository.TeamRepository;
@ -81,7 +82,7 @@ public class UserDetailServiceTest implements TestData {
}
@Test
public void checkUpdatedInformationOfRegisteredUser() {
public void checkUpdatedInformationOfRegisteredUser() throws Exception {
var user = new ApplicationUser("Username", "Password", false);
userRepository.save(user);
@ -90,6 +91,7 @@ public class UserDetailServiceTest implements TestData {
.withUsername("Updated").withPassword("UpPass").build();
var updates = userService.updateUser(userUpdate, user.getUsername());
assertAll(
() -> assertNotNull(updates),
() -> assertEquals(user.getAdmin(), updates.getAdmin()),
@ -98,6 +100,24 @@ public class UserDetailServiceTest implements TestData {
);
}
@Test
public void checkUpdateInformationOfRegisteredUser_UsernameAlreadyExists() throws Exception {
var sameUsername = "Username";
var uniqueUsername = "Unique";
var firstUser = new ApplicationUser(sameUsername, "Password");
userRepository.save(firstUser);
var secondUser = new ApplicationUser(uniqueUsername, "Password");
userRepository.save(secondUser);
var userUpdate = UserLoginDto.UserLoginDtoBuilder.anUserLoginDto()
.withUsername(sameUsername).withPassword("UpPass").build();
assertThrows(UserAlreadyExistsException.class, () -> {
var updated = userService.updateUser(userUpdate, uniqueUsername);
}, "User with username '%s' already exists.".formatted(sameUsername));
}
@Test
public void checkDeleteOfRegisteredUserCascading_SharedQualificationParticipationAndMatch() {

View file

@ -94,6 +94,7 @@ export class UserEndpointService {
}
/**
* Delete user and all data belonging to them(Tournaments, Teams, etc) from the database.
* @param username
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
@ -157,6 +158,7 @@ export class UserEndpointService {
}
/**
* Get detailed information about user and their tournaments.
* @param username
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
@ -293,6 +295,7 @@ export class UserEndpointService {
}
/**
* Update username and password for user.
* @param username
* @param userLoginDto
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.

View file

@ -13,7 +13,8 @@
export interface UserDetailDto {
username: string;
password: string;
role: string;
tournaments: Array<string>;
closedTournaments: number;
openTournaments: number;
}

View file

@ -25,6 +25,7 @@ import { TournamentsComponent } from './components/tournaments/tournaments.compo
import { TournamentCardComponent } from './components/tournament-card/tournament-card.component';
import { UserDetailComponent } from './components/user-detail/user-detail.component';
import { UpdateUserComponent } from './components/update-user/update-user.component';
import { ConfirmDialogComponent } from './components/confirm-dialog/confirm-dialog.component';
@NgModule({
declarations: [
@ -39,6 +40,7 @@ import { UpdateUserComponent } from './components/update-user/update-user.compon
TournamentCardComponent,
UserDetailComponent,
UpdateUserComponent,
ConfirmDialogComponent,
],
imports: [
BrowserModule,

View file

@ -0,0 +1,12 @@
<h1 mat-dialog-title>Confirm {{ actionToPerform }}</h1>
<div mat-dialog-content>Are you sure you want to do this?</div>
<div mat-dialog-actions>
<button mat-flat-button color="warn" aria-label="cancel-action button" (click)="this.dialogRef.close(false);">
Cancel
<mat-icon>cancel</mat-icon>
</button>
<button mat-flat-button color="primary" aria-label="ok button" (click)="this.dialogRef.close(true);" class="center-content">
Confirm
<mat-icon>check</mat-icon>
</button>
</div>

View file

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

View file

@ -0,0 +1,18 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
@Component({
selector: 'app-confirm-dialog',
templateUrl: './confirm-dialog.component.html',
styleUrl: './confirm-dialog.component.scss'
})
export class ConfirmDialogComponent {
constructor(
@Inject(MAT_DIALOG_DATA) private action: string,
public dialogRef: MatDialogRef<ConfirmDialogComponent>,
) {}
actionToPerform: string = this.action;
}

View file

@ -11,6 +11,7 @@ import { Message } from '../../dtos/message';
import { UntypedFormBuilder, NgForm } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { HttpErrorResponse } from '@angular/common/http';
import { UsernameService } from 'src/app/services/user/username.service';
@Component({
selector: 'app-message',
@ -20,6 +21,7 @@ import { HttpErrorResponse } from '@angular/common/http';
export class MessageComponent implements OnInit {
// After first submission attempt, form validation will start
submitted = false;
username: string = ""
currentMessage: Message = new Message();
@ -30,7 +32,7 @@ export class MessageComponent implements OnInit {
private formBuilder: UntypedFormBuilder,
private cd: ChangeDetectorRef,
private snackBar: MatSnackBar,
) {}
) { }
ngOnInit() {
this.loadMessage();

View file

@ -11,6 +11,7 @@ import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
import { AuthRequest } from 'src/app/dtos/auth-request';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UsernameService } from 'src/app/services/user/username.service';
const passwordMatchingValidator: ValidatorFn = (
control: AbstractControl,
@ -46,6 +47,7 @@ export class RegisterComponent {
private authService: AuthService,
private router: Router,
private snackBar: MatSnackBar,
private usernameService: UsernameService,
) {}
/**
@ -78,6 +80,7 @@ export class RegisterComponent {
this.snackBar.open(`Successfully registered new user '${authRequest.username}'`, 'Close', {
duration: 3000,
});
this.usernameService.setData(authRequest.username);
this.router.navigate(['/']);
},
error: error => {

View file

@ -1,11 +1,12 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, Inject } from '@angular/core';
import { FormControl, FormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { UserEndpointService, UserLoginDto } from 'openapi-generated';
import { AuthRequest } from 'src/app/dtos/auth-request';
import { AuthService } from 'src/app/services/auth.service';
import { UserService } from 'src/app/services/user/user.service';
@Component({
selector: 'app-update-user',
@ -19,12 +20,12 @@ export class UpdateUserComponent {
});
constructor(
@Inject(MAT_DIALOG_DATA) private userInfo: String,
@Inject(MAT_DIALOG_DATA) private userInfo: string,
private formBuilder: UntypedFormBuilder,
private router: Router,
private dialogRef: MatDialogRef<UpdateUserComponent>,
private snackBar: MatSnackBar,
private userService: UserService,
private userService: UserEndpointService,
private authService: AuthService,
) {
this.updateForm = this.formBuilder.group({
@ -38,11 +39,11 @@ export class UpdateUserComponent {
*/
updateUser() {
if (this.updateForm.valid) {
const updatedInfo: AuthRequest = new AuthRequest(
this.updateForm.controls.username.value!,
this.updateForm.controls.password.value!,
);
this.userService.update(updatedInfo, this.userInfo).subscribe({
const updatedInfo: UserLoginDto = {
username: this.updateForm.controls.username.value!,
password: this.updateForm.controls.password.value!,
};
this.userService.update(this.userInfo, updatedInfo).subscribe({
next: next => {
this.snackBar.open('Successfully updated user', 'OK', { duration: 3000 });
this.authService.logoutUser();
@ -51,7 +52,7 @@ export class UpdateUserComponent {
},
error: error => {
console.log(error);
this.snackBar.open('Error updating account data.', 'OK', { duration: 2500 });
this.defaultServiceErrorHandling(error);
},
});
} else {
@ -62,4 +63,17 @@ export class UpdateUserComponent {
cancel() {
console.log('Cancel Update User');
}
private defaultServiceErrorHandling(error: HttpErrorResponse) {
let errorMessage = '';
console.log(JSON.stringify(error))
if (typeof error.error === 'object') {
errorMessage = error.error.error;
} else {
errorMessage = error.error;
}
this.snackBar.open("Error: " + errorMessage, 'OK', {
duration: 5000,
});
}
}

View file

@ -3,7 +3,12 @@
<mat-card-title>User Information</mat-card-title>
<mat-card-content>
<p><strong>Username:</strong> {{ username }}</p>
<p><strong>Password:</strong> {{ password }}</p>
<p><strong>Open Registrations:</strong> {{ openTournaments }}</p>
<p><strong>Closed Registrations:</strong> {{ closedTournaments }}</p>
<p><strong>List of Tournaments:</strong></p>
<ul>
<li *ngFor="let tournament of tournaments">{{ tournament }}</li>
</ul>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="warn" (click)="onDelete()">

View file

@ -7,6 +7,8 @@ import { OpenLoginService } from 'src/app/services/open-login.service';
import { UserService } from 'src/app/services/user/user.service';
import { UpdateUserComponent } from '../update-user/update-user.component';
import { HttpErrorResponse } from '@angular/common/http';
import { UserEndpointService, UserDetailDto } from 'openapi-generated';
import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component';
@Component({
selector: 'app-user-detail',
@ -15,14 +17,15 @@ import { HttpErrorResponse } from '@angular/common/http';
})
export class UserDetailComponent implements OnInit {
username: string = '';
password: string = '';
tournaments: Array<string> = [];
closedTournaments: number = -1;
openTournaments: number = -1;
constructor(
private router: Router,
private route: ActivatedRoute,
private authService: AuthService,
private openLoginService: OpenLoginService,
private userService: UserService,
private userEndpointService: UserEndpointService,
private snackBar: MatSnackBar,
private matDialog: MatDialog,
) {}
@ -30,11 +33,13 @@ export class UserDetailComponent implements OnInit {
ngOnInit(): void {
this.route.params.subscribe(params => {
this.username = params['username'];
console.log(params['username']);
this.userService.detail(this.username).subscribe({
next: next => {
console.log("UserDetailCOmponent: " + params['username']);
this.userEndpointService.details(this.username).subscribe({
next: next => {
this.username = next.username;
this.password = next.password;
this.openTournaments = next.openTournaments;
this.closedTournaments = next.closedTournaments;
this.tournaments = next.tournaments;
},
error: error => {
console.error('Error fetching user data', error);
@ -46,16 +51,21 @@ export class UserDetailComponent implements OnInit {
onDelete() {
console.log('Delete user');
this.userService.delete(this.username).subscribe({
next: resp => {
this.authService.logoutUser();
this.router.navigate(['/']);
},
error: error => {
console.error('Error deleting user', error);
this.defaultServiceErrorHandling(error);
},
});
const dialogRef = this.matDialog.open(ConfirmDialogComponent, { width: '300px', data: `Delete ${this.username}` })
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.userEndpointService._delete(this.username).subscribe({
next: _ => {
this.authService.logoutUser();
this.router.navigate(['/']);
},
error: error => {
console.error('Error deleting user', error);
this.defaultServiceErrorHandling(error);
},
});
}
})
}
onChange() {

View file

@ -1,9 +1,15 @@
import { Injectable } from '@angular/core';
import { Injectable, OnInit } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UsernameService {
export class UsernameService implements OnInit {
inited: boolean = false;
ngOnInit(): void {
console.log("OnINit Usernameservice");
this.inited = true;
}
private username: string = '';
setData(data: string) {

View file

@ -2,6 +2,7 @@
@import '@fontsource/roboto';
@import '@fontsource/material-icons';
html,
body {
height: 100%;