mirror of
https://codeberg.org/beerbrawl/beerbrawl.git
synced 2024-09-22 21:20:52 +02:00
Merge branch 'development' into 'master'
Development See merge request 2024ss-se-pr-group/24ss-se-pr-qse-11!140
This commit is contained in:
commit
e6fb3bc2f1
|
@ -4,12 +4,14 @@ 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.TestDataService;
|
||||
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 lombok.AllArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
@ -32,6 +34,7 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping(value = UserEndpoint.BASE_ENDPOINT)
|
||||
public class UserEndpoint {
|
||||
public static final String BASE_ENDPOINT = "/api/v1/user";
|
||||
|
@ -39,11 +42,8 @@ public class UserEndpoint {
|
|||
|
||||
private final UserService userService;
|
||||
private final TournamentService tournamentService;
|
||||
private final TestDataService testDataService;
|
||||
|
||||
public UserEndpoint(UserService userService, TournamentService tournamentService) {
|
||||
this.userService = userService;
|
||||
this.tournamentService = tournamentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new user in the system. If a user with the same username already exists,
|
||||
|
@ -157,6 +157,22 @@ public class UserEndpoint {
|
|||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Username and password for user.
|
||||
* The target user is retrieved through the JWT
|
||||
*r
|
||||
*/
|
||||
@GetMapping(value = "genTestData", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@Operation(summary = "Get detailed information about user and their tournaments.", security = @SecurityRequirement(name = "apiKey"))
|
||||
public void generateTestData(
|
||||
Authentication authentication
|
||||
) {
|
||||
this.testDataService.generateTestDataForUser(authentication.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void logClientError(HttpStatus status, String message, Exception e) {
|
||||
LOG.warn("{} {}: {}: {}", status.value(), message, e.getClass().getSimpleName(), e.getMessage());
|
||||
|
|
|
@ -27,6 +27,15 @@ public interface TournamentRepository extends JpaRepository<Tournament, Long> {
|
|||
*/
|
||||
List<Tournament> findAllByOrganizerIdOrderByNameAsc(Long organizerId);
|
||||
|
||||
/**
|
||||
* Find all tournaments where the organizer's username matches the provided username.
|
||||
*
|
||||
* @param organizerUsername The username of the organizer
|
||||
* @return List of tournaments organized by the provided username
|
||||
*/
|
||||
List<Tournament> findAllByOrganizerUsername(String organizerUsername);
|
||||
|
||||
|
||||
/**
|
||||
* Find all tournaments with their qualification matches eagerly loaded
|
||||
* where the organizer's ID matches the provided ID, ordered by name (ascending).
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package at.ac.tuwien.sepr.groupphase.backend.service;
|
||||
|
||||
public interface TestDataService {
|
||||
void generateTestDataForUser(String personName);
|
||||
}
|
|
@ -0,0 +1,336 @@
|
|||
package at.ac.tuwien.sepr.groupphase.backend.service.impl;
|
||||
|
||||
import at.ac.tuwien.sepr.groupphase.backend.endpoint.dto.TournamentUpdateQualificationMatchDto;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.entity.BeerPongTable;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.entity.QualificationMatch;
|
||||
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.entity.domainservice.MatchDomainService;
|
||||
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.QualificationParticipationRepository;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.repository.TeamRepository;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.repository.TournamentRepository;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.repository.UserRepository;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.service.TestDataService;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.service.TournamentKoPhaseService;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.service.TournamentQualificationService;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.service.TournamentService;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.service.TournamentTeamService;
|
||||
import at.ac.tuwien.sepr.groupphase.backend.util.BeerDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class TestDataServiceImpl implements TestDataService {
|
||||
|
||||
private final TournamentRepository tournamentRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final TeamRepository teamRepository;
|
||||
private final TournamentTeamService teamService;
|
||||
private final TournamentService tournamentService;
|
||||
private final BeerPongTableRepository beerPongTableRepository;
|
||||
private final MatchDomainService matchDomainService;
|
||||
private final TournamentQualificationService qualificationService;
|
||||
private final TournamentKoPhaseService koPhaseService;
|
||||
private final QualificationParticipationRepository qualificationParticipationRepository;
|
||||
private final QualificationMatchRepository qualificationMatchRepository;
|
||||
|
||||
@Override
|
||||
public void generateTestDataForUser(String username) {
|
||||
var tournaments = this.tournamentRepository.findAllByOrganizerUsername(username);
|
||||
tournamentRepository.deleteAllById(tournaments.stream().map(Tournament::getId).toList());
|
||||
|
||||
final var tournament1 = new Tournament(
|
||||
"Semesterclosing Turnier",
|
||||
LocalDateTime.of(LocalDate.of(2024, 6, 27), LocalTime.of(18, 0)),
|
||||
32L,
|
||||
"Willkommen zum Semesterclosing Beerpongturnier! Viel Spaß! Es gibt tolle Preise zu gewinnen!",
|
||||
userRepository.findByUsername(username));
|
||||
|
||||
tournamentRepository.saveAllAndFlush(List.of(tournament1));
|
||||
var teamNames = getTeamNames();
|
||||
|
||||
|
||||
final var teams1 = IntStream.range(0, 32)
|
||||
.mapToObj(i -> new Team(teamNames[i], tournament1))
|
||||
.toList();
|
||||
teamRepository.saveAllAndFlush(teams1);
|
||||
|
||||
var tableNames = List.of("Innen1", "Innen2", "Innen3", "Terasse");
|
||||
var tables1 = IntStream.range(0, 4)
|
||||
.mapToObj(i -> new BeerPongTable(tableNames.get(i), tournament1))
|
||||
.toList();
|
||||
|
||||
beerPongTableRepository.saveAllAndFlush(tables1);
|
||||
generateTournamentWithFinishedQualiPhaseAndDifferentScores(username);
|
||||
}
|
||||
|
||||
protected void generateTournamentWithFinishedQualiPhaseAndDifferentScores(String username) {
|
||||
var tournament = new Tournament(
|
||||
"Ferienturnier", BeerDateTime.nowUtc().plusDays(1), 40L,
|
||||
"Willkommen zum Ferienturnier! Viel Spaß! Es gibt tolle Preise zu gewinnen!",
|
||||
userRepository.findByUsername(username));
|
||||
tournamentService.create(tournament, username);
|
||||
|
||||
generate32Teams(tournament);
|
||||
|
||||
qualificationService.generateQualificationMatchesForTournament(tournament.getId(), username);
|
||||
|
||||
markAllTeamsAsReady(tournament);
|
||||
|
||||
var alwaysWinsId = teamRepository.findAllByTournamentId(tournament.getId()).get(0).getId();
|
||||
var alwaysLoosesId = teamRepository.findAllByTournamentId(tournament.getId()).get(1).getId();
|
||||
|
||||
// all the team ids that have already won one match
|
||||
var teamsWithOneWin = new LinkedList<Long>();
|
||||
|
||||
// we also track the teams in the specific
|
||||
var teamsWith5Points = new LinkedList<Long>();
|
||||
var teamsWithMoreThan5Points = new LinkedList<Long>();
|
||||
var teamsWithLessThan5Points = new LinkedList<Long>();
|
||||
|
||||
final var qMatches = qualificationMatchRepository.findAllByTournamentId(tournament.getId());
|
||||
for (var i = 0; i < qMatches.size() - 1; i++) {
|
||||
var qm = qMatches.get(i);
|
||||
markParticipantsAsDrinksCollected(tournament, qm);
|
||||
|
||||
var winnerInfo = determineWinner(
|
||||
qm, alwaysWinsId, alwaysLoosesId,
|
||||
teamsWithOneWin, teamsWith5Points,
|
||||
teamsWithMoreThan5Points, teamsWithLessThan5Points
|
||||
);
|
||||
|
||||
qualificationService.updateQualificationMatch(
|
||||
tournament.getId(), qm.getId(),
|
||||
new TournamentUpdateQualificationMatchDto(
|
||||
new TournamentUpdateQualificationMatchDto.ScoreUpdateDto(
|
||||
winnerInfo.winnerId, winnerInfo.winnerPoints
|
||||
),
|
||||
null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var tableNames = List.of("Tisch 1", "Tisch 2", "Tisch 3");
|
||||
var tables1 = IntStream.range(0, 3)
|
||||
.mapToObj(i -> new BeerPongTable(tableNames.get(i), tournament))
|
||||
.toList();
|
||||
beerPongTableRepository.saveAllAndFlush(tables1);
|
||||
|
||||
matchDomainService.scheduleQualiMatches(tournament.getId());
|
||||
|
||||
}
|
||||
|
||||
protected void generate32Teams(Tournament tournament) {
|
||||
var teamNames = getTeamNames();
|
||||
for (int i = 1; i <= 32; i++) {
|
||||
var result = teamService.signupTeamForTournament(tournament.getId(),
|
||||
tournament.getPublicAccessToken(), teamNames[i]);
|
||||
if (result != Tournament.SignupTeamResult.SUCCESS) {
|
||||
throw new IllegalStateException("Failed to sign up team for tournament");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void markAllTeamsAsReady(Tournament tournament) {
|
||||
for (final var team : teamRepository.findAllByTournamentId(tournament.getId())) {
|
||||
teamService.markTeamAsReady(tournament.getId(), team.getId());
|
||||
}
|
||||
}
|
||||
|
||||
protected void markParticipantsAsDrinksCollected(Tournament tournament, QualificationMatch qm) {
|
||||
qm.getParticipations()
|
||||
.stream().map(p -> p.getTeam().getId())
|
||||
.forEach(p -> qualificationService.updateQualificationMatch(
|
||||
tournament.getId(), qm.getId(),
|
||||
new TournamentUpdateQualificationMatchDto(
|
||||
null, new TournamentUpdateQualificationMatchDto.DrinksPickupDto(p)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
protected WinnerInfo determineWinner(
|
||||
QualificationMatch qm,
|
||||
Long alwaysWinsId,
|
||||
Long alwaysLoosesId,
|
||||
LinkedList<Long> teamsWithOneWin,
|
||||
LinkedList<Long> teamsWith5Points,
|
||||
LinkedList<Long> teamsWithMoreThan5Points,
|
||||
LinkedList<Long> teamsWithLessThan5Points
|
||||
) {
|
||||
var currentTeamIds = qm.getParticipations().stream().map(p -> p.getTeam().getId()).toList();
|
||||
Long winnerId;
|
||||
Long winnerPoints;
|
||||
|
||||
if (currentTeamIds.contains(alwaysWinsId)) {
|
||||
// the match includes the "alwaysWins" team, so it wins
|
||||
winnerId = alwaysWinsId;
|
||||
winnerPoints = 10L;
|
||||
} else if (currentTeamIds.contains(alwaysLoosesId)) {
|
||||
// the match includes the "alwaysLooses" team, so it looses
|
||||
winnerId = currentTeamIds.stream().filter(id -> !Objects.equals(id, alwaysLoosesId)).findFirst().get();
|
||||
teamsWithOneWin.add(winnerId);
|
||||
winnerPoints = getWinnerPoints(teamsWith5Points, teamsWithMoreThan5Points, teamsWithLessThan5Points, winnerId);
|
||||
} else {
|
||||
var teamWithOneWin = currentTeamIds.stream().filter(teamsWithOneWin::contains).findFirst();
|
||||
if (teamWithOneWin.isPresent()) {
|
||||
//the match includes a team that has already won one match, so the other team wins
|
||||
winnerId = currentTeamIds.stream().filter(id -> !Objects.equals(id, teamWithOneWin.get())).findFirst().get();
|
||||
} else {
|
||||
// the match includes two teams that have not won a match yet, so we just pick the first one
|
||||
winnerId = currentTeamIds.getFirst();
|
||||
}
|
||||
teamsWithOneWin.add(winnerId);
|
||||
winnerPoints = getWinnerPoints(teamsWith5Points, teamsWithMoreThan5Points, teamsWithLessThan5Points, winnerId);
|
||||
}
|
||||
|
||||
return new WinnerInfo(winnerId, winnerPoints);
|
||||
}
|
||||
|
||||
private static class WinnerInfo {
|
||||
Long winnerId;
|
||||
Long winnerPoints;
|
||||
|
||||
WinnerInfo(Long winnerId, Long winnerPoints) {
|
||||
this.winnerId = winnerId;
|
||||
this.winnerPoints = winnerPoints;
|
||||
}
|
||||
}
|
||||
|
||||
protected Long getWinnerPoints(LinkedList<Long> teamsWith5Points, LinkedList<Long> teamsWithMoreThan5Points, LinkedList<Long> teamsWithLessThan5Points, Long winnerId) {
|
||||
Long winnerPoints;
|
||||
if (teamsWith5Points.size() < 4) {
|
||||
// it joins the group with exactly 5 points
|
||||
winnerPoints = 5L;
|
||||
teamsWith5Points.add(winnerId);
|
||||
} else {
|
||||
if (teamsWithMoreThan5Points.size() < teamsWithLessThan5Points.size()) {
|
||||
// it joins the group with more than 5 points
|
||||
// random number from 1 to 4 (both inclusive)
|
||||
winnerPoints = 1L + new Random().nextInt(4);
|
||||
teamsWithMoreThan5Points.add(winnerId);
|
||||
} else {
|
||||
// it joins the group with less than 5 points
|
||||
// random number from 6 to 9 (both inclusive)
|
||||
winnerPoints = 6L + new Random().nextInt(4);
|
||||
teamsWithLessThan5Points.add(winnerId);
|
||||
}
|
||||
}
|
||||
return winnerPoints;
|
||||
}
|
||||
|
||||
String[] getTeamNames() {
|
||||
return new String[]{
|
||||
"Pongmeister",
|
||||
"Bierathleten",
|
||||
"Becherstürmer",
|
||||
"Hopfenhüpfer",
|
||||
"PongProfis",
|
||||
"Bierbuddies",
|
||||
"Schaumjäger",
|
||||
"Ponghelden",
|
||||
"Becherritter",
|
||||
"Bierwerfer",
|
||||
"Bierbongers",
|
||||
"Becherbullen",
|
||||
"Braumeister",
|
||||
"PongPioniere",
|
||||
"Bieronauten",
|
||||
"Bechermagier",
|
||||
"Hopfenheroes",
|
||||
"Bierflieger",
|
||||
"Pongkönige",
|
||||
"Braubrüder",
|
||||
"Becherblitz",
|
||||
"BierballKrieger",
|
||||
"PingpongPrinzen",
|
||||
"Bierkapitäne",
|
||||
"Pongpiraten",
|
||||
"Becherbarden",
|
||||
"BierballBrigade",
|
||||
"Schaumstürmer",
|
||||
"Pongprofs",
|
||||
"HopfenHüpfende",
|
||||
"BierballBataillon",
|
||||
"Pongpartisanen",
|
||||
"Becherbomber",
|
||||
"Braukrieger",
|
||||
"BierballHexer",
|
||||
"Pongpropheten",
|
||||
"Becherbarone",
|
||||
"Hopfenhaie",
|
||||
"BierballFestung",
|
||||
"Pongpäpste",
|
||||
"Becherbären",
|
||||
"Bierbaronen",
|
||||
"Pongpanther",
|
||||
"Becherbrigade",
|
||||
"SchaumSchützen",
|
||||
"BierballTruppe",
|
||||
"Pongpatrioten",
|
||||
"Bechergarde",
|
||||
"Hopfenhexer",
|
||||
"BierballBauern",
|
||||
"Pongpiloten",
|
||||
"Becherbazis",
|
||||
"BrauBanditen",
|
||||
"BierballBosse",
|
||||
"Becherbrecher",
|
||||
"Bierbären",
|
||||
"Schaumkrieger",
|
||||
"Pongplünderer",
|
||||
"Becherbosse",
|
||||
"Hopfenhelden",
|
||||
"BierballKompanie",
|
||||
"Pongprinzen",
|
||||
"Becherblitzer",
|
||||
"Braublitz",
|
||||
"BierballBomber",
|
||||
"Pongchamps",
|
||||
"Becherbuddies",
|
||||
"Bierbrüder",
|
||||
"Schaumjäger",
|
||||
"Pongpiloten",
|
||||
"Bechermagier",
|
||||
"Bierbarone",
|
||||
"Hopfenhengste",
|
||||
"BierballBande",
|
||||
"Becherbarone",
|
||||
"Schaumstürmer",
|
||||
"Bierkapitäne",
|
||||
"Pongköniginnen",
|
||||
"Becherballer",
|
||||
"Braukapitäne",
|
||||
"BierballBerserker",
|
||||
"Pongmeister",
|
||||
"Becherhelden",
|
||||
"Hopfenhüpfer",
|
||||
"Bieronauten",
|
||||
"Pongprofs",
|
||||
"Becherwächter",
|
||||
"Schaumritter",
|
||||
"BierballBotsch",
|
||||
"Ponghelden",
|
||||
"Becherblitz",
|
||||
"Braumeister",
|
||||
"BierballZauberer",
|
||||
"Pongchampions",
|
||||
"Becherbomber",
|
||||
"SchaumSchützen"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,9 +144,9 @@ public class TournamentQualificationServiceImpl implements TournamentQualificati
|
|||
.orElseThrow(() -> new NotFoundException("Match not found"));
|
||||
}
|
||||
|
||||
private void updateQualificationMatchDrinksStatus(
|
||||
QualificationMatch match,
|
||||
TournamentUpdateQualificationMatchDto.DrinksPickupDto updateDto
|
||||
protected void updateQualificationMatchDrinksStatus(
|
||||
QualificationMatch match,
|
||||
TournamentUpdateQualificationMatchDto.DrinksPickupDto updateDto
|
||||
) {
|
||||
final var participation = match.getParticipations()
|
||||
.stream().filter(p -> p.getTeam().getId() == updateDto.teamId())
|
||||
|
|
|
@ -62,7 +62,7 @@ public class TournamentTeamServiceImpl implements TournamentTeamService {
|
|||
throw new BadTournamentPublicAccessTokenException();
|
||||
}
|
||||
|
||||
if (tournament.getRegistrationEnd().isBefore(BeerDateTime.nowUtc())) {
|
||||
if (tournament.getRegistrationEnd().isBefore(BeerDateTime.nowUtc()) || !tournament.getQualificationMatches().isEmpty()) {
|
||||
return SignupTeamResult.REGISTRATION_CLOSED;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: v0
|
||||
*
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
|
@ -13,7 +13,7 @@
|
|||
|
||||
import { Inject, Injectable, Optional } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders, HttpParams,
|
||||
HttpResponse, HttpEvent, HttpParameterCodec, HttpContext
|
||||
HttpResponse, HttpEvent, HttpParameterCodec, HttpContext
|
||||
} from '@angular/common/http';
|
||||
import { CustomHttpParameterCodec } from '../encoder';
|
||||
import { Observable } from 'rxjs';
|
||||
|
@ -95,7 +95,7 @@ export class UserEndpointService {
|
|||
|
||||
/**
|
||||
* Delete user and all data belonging to them(Tournaments, Teams, etc) from the database.
|
||||
* @param username
|
||||
* @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.
|
||||
*/
|
||||
|
@ -159,7 +159,7 @@ export class UserEndpointService {
|
|||
|
||||
/**
|
||||
* Get detailed information about user and their tournaments.
|
||||
* @param username
|
||||
* @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.
|
||||
*/
|
||||
|
@ -222,7 +222,66 @@ export class UserEndpointService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param userLoginDto
|
||||
* Get detailed information about user and their tournaments.
|
||||
* @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.
|
||||
*/
|
||||
public generateTestData(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable<any>;
|
||||
public generateTestData(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<any>>;
|
||||
public generateTestData(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<any>>;
|
||||
public generateTestData(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable<any> {
|
||||
|
||||
let localVarHeaders = this.defaultHeaders;
|
||||
|
||||
let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
|
||||
if (localVarHttpHeaderAcceptSelected === undefined) {
|
||||
// to determine the Accept header
|
||||
const httpHeaderAccepts: string[] = [
|
||||
];
|
||||
localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
|
||||
}
|
||||
if (localVarHttpHeaderAcceptSelected !== undefined) {
|
||||
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
|
||||
}
|
||||
|
||||
let localVarHttpContext: HttpContext | undefined = options && options.context;
|
||||
if (localVarHttpContext === undefined) {
|
||||
localVarHttpContext = new HttpContext();
|
||||
}
|
||||
|
||||
let localVarTransferCache: boolean | undefined = options && options.transferCache;
|
||||
if (localVarTransferCache === undefined) {
|
||||
localVarTransferCache = true;
|
||||
}
|
||||
|
||||
|
||||
let responseType_: 'text' | 'json' | 'blob' = 'json';
|
||||
if (localVarHttpHeaderAcceptSelected) {
|
||||
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
|
||||
responseType_ = 'text';
|
||||
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
|
||||
responseType_ = 'json';
|
||||
} else {
|
||||
responseType_ = 'blob';
|
||||
}
|
||||
}
|
||||
|
||||
let localVarPath = `/api/v1/user/genTestData`;
|
||||
return this.httpClient.request<any>('get', `${this.configuration.basePath}${localVarPath}`,
|
||||
{
|
||||
context: localVarHttpContext,
|
||||
responseType: <any>responseType_,
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: localVarHeaders,
|
||||
observe: observe,
|
||||
transferCache: localVarTransferCache,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userLoginDto
|
||||
* @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.
|
||||
*/
|
||||
|
@ -295,8 +354,8 @@ export class UserEndpointService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param username
|
||||
* @param userLoginDto
|
||||
* @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.
|
||||
* @param reportProgress flag to report request and response progress.
|
||||
*/
|
||||
|
|
|
@ -2,7 +2,14 @@
|
|||
class="qualification-line-container"
|
||||
[class.currently-playing-light]="match().startTime && !match().endTime && !viewOnly()"
|
||||
[class.currently-playing-dark]="match().startTime && !match().endTime && viewOnly()"
|
||||
[class.admin-qualification-line-container]="!viewOnly()"
|
||||
[class.public-qualification-line-container]="viewOnly()"
|
||||
>
|
||||
@if (!viewOnly()) {
|
||||
<div [class.table-light]="match().table?.name">
|
||||
{{ match().table?.name }}
|
||||
</div>
|
||||
}
|
||||
<div
|
||||
class="qualification-team"
|
||||
[class.text-winner]="participant1()?.isWinner === true"
|
||||
|
@ -20,6 +27,7 @@
|
|||
class="team-action-button"
|
||||
data-cy="team1-action-button"
|
||||
mat-icon-button
|
||||
[disabled]="disableMatchEdit()"
|
||||
aria-label="Mark team drinks as picked up for this match"
|
||||
(click)="markTeamDrinksPickedUp.emit(participant1()!)"
|
||||
[matTooltip]="'Mark team drinks as picked up for this match'"
|
||||
|
@ -32,13 +40,17 @@
|
|||
}
|
||||
</div>
|
||||
<div class="center-result-column">
|
||||
@if (match().table && !match()?.winnerPoints) {
|
||||
<p class="beerpong-table-name-floating">{{ match().table?.name }}</p>
|
||||
@if (match().table && !match()?.winnerPoints && viewOnly()) {
|
||||
<p [class.table-dark]="match().table?.name">{{ match().table?.name }}</p>
|
||||
}
|
||||
<div [matTooltip]="resultTooltip" aria-label="Enter match result">
|
||||
@if (match()?.winnerPoints) {
|
||||
@if (!viewOnly()) {
|
||||
<button mat-button [disabled]="!matchHasStarted" (click)="openMatchResultDialog()">
|
||||
<button
|
||||
mat-button
|
||||
[disabled]="!matchHasStarted || disableMatchEdit()"
|
||||
(click)="openMatchResultDialog()"
|
||||
>
|
||||
{{ matchResultString }}
|
||||
</button>
|
||||
} @else {
|
||||
|
@ -48,7 +60,7 @@
|
|||
@if (!viewOnly()) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[disabled]="!matchHasStarted"
|
||||
[disabled]="!matchHasStarted || disableMatchEdit()"
|
||||
(click)="openMatchResultDialog()"
|
||||
data-cy="enter-match-results-btn"
|
||||
>
|
||||
|
@ -57,8 +69,6 @@
|
|||
} @else {
|
||||
@if (!match().table) {
|
||||
vs.
|
||||
} @else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +91,7 @@
|
|||
class="team-action-button"
|
||||
data-cy="team2-action-button"
|
||||
mat-icon-button
|
||||
[disabled]="disableMatchEdit()"
|
||||
aria-label="Mark team drinks as picked up for this match"
|
||||
(click)="markTeamDrinksPickedUp.emit(participant2()!)"
|
||||
[matTooltip]="'Mark team drinks as picked up for this match'"
|
||||
|
|
|
@ -4,12 +4,19 @@
|
|||
.qualification-line-container {
|
||||
height: 3.2rem;
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 2fr 3fr 1fr;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--mat-table-row-item-outline-color);
|
||||
}
|
||||
|
||||
.admin-qualification-line-container {
|
||||
grid-template-columns: 1fr 3fr 2fr 3fr 1fr;
|
||||
}
|
||||
|
||||
.public-qualification-line-container {
|
||||
grid-template-columns: 3fr 2fr 3fr 1fr;
|
||||
}
|
||||
|
||||
.text-winner {
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
|
@ -22,7 +29,7 @@
|
|||
.team-action-button {
|
||||
position: absolute;
|
||||
top: -1.5rem;
|
||||
right: -2.4rem;
|
||||
right: -2.2rem;
|
||||
scale: 0.7;
|
||||
color: black;
|
||||
}
|
||||
|
@ -38,13 +45,6 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.beerpong-table-name-floating {
|
||||
font-weight: bold;
|
||||
margin: 0px;
|
||||
// put this on top of the result display, I found no clean way to do this
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
|
||||
.currently-playing-icon {
|
||||
position: absolute;
|
||||
top: -0.8rem;
|
||||
|
@ -88,3 +88,26 @@
|
|||
.currently-playing-dark {
|
||||
background-color: var(--color-background-dark-success);
|
||||
}
|
||||
|
||||
.table-light {
|
||||
font-size: 0.8rem;
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background-color: var(--color-background-light-success);
|
||||
text-align: center;
|
||||
border-radius: 0.3rem;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
box-shadow: 0 0 0.4rem 0.1rem var(--color-background-dark-success);
|
||||
}
|
||||
|
||||
.table-dark {
|
||||
font-weight: bold;
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background-color: var(--color-background-dark-success);
|
||||
text-align: center;
|
||||
border-radius: 0.3rem;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ export class QualificationMatchLineComponent {
|
|||
tournamentId = input<number>();
|
||||
teams = input<Map<number, TeamDto>>();
|
||||
viewOnly = input<boolean>(false);
|
||||
disableMatchEdit = input<boolean>(false);
|
||||
match = model<TournamentQualificationMatchDto>({});
|
||||
|
||||
onMatchUpdate = output<TournamentQualificationMatchDto>();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
[tournamentId]="tournamentId()"
|
||||
[teams]="teamDtos()"
|
||||
[viewOnly]="viewOnly()"
|
||||
[disableMatchEdit]="disableMatchEdit()"
|
||||
></app-qualification-match-line>
|
||||
}
|
||||
@if (qualificationMatches().length === 0 && showEmptyMessage()) {
|
||||
|
|
|
@ -29,6 +29,7 @@ export class QualificationMatchesComponent {
|
|||
tournamentId = input.required<number>();
|
||||
viewOnly = input<boolean>(false);
|
||||
teamDtos = input<Map<number, TeamDto>>(new Map());
|
||||
disableMatchEdit = input<boolean>(false);
|
||||
renderedRows = viewChildren<QueryList<ElementRef<HTMLTableRowElement>>>(
|
||||
'matchLineElement',
|
||||
// @ts-ignore - this is a bug in the typescript definitions
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
@if (nameFormControl.errors?.maxParticipantsReached) {
|
||||
<mat-error>No more spots left!</mat-error>
|
||||
}
|
||||
@if (nameFormControl.errors?.registrationClosed) {
|
||||
<mat-error> Registration is closed! </mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<button mat-raised-button type="submit">Sign up!</button>
|
||||
</form>
|
||||
|
|
|
@ -80,6 +80,9 @@ export class TeamSignupComponent {
|
|||
case TournamentSignupTeamResponseDto.SignupTeamResultEnum.MaxParticipantsReached:
|
||||
this.nameFormControl.setErrors({ maxParticipantsReached: true });
|
||||
return of();
|
||||
case TournamentSignupTeamResponseDto.SignupTeamResultEnum.RegistrationClosed:
|
||||
this.nameFormControl.setErrors({ registrationClosed: true });
|
||||
return of();
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -28,14 +28,13 @@
|
|||
</div>
|
||||
<div class="form-actions" mat-dialog-actions>
|
||||
<button
|
||||
mat-flat-button
|
||||
mat-button
|
||||
data-cy="cancel-btn"
|
||||
color="warn"
|
||||
aria-label="Cancel entering match results"
|
||||
mat-dialog-close
|
||||
>
|
||||
Cancel
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-flat-button
|
||||
|
@ -46,7 +45,6 @@
|
|||
type="submit"
|
||||
>
|
||||
Update match results
|
||||
<mat-icon>check</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -109,13 +109,12 @@
|
|||
<mat-card-actions>
|
||||
<a
|
||||
[disabled]="this.tournamentForm.disabled"
|
||||
mat-flat-button
|
||||
mat-button
|
||||
[routerLink]="'..'"
|
||||
color="warn"
|
||||
style="margin-right: var(--spacing2)"
|
||||
>
|
||||
Cancel
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</a>
|
||||
|
||||
<button
|
||||
|
@ -126,7 +125,6 @@
|
|||
id="edit-tournament"
|
||||
>
|
||||
Edit
|
||||
<mat-icon>check</mat-icon>
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</form>
|
||||
|
|
|
@ -7,16 +7,10 @@
|
|||
Public live page
|
||||
<mat-icon>share</mat-icon>
|
||||
</button>
|
||||
<div
|
||||
[matTooltip]="
|
||||
!qualificationMatchesFinished()
|
||||
? 'All qualification matches must be finished before starting the knockout phase.'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<div [matTooltip]="getStartKoPhaseButtonTooltip()">
|
||||
<button
|
||||
mat-flat-button
|
||||
[disabled]="!qualificationMatchesFinished()"
|
||||
[disabled]="!qualificationMatchesFinished() || koPhaseStarted()"
|
||||
(click)="startKoPhase()"
|
||||
color="primary"
|
||||
>
|
||||
|
@ -31,6 +25,7 @@
|
|||
[qualificationMatches]="qualificationMatches()"
|
||||
[tournamentId]="tournamentId()"
|
||||
[teamDtos]="teamDtos()"
|
||||
[disableMatchEdit]="koPhaseStarted()"
|
||||
(generateQualificationMatches)="generateQualificationMatches()"
|
||||
(onMatchUpdate)="onMatchUpdate($event)"
|
||||
(markTeamDrinksPickedUp)="markTeamDrinksPickedUp($event)"
|
||||
|
|
|
@ -75,6 +75,9 @@ export class TournamentQualificationPhaseComponent implements OnInit, OnDestroy
|
|||
this.qualificationMatches().length > 0
|
||||
);
|
||||
});
|
||||
koPhaseStarted = computed(() => {
|
||||
return this.tournament()?.allKoMatches !== undefined && this.tournament()!.allKoMatches > 0;
|
||||
});
|
||||
tournament = signal<TournamentOverviewDto | undefined>(undefined);
|
||||
|
||||
infoElement = viewChild<ElementRef>('qualifiedInfo');
|
||||
|
@ -343,4 +346,16 @@ export class TournamentQualificationPhaseComponent implements OnInit, OnDestroy
|
|||
title: 'Public link to qualification phase',
|
||||
});
|
||||
}
|
||||
|
||||
getStartKoPhaseButtonTooltip(): string {
|
||||
if (this.koPhaseStarted()) {
|
||||
return 'KO phase already started';
|
||||
}
|
||||
|
||||
if (!this.qualificationMatchesFinished()) {
|
||||
return 'Qualification matches not finished';
|
||||
}
|
||||
|
||||
return 'Start KO phase';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<button mat-flat-button class="delete-button" (click)="onDelete()">
|
||||
<mat-icon>delete</mat-icon> Delete your Account
|
||||
</button>
|
||||
<button mat-button class="delete-button" (click)="genTestData()">Generate Testdata</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { UpdateUserComponent } from '../update-user/update-user.component';
|
|||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { UserEndpointService, UserDetailDto } from '@api';
|
||||
import { ConfirmationService } from '../../services/confirmation.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-detail',
|
||||
|
@ -90,4 +91,18 @@ export class UserDetailComponent implements OnInit {
|
|||
duration: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
async genTestData() {
|
||||
await firstValueFrom(this.userService.generateTestData()).then(
|
||||
() => {
|
||||
this.snackBar.open('Successfully generated test data', 'Close', {
|
||||
duration: 5000,
|
||||
});
|
||||
},
|
||||
(error: HttpErrorResponse) => {
|
||||
console.error('Error generating test data', error);
|
||||
this.defaultServiceErrorHandling(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue