diff --git a/frontend/app/views/main/main-view.css b/frontend/app/views/main/main-view.css index b68d659..497b85d 100644 --- a/frontend/app/views/main/main-view.css +++ b/frontend/app/views/main/main-view.css @@ -29,6 +29,14 @@ margin-left: var(--lumo-space-xs); } +.no-margin-left { + margin-left: 0; +} + +a { + color: inherit; +} + /*//////////////////*/ /* APP-LAYOUT-STUFF */ /*//////////////////*/ diff --git a/frontend/app/views/matchday/matchday-view.css b/frontend/app/views/matchday/matchday-view.css index c21ddb3..6ab8d0d 100644 --- a/frontend/app/views/matchday/matchday-view.css +++ b/frontend/app/views/matchday/matchday-view.css @@ -1,4 +1,4 @@ -.matchday-view .column_header { +.matchday-view .column-header { font-weight: bold; font-size: large; } diff --git a/frontend/app/views/player/player-view.css b/frontend/app/views/player/player-view.css new file mode 100644 index 0000000..fc95733 --- /dev/null +++ b/frontend/app/views/player/player-view.css @@ -0,0 +1,10 @@ +.player-view .player-header-label-layout { + width: 100%; + justify-content: center; + font-size: x-large; +} + +.player-view .column-header { + font-weight: bold; + font-size: large; +} diff --git a/src/main/java/app/data/entity/Player.java b/src/main/java/app/data/entity/Player.java index 1efd67e..8ac7474 100644 --- a/src/main/java/app/data/entity/Player.java +++ b/src/main/java/app/data/entity/Player.java @@ -1,5 +1,6 @@ package app.data.entity; +import app.navigation.Navigable; import lombok.Getter; import lombok.Setter; @@ -12,7 +13,7 @@ import java.util.Collection; @Table(name = "player", schema = "public", catalog = "chessleague") -public class Player extends AbstractEntity { +public class Player extends AbstractEntity implements Navigable { @Basic @Column(name = "name", diff --git a/src/main/java/app/data/entity/Season.java b/src/main/java/app/data/entity/Season.java index 2b56cc0..cacb330 100644 --- a/src/main/java/app/data/entity/Season.java +++ b/src/main/java/app/data/entity/Season.java @@ -46,6 +46,7 @@ public class Season extends AbstractEntity implements Navigable { private Integer weekdayOfViewChange; @OneToMany(mappedBy = "season", + fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) private Collection matchdays; diff --git a/src/main/java/app/data/service/PlayerForTableProvider.java b/src/main/java/app/data/service/PlayerForTableProvider.java new file mode 100644 index 0000000..d398fea --- /dev/null +++ b/src/main/java/app/data/service/PlayerForTableProvider.java @@ -0,0 +1,191 @@ +package app.data.service; + +import app.data.bean.CalculatedMatch; +import app.data.bean.PlayerForTable; +import app.data.entity.Matchday; +import app.data.entity.Player; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class PlayerForTableProvider { + private final PlayerService playerService; + private final MatchdayService matchdayService; + + private final List matchdays = new ArrayList<>(); + private final List calculatedMatches = new ArrayList<>(); + private final List players; + + public PlayerForTableProvider(PlayerService playerService, MatchdayService matchdayService, Matchday matchday) { + this.playerService = playerService; + this.matchdayService = matchdayService; + this.players = playerService.getRepository().findAll(); + matchdays.addAll(matchdayService.getMatchdaysSorted(matchday.getSeason()).stream() + .filter(matchdayToFilter -> matchdayToFilter.getNumber() <= matchday.getNumber()) + .collect(Collectors.toList())); + + calculatedMatches.addAll(matchdays.stream() + .flatMap((Function>) matchdayToMap -> matchdayService.getCalculatedMatches(matchdayToMap).stream()) + .collect(Collectors.toList())); + } + + List getPlayersForTableSorted(boolean calcDiffToLastMatchday) { + List playerForTableList = calcSortedPlayerForTableList(); + int offset = 0; + PlayerForTable currentPlayer; + for (int i = 0; i < playerForTableList.size(); i++) { + currentPlayer = playerForTableList.get(i); + offset = getOffset(playerForTableList, offset, currentPlayer, i); + currentPlayer.setPlace(i + 1 - offset); + currentPlayer.setPlaceString(offset == 0 ? String.valueOf(i + 1) : ""); + } + if (calcDiffToLastMatchday) calcAndSetPlaceDiffToLastMatchday(playerForTableList); + return playerForTableList; + } + + private int getOffset(List playerForTableList, int offset, PlayerForTable currentPlayer, int i) { + PlayerForTable lastPlayer; + if (i > 0) { + lastPlayer = playerForTableList.get(i - 1); + // TODO: add direct comparison below + if (playersHaveCompletelyEqualScores(lastPlayer, currentPlayer)) + return offset + 1; + return 0; + } + return offset; + } + + private boolean playersHaveCompletelyEqualScores(PlayerForTable lastPlayer, PlayerForTable currentPlayer) { + return Objects.equals(currentPlayer.getMatchPoints(), lastPlayer.getMatchPoints()) + && Objects.equals(currentPlayer.getGamePointsForSelf(), lastPlayer.getGamePointsForSelf()) + && Objects.equals(currentPlayer.getGamePointsForOpponents(), lastPlayer.getGamePointsForOpponents()); + } + + private List calcSortedPlayerForTableList() { + return players.stream() + .map(this::getPlayerForTable) + .sorted(Comparator.comparingDouble(PlayerForTable::getGamePointsForSelf).reversed()) + .sorted(Comparator.comparingDouble(PlayerForTable::getGamePointDiff).reversed()) + .sorted(Comparator.comparingInt(PlayerForTable::getMatchPoints).reversed()) + .collect(Collectors.toList()); + } + + private void calcAndSetPlaceDiffToLastMatchday(List playerForTableList) { + if (matchdays.size() < 2) { + playerForTableList.forEach(playerForTable -> playerForTable.setPlaceDiffToLastMatchday(Integer.MAX_VALUE)); + return; + } + List listForLastMatchday = getListForLastMatchday(); + playerForTableList.forEach(playerForTable -> calcAndSetPlaceDiffToLastMatchday(playerForTable, listForLastMatchday)); + + } + + private void calcAndSetPlaceDiffToLastMatchday(PlayerForTable playerForTable, List listForLastMatchday) { + int placeLastMatchday = listForLastMatchday.stream() + .filter(playerForTableLastMatchday -> playerForTable.getPlayer().equals(playerForTableLastMatchday.getPlayer())) + .findFirst() + .map(PlayerForTable::getPlace) + .orElseThrow(); + playerForTable.setPlaceDiffToLastMatchday(placeLastMatchday - playerForTable.getPlace()); + } + + private List getListForLastMatchday() { + return new PlayerForTableProvider(playerService, matchdayService, matchdays.get(matchdays.size() - 2)).getPlayersForTableSorted(false); + } + + private PlayerForTable getPlayerForTable(Player player) { + List matchesAsPlayer1 = getMatchesAsPlayer1(player); + List matchesAsPlayer2 = getMatchesAsPlayer2(player); + + Map> map = getWinStateLists(matchesAsPlayer1, matchesAsPlayer2); + + int amountOfMatchesWon = map.get(WinState.WON).size(); + int amountOfMatchesDrawn = map.get(WinState.DRAWN).size(); + int amountOfMatchesLost = map.get(WinState.LOST).size(); + + int amountOfMatches = amountOfMatchesWon + amountOfMatchesDrawn + amountOfMatchesLost; + int matchPoints = amountOfMatchesWon * 2 + amountOfMatchesDrawn; + + double gamePointsForSelf = getGamePointsForSelf(matchesAsPlayer1, matchesAsPlayer2); + double gamePointsForOpponents = getGamePointsForOpponents(matchesAsPlayer1, matchesAsPlayer2); + double gamePointDiff = gamePointsForSelf - gamePointsForOpponents; + + return new PlayerForTable(player, + matchPoints, + amountOfMatches, + amountOfMatchesWon, + amountOfMatchesDrawn, + amountOfMatchesLost, + gamePointsForSelf, + gamePointsForOpponents, + gamePointDiff); + } + + private List getMatchesAsPlayer1(Player player) { + return calculatedMatches.stream() + .filter(match -> match.getPlayer1().equals(player)) + .collect(Collectors.toList()); + } + + private List getMatchesAsPlayer2(Player player) { + return calculatedMatches.stream() + .filter(match -> match.getPlayer2().equals(player)) + .collect(Collectors.toList()); + } + + private double getGamePointsForSelf(List matchesAsPlayer1, List matchesAsPlayer2) { + double gamePointsForSelf = 0; + gamePointsForSelf += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore1).sum(); + gamePointsForSelf += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore2).sum(); + return gamePointsForSelf; + } + + private double getGamePointsForOpponents(List matchesAsPlayer1, List matchesAsPlayer2) { + double gamePointsForOpponents = 0; + gamePointsForOpponents += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore2).sum(); + gamePointsForOpponents += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore1).sum(); + return gamePointsForOpponents; + } + + private Map> getWinStateLists(List matchesAsPlayer1, List matchesAsPlayer2) { + Map> map = new HashMap<>(); + map.put(WinState.WON, new ArrayList<>()); + map.put(WinState.DRAWN, new ArrayList<>()); + map.put(WinState.LOST, new ArrayList<>()); + + for (CalculatedMatch match : matchesAsPlayer1) { + if (match.getScore1() > match.getScore2()) { + map.get(WinState.WON).add(match); + continue; + } + if (match.getScore1() < match.getScore2()) { + map.get(WinState.LOST).add(match); + continue; + } + if (match.getScore1() > 0) { + map.get(WinState.DRAWN).add(match); + } + } + + for (CalculatedMatch match : matchesAsPlayer2) { + if (match.getScore2() > match.getScore1()) { + map.get(WinState.WON).add(match); + continue; + } + if (match.getScore2() < match.getScore1()) { + map.get(WinState.LOST).add(match); + continue; + } + if (match.getScore2() > 0) { + map.get(WinState.DRAWN).add(match); + } + } + return map; + } + + private enum WinState { + WON, DRAWN, LOST + } +} diff --git a/src/main/java/app/data/service/PlayerService.java b/src/main/java/app/data/service/PlayerService.java index 3a5de23..585bb67 100644 --- a/src/main/java/app/data/service/PlayerService.java +++ b/src/main/java/app/data/service/PlayerService.java @@ -2,20 +2,24 @@ package app.data.service; import app.data.bean.CalculatedMatch; import app.data.bean.PlayerForTable; +import app.data.entity.Match; import app.data.entity.Matchday; import app.data.entity.Player; +import app.data.entity.Season; import app.data.repository.PlayerRepository; +import app.navigation.NavigableService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.vaadin.artur.helpers.CrudService; import java.util.*; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @Service -public class PlayerService extends CrudService { +public class PlayerService extends CrudService implements NavigableService { private final PlayerRepository repository; @@ -34,7 +38,7 @@ public class PlayerService extends CrudService { } public List getPlayersForTable(Matchday matchday) { - return new PlayerForTableProvider(matchday).getPlayersForTableSorted(true); + return new PlayerForTableProvider(this, matchdayService, matchday).getPlayersForTableSorted(true); } public List getAllPlayersSorted() { @@ -43,178 +47,18 @@ public class PlayerService extends CrudService { .collect(Collectors.toList()); } - private class PlayerForTableProvider { - private final List matchdays = new ArrayList<>(); - private final List calculatedMatches = new ArrayList<>(); - private final List players = repository.findAll(); - - public PlayerForTableProvider(Matchday matchday) { - matchdays.addAll(matchdayService.getMatchdaysSorted(matchday.getSeason()).stream() - .filter(matchdayToFilter -> matchdayToFilter.getNumber() <= matchday.getNumber()) - .collect(Collectors.toList())); - - calculatedMatches.addAll(matchdays.stream() - .flatMap((Function>) matchdayToMap -> matchService.getCalculatedMatches(matchdayToMap).stream()) - .collect(Collectors.toList())); - } - - private List getPlayersForTableSorted(boolean calcDiffToLastMatchday) { - List playerForTableList = calcSortedPlayerForTableList(); - int offset = 0; - PlayerForTable lastPlayer; - PlayerForTable currentPlayer; - for (int i = 0; i < playerForTableList.size(); i++) { - currentPlayer = playerForTableList.get(i); - offset = getOffset(playerForTableList, offset, currentPlayer, i); - currentPlayer.setPlace(i + 1 - offset); - currentPlayer.setPlaceString(offset == 0 ? String.valueOf(i + 1) : ""); - } - if (calcDiffToLastMatchday) calcAndSetPlaceDiffToLastMatchday(playerForTableList); - return playerForTableList; - } - - private int getOffset(List playerForTableList, int offset, PlayerForTable currentPlayer, int i) { - PlayerForTable lastPlayer; - if (i > 0) { - lastPlayer = playerForTableList.get(i - 1); - // TODO: add direct comparison below - if (playersHaveCompletelyEqualScores(lastPlayer, currentPlayer)) - return offset + 1; - return 0; - } - return offset; - } - - private boolean playersHaveCompletelyEqualScores(PlayerForTable lastPlayer, PlayerForTable currentPlayer) { - return Objects.equals(currentPlayer.getMatchPoints(), lastPlayer.getMatchPoints()) - && Objects.equals(currentPlayer.getGamePointsForSelf(), lastPlayer.getGamePointsForSelf()) - && Objects.equals(currentPlayer.getGamePointsForOpponents(), lastPlayer.getGamePointsForOpponents()); - } - - private List calcSortedPlayerForTableList() { - return players.stream() - .map(this::getPlayerForTable) - .sorted(Comparator.comparingDouble(PlayerForTable::getGamePointsForSelf).reversed()) - .sorted(Comparator.comparingDouble(PlayerForTable::getGamePointDiff).reversed()) - .sorted(Comparator.comparingInt(PlayerForTable::getMatchPoints).reversed()) - .collect(Collectors.toList()); - } - - private void calcAndSetPlaceDiffToLastMatchday(List playerForTableList) { - if (matchdays.size() < 2) { - playerForTableList.forEach(playerForTable -> playerForTable.setPlaceDiffToLastMatchday(Integer.MAX_VALUE)); - return; - } - List listForLastMatchday = getListForLastMatchday(); - playerForTableList.forEach(playerForTable -> calcAndSetPlaceDiffToLastMatchday(playerForTable, listForLastMatchday)); - - } - - private void calcAndSetPlaceDiffToLastMatchday(PlayerForTable playerForTable, List listForLastMatchday) { - int placeLastMatchday = listForLastMatchday.stream() - .filter(playerForTableLastMatchday -> playerForTable.getPlayer().equals(playerForTableLastMatchday.getPlayer())) - .findFirst() - .map(PlayerForTable::getPlace) - .orElseThrow(); - playerForTable.setPlaceDiffToLastMatchday(placeLastMatchday - playerForTable.getPlace()); - } - - private List getListForLastMatchday() { - return new PlayerForTableProvider(matchdays.get(matchdays.size() - 2)).getPlayersForTableSorted(false); - } - - private PlayerForTable getPlayerForTable(Player player) { - List matchesAsPlayer1 = getMatchesAsPlayer1(player); - List matchesAsPlayer2 = getMatchesAsPlayer2(player); - - Map> map = getWinStateLists(matchesAsPlayer1, matchesAsPlayer2); - - int amountOfMatchesWon = map.get(WinState.WON).size(); - int amountOfMatchesDrawn = map.get(WinState.DRAWN).size(); - int amountOfMatchesLost = map.get(WinState.LOST).size(); - - int amountOfMatches = amountOfMatchesWon + amountOfMatchesDrawn + amountOfMatchesLost; - int matchPoints = amountOfMatchesWon * 2 + amountOfMatchesDrawn; - - double gamePointsForSelf = getGamePointsForSelf(matchesAsPlayer1, matchesAsPlayer2); - double gamePointsForOpponents = getGamePointsForOpponents(matchesAsPlayer1, matchesAsPlayer2); - double gamePointDiff = gamePointsForSelf - gamePointsForOpponents; - - return new PlayerForTable(player, - matchPoints, - amountOfMatches, - amountOfMatchesWon, - amountOfMatchesDrawn, - amountOfMatchesLost, - gamePointsForSelf, - gamePointsForOpponents, - gamePointDiff); - } - - private List getMatchesAsPlayer1(Player player) { - return calculatedMatches.stream() - .filter(match -> match.getPlayer1().equals(player)) - .collect(Collectors.toList()); - } - - private List getMatchesAsPlayer2(Player player) { - return calculatedMatches.stream() - .filter(match -> match.getPlayer2().equals(player)) - .collect(Collectors.toList()); - } - - private double getGamePointsForSelf(List matchesAsPlayer1, List matchesAsPlayer2) { - double gamePointsForSelf = 0; - gamePointsForSelf += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore1).sum(); - gamePointsForSelf += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore2).sum(); - return gamePointsForSelf; - } - - private double getGamePointsForOpponents(List matchesAsPlayer1, List matchesAsPlayer2) { - double gamePointsForOpponents = 0; - gamePointsForOpponents += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore2).sum(); - gamePointsForOpponents += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore1).sum(); - return gamePointsForOpponents; - } - - private Map> getWinStateLists(List matchesAsPlayer1, List matchesAsPlayer2) { - Map> map = new HashMap<>(); - map.put(WinState.WON, new ArrayList<>()); - map.put(WinState.DRAWN, new ArrayList<>()); - map.put(WinState.LOST, new ArrayList<>()); - - for (CalculatedMatch match : matchesAsPlayer1) { - if (match.getScore1() > match.getScore2()) { - map.get(WinState.WON).add(match); - continue; - } - if (match.getScore1() < match.getScore2()) { - map.get(WinState.LOST).add(match); - continue; - } - if (match.getScore1() > 0) { - map.get(WinState.DRAWN).add(match); - } - } - - for (CalculatedMatch match : matchesAsPlayer2) { - if (match.getScore2() > match.getScore1()) { - map.get(WinState.WON).add(match); - continue; - } - if (match.getScore2() < match.getScore1()) { - map.get(WinState.LOST).add(match); - continue; - } - if (match.getScore2() > 0) { - map.get(WinState.DRAWN).add(match); - } - } - return map; - } + @Override + public Class getNavigableClass() { + return Player.class; } - private enum WinState { - WON, DRAWN, LOST + public List getCalculatedMatchesSorted(Player player, Season season) { + return matchdayService.getMatchdaysSorted(season).stream() + .flatMap(matchday -> matchday.getMatches().stream()) + .distinct() + .filter(match -> match.getPlayer1().equals(player) || match.getPlayer2().equals(player)) + .map(matchService::getCalculatedMatch) + .collect(Collectors.toList()); } + } diff --git a/src/main/java/app/navigation/Navigation.java b/src/main/java/app/navigation/Navigation.java index aae24a1..6d1d54b 100644 --- a/src/main/java/app/navigation/Navigation.java +++ b/src/main/java/app/navigation/Navigation.java @@ -113,13 +113,16 @@ public abstract class Navigation implements HasUrlParameter { protected HasValue.ValueChangeListener, T>> selectValueChangeListener(Class clazz) { return event -> { - if (!isLastClass(clazz)) { - Class childClass = getChildClass(clazz); - fillChildrenSelectWithData(event.getValue(), childClass); - autoselect(childClass); - return; + if (event.getValue() != null) { + if (!isLastClass(clazz)) { + Class childClass = getChildClass(clazz); + fillChildrenSelectWithData(event.getValue(), childClass); + autoselect(childClass); + return; + } + doPostSelectionStuff(); } - doPostSelectionStuff(); + // TODO: add else? }; } diff --git a/src/main/java/app/navigation/AbstractNavigationHeader.java b/src/main/java/app/navigation/NavigationHeader.java similarity index 77% rename from src/main/java/app/navigation/AbstractNavigationHeader.java rename to src/main/java/app/navigation/NavigationHeader.java index 2dce644..04c1418 100644 --- a/src/main/java/app/navigation/AbstractNavigationHeader.java +++ b/src/main/java/app/navigation/NavigationHeader.java @@ -3,10 +3,10 @@ package app.navigation; import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -public abstract class AbstractNavigationHeader extends HorizontalLayout { +public abstract class NavigationHeader extends HorizontalLayout { protected final T navigation; - public AbstractNavigationHeader(T navigation) { + public NavigationHeader(T navigation) { this.navigation = navigation; defineLayout(); defineChildren(); diff --git a/src/main/java/app/navigation/NavigationService.java b/src/main/java/app/navigation/NavigationService.java index 4d270f9..be02026 100644 --- a/src/main/java/app/navigation/NavigationService.java +++ b/src/main/java/app/navigation/NavigationService.java @@ -3,5 +3,5 @@ package app.navigation; public interface NavigationService { T getNewNavigation(); - AbstractNavigationHeader getNewNavigationHeader(T navigation); + NavigationHeader getNewNavigationHeader(T navigation); } diff --git a/src/main/java/app/navigation/match/MatchNavigationService.java b/src/main/java/app/navigation/match/MatchNavigationService.java index 97fe213..a3c7859 100644 --- a/src/main/java/app/navigation/match/MatchNavigationService.java +++ b/src/main/java/app/navigation/match/MatchNavigationService.java @@ -3,7 +3,7 @@ package app.navigation.match; import app.data.service.MatchService; import app.data.service.MatchdayService; import app.data.service.SeasonService; -import app.navigation.AbstractNavigationHeader; +import app.navigation.NavigationHeader; import app.navigation.NavigationService; import app.navigation.match.components.MatchNavigationHeader; import org.springframework.beans.factory.annotation.Autowired; @@ -30,7 +30,7 @@ public class MatchNavigationService implements NavigationService getNewNavigationHeader(MatchNavigation navigation) { + public NavigationHeader getNewNavigationHeader(MatchNavigation navigation) { return new MatchNavigationHeader(navigation); } diff --git a/src/main/java/app/navigation/match/components/MatchNavigationHeader.java b/src/main/java/app/navigation/match/components/MatchNavigationHeader.java index 98cbe83..b9178f0 100644 --- a/src/main/java/app/navigation/match/components/MatchNavigationHeader.java +++ b/src/main/java/app/navigation/match/components/MatchNavigationHeader.java @@ -1,10 +1,10 @@ package app.navigation.match.components; -import app.navigation.AbstractNavigationHeader; +import app.navigation.NavigationHeader; import app.navigation.match.MatchNavigation; import com.vaadin.flow.component.html.Label; -public class MatchNavigationHeader extends AbstractNavigationHeader { +public class MatchNavigationHeader extends NavigationHeader { public MatchNavigationHeader(MatchNavigation navigation) { super(navigation); diff --git a/src/main/java/app/navigation/matchday/MatchdayNavigationService.java b/src/main/java/app/navigation/matchday/MatchdayNavigationService.java index 4d7085e..cbf6c77 100644 --- a/src/main/java/app/navigation/matchday/MatchdayNavigationService.java +++ b/src/main/java/app/navigation/matchday/MatchdayNavigationService.java @@ -2,7 +2,7 @@ package app.navigation.matchday; import app.data.service.MatchdayService; import app.data.service.SeasonService; -import app.navigation.AbstractNavigationHeader; +import app.navigation.NavigationHeader; import app.navigation.NavigationService; import app.navigation.matchday.components.MatchdayNavigationHeader; import org.springframework.beans.factory.annotation.Autowired; @@ -26,7 +26,7 @@ public class MatchdayNavigationService implements NavigationService getNewNavigationHeader(MatchdayNavigation navigation) { + public NavigationHeader getNewNavigationHeader(MatchdayNavigation navigation) { return new MatchdayNavigationHeader(navigation); } } diff --git a/src/main/java/app/navigation/matchday/components/MatchdayNavigationHeader.java b/src/main/java/app/navigation/matchday/components/MatchdayNavigationHeader.java index b628647..cb9a017 100644 --- a/src/main/java/app/navigation/matchday/components/MatchdayNavigationHeader.java +++ b/src/main/java/app/navigation/matchday/components/MatchdayNavigationHeader.java @@ -1,10 +1,10 @@ package app.navigation.matchday.components; -import app.navigation.AbstractNavigationHeader; +import app.navigation.NavigationHeader; import app.navigation.matchday.MatchdayNavigation; import com.vaadin.flow.component.html.Label; -public class MatchdayNavigationHeader extends AbstractNavigationHeader { +public class MatchdayNavigationHeader extends NavigationHeader { public MatchdayNavigationHeader(MatchdayNavigation navigation) { super(navigation); diff --git a/src/main/java/app/navigation/player/PlayerNavigation.java b/src/main/java/app/navigation/player/PlayerNavigation.java new file mode 100644 index 0000000..732a02f --- /dev/null +++ b/src/main/java/app/navigation/player/PlayerNavigation.java @@ -0,0 +1,84 @@ +package app.navigation.player; + +import app.data.entity.Player; +import app.data.entity.Season; +import app.data.service.PlayerService; +import app.data.service.SeasonService; +import app.navigation.Navigable; +import app.navigation.Navigation; +import com.vaadin.flow.component.select.Select; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +import java.util.List; +import java.util.Optional; + +public class PlayerNavigation extends Navigation { + + PlayerNavigation(@Autowired PlayerService playerService, + @Autowired SeasonService seasonService) { + super(playerService, seasonService); + } + + @Override + @SuppressWarnings("unchecked") + protected List getChildren(@Nullable PARENT parent, @NonNull Class childClass) { + if (childClass.equals(Player.class)) { + return (List) getPlayerService().getAllPlayersSorted(); + } + if (childClass.equals(Season.class)) { + return (List) getSeasonService().getAllSeasonsForPlayerSorted((Player) parent); + } + throw new UnsupportedOperationException(String.format("This method is not supported for childClass %s", childClass.getSimpleName())); + } + + @Override + @SuppressWarnings("unchecked") + protected T getDefaultValue(Class clazz) { + if(clazz.equals(Player.class)) { + assert !getPlayerList().isEmpty(); + return (T) getPlayerList().get(0); + } + if (clazz.equals(Season.class)) { + assert !getSeasonList().isEmpty(); + return (T) getSeasonList().get(getSeasonList().size() - 1); + } + throw new UnsupportedOperationException(String.format("This method is not supported for clazz %s", clazz.getSimpleName())); + } + public PlayerService getPlayerService() { + return (PlayerService) serviceMap.get(Player.class); + } + + public SeasonService getSeasonService() { + return (SeasonService) serviceMap.get(Season.class); + } + + @SuppressWarnings("unchecked") + public List getSeasonList() { + return (List) listMap.get(Season.class); + } + + @SuppressWarnings("unchecked") + public List getPlayerList() { + return (List) listMap.get(Player.class); + } + + @SuppressWarnings("unchecked") + public Select getSeasonSelect() { + return (Select) selectMap.get(Season.class); + } + + @SuppressWarnings("unchecked") + public Select getPlayerSelect() { + return (Select) selectMap.get(Player.class); + } + + public Optional getSelectedPlayer() { + return getPlayerSelect().getOptionalValue(); + } + + public Optional getSelectedSeason() { + return getSeasonSelect().getOptionalValue(); + } +} diff --git a/src/main/java/app/navigation/player/PlayerNavigationService.java b/src/main/java/app/navigation/player/PlayerNavigationService.java new file mode 100644 index 0000000..8774fb8 --- /dev/null +++ b/src/main/java/app/navigation/player/PlayerNavigationService.java @@ -0,0 +1,32 @@ +package app.navigation.player; + +import app.data.service.PlayerService; +import app.data.service.SeasonService; +import app.navigation.NavigationHeader; +import app.navigation.NavigationService; +import app.navigation.player.components.PlayerNavigationHeader; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class PlayerNavigationService implements NavigationService { + + private final PlayerService playerService; + private final SeasonService seasonService; + + public PlayerNavigationService(@Autowired PlayerService playerService, + @Autowired SeasonService seasonService) { + this.playerService = playerService; + this.seasonService = seasonService; + } + + @Override + public PlayerNavigation getNewNavigation() { + return new PlayerNavigation(playerService, seasonService); + } + + @Override + public NavigationHeader getNewNavigationHeader(PlayerNavigation navigation) { + return new PlayerNavigationHeader(navigation); + } +} diff --git a/src/main/java/app/navigation/player/components/PlayerNavigationHeader.java b/src/main/java/app/navigation/player/components/PlayerNavigationHeader.java new file mode 100644 index 0000000..993ea98 --- /dev/null +++ b/src/main/java/app/navigation/player/components/PlayerNavigationHeader.java @@ -0,0 +1,19 @@ +package app.navigation.player.components; + +import app.navigation.NavigationHeader; +import app.navigation.player.PlayerNavigation; +import com.vaadin.flow.component.html.Label; + +public class PlayerNavigationHeader extends NavigationHeader { + + public PlayerNavigationHeader(PlayerNavigation navigation) { + super(navigation); + } + + @Override + protected void defineChildren() { + removeAll(); + add(new Label("Player:"), navigation.getPlayerSelect()); + add(new Label("Season:"), navigation.getSeasonSelect()); + } +} diff --git a/src/main/java/app/navigation/player/components/button/NextPlayerButton.java b/src/main/java/app/navigation/player/components/button/NextPlayerButton.java new file mode 100644 index 0000000..bbeb926 --- /dev/null +++ b/src/main/java/app/navigation/player/components/button/NextPlayerButton.java @@ -0,0 +1,37 @@ +package app.navigation.player.components.button; + +import app.data.entity.Player; +import app.navigation.player.PlayerNavigation; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.icon.VaadinIcon; + +import java.util.List; +import java.util.Optional; + +public class NextPlayerButton extends Button { + + private final PlayerNavigation playerNavigation; + + public NextPlayerButton(PlayerNavigation playerNavigation) { + this.playerNavigation = playerNavigation; + + setIcon(VaadinIcon.ARROW_RIGHT.create()); + + playerNavigation.addRunnableToBeRunAfterSelection(this::configure); + } + + private void configure() { + Optional nextPlayer = getNextPlayer(); + setEnabled(nextPlayer.isPresent()); + addClickListener(event -> nextPlayer.ifPresent(player -> playerNavigation.getPlayerSelect().setValue(player))); + } + + private Optional getNextPlayer() { + int index = PlayerButtonUtils.getPlayerIndex(playerNavigation); + if (index >= 0) { + List playerList = playerNavigation.getPlayerList(); + return Optional.of(playerList.get(Math.floorMod(index + 1, playerList.size()))); + } + return Optional.empty(); + } +} diff --git a/src/main/java/app/navigation/player/components/button/PlayerButtonUtils.java b/src/main/java/app/navigation/player/components/button/PlayerButtonUtils.java new file mode 100644 index 0000000..1a5620e --- /dev/null +++ b/src/main/java/app/navigation/player/components/button/PlayerButtonUtils.java @@ -0,0 +1,14 @@ +package app.navigation.player.components.button; + +import app.navigation.player.PlayerNavigation; + +import java.util.concurrent.atomic.AtomicInteger; + +class PlayerButtonUtils { + + static int getPlayerIndex(PlayerNavigation playerNavigation) { + AtomicInteger index = new AtomicInteger(-1); + playerNavigation.getSelectedPlayer().ifPresent(player -> index.set(playerNavigation.getPlayerList().indexOf(player))); + return index.get(); + } +} diff --git a/src/main/java/app/navigation/player/components/button/PrevPlayerButton.java b/src/main/java/app/navigation/player/components/button/PrevPlayerButton.java new file mode 100644 index 0000000..4723279 --- /dev/null +++ b/src/main/java/app/navigation/player/components/button/PrevPlayerButton.java @@ -0,0 +1,37 @@ +package app.navigation.player.components.button; + +import app.data.entity.Player; +import app.navigation.player.PlayerNavigation; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.icon.VaadinIcon; + +import java.util.List; +import java.util.Optional; + +public class PrevPlayerButton extends Button { + + private final PlayerNavigation playerNavigation; + + public PrevPlayerButton(PlayerNavigation playerNavigation) { + this.playerNavigation = playerNavigation; + + setIcon(VaadinIcon.ARROW_LEFT.create()); + + playerNavigation.addRunnableToBeRunAfterSelection(this::configure); + } + + private void configure() { + Optional prevPlayer = getPrevPlayer(); + setEnabled(prevPlayer.isPresent()); + addClickListener(event -> prevPlayer.ifPresent(player -> playerNavigation.getPlayerSelect().setValue(player))); + } + + private Optional getPrevPlayer() { + int index = PlayerButtonUtils.getPlayerIndex(playerNavigation); + if (index >= 0) { + List playerList = playerNavigation.getPlayerList(); + return Optional.of(playerList.get(Math.floorMod(index - 1, playerList.size()))); + } + return Optional.empty(); + } +} diff --git a/src/main/java/app/utils/ComponentUtils.java b/src/main/java/app/utils/ComponentUtils.java index 5669b0d..4436644 100644 --- a/src/main/java/app/utils/ComponentUtils.java +++ b/src/main/java/app/utils/ComponentUtils.java @@ -1,15 +1,41 @@ package app.utils; import app.data.entity.Player; +import app.data.entity.Season; +import app.views.player.PlayerView; +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.html.Anchor; import com.vaadin.flow.component.html.Label; +import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.router.RouterLink; public class ComponentUtils { private ComponentUtils() { } - public static HorizontalLayout getPlayerLabel(Player player) { - return getFormattedLabelWithParentheses(player.getName(), player.getNickname()); + public static void configurePlayerLabel(HorizontalLayout layout, Player player, Season season) { + String param = String.format("%s/%s/", EntityStringUtils.getPlayerStringForURL(player), EntityStringUtils.getSeasonStringForURL(season)); + RouterLink playerLink = new RouterLink(player.getName(), PlayerView.class, param); + playerLink.addClassName("bold-label"); + + Anchor chessComLink = new Anchor(player.getPlayerInfo().getUrl(), player.getNickname()); + chessComLink.setTarget("_blank"); + chessComLink.addClassName("no-margin-left"); + + configureFormattedComponentsWithParentheses(layout, playerLink, chessComLink); + } + + public static HorizontalLayout getPlayerLabel(Player player, Season season, boolean center) { + HorizontalLayout layout = new HorizontalLayout(); + configurePlayerLabel(layout, player, season); + + if (center) { + layout.setWidthFull(); + layout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER); + } + + return layout; } public static HorizontalLayout getFormattedLabelWithParentheses(String mainText, String textInParentheses) { @@ -21,10 +47,21 @@ public class ComponentUtils { public static void configureFormattedLabelWithParentheses(HorizontalLayout horizontalLayout, String mainText, String textInParentheses) { Label mainLabel = new Label(mainText); mainLabel.addClassName("bold-label"); - Label parenthesesLabel = new Label(String.format("(%s)", textInParentheses)); - parenthesesLabel.addClassName("parentheses-label"); + + Label parentheseslabel = new Label(textInParentheses); + parentheseslabel.addClassName("no-margin-left"); + + configureFormattedComponentsWithParentheses(horizontalLayout, mainLabel, parentheseslabel); + } + + public static void configureFormattedComponentsWithParentheses(HorizontalLayout horizontalLayout, Component mainComponent, Component parenthesesComponent) { + + Label leftParenthesis = new Label("("); + leftParenthesis.addClassName("parentheses-label"); + Label rightParenthesis = new Label(")"); + rightParenthesis.addClassName("no-margin-left"); horizontalLayout.removeAll(); - horizontalLayout.add(mainLabel, parenthesesLabel); + horizontalLayout.add(mainComponent, leftParenthesis, parenthesesComponent, rightParenthesis); } } diff --git a/src/main/java/app/views/main/MainView.java b/src/main/java/app/views/main/MainView.java index 658b339..e60ce3e 100644 --- a/src/main/java/app/views/main/MainView.java +++ b/src/main/java/app/views/main/MainView.java @@ -1,6 +1,7 @@ package app.views.main; import app.views.matchday.MatchdayView; +import app.views.player.PlayerView; import app.views.table.TableView; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.ComponentUtil; @@ -101,6 +102,7 @@ public class MainView extends AppLayout { // createTab("Map", MapView.class), createTab("Table", TableView.class), createTab("Results", MatchdayView.class), + createTab("Players", PlayerView.class), // createTab("Match", MatchView.class) }; } diff --git a/src/main/java/app/views/match/components/MatchComponent.java b/src/main/java/app/views/match/components/MatchComponent.java index 2de3f17..9716195 100644 --- a/src/main/java/app/views/match/components/MatchComponent.java +++ b/src/main/java/app/views/match/components/MatchComponent.java @@ -3,6 +3,7 @@ package app.views.match.components; import app.data.bean.CalculatedMatch; import app.data.entity.Game; import app.data.entity.Match; +import app.data.entity.Season; import app.data.service.ChessComService; import app.data.service.MatchService; import app.gameimage.GameImageService; @@ -129,8 +130,9 @@ public class MatchComponent extends Div implements ContentConfigurable { } private void configureHeaderPlayersLayout() { - HorizontalLayout player1 = ComponentUtils.getPlayerLabel(calculatedMatch.getPlayer1()); - HorizontalLayout player2 = ComponentUtils.getPlayerLabel(calculatedMatch.getPlayer2()); + Season season = match.getMatchday().getSeason(); + HorizontalLayout player1 = ComponentUtils.getPlayerLabel(calculatedMatch.getPlayer1(), season, false); + HorizontalLayout player2 = ComponentUtils.getPlayerLabel(calculatedMatch.getPlayer2(), season, false); Label vs = new Label("vs."); headerPlayersLayout.removeAll(); diff --git a/src/main/java/app/views/match/components/utils/GameCardUtils.java b/src/main/java/app/views/match/components/utils/GameCardUtils.java index 1e2f70b..ff53035 100644 --- a/src/main/java/app/views/match/components/utils/GameCardUtils.java +++ b/src/main/java/app/views/match/components/utils/GameCardUtils.java @@ -44,7 +44,7 @@ public class GameCardUtils { div.addClassName("player"); div.addClassName(white ? "white-player" : "black-player"); - HorizontalLayout playerLabel = ComponentUtils.getPlayerLabel(player); + HorizontalLayout playerLabel = ComponentUtils.getPlayerLabel(player, game.getMatch().getMatchday().getSeason(), true); playerLabel.setWidthFull(); playerLabel.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER); diff --git a/src/main/java/app/views/matchday/components/MatchdayCard.java b/src/main/java/app/views/matchday/components/MatchdayCard.java index 623a105..0d7ae2d 100644 --- a/src/main/java/app/views/matchday/components/MatchdayCard.java +++ b/src/main/java/app/views/matchday/components/MatchdayCard.java @@ -2,13 +2,13 @@ package app.views.matchday.components; import app.data.bean.CalculatedMatch; import app.data.entity.Matchday; +import app.data.entity.Season; import app.navigation.matchday.MatchdayNavigation; import app.navigation.matchday.components.button.NextMatchdayButton; import app.navigation.matchday.components.button.PrevMatchdayButton; import app.utils.ComponentUtils; import app.utils.EntityStringUtils; import app.utils.StringUtils; -import app.utils.VaadinUtils; import app.views.navigation.interfaces.ContentConfigurable; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; @@ -37,7 +37,6 @@ public class MatchdayCard extends Div implements ContentConfigurable { private final HorizontalLayout headerLabelLayout = new HorizontalLayout(); private final Grid grid = new Grid<>(); - public MatchdayCard(MatchdayNavigation matchdayNavigation) { this.matchdayNavigation = matchdayNavigation; @@ -61,16 +60,15 @@ public class MatchdayCard extends Div implements ContentConfigurable { headerLabelLayout.addClassName("matchday-header-label-layout"); } - private void defineGrid() { Label headerPlayer1 = new Label("Player 1"); - headerPlayer1.addClassName("column_header"); + headerPlayer1.addClassName("column-header"); Label headerPlayer2 = new Label("Player 2"); - headerPlayer2.addClassName("column_header"); + headerPlayer2.addClassName("column-header"); Label headerResult = new Label("Result"); - headerResult.addClassName("column_header"); + headerResult.addClassName("column-header"); - grid.addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer1)) + grid.addComponentColumn(calculatedMatch -> ComponentUtils.getPlayerLabel(calculatedMatch.getPlayer1(), getSeason(calculatedMatch), true)) .setHeader(headerPlayer1) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("13em") @@ -81,7 +79,7 @@ public class MatchdayCard extends Div implements ContentConfigurable { .setTextAlign(ColumnTextAlign.CENTER) .setWidth("4em"); - grid.addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer2)) + grid.addComponentColumn(calculatedMatch -> ComponentUtils.getPlayerLabel(calculatedMatch.getPlayer2(), getSeason(calculatedMatch), true)) .setHeader(headerPlayer2) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("13em") @@ -103,6 +101,7 @@ public class MatchdayCard extends Div implements ContentConfigurable { GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES); } + @SuppressWarnings("DuplicatedCode") // TODO: get rid of duplicates private Button createButton(CalculatedMatch match) { Button button = new Button(); button.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE); @@ -122,6 +121,10 @@ public class MatchdayCard extends Div implements ContentConfigurable { return button; } + private Season getSeason(CalculatedMatch calculatedMatch) { + return calculatedMatch.getMatch().getMatchday().getSeason(); + } + private String getResultString(CalculatedMatch match) { return StringUtils.getResultString(":", match.getScore1(), match.getScore2()); } diff --git a/src/main/java/app/views/navigation/NavigationViewBase.java b/src/main/java/app/views/navigation/NavigationViewBase.java index 6c07c5a..4efecad 100644 --- a/src/main/java/app/views/navigation/NavigationViewBase.java +++ b/src/main/java/app/views/navigation/NavigationViewBase.java @@ -1,6 +1,6 @@ package app.views.navigation; -import app.navigation.AbstractNavigationHeader; +import app.navigation.NavigationHeader; import app.navigation.Navigation; import app.navigation.NavigationService; import com.vaadin.flow.component.orderedlayout.FlexComponent; @@ -13,7 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; public abstract class NavigationViewBase extends VerticalLayout implements HasUrlParameter { protected final Navigation navigation; - protected final AbstractNavigationHeader navigationHeader; + protected final NavigationHeader navigationHeader; @SuppressWarnings("unchecked") protected NavigationViewBase(@Autowired NavigationService navigationService, String route) { diff --git a/src/main/java/app/views/player/PlayerView.java b/src/main/java/app/views/player/PlayerView.java new file mode 100644 index 0000000..9333dc4 --- /dev/null +++ b/src/main/java/app/views/player/PlayerView.java @@ -0,0 +1,45 @@ +package app.views.player; + +import app.navigation.player.PlayerNavigation; +import app.navigation.player.PlayerNavigationService; +import app.views.main.MainView; +import app.views.navigation.NavigationViewBase; +import app.views.player.components.PlayerCard; +import com.vaadin.flow.component.dependency.CssImport; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import org.springframework.beans.factory.annotation.Autowired; + +@CssImport("app/views/player/player-view.css") +@Route(value = "player", layout = MainView.class) +@PageTitle("Schachliga DACH - Players") +public class PlayerView extends NavigationViewBase{ + + private PlayerCard playerCard; + + protected PlayerView(@Autowired PlayerNavigationService playerNavigationService) { + super(playerNavigationService, "player"); + + addClassName("player-view"); + + defineLayout(); + } + + //////////// + // LAYOUT // + //////////// + + private void defineLayout() { + playerCard = new PlayerCard((PlayerNavigation) navigation); + add(playerCard); + } + + ///////////// + // CONTENT // + ///////////// + + @Override + protected void configureContent() { + playerCard.configureContent(); + } +} diff --git a/src/main/java/app/views/player/components/PlayerCard.java b/src/main/java/app/views/player/components/PlayerCard.java new file mode 100644 index 0000000..26540b1 --- /dev/null +++ b/src/main/java/app/views/player/components/PlayerCard.java @@ -0,0 +1,142 @@ +package app.views.player.components; + +import app.data.bean.CalculatedMatch; +import app.data.entity.Player; +import app.data.entity.Season; +import app.navigation.player.PlayerNavigation; +import app.navigation.player.components.button.NextPlayerButton; +import app.navigation.player.components.button.PrevPlayerButton; +import app.utils.ComponentUtils; +import app.utils.EntityStringUtils; +import app.utils.StringUtils; +import app.views.navigation.interfaces.ContentConfigurable; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.grid.ColumnTextAlign; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.grid.GridVariant; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Label; +import com.vaadin.flow.component.icon.VaadinIcon; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; + +import java.util.NoSuchElementException; + +public class PlayerCard extends Div implements ContentConfigurable { + + private final PlayerNavigation playerNavigation; + + private final HorizontalLayout header = new HorizontalLayout(); + private final HorizontalLayout headerLabelLayout = new HorizontalLayout(); + private final Grid grid = new Grid<>(); + + private Player player; + + + public PlayerCard(PlayerNavigation playerNavigation) { + this.playerNavigation = playerNavigation; + + addClassName("card"); + add(new VerticalLayout(header, grid)); + + defineHeader(); + defineGrid(); + } + + private void defineHeader() { + header.add(new PrevPlayerButton(this.playerNavigation), headerLabelLayout, new NextPlayerButton(this.playerNavigation)); + header.setWidthFull(); + + headerLabelLayout.addClassName("player-header-label-layout"); + } + + private void defineGrid() { + Label headerMatchday = new Label("Matchday"); + headerMatchday.addClassName("column-header"); + Label headerOpponent = new Label("Opponent"); + headerOpponent.addClassName("column-header"); + Label headerResult = new Label("Result"); + headerResult.addClassName("column-header"); + + grid.addColumn(calculatedMatch -> calculatedMatch.getMatch().getMatchday().getNumber()) + .setHeader(headerMatchday) + .setTextAlign(ColumnTextAlign.CENTER) + .setWidth("7em"); + + grid.addComponentColumn(calculatedMatch -> ComponentUtils.getPlayerLabel(getOpponent(calculatedMatch), getSeason(calculatedMatch), true)) + .setHeader(headerOpponent) + .setTextAlign(ColumnTextAlign.CENTER) + .setWidth("13em"); + + grid.addColumn(this::getResultString) + .setHeader(headerResult) + .setTextAlign(ColumnTextAlign.CENTER) + .setWidth("6em"); + + grid.addComponentColumn(this::createButton) + .setTextAlign(ColumnTextAlign.CENTER) + .setWidth("4em"); + + grid.setWidth("31em"); // TODO: find a way to set this dynamically based on column widths + grid.setHeightByRows(true); + + grid.addThemeVariants(GridVariant.LUMO_NO_BORDER, + GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES); + } + + @SuppressWarnings("DuplicatedCode") // TODO; get rid of duplicates + private Button createButton(CalculatedMatch match) { + Button button = new Button(); + button.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE); + + String seasonParam = EntityStringUtils.getSeasonStringForURL(playerNavigation.getSelectedSeason().orElseThrow()); + String matchdayParam = EntityStringUtils.getMatchdayStringForURL(match.getMatch().getMatchday()); + String matchParam = EntityStringUtils.getMatchStringForURL(match.getMatch()); + + String targetWildcardParam = String.format("match/%s/%s/%s/", seasonParam, matchdayParam, matchParam); + + if (match.getScore1() == 0 && match.getScore2() == 0) { + button.setIcon(VaadinIcon.PENCIL.create()); + button.addClickListener(event -> UI.getCurrent().navigate(targetWildcardParam + "edit")); + return button; + } + + button.setIcon(VaadinIcon.EYE.create()); + button.addClickListener(event -> UI.getCurrent().navigate(targetWildcardParam)); + return button; + } + + private Player getOpponent(CalculatedMatch match) { + return match.getPlayer1().equals(player) ? match.getPlayer2() : match.getPlayer1(); + } + + private Season getSeason(CalculatedMatch calculatedMatch) { + return calculatedMatch.getMatch().getMatchday().getSeason(); + } + + private String getResultString(CalculatedMatch match) { + boolean isPlayer1 = match.getPlayer1().equals(player); + double ownScore = isPlayer1 ? match.getScore1() : match.getScore2(); + double opponentScore = isPlayer1 ? match.getScore2() : match.getScore1(); + return StringUtils.getResultString(":", ownScore, opponentScore); + } + + @Override + public void configureContent() { + try { + player = playerNavigation.getSelectedPlayer().orElseThrow(); + Season season = playerNavigation.getSelectedSeason().orElseThrow(); + configureHeaderLabels(player, season); + grid.setItems(playerNavigation.getPlayerService().getCalculatedMatchesSorted(player, season)); + } catch (NoSuchElementException e) { + removeAll(); + add(playerNavigation.getValidationLabel()); + } + } + + private void configureHeaderLabels(Player player, Season season) { + ComponentUtils.configurePlayerLabel(headerLabelLayout, player, season); + } +} diff --git a/src/main/java/app/views/table/components/TableCard.java b/src/main/java/app/views/table/components/TableCard.java index 36816f9..21fd705 100644 --- a/src/main/java/app/views/table/components/TableCard.java +++ b/src/main/java/app/views/table/components/TableCard.java @@ -11,6 +11,7 @@ import app.utils.EntityStringUtils; import app.utils.StringUtils; import app.utils.VaadinUtils; import app.views.navigation.interfaces.ContentConfigurable; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.grid.ColumnTextAlign; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; @@ -21,6 +22,7 @@ import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.function.ValueProvider; import org.springframework.beans.factory.annotation.Autowired; import java.util.NoSuchElementException; @@ -82,7 +84,7 @@ public class TableCard extends Div implements ContentConfigurable { .setTextAlign(ColumnTextAlign.START) .setWidth("5em"); - grid.addColumn(VaadinUtils.getPlayerRenderer(PlayerForTable::getPlayer)) + grid.addComponentColumn(playerForTable -> ComponentUtils.getPlayerLabel(playerForTable.getPlayer(), matchdayNavigation.getSelectedSeason().orElseThrow(), false)) .setHeader(headerPlayer) .setTextAlign(ColumnTextAlign.START) .setWidth("13em");