Browse Source

Layout

master
GAM 4 years ago
parent
commit
c941c19411
29 changed files with 725 additions and 211 deletions
  1. +8
    -0
      frontend/app/views/main/main-view.css
  2. +1
    -1
      frontend/app/views/matchday/matchday-view.css
  3. +10
    -0
      frontend/app/views/player/player-view.css
  4. +2
    -1
      src/main/java/app/data/entity/Player.java
  5. +1
    -0
      src/main/java/app/data/entity/Season.java
  6. +191
    -0
      src/main/java/app/data/service/PlayerForTableProvider.java
  7. +17
    -173
      src/main/java/app/data/service/PlayerService.java
  8. +9
    -6
      src/main/java/app/navigation/Navigation.java
  9. +2
    -2
      src/main/java/app/navigation/NavigationHeader.java
  10. +1
    -1
      src/main/java/app/navigation/NavigationService.java
  11. +2
    -2
      src/main/java/app/navigation/match/MatchNavigationService.java
  12. +2
    -2
      src/main/java/app/navigation/match/components/MatchNavigationHeader.java
  13. +2
    -2
      src/main/java/app/navigation/matchday/MatchdayNavigationService.java
  14. +2
    -2
      src/main/java/app/navigation/matchday/components/MatchdayNavigationHeader.java
  15. +84
    -0
      src/main/java/app/navigation/player/PlayerNavigation.java
  16. +32
    -0
      src/main/java/app/navigation/player/PlayerNavigationService.java
  17. +19
    -0
      src/main/java/app/navigation/player/components/PlayerNavigationHeader.java
  18. +37
    -0
      src/main/java/app/navigation/player/components/button/NextPlayerButton.java
  19. +14
    -0
      src/main/java/app/navigation/player/components/button/PlayerButtonUtils.java
  20. +37
    -0
      src/main/java/app/navigation/player/components/button/PrevPlayerButton.java
  21. +42
    -5
      src/main/java/app/utils/ComponentUtils.java
  22. +2
    -0
      src/main/java/app/views/main/MainView.java
  23. +4
    -2
      src/main/java/app/views/match/components/MatchComponent.java
  24. +1
    -1
      src/main/java/app/views/match/components/utils/GameCardUtils.java
  25. +11
    -8
      src/main/java/app/views/matchday/components/MatchdayCard.java
  26. +2
    -2
      src/main/java/app/views/navigation/NavigationViewBase.java
  27. +45
    -0
      src/main/java/app/views/player/PlayerView.java
  28. +142
    -0
      src/main/java/app/views/player/components/PlayerCard.java
  29. +3
    -1
      src/main/java/app/views/table/components/TableCard.java

+ 8
- 0
frontend/app/views/main/main-view.css View File

@ -29,6 +29,14 @@
margin-left: var(--lumo-space-xs); margin-left: var(--lumo-space-xs);
} }
.no-margin-left {
margin-left: 0;
}
a {
color: inherit;
}
/*//////////////////*/ /*//////////////////*/
/* APP-LAYOUT-STUFF */ /* APP-LAYOUT-STUFF */
/*//////////////////*/ /*//////////////////*/


+ 1
- 1
frontend/app/views/matchday/matchday-view.css View File

@ -1,4 +1,4 @@
.matchday-view .column_header {
.matchday-view .column-header {
font-weight: bold; font-weight: bold;
font-size: large; font-size: large;
} }


+ 10
- 0
frontend/app/views/player/player-view.css View File

@ -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;
}

+ 2
- 1
src/main/java/app/data/entity/Player.java View File

@ -1,5 +1,6 @@
package app.data.entity; package app.data.entity;
import app.navigation.Navigable;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -12,7 +13,7 @@ import java.util.Collection;
@Table(name = "player", @Table(name = "player",
schema = "public", schema = "public",
catalog = "chessleague") catalog = "chessleague")
public class Player extends AbstractEntity {
public class Player extends AbstractEntity implements Navigable {
@Basic @Basic
@Column(name = "name", @Column(name = "name",


+ 1
- 0
src/main/java/app/data/entity/Season.java View File

@ -46,6 +46,7 @@ public class Season extends AbstractEntity implements Navigable {
private Integer weekdayOfViewChange; private Integer weekdayOfViewChange;
@OneToMany(mappedBy = "season", @OneToMany(mappedBy = "season",
fetch = FetchType.EAGER,
cascade = CascadeType.ALL, cascade = CascadeType.ALL,
orphanRemoval = true) orphanRemoval = true)
private Collection<Matchday> matchdays; private Collection<Matchday> matchdays;


+ 191
- 0
src/main/java/app/data/service/PlayerForTableProvider.java View File

@ -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<Matchday> matchdays = new ArrayList<>();
private final List<CalculatedMatch> calculatedMatches = new ArrayList<>();
private final List<Player> 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<Matchday, Stream<CalculatedMatch>>) matchdayToMap -> matchdayService.getCalculatedMatches(matchdayToMap).stream())
.collect(Collectors.toList()));
}
List<PlayerForTable> getPlayersForTableSorted(boolean calcDiffToLastMatchday) {
List<PlayerForTable> 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<PlayerForTable> 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<PlayerForTable> 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<PlayerForTable> playerForTableList) {
if (matchdays.size() < 2) {
playerForTableList.forEach(playerForTable -> playerForTable.setPlaceDiffToLastMatchday(Integer.MAX_VALUE));
return;
}
List<PlayerForTable> listForLastMatchday = getListForLastMatchday();
playerForTableList.forEach(playerForTable -> calcAndSetPlaceDiffToLastMatchday(playerForTable, listForLastMatchday));
}
private void calcAndSetPlaceDiffToLastMatchday(PlayerForTable playerForTable, List<PlayerForTable> listForLastMatchday) {
int placeLastMatchday = listForLastMatchday.stream()
.filter(playerForTableLastMatchday -> playerForTable.getPlayer().equals(playerForTableLastMatchday.getPlayer()))
.findFirst()
.map(PlayerForTable::getPlace)
.orElseThrow();
playerForTable.setPlaceDiffToLastMatchday(placeLastMatchday - playerForTable.getPlace());
}
private List<PlayerForTable> getListForLastMatchday() {
return new PlayerForTableProvider(playerService, matchdayService, matchdays.get(matchdays.size() - 2)).getPlayersForTableSorted(false);
}
private PlayerForTable getPlayerForTable(Player player) {
List<CalculatedMatch> matchesAsPlayer1 = getMatchesAsPlayer1(player);
List<CalculatedMatch> matchesAsPlayer2 = getMatchesAsPlayer2(player);
Map<WinState, List<CalculatedMatch>> 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<CalculatedMatch> getMatchesAsPlayer1(Player player) {
return calculatedMatches.stream()
.filter(match -> match.getPlayer1().equals(player))
.collect(Collectors.toList());
}
private List<CalculatedMatch> getMatchesAsPlayer2(Player player) {
return calculatedMatches.stream()
.filter(match -> match.getPlayer2().equals(player))
.collect(Collectors.toList());
}
private double getGamePointsForSelf(List<CalculatedMatch> matchesAsPlayer1, List<CalculatedMatch> matchesAsPlayer2) {
double gamePointsForSelf = 0;
gamePointsForSelf += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore1).sum();
gamePointsForSelf += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore2).sum();
return gamePointsForSelf;
}
private double getGamePointsForOpponents(List<CalculatedMatch> matchesAsPlayer1, List<CalculatedMatch> matchesAsPlayer2) {
double gamePointsForOpponents = 0;
gamePointsForOpponents += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore2).sum();
gamePointsForOpponents += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore1).sum();
return gamePointsForOpponents;
}
private Map<WinState, List<CalculatedMatch>> getWinStateLists(List<CalculatedMatch> matchesAsPlayer1, List<CalculatedMatch> matchesAsPlayer2) {
Map<WinState, List<CalculatedMatch>> 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
}
}

+ 17
- 173
src/main/java/app/data/service/PlayerService.java View File

@ -2,20 +2,24 @@ package app.data.service;
import app.data.bean.CalculatedMatch; import app.data.bean.CalculatedMatch;
import app.data.bean.PlayerForTable; import app.data.bean.PlayerForTable;
import app.data.entity.Match;
import app.data.entity.Matchday; import app.data.entity.Matchday;
import app.data.entity.Player; import app.data.entity.Player;
import app.data.entity.Season;
import app.data.repository.PlayerRepository; import app.data.repository.PlayerRepository;
import app.navigation.NavigableService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.vaadin.artur.helpers.CrudService; import org.vaadin.artur.helpers.CrudService;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@Service @Service
public class PlayerService extends CrudService<Player, Integer> {
public class PlayerService extends CrudService<Player, Integer> implements NavigableService<Player> {
private final PlayerRepository repository; private final PlayerRepository repository;
@ -34,7 +38,7 @@ public class PlayerService extends CrudService<Player, Integer> {
} }
public List<PlayerForTable> getPlayersForTable(Matchday matchday) { public List<PlayerForTable> getPlayersForTable(Matchday matchday) {
return new PlayerForTableProvider(matchday).getPlayersForTableSorted(true);
return new PlayerForTableProvider(this, matchdayService, matchday).getPlayersForTableSorted(true);
} }
public List<Player> getAllPlayersSorted() { public List<Player> getAllPlayersSorted() {
@ -43,178 +47,18 @@ public class PlayerService extends CrudService<Player, Integer> {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private class PlayerForTableProvider {
private final List<Matchday> matchdays = new ArrayList<>();
private final List<CalculatedMatch> calculatedMatches = new ArrayList<>();
private final List<Player> 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<Matchday, Stream<CalculatedMatch>>) matchdayToMap -> matchService.getCalculatedMatches(matchdayToMap).stream())
.collect(Collectors.toList()));
}
private List<PlayerForTable> getPlayersForTableSorted(boolean calcDiffToLastMatchday) {
List<PlayerForTable> 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<PlayerForTable> 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<PlayerForTable> 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<PlayerForTable> playerForTableList) {
if (matchdays.size() < 2) {
playerForTableList.forEach(playerForTable -> playerForTable.setPlaceDiffToLastMatchday(Integer.MAX_VALUE));
return;
}
List<PlayerForTable> listForLastMatchday = getListForLastMatchday();
playerForTableList.forEach(playerForTable -> calcAndSetPlaceDiffToLastMatchday(playerForTable, listForLastMatchday));
}
private void calcAndSetPlaceDiffToLastMatchday(PlayerForTable playerForTable, List<PlayerForTable> listForLastMatchday) {
int placeLastMatchday = listForLastMatchday.stream()
.filter(playerForTableLastMatchday -> playerForTable.getPlayer().equals(playerForTableLastMatchday.getPlayer()))
.findFirst()
.map(PlayerForTable::getPlace)
.orElseThrow();
playerForTable.setPlaceDiffToLastMatchday(placeLastMatchday - playerForTable.getPlace());
}
private List<PlayerForTable> getListForLastMatchday() {
return new PlayerForTableProvider(matchdays.get(matchdays.size() - 2)).getPlayersForTableSorted(false);
}
private PlayerForTable getPlayerForTable(Player player) {
List<CalculatedMatch> matchesAsPlayer1 = getMatchesAsPlayer1(player);
List<CalculatedMatch> matchesAsPlayer2 = getMatchesAsPlayer2(player);
Map<WinState, List<CalculatedMatch>> 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<CalculatedMatch> getMatchesAsPlayer1(Player player) {
return calculatedMatches.stream()
.filter(match -> match.getPlayer1().equals(player))
.collect(Collectors.toList());
}
private List<CalculatedMatch> getMatchesAsPlayer2(Player player) {
return calculatedMatches.stream()
.filter(match -> match.getPlayer2().equals(player))
.collect(Collectors.toList());
}
private double getGamePointsForSelf(List<CalculatedMatch> matchesAsPlayer1, List<CalculatedMatch> matchesAsPlayer2) {
double gamePointsForSelf = 0;
gamePointsForSelf += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore1).sum();
gamePointsForSelf += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore2).sum();
return gamePointsForSelf;
}
private double getGamePointsForOpponents(List<CalculatedMatch> matchesAsPlayer1, List<CalculatedMatch> matchesAsPlayer2) {
double gamePointsForOpponents = 0;
gamePointsForOpponents += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore2).sum();
gamePointsForOpponents += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore1).sum();
return gamePointsForOpponents;
}
private Map<WinState, List<CalculatedMatch>> getWinStateLists(List<CalculatedMatch> matchesAsPlayer1, List<CalculatedMatch> matchesAsPlayer2) {
Map<WinState, List<CalculatedMatch>> 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<Player> getNavigableClass() {
return Player.class;
} }
private enum WinState {
WON, DRAWN, LOST
public List<CalculatedMatch> 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());
} }
} }

+ 9
- 6
src/main/java/app/navigation/Navigation.java View File

@ -113,13 +113,16 @@ public abstract class Navigation implements HasUrlParameter<String> {
protected <T extends Navigable> HasValue.ValueChangeListener<AbstractField.ComponentValueChangeEvent<Select<T>, T>> selectValueChangeListener(Class<T> clazz) { protected <T extends Navigable> HasValue.ValueChangeListener<AbstractField.ComponentValueChangeEvent<Select<T>, T>> selectValueChangeListener(Class<T> clazz) {
return event -> { return event -> {
if (!isLastClass(clazz)) {
Class<? extends Navigable> childClass = getChildClass(clazz);
fillChildrenSelectWithData(event.getValue(), childClass);
autoselect(childClass);
return;
if (event.getValue() != null) {
if (!isLastClass(clazz)) {
Class<? extends Navigable> childClass = getChildClass(clazz);
fillChildrenSelectWithData(event.getValue(), childClass);
autoselect(childClass);
return;
}
doPostSelectionStuff();
} }
doPostSelectionStuff();
// TODO: add else?
}; };
} }


src/main/java/app/navigation/AbstractNavigationHeader.java → src/main/java/app/navigation/NavigationHeader.java View File

@ -3,10 +3,10 @@ package app.navigation;
import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
public abstract class AbstractNavigationHeader<T extends Navigation> extends HorizontalLayout {
public abstract class NavigationHeader<T extends Navigation> extends HorizontalLayout {
protected final T navigation; protected final T navigation;
public AbstractNavigationHeader(T navigation) {
public NavigationHeader(T navigation) {
this.navigation = navigation; this.navigation = navigation;
defineLayout(); defineLayout();
defineChildren(); defineChildren();

+ 1
- 1
src/main/java/app/navigation/NavigationService.java View File

@ -3,5 +3,5 @@ package app.navigation;
public interface NavigationService<T extends Navigation> { public interface NavigationService<T extends Navigation> {
T getNewNavigation(); T getNewNavigation();
AbstractNavigationHeader<T> getNewNavigationHeader(T navigation);
NavigationHeader<T> getNewNavigationHeader(T navigation);
} }

+ 2
- 2
src/main/java/app/navigation/match/MatchNavigationService.java View File

@ -3,7 +3,7 @@ package app.navigation.match;
import app.data.service.MatchService; import app.data.service.MatchService;
import app.data.service.MatchdayService; import app.data.service.MatchdayService;
import app.data.service.SeasonService; import app.data.service.SeasonService;
import app.navigation.AbstractNavigationHeader;
import app.navigation.NavigationHeader;
import app.navigation.NavigationService; import app.navigation.NavigationService;
import app.navigation.match.components.MatchNavigationHeader; import app.navigation.match.components.MatchNavigationHeader;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -30,7 +30,7 @@ public class MatchNavigationService implements NavigationService<MatchNavigation
} }
@Override @Override
public AbstractNavigationHeader<MatchNavigation> getNewNavigationHeader(MatchNavigation navigation) {
public NavigationHeader<MatchNavigation> getNewNavigationHeader(MatchNavigation navigation) {
return new MatchNavigationHeader(navigation); return new MatchNavigationHeader(navigation);
} }


+ 2
- 2
src/main/java/app/navigation/match/components/MatchNavigationHeader.java View File

@ -1,10 +1,10 @@
package app.navigation.match.components; package app.navigation.match.components;
import app.navigation.AbstractNavigationHeader;
import app.navigation.NavigationHeader;
import app.navigation.match.MatchNavigation; import app.navigation.match.MatchNavigation;
import com.vaadin.flow.component.html.Label; import com.vaadin.flow.component.html.Label;
public class MatchNavigationHeader extends AbstractNavigationHeader<MatchNavigation> {
public class MatchNavigationHeader extends NavigationHeader<MatchNavigation> {
public MatchNavigationHeader(MatchNavigation navigation) { public MatchNavigationHeader(MatchNavigation navigation) {
super(navigation); super(navigation);


+ 2
- 2
src/main/java/app/navigation/matchday/MatchdayNavigationService.java View File

@ -2,7 +2,7 @@ package app.navigation.matchday;
import app.data.service.MatchdayService; import app.data.service.MatchdayService;
import app.data.service.SeasonService; import app.data.service.SeasonService;
import app.navigation.AbstractNavigationHeader;
import app.navigation.NavigationHeader;
import app.navigation.NavigationService; import app.navigation.NavigationService;
import app.navigation.matchday.components.MatchdayNavigationHeader; import app.navigation.matchday.components.MatchdayNavigationHeader;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -26,7 +26,7 @@ public class MatchdayNavigationService implements NavigationService<MatchdayNavi
} }
@Override @Override
public AbstractNavigationHeader<MatchdayNavigation> getNewNavigationHeader(MatchdayNavigation navigation) {
public NavigationHeader<MatchdayNavigation> getNewNavigationHeader(MatchdayNavigation navigation) {
return new MatchdayNavigationHeader(navigation); return new MatchdayNavigationHeader(navigation);
} }
} }

+ 2
- 2
src/main/java/app/navigation/matchday/components/MatchdayNavigationHeader.java View File

@ -1,10 +1,10 @@
package app.navigation.matchday.components; package app.navigation.matchday.components;
import app.navigation.AbstractNavigationHeader;
import app.navigation.NavigationHeader;
import app.navigation.matchday.MatchdayNavigation; import app.navigation.matchday.MatchdayNavigation;
import com.vaadin.flow.component.html.Label; import com.vaadin.flow.component.html.Label;
public class MatchdayNavigationHeader extends AbstractNavigationHeader<MatchdayNavigation> {
public class MatchdayNavigationHeader extends NavigationHeader<MatchdayNavigation> {
public MatchdayNavigationHeader(MatchdayNavigation navigation) { public MatchdayNavigationHeader(MatchdayNavigation navigation) {
super(navigation); super(navigation);


+ 84
- 0
src/main/java/app/navigation/player/PlayerNavigation.java View File

@ -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 <PARENT extends Navigable, CHILD extends Navigable> List<CHILD> getChildren(@Nullable PARENT parent, @NonNull Class<CHILD> childClass) {
if (childClass.equals(Player.class)) {
return (List<CHILD>) getPlayerService().getAllPlayersSorted();
}
if (childClass.equals(Season.class)) {
return (List<CHILD>) getSeasonService().getAllSeasonsForPlayerSorted((Player) parent);
}
throw new UnsupportedOperationException(String.format("This method is not supported for childClass %s", childClass.getSimpleName()));
}
@Override
@SuppressWarnings("unchecked")
protected <T extends Navigable> T getDefaultValue(Class<T> 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<Season> getSeasonList() {
return (List<Season>) listMap.get(Season.class);
}
@SuppressWarnings("unchecked")
public List<Player> getPlayerList() {
return (List<Player>) listMap.get(Player.class);
}
@SuppressWarnings("unchecked")
public Select<Season> getSeasonSelect() {
return (Select<Season>) selectMap.get(Season.class);
}
@SuppressWarnings("unchecked")
public Select<Player> getPlayerSelect() {
return (Select<Player>) selectMap.get(Player.class);
}
public Optional<Player> getSelectedPlayer() {
return getPlayerSelect().getOptionalValue();
}
public Optional<Season> getSelectedSeason() {
return getSeasonSelect().getOptionalValue();
}
}

+ 32
- 0
src/main/java/app/navigation/player/PlayerNavigationService.java View File

@ -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<PlayerNavigation> {
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<PlayerNavigation> getNewNavigationHeader(PlayerNavigation navigation) {
return new PlayerNavigationHeader(navigation);
}
}

+ 19
- 0
src/main/java/app/navigation/player/components/PlayerNavigationHeader.java View File

@ -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<PlayerNavigation> {
public PlayerNavigationHeader(PlayerNavigation navigation) {
super(navigation);
}
@Override
protected void defineChildren() {
removeAll();
add(new Label("Player:"), navigation.getPlayerSelect());
add(new Label("Season:"), navigation.getSeasonSelect());
}
}

+ 37
- 0
src/main/java/app/navigation/player/components/button/NextPlayerButton.java View File

@ -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<Player> nextPlayer = getNextPlayer();
setEnabled(nextPlayer.isPresent());
addClickListener(event -> nextPlayer.ifPresent(player -> playerNavigation.getPlayerSelect().setValue(player)));
}
private Optional<Player> getNextPlayer() {
int index = PlayerButtonUtils.getPlayerIndex(playerNavigation);
if (index >= 0) {
List<Player> playerList = playerNavigation.getPlayerList();
return Optional.of(playerList.get(Math.floorMod(index + 1, playerList.size())));
}
return Optional.empty();
}
}

+ 14
- 0
src/main/java/app/navigation/player/components/button/PlayerButtonUtils.java View File

@ -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();
}
}

+ 37
- 0
src/main/java/app/navigation/player/components/button/PrevPlayerButton.java View File

@ -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<Player> prevPlayer = getPrevPlayer();
setEnabled(prevPlayer.isPresent());
addClickListener(event -> prevPlayer.ifPresent(player -> playerNavigation.getPlayerSelect().setValue(player)));
}
private Optional<Player> getPrevPlayer() {
int index = PlayerButtonUtils.getPlayerIndex(playerNavigation);
if (index >= 0) {
List<Player> playerList = playerNavigation.getPlayerList();
return Optional.of(playerList.get(Math.floorMod(index - 1, playerList.size())));
}
return Optional.empty();
}
}

+ 42
- 5
src/main/java/app/utils/ComponentUtils.java View File

@ -1,15 +1,41 @@
package app.utils; package app.utils;
import app.data.entity.Player; 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.html.Label;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.RouterLink;
public class ComponentUtils { public class ComponentUtils {
private 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) { 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) { public static void configureFormattedLabelWithParentheses(HorizontalLayout horizontalLayout, String mainText, String textInParentheses) {
Label mainLabel = new Label(mainText); Label mainLabel = new Label(mainText);
mainLabel.addClassName("bold-label"); 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.removeAll();
horizontalLayout.add(mainLabel, parenthesesLabel);
horizontalLayout.add(mainComponent, leftParenthesis, parenthesesComponent, rightParenthesis);
} }
} }

+ 2
- 0
src/main/java/app/views/main/MainView.java View File

@ -1,6 +1,7 @@
package app.views.main; package app.views.main;
import app.views.matchday.MatchdayView; import app.views.matchday.MatchdayView;
import app.views.player.PlayerView;
import app.views.table.TableView; import app.views.table.TableView;
import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentUtil; import com.vaadin.flow.component.ComponentUtil;
@ -101,6 +102,7 @@ public class MainView extends AppLayout {
// createTab("Map", MapView.class), // createTab("Map", MapView.class),
createTab("Table", TableView.class), createTab("Table", TableView.class),
createTab("Results", MatchdayView.class), createTab("Results", MatchdayView.class),
createTab("Players", PlayerView.class),
// createTab("Match", MatchView.class) // createTab("Match", MatchView.class)
}; };
} }


+ 4
- 2
src/main/java/app/views/match/components/MatchComponent.java View File

@ -3,6 +3,7 @@ package app.views.match.components;
import app.data.bean.CalculatedMatch; import app.data.bean.CalculatedMatch;
import app.data.entity.Game; import app.data.entity.Game;
import app.data.entity.Match; import app.data.entity.Match;
import app.data.entity.Season;
import app.data.service.ChessComService; import app.data.service.ChessComService;
import app.data.service.MatchService; import app.data.service.MatchService;
import app.gameimage.GameImageService; import app.gameimage.GameImageService;
@ -129,8 +130,9 @@ public class MatchComponent extends Div implements ContentConfigurable {
} }
private void configureHeaderPlayersLayout() { 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."); Label vs = new Label("vs.");
headerPlayersLayout.removeAll(); headerPlayersLayout.removeAll();


+ 1
- 1
src/main/java/app/views/match/components/utils/GameCardUtils.java View File

@ -44,7 +44,7 @@ public class GameCardUtils {
div.addClassName("player"); div.addClassName("player");
div.addClassName(white ? "white-player" : "black-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.setWidthFull();
playerLabel.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER); playerLabel.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);


+ 11
- 8
src/main/java/app/views/matchday/components/MatchdayCard.java View File

@ -2,13 +2,13 @@ package app.views.matchday.components;
import app.data.bean.CalculatedMatch; import app.data.bean.CalculatedMatch;
import app.data.entity.Matchday; import app.data.entity.Matchday;
import app.data.entity.Season;
import app.navigation.matchday.MatchdayNavigation; import app.navigation.matchday.MatchdayNavigation;
import app.navigation.matchday.components.button.NextMatchdayButton; import app.navigation.matchday.components.button.NextMatchdayButton;
import app.navigation.matchday.components.button.PrevMatchdayButton; import app.navigation.matchday.components.button.PrevMatchdayButton;
import app.utils.ComponentUtils; import app.utils.ComponentUtils;
import app.utils.EntityStringUtils; import app.utils.EntityStringUtils;
import app.utils.StringUtils; import app.utils.StringUtils;
import app.utils.VaadinUtils;
import app.views.navigation.interfaces.ContentConfigurable; import app.views.navigation.interfaces.ContentConfigurable;
import com.vaadin.flow.component.UI; import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button; 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 HorizontalLayout headerLabelLayout = new HorizontalLayout();
private final Grid<CalculatedMatch> grid = new Grid<>(); private final Grid<CalculatedMatch> grid = new Grid<>();
public MatchdayCard(MatchdayNavigation matchdayNavigation) { public MatchdayCard(MatchdayNavigation matchdayNavigation) {
this.matchdayNavigation = matchdayNavigation; this.matchdayNavigation = matchdayNavigation;
@ -61,16 +60,15 @@ public class MatchdayCard extends Div implements ContentConfigurable {
headerLabelLayout.addClassName("matchday-header-label-layout"); headerLabelLayout.addClassName("matchday-header-label-layout");
} }
private void defineGrid() { private void defineGrid() {
Label headerPlayer1 = new Label("Player 1"); Label headerPlayer1 = new Label("Player 1");
headerPlayer1.addClassName("column_header");
headerPlayer1.addClassName("column-header");
Label headerPlayer2 = new Label("Player 2"); Label headerPlayer2 = new Label("Player 2");
headerPlayer2.addClassName("column_header");
headerPlayer2.addClassName("column-header");
Label headerResult = new Label("Result"); 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) .setHeader(headerPlayer1)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("13em") .setWidth("13em")
@ -81,7 +79,7 @@ public class MatchdayCard extends Div implements ContentConfigurable {
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("4em"); .setWidth("4em");
grid.addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer2))
grid.addComponentColumn(calculatedMatch -> ComponentUtils.getPlayerLabel(calculatedMatch.getPlayer2(), getSeason(calculatedMatch), true))
.setHeader(headerPlayer2) .setHeader(headerPlayer2)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("13em") .setWidth("13em")
@ -103,6 +101,7 @@ public class MatchdayCard extends Div implements ContentConfigurable {
GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES); GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES);
} }
@SuppressWarnings("DuplicatedCode") // TODO: get rid of duplicates
private Button createButton(CalculatedMatch match) { private Button createButton(CalculatedMatch match) {
Button button = new Button(); Button button = new Button();
button.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE); button.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
@ -122,6 +121,10 @@ public class MatchdayCard extends Div implements ContentConfigurable {
return button; return button;
} }
private Season getSeason(CalculatedMatch calculatedMatch) {
return calculatedMatch.getMatch().getMatchday().getSeason();
}
private String getResultString(CalculatedMatch match) { private String getResultString(CalculatedMatch match) {
return StringUtils.getResultString(":", match.getScore1(), match.getScore2()); return StringUtils.getResultString(":", match.getScore1(), match.getScore2());
} }


+ 2
- 2
src/main/java/app/views/navigation/NavigationViewBase.java View File

@ -1,6 +1,6 @@
package app.views.navigation; package app.views.navigation;
import app.navigation.AbstractNavigationHeader;
import app.navigation.NavigationHeader;
import app.navigation.Navigation; import app.navigation.Navigation;
import app.navigation.NavigationService; import app.navigation.NavigationService;
import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.FlexComponent;
@ -13,7 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
public abstract class NavigationViewBase<T extends Navigation> extends VerticalLayout implements HasUrlParameter<String> { public abstract class NavigationViewBase<T extends Navigation> extends VerticalLayout implements HasUrlParameter<String> {
protected final Navigation navigation; protected final Navigation navigation;
protected final AbstractNavigationHeader<T> navigationHeader;
protected final NavigationHeader<T> navigationHeader;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected NavigationViewBase(@Autowired NavigationService<T> navigationService, String route) { protected NavigationViewBase(@Autowired NavigationService<T> navigationService, String route) {


+ 45
- 0
src/main/java/app/views/player/PlayerView.java View File

@ -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<PlayerNavigation>{
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();
}
}

+ 142
- 0
src/main/java/app/views/player/components/PlayerCard.java View File

@ -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<CalculatedMatch> 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);
}
}

+ 3
- 1
src/main/java/app/views/table/components/TableCard.java View File

@ -11,6 +11,7 @@ import app.utils.EntityStringUtils;
import app.utils.StringUtils; import app.utils.StringUtils;
import app.utils.VaadinUtils; import app.utils.VaadinUtils;
import app.views.navigation.interfaces.ContentConfigurable; 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.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridVariant; 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.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.function.ValueProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@ -82,7 +84,7 @@ public class TableCard extends Div implements ContentConfigurable {
.setTextAlign(ColumnTextAlign.START) .setTextAlign(ColumnTextAlign.START)
.setWidth("5em"); .setWidth("5em");
grid.addColumn(VaadinUtils.getPlayerRenderer(PlayerForTable::getPlayer))
grid.addComponentColumn(playerForTable -> ComponentUtils.getPlayerLabel(playerForTable.getPlayer(), matchdayNavigation.getSelectedSeason().orElseThrow(), false))
.setHeader(headerPlayer) .setHeader(headerPlayer)
.setTextAlign(ColumnTextAlign.START) .setTextAlign(ColumnTextAlign.START)
.setWidth("13em"); .setWidth("13em");


Loading…
Cancel
Save