Browse Source

ChessCom-Functionality, restructured navigation

master
GAM 4 years ago
parent
commit
eeac74e5ec
17 changed files with 774 additions and 291 deletions
  1. +7
    -3
      pom.xml
  2. +15
    -0
      src/main/java/com/example/application/data/chesscom/ChessComArchive.java
  3. +15
    -0
      src/main/java/com/example/application/data/chesscom/ChessComArchiveList.java
  4. +134
    -0
      src/main/java/com/example/application/data/chesscom/ChessComGame.java
  5. +1
    -1
      src/main/java/com/example/application/data/entity/Season.java
  6. +81
    -0
      src/main/java/com/example/application/data/service/ChessComService.java
  7. +12
    -5
      src/main/java/com/example/application/data/service/MatchdayService.java
  8. +10
    -5
      src/main/java/com/example/application/data/service/SeasonService.java
  9. +48
    -0
      src/main/java/com/example/application/utils/ChessComUtils.java
  10. +41
    -0
      src/main/java/com/example/application/utils/HttpUtils.java
  11. +118
    -79
      src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigation.java
  12. +1
    -2
      src/main/java/com/example/application/views/main/MainView.java
  13. +9
    -2
      src/main/java/com/example/application/views/masterdetail/MasterDetailView.java
  14. +22
    -0
      src/main/java/com/example/application/views/results/MatchView.java
  15. +140
    -0
      src/main/java/com/example/application/views/results/MatchdayView.java
  16. +45
    -154
      src/main/java/com/example/application/views/results/ResultsView.java
  17. +75
    -40
      src/main/java/com/example/application/views/table/TableView.java

+ 7
- 3
pom.xml View File

@ -127,9 +127,9 @@
</dependency> </dependency>
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>org.hibernate</groupId>-->
<!-- <artifactId>hibernate-core</artifactId>-->
<!-- <version>5.4.29.Final</version>-->
<!-- <groupId>com.google.code.gson</groupId>-->
<!-- <artifactId>gson</artifactId>-->
<!-- <version>2.3.1</version>-->
<!-- </dependency>--> <!-- </dependency>-->
<dependency> <dependency>
@ -169,6 +169,10 @@
<version>3.8.1</version> <version>3.8.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>


+ 15
- 0
src/main/java/com/example/application/data/chesscom/ChessComArchive.java View File

@ -0,0 +1,15 @@
package com.example.application.data.chesscom;
import java.util.List;
public class ChessComArchive {
private List<ChessComGame> games;
public List<ChessComGame> getGames() {
return games;
}
public void setGames(List<ChessComGame> games) {
this.games = games;
}
}

+ 15
- 0
src/main/java/com/example/application/data/chesscom/ChessComArchiveList.java View File

@ -0,0 +1,15 @@
package com.example.application.data.chesscom;
import java.util.List;
public class ChessComArchiveList {
private List<String> archives;
public List<String> getArchives() {
return archives;
}
public void setArchives(List<String> archives) {
this.archives = archives;
}
}

+ 134
- 0
src/main/java/com/example/application/data/chesscom/ChessComGame.java View File

@ -0,0 +1,134 @@
package com.example.application.data.chesscom;
public class ChessComGame {
private String url;
private String pgn;
private String timeControl;
private long endTime;
private boolean rated;
private String fen;
private String timeClass;
private String rules;
private ChessComGamePlayer white;
private ChessComGamePlayer black;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPgn() {
return pgn;
}
public void setPgn(String pgn) {
this.pgn = pgn;
}
public String getTimeControl() {
return timeControl;
}
public void setTimeControl(String timeControl) {
this.timeControl = timeControl;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
public boolean isRated() {
return rated;
}
public void setRated(boolean rated) {
this.rated = rated;
}
public String getFen() {
return fen;
}
public void setFen(String fen) {
this.fen = fen;
}
public String getTimeClass() {
return timeClass;
}
public void setTimeClass(String timeClass) {
this.timeClass = timeClass;
}
public String getRules() {
return rules;
}
public void setRules(String rules) {
this.rules = rules;
}
public ChessComGamePlayer getWhite() {
return white;
}
public void setWhite(ChessComGamePlayer white) {
this.white = white;
}
public ChessComGamePlayer getBlack() {
return black;
}
public void setBlack(ChessComGamePlayer black) {
this.black = black;
}
public static class ChessComGamePlayer {
private int rating;
private String result;
private String id;
private String username;
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
}

+ 1
- 1
src/main/java/com/example/application/data/entity/Season.java View File

@ -69,7 +69,7 @@ public class Season {
public String toString() { public String toString() {
String s = yearStart.toString(); String s = yearStart.toString();
if (!yearEnd.equals(yearStart)) { if (!yearEnd.equals(yearStart)) {
s += "/" + yearEnd.toString();
s += "-" + yearEnd.toString();
} }
return s; return s;
} }


+ 81
- 0
src/main/java/com/example/application/data/service/ChessComService.java View File

@ -0,0 +1,81 @@
package com.example.application.data.service;
import com.example.application.data.chesscom.ChessComArchive;
import com.example.application.data.chesscom.ChessComArchiveList;
import com.example.application.data.chesscom.ChessComGame;
import com.example.application.data.entity.Game;
import com.example.application.data.entity.GameInfo;
import com.example.application.data.entity.Player;
import com.example.application.utils.ChessComUtils;
import com.example.application.utils.HttpUtils;
import com.google.gson.Gson;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class ChessComService {
// TODO: make everything nullsafe
private static final Gson gson = new Gson();
public ChessComService() {
}
@NonNull
public List<Game> getLatestGamesBetweenPlayers(@NonNull Player player1, @NonNull Player player2, int minimum_amount_of_games) {
String url = String.format("https://api.chess.com/pub/player/%s/games/archives", ChessComUtils.getUrlPart(player1));
List<String> archiveUrls = gson.fromJson(HttpUtils.getJson(url), ChessComArchiveList.class).getArchives();
archiveUrls.sort(Collections.reverseOrder());
if (archiveUrls.size() >= 2) {
archiveUrls = archiveUrls.subList(0, 2);
}
List<Game> list = new ArrayList<>();
for (String archiveUrl : archiveUrls) {
String unpreparedJson = HttpUtils.getJson(archiveUrl);
if (unpreparedJson == null) {
continue;
}
String preparedJson = ChessComUtils.getPreparedArchiveJson(unpreparedJson);
List<ChessComGame> games = gson.fromJson(preparedJson, ChessComArchive.class).getGames();
list.addAll(games.stream()
.sorted(Comparator.comparingLong(ChessComGame::getEndTime).reversed())
.filter(chessComGame -> ChessComUtils.isGameBetween(chessComGame, player1, player2))
.filter(ChessComUtils::hasValidTimeControl)
.map(chessComGame -> getGame(chessComGame, player1, player2))
.collect(Collectors.toList()));
if (list.size() >= minimum_amount_of_games) {
break;
}
}
return list;
} // TODO: find exactly two games of each time control
@NonNull
private Game getGame(@NonNull ChessComGame chessComGame, @NonNull Player player1, @NonNull Player player2) {
Game game = new Game();
GameInfo gameInfo = new GameInfo();
game.setPlayer1IsWhite(chessComGame.getWhite().getUsername().equals(player1.getNickname()));
game.setResult(ChessComUtils.getResult(chessComGame));
game.setGameInfo(gameInfo);
gameInfo.setUrl(chessComGame.getUrl());
gameInfo.setFormat(Integer.parseInt(chessComGame.getTimeControl())/60); // TODO: change gameinfo.format in database to reflect this!
gameInfo.setGame(game);
return game; // important: match is not set here!
}
}

+ 12
- 5
src/main/java/com/example/application/data/service/MatchdayService.java View File

@ -4,12 +4,12 @@ import com.example.application.data.entity.Matchday;
import com.example.application.data.entity.Season; import com.example.application.data.entity.Season;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
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.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -34,6 +34,14 @@ public class MatchdayService extends CrudService<Matchday, Integer> {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public Optional<Matchday> getFirstMatchdayForSeason(@NonNull Season season) {
List<Matchday> matchdays = getMatchdaysForSeasonSorted(season);
if (matchdays.isEmpty()) {
return Optional.empty();
}
return Optional.of(matchdays.get(0));
}
@NonNull @NonNull
public List<Matchday> getMatchdaysWithActivityForSeasonSorted(@NonNull Season season) { public List<Matchday> getMatchdaysWithActivityForSeasonSorted(@NonNull Season season) {
return repository.findAll().stream() return repository.findAll().stream()
@ -43,13 +51,12 @@ public class MatchdayService extends CrudService<Matchday, Integer> {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Nullable
public Matchday getLastMatchdayWithActivityForSeason(@NonNull Season season) {
public Optional<Matchday> getLastMatchdayWithActivityForSeason(@NonNull Season season) {
List<Matchday> matchdaysWithActivity = getMatchdaysWithActivityForSeasonSorted(season); List<Matchday> matchdaysWithActivity = getMatchdaysWithActivityForSeasonSorted(season);
if (matchdaysWithActivity.isEmpty()) { if (matchdaysWithActivity.isEmpty()) {
return null;
return Optional.empty();
} }
return matchdaysWithActivity.get(matchdaysWithActivity.size()-1);
return Optional.of(matchdaysWithActivity.get(matchdaysWithActivity.size()-1));
} }
public boolean hasActivity(@NonNull Matchday matchday) { public boolean hasActivity(@NonNull Matchday matchday) {


+ 10
- 5
src/main/java/com/example/application/data/service/SeasonService.java View File

@ -2,12 +2,12 @@ package com.example.application.data.service;
import com.example.application.data.entity.Season; import com.example.application.data.entity.Season;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
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.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -31,13 +31,18 @@ public class SeasonService extends CrudService<Season, Integer> {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Nullable
public Season getLatestSeason() {
public Optional<Season> getLatestSeason() {
List<Season> allSeasonsSorted = getAllSeasonsSorted(); List<Season> allSeasonsSorted = getAllSeasonsSorted();
if (allSeasonsSorted.isEmpty()) { if (allSeasonsSorted.isEmpty()) {
return null;
return Optional.empty();
} }
return allSeasonsSorted.get(allSeasonsSorted.size() - 1);
return Optional.of(allSeasonsSorted.get(allSeasonsSorted.size() - 1));
}
public Optional<Season> getSeason(String name) {
return repository.findAll().stream()
.filter(season -> season.toString().equals(name))
.findFirst();
} }
} }

+ 48
- 0
src/main/java/com/example/application/utils/ChessComUtils.java View File

@ -0,0 +1,48 @@
package com.example.application.utils;
import com.example.application.data.chesscom.ChessComGame;
import com.example.application.data.entity.Player;
import org.springframework.lang.NonNull;
public class ChessComUtils {
private ChessComUtils() {}
public static String getPreparedArchiveJson(String unpreparedArchiveJson) {
return unpreparedArchiveJson
.replace("@id", "id")
.replace("end_time", "endTime")
.replace("time_class", "timeClass")
.replace("time_control", "timeControl");
}
public static String getUrlPart(Player player) {
return player.getNickname().toLowerCase();
}
public static int getResult(ChessComGame chessComGame) {
if (chessComGame.getWhite().getResult().equals("win")) {
return 1;
}
if (chessComGame.getBlack().getResult().equals("win")) {
return -1;
}
return 0;
}
public static boolean isGameBetween(@NonNull ChessComGame game, @NonNull Player player1, @NonNull Player player2) {
String white = game.getWhite().getUsername();
String black = game.getBlack().getUsername();
String name1 = player1.getNickname();
String name2 = player2.getNickname();
return (white.equals(name1) || white.equals(name2)) && (black.equals(name1) || black.equals(name2));
}
public static boolean hasValidTimeControl(ChessComGame game) {
String timeControl = game.getTimeControl();
if (timeControl != null) {
return timeControl.equals("600") || timeControl.equals("300") || timeControl.equals("180");
}
return false;
}
}

+ 41
- 0
src/main/java/com/example/application/utils/HttpUtils.java View File

@ -0,0 +1,41 @@
package com.example.application.utils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
public class HttpUtils {
private HttpUtils() {
}
@Nullable
public static String getJson(@NonNull String url) {
try {
URL url_ = new URL(url);
HttpURLConnection conn = (HttpURLConnection) url_.openConnection();
conn.setRequestMethod("GET");
conn.connect();
int responsecode = conn.getResponseCode();
if (responsecode != 200) {
throw new RuntimeException("HttpResponseCode: " + responsecode);
}
StringBuilder jsonBuilder = new StringBuilder();
Scanner scanner = new Scanner(url_.openStream());
while (scanner.hasNext()) {
jsonBuilder.append(scanner.nextLine());
}
scanner.close();
return jsonBuilder.toString();
} catch (IOException e) {
return null;
}
}
}

src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigationView.java → src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigation.java View File

@ -4,69 +4,62 @@ import com.example.application.data.entity.Matchday;
import com.example.application.data.entity.Season; import com.example.application.data.entity.Season;
import com.example.application.data.service.MatchdayService; import com.example.application.data.service.MatchdayService;
import com.example.application.data.service.SeasonService; import com.example.application.data.service.SeasonService;
import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Label; import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.icon.Icon;
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.select.Select; import com.vaadin.flow.component.select.Select;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.WildcardParameter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
public abstract class SeasonAndMatchdayNavigationView extends Div implements HasUrlParameter<String> {
public class SeasonAndMatchdayNavigation {
// TODO: show dropdown menus also for invalid URLs (with content that fits the situation) // TODO: show dropdown menus also for invalid URLs (with content that fits the situation)
protected final SeasonService seasonService;
protected final MatchdayService matchdayService;
protected String seasonParam;
protected String matchdayParam;
private final SeasonService seasonService;
private final MatchdayService matchdayService;
private final Label invalidUrlLabel = new Label(); private final Label invalidUrlLabel = new Label();
protected final VerticalLayout outer = new VerticalLayout();
protected final HorizontalLayout selectionLayout = new HorizontalLayout();
protected final List<Season> seasonList = new ArrayList<>();
protected final Select<Season> seasonSelect = new Select<>();
private final HorizontalLayout selectionLayout = new HorizontalLayout();
protected final List<Matchday> matchdayList = new ArrayList<>();
protected final Select<Matchday> matchdaySelect = new Select<>();
protected Component contentLayout;
private final List<Season> seasonList = new ArrayList<>();
private final Select<Season> seasonSelect = new Select<>();
public SeasonAndMatchdayNavigationView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService) {
this.seasonService = seasonService;
this.matchdayService = matchdayService;
private final List<Matchday> matchdayList = new ArrayList<>();
private final Select<Matchday> matchdaySelect = new Select<>();
private final String route;
private final boolean onlyMatchdaysWithActivity;
private Runnable runnableToBeRunAfterSelection;
private final Button prevButton = new Button(new Icon(VaadinIcon.ARROW_LEFT));
private final Button nextButton = new Button(new Icon(VaadinIcon.ARROW_RIGHT));
addClassName("results-view");
private String seasonParam;
private String matchdayParam;
configureOuterLayout();
configureSelectionLayout();
configureContentLayout();
public SeasonAndMatchdayNavigation(String route,
@Autowired SeasonService seasonService,
@Autowired MatchdayService matchdayService) {
this(route, seasonService, matchdayService, false);
} }
protected abstract String route();
protected abstract void configureContentLayout();
protected abstract void configureContent();
protected abstract boolean showOnlyMatchdaysWithActivity();
public SeasonAndMatchdayNavigation(String route,
@Autowired SeasonService seasonService,
@Autowired MatchdayService matchdayService,
boolean onlyMatchdaysWithActivity) {
this.route = route;
this.seasonService = seasonService;
this.matchdayService = matchdayService;
this.onlyMatchdaysWithActivity = onlyMatchdaysWithActivity;
private void configureOuterLayout() {
add(outer);
outer.setAlignItems(FlexComponent.Alignment.CENTER);
configureSelectionLayout();
} }
private void configureSelectionLayout() { private void configureSelectionLayout() {
@ -83,19 +76,19 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has
} }
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Matchday>, Matchday>> matchdaySelectValueChangeListener() { private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Matchday>, Matchday>> matchdaySelectValueChangeListener() {
return matchdayChangeEvent -> getUI().ifPresent(ui -> {
return matchdayChangeEvent -> {
Season season = seasonSelect.getValue(); Season season = seasonSelect.getValue();
Matchday matchday = matchdayChangeEvent.getValue(); Matchday matchday = matchdayChangeEvent.getValue();
if (season != null && matchday != null) { if (season != null && matchday != null) {
String seasonParam = season.toString(); String seasonParam = season.toString();
String matchdayParam = matchday.toString(); String matchdayParam = matchday.toString();
navigate(ui, seasonParam, matchdayParam);
navigate(seasonParam, matchdayParam);
} }
});
};
} }
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Season>, Season>> seasonSelectValueChangeListener() { private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Season>, Season>> seasonSelectValueChangeListener() {
return seasonChangeEvent -> getUI().ifPresent(ui -> {
return seasonChangeEvent -> {
Season newSeason = seasonChangeEvent.getValue(); Season newSeason = seasonChangeEvent.getValue();
if (newSeason != null) { if (newSeason != null) {
String seasonParam = newSeason.toString(); String seasonParam = newSeason.toString();
@ -109,9 +102,9 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has
} }
matchdayParam = matchdayInNewSeason == null ? "1" : matchdayParam; matchdayParam = matchdayInNewSeason == null ? "1" : matchdayParam;
navigate(ui, seasonParam, matchdayParam);
navigate(seasonParam, matchdayParam);
} }
});
};
} }
private void fillSeasonSelectWithData() { private void fillSeasonSelectWithData() {
@ -122,73 +115,62 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has
private void fillMatchdaySelectWithData() { private void fillMatchdaySelectWithData() {
matchdayList.clear(); matchdayList.clear();
List<Matchday> matchdaysToAdd = showOnlyMatchdaysWithActivity() ?
List<Matchday> matchdaysToAdd = onlyMatchdaysWithActivity ?
matchdayService.getMatchdaysWithActivityForSeasonSorted(seasonSelect.getValue()) : matchdayService.getMatchdaysForSeasonSorted(seasonSelect.getValue()); matchdayService.getMatchdaysWithActivityForSeasonSorted(seasonSelect.getValue()) : matchdayService.getMatchdaysForSeasonSorted(seasonSelect.getValue());
matchdayList.addAll(matchdaysToAdd); matchdayList.addAll(matchdaysToAdd);
matchdaySelect.setItems(matchdayList); matchdaySelect.setItems(matchdayList);
} }
protected boolean isMatchDayParamValid(@NonNull String matchdayParam) {
private boolean isMatchDayParamValid(@NonNull String matchdayParam) {
return matchdayList.stream().anyMatch(matchday -> matchdayParam.equals(matchday.toString())); return matchdayList.stream().anyMatch(matchday -> matchdayParam.equals(matchday.toString()));
} }
protected void navigate(UI ui, String seasonParam, String matchdayParam) {
ui.navigate(String.format("%s/%s/%s/", route(), seasonParam, matchdayParam));
private void navigate(String seasonParam, String matchdayParam) {
UI.getCurrent().navigate(String.format("%s/%s/%s/", route, seasonParam, matchdayParam));
// setParameter(seasonParam, matchdayParam);
} }
@Override
public void setParameter(BeforeEvent beforeEvent, @WildcardParameter String param) {
outer.removeAll();
public void setParameter(String seasonParam, String matchdayParam) {
// TODO: handle corner case where first matchday has no active games yet: still show first matchday // TODO: handle corner case where first matchday has no active games yet: still show first matchday
if (param.equals("")) {
Season latestSeason = seasonService.getLatestSeason();
if (latestSeason != null) {
Matchday latestMatchday = matchdayService.getLastMatchdayWithActivityForSeason(latestSeason);
if (latestMatchday != null) {
String seasonParam = latestSeason.toString();
String matchdayParam = latestMatchday.toString();
navigate(UI.getCurrent(), seasonParam, matchdayParam);
setParameter(beforeEvent, String.format("%s/%s/", seasonParam, matchdayParam)); // TODO: check why this line seems to be necessary
if (seasonParam == null || seasonParam.equals("") || matchdayParam == null || matchdayParam.equals("")) {
Optional<Season> latestSeason = seasonService.getLatestSeason();
if (latestSeason.isPresent()) {
Optional<Matchday> latestMatchday = matchdayService.getLastMatchdayWithActivityForSeason(latestSeason.get());
if (latestMatchday.isPresent()) {
seasonParam = latestSeason.get().toString();
matchdayParam = latestMatchday.get().toString();
navigate(seasonParam, matchdayParam);
setParameter(seasonParam, matchdayParam);
} }
} }
return; return;
} }
if (!param.matches("^[0-9]*/[0-9]*/?$")) {
invalidUrlLabel.setText("Invalid URL! Please provide params in the form season/matchday/");
outer.add(invalidUrlLabel);
return;
}
String[] params = param.split("/");
seasonParam = params[0];
matchdayParam = params[1];
Season season = getSeasonFromParam(seasonParam); Season season = getSeasonFromParam(seasonParam);
if (season == null) { if (season == null) {
invalidUrlLabel.setText(String.format("Invalid URL! Season \"%s\" does not exist in the database!", seasonParam)); invalidUrlLabel.setText(String.format("Invalid URL! Season \"%s\" does not exist in the database!", seasonParam));
outer.add(invalidUrlLabel);
return; return;
} }
seasonSelect.setValue(season); seasonSelect.setValue(season);
this.seasonParam = seasonParam;
fillMatchdaySelectWithData(); fillMatchdaySelectWithData();
Matchday matchday = getMatchdayFromParam(matchdayParam); Matchday matchday = getMatchdayFromParam(matchdayParam);
if (matchday == null) { if (matchday == null) {
String messageExtra = showOnlyMatchdaysWithActivity() ? " or has no games played yet" : "";
String messageExtra = onlyMatchdaysWithActivity ? " or has no games played yet" : "";
invalidUrlLabel.setText(String.format("Invalid URL! Matchday \"%s\" in Season \"%s\" does not exist in the database%s!", matchdayParam, seasonParam, messageExtra)); invalidUrlLabel.setText(String.format("Invalid URL! Matchday \"%s\" in Season \"%s\" does not exist in the database%s!", matchdayParam, seasonParam, messageExtra));
outer.add(invalidUrlLabel);
return; return;
} }
matchdaySelect.setValue(matchday); matchdaySelect.setValue(matchday);
this.matchdayParam = matchdayParam;
configureButtons();
outer.add(selectionLayout);
outer.add(contentLayout);
configureContent();
if ((runnableToBeRunAfterSelection) != null) {
runnableToBeRunAfterSelection.run();
}
} }
@Nullable @Nullable
@ -216,4 +198,61 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has
} }
return null; return null;
} }
public void setRunnableToBeRunAfterSelection(Runnable runnableToBeRunAfterSelection) {
this.runnableToBeRunAfterSelection = runnableToBeRunAfterSelection;
}
private void configureButtons() {
prevButton.setEnabled(isMatchDayParamValid(getPrevMatchdayParam()));
prevButton.addClickListener(getButtonClickListener(getPrevMatchdayParam()));
nextButton.setEnabled(isMatchDayParamValid(getNextMatchdayParam()));
nextButton.addClickListener(getButtonClickListener(getNextMatchdayParam()));
}
private ComponentEventListener<ClickEvent<Button>> getButtonClickListener(String matchdayParam) {
return buttonClickEvent -> navigate(seasonParam, matchdayParam);
}
private String getPrevMatchdayParam() {
try {
return String.valueOf(Integer.parseInt(matchdayParam) - 1);
} catch (NumberFormatException e) {
return "";
}
}
private String getNextMatchdayParam() {
try {
return String.valueOf(Integer.parseInt(matchdayParam) + 1);
} catch (NumberFormatException e) {
return "";
}
}
public Optional<Matchday> getSelectedMatchday() {
return matchdaySelect.getOptionalValue();
}
public Optional<Season> getSelectedSeason() {
return seasonSelect.getOptionalValue();
}
public HorizontalLayout getSelectionLayout() {
return selectionLayout;
}
public Button getPrevButton() {
return prevButton;
}
public Button getNextButton() {
return nextButton;
}
public Label getInvalidUrlLabel() {
return invalidUrlLabel;
}
} }

+ 1
- 2
src/main/java/com/example/application/views/main/MainView.java View File

@ -6,7 +6,6 @@ import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentUtil; import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.applayout.DrawerToggle; import com.vaadin.flow.component.applayout.DrawerToggle;
import com.vaadin.flow.component.avatar.Avatar;
import com.vaadin.flow.component.dependency.CssImport; import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dependency.JsModule; import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.html.H1; import com.vaadin.flow.component.html.H1;
@ -37,8 +36,8 @@ import java.util.Optional;
@Theme(value = Lumo.class, variant = Lumo.DARK) @Theme(value = Lumo.class, variant = Lumo.DARK)
public class MainView extends AppLayout { public class MainView extends AppLayout {
// TODO: Add Localization // TODO: Add Localization
// TODO: View for adding match data
// TODO: View for each match // TODO: View for each match
// TODO: View for adding match data
// TODO: Handle database connection with environment variables // TODO: Handle database connection with environment variables
// TODO: make it look even more like chess.com (use darker colors etc.) // TODO: make it look even more like chess.com (use darker colors etc.)
// TODO: add light theme like on chess.com // TODO: add light theme like on chess.com


+ 9
- 2
src/main/java/com/example/application/views/masterdetail/MasterDetailView.java View File

@ -24,8 +24,9 @@ import com.vaadin.flow.data.renderer.TemplateRenderer;
import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.artur.helpers.CrudServiceDataProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional; import java.util.Optional;
@CssImport("./views/masterdetail/master-detail-view.css") @CssImport("./views/masterdetail/master-detail-view.css")
@ -73,7 +74,13 @@ public class MasterDetailView extends Div {
.withProperty("important", SamplePerson::isImportant); .withProperty("important", SamplePerson::isImportant);
grid.addColumn(importantRenderer).setHeader("Important").setAutoWidth(true); grid.addColumn(importantRenderer).setHeader("Important").setAutoWidth(true);
grid.setDataProvider(new CrudServiceDataProvider<>(samplePersonService));
List<SamplePerson> samplePeople = new ArrayList<>();
samplePeople.add(new SamplePerson());
samplePeople.add(new SamplePerson());
samplePeople.add(new SamplePerson());
grid.setItems(samplePeople);
// grid.setDataProvider(new CrudServiceDataProvider<>(samplePersonService));
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
grid.setHeightFull(); grid.setHeightFull();


+ 22
- 0
src/main/java/com/example/application/views/results/MatchView.java View File

@ -0,0 +1,22 @@
package com.example.application.views.results;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
public class MatchView extends Div implements HasUrlParameter<String[]> {
// private final ChessComService chessComService;
// private final PlayerService playerService;
// public ResultsViewRight(@Autowired ChessComService chessComService, @Autowired PlayerService playerService) {
public MatchView() {
// this.chessComService = chessComService;
// this.playerService = playerService;
}
@Override
public void setParameter(BeforeEvent beforeEvent, String[] SeasonParamAndMatchdayParamAndGameParam) {
}
}

+ 140
- 0
src/main/java/com/example/application/views/results/MatchdayView.java View File

@ -0,0 +1,140 @@
package com.example.application.views.results;
import com.example.application.data.bean.CalculatedMatch;
import com.example.application.data.entity.Matchday;
import com.example.application.data.service.MatchService;
import com.example.application.data.service.MatchdayService;
import com.example.application.data.service.SeasonService;
import com.example.application.utils.StringUtils;
import com.example.application.utils.VaadinUtils;
import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigation;
import com.vaadin.flow.component.button.Button;
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.Label;
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 com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.WildcardParameter;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Optional;
public class MatchdayView extends VerticalLayout implements HasUrlParameter<String> {
private final MatchService matchService;
private final SeasonAndMatchdayNavigation seasonAndMatchdayNavigation;
private final VerticalLayout matchdayLayout = new VerticalLayout();
private final Label matchdayHeader = new Label();
private final Grid<CalculatedMatch> grid = new Grid<>();
private final Button prevButton;
private final Button nextButton;
public MatchdayView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired MatchService matchService) {
this.seasonAndMatchdayNavigation = new SeasonAndMatchdayNavigation("results", seasonService, matchdayService);
this.seasonAndMatchdayNavigation.setRunnableToBeRunAfterSelection(this::configureContent);
this.matchService = matchService;
prevButton = seasonAndMatchdayNavigation.getPrevButton();
nextButton = seasonAndMatchdayNavigation.getNextButton();
configureContentLayout();
}
////////////
// LAYOUT //
////////////
protected void configureContentLayout() {
setWidthFull();
setHeightFull();
setAlignItems(FlexComponent.Alignment.CENTER);
add(seasonAndMatchdayNavigation.getSelectionLayout(), matchdayLayout);
// TODO add background color for content
matchdayHeader.addClassName("matchday_header"); // TODO: add dates
matchdayHeader.setWidth("100%");
HorizontalLayout matchdayHeaderLayout = new HorizontalLayout(prevButton, matchdayHeader, nextButton);
matchdayHeaderLayout.setWidthFull();
matchdayLayout.setPadding(false);
matchdayLayout.add(matchdayHeaderLayout, grid);
matchdayLayout.setAlignItems(FlexComponent.Alignment.CENTER);
matchdayLayout.setWidth("");
Label headerPlayer1 = new Label("Player 1");
headerPlayer1.addClassName("column_header");
Label headerPlayer2 = new Label("Player 2");
headerPlayer2.addClassName("column_header");
grid.addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer1))
.setHeader(headerPlayer1)
.setTextAlign(ColumnTextAlign.END)
.setWidth("13em")
.setFlexGrow(1);
grid.addColumn((ValueProvider<CalculatedMatch, String>) this::getResultString)
.setHeader("vs.")
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("6em");
grid.addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer2))
.setHeader(headerPlayer2)
.setTextAlign(ColumnTextAlign.START)
.setWidth("13em")
.setFlexGrow(1);
grid.setWidth("32em"); // 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);
}
private String getResultString(CalculatedMatch match) {
return StringUtils.getResultString(match.getScore1(), match.getScore2());
}
/////////////
// CONTENT //
/////////////
protected void configureContent() {
Optional<Matchday> selectedMatchday = seasonAndMatchdayNavigation.getSelectedMatchday();
selectedMatchday.ifPresent(matchday -> {
matchdayHeader.setText(String.format("Matchday %s", matchday.toString()));
grid.setItems(matchService.getCalculatedMatches(matchday));
});
}
@Override
public void setParameter(BeforeEvent event, @WildcardParameter String param) {
String[] params = param.split("/");
String seasonParam;
String matchdayParam;
switch (params.length) {
case 0:
seasonParam = "";
matchdayParam = "";
break;
case 1:
seasonParam = params[0];
matchdayParam = "";
break;
default:
seasonParam = params[0];
matchdayParam = params[1];
}
seasonAndMatchdayNavigation.setParameter(seasonParam, matchdayParam);
}
}

+ 45
- 154
src/main/java/com/example/application/views/results/ResultsView.java View File

@ -1,181 +1,72 @@
package com.example.application.views.results; package com.example.application.views.results;
import com.example.application.data.bean.CalculatedMatch;
import com.example.application.data.service.MatchService;
import com.example.application.data.service.MatchdayService;
import com.example.application.data.service.SeasonService;
import com.example.application.utils.StringUtils;
import com.example.application.utils.VaadinUtils;
import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigationView;
import com.example.application.data.service.*;
import com.example.application.views.main.MainView; import com.example.application.views.main.MainView;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.CssImport; import com.vaadin.flow.component.dependency.CssImport;
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.Label;
import com.vaadin.flow.component.icon.Icon;
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 com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.splitlayout.SplitLayout;
import com.vaadin.flow.router.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@CssImport("./views/results/results-view.css") @CssImport("./views/results/results-view.css")
@Route(value = "results", layout = MainView.class) @Route(value = "results", layout = MainView.class)
@PageTitle("Schachliga DACH - Results") @PageTitle("Schachliga DACH - Results")
public class ResultsView extends SeasonAndMatchdayNavigationView {
public class ResultsView extends Div implements HasUrlParameter<String> {
private final MatchService matchService;
private final SplitLayout splitLayout = new SplitLayout();
private VerticalLayout matchdayLayout;
private Label matchdayHeader;
private Grid<CalculatedMatch> grid;
private final MatchdayView matchdayView;
private final MatchView matchView;
private Button prevButton;
private Button nextButton;
public ResultsView(@Autowired SeasonService seasonService,
@Autowired MatchdayService matchdayService,
@Autowired MatchService matchService,
@Autowired ChessComService chessComService,
@Autowired PlayerService playerService) {
public ResultsView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired MatchService matchService) {
super(seasonService, matchdayService);
this.matchService = matchService;
addClassName("results-view");
}
@Override
protected String route() {
return "results";
}
@Override
protected boolean showOnlyMatchdaysWithActivity() {
return false;
}
////////////
// LAYOUT //
////////////
this.matchdayView = new MatchdayView(seasonService, matchdayService, matchService);
this.matchView = new MatchView();
private VerticalLayout getMatchdayLayout() {
if (matchdayLayout == null) {
matchdayLayout = new VerticalLayout();
}
return matchdayLayout;
}
private Label getMatchdayHeader() {
if (matchdayHeader == null) {
matchdayHeader = new Label();
}
return matchdayHeader;
}
private Grid<CalculatedMatch> getGrid() {
if (grid == null) {
grid = new Grid<>();
}
return grid;
}
addClassName("results-view");
private Button getPrevButton() {
if (prevButton == null) {
prevButton = new Button(new Icon(VaadinIcon.ARROW_LEFT));
}
return prevButton;
configureLayout();
// disableSplit();
enableSplit();
} }
private Button getNextButton() {
if (nextButton == null) {
nextButton = new Button(new Icon(VaadinIcon.ARROW_RIGHT));
}
return nextButton;
public void enableSplit() {
removeAll();
add(splitLayout);
} }
@Override
protected void configureContentLayout() {
contentLayout = getMatchdayLayout();
configureMatchdayLayout();
public void disableSplit() {
removeAll();
add(matchdayView);
} }
private void configureMatchdayLayout() {
// TODO add background color for content
getMatchdayHeader().addClassName("matchday_header"); // TODO: add dates
getMatchdayHeader().setWidth("100%");
HorizontalLayout matchdayHeaderLayout = new HorizontalLayout(getPrevButton(), getMatchdayHeader(), getNextButton());
matchdayHeaderLayout.setWidthFull();
getMatchdayLayout().setPadding(false);
getMatchdayLayout().add(matchdayHeaderLayout, getGrid());
getMatchdayLayout().setAlignItems(FlexComponent.Alignment.CENTER);
getMatchdayLayout().setWidth("");
Label headerPlayer1 = new Label("Player 1");
headerPlayer1.addClassName("column_header");
Label headerPlayer2 = new Label("Player 2");
headerPlayer2.addClassName("column_header");
private void configureLayout() {
setWidthFull();
setHeightFull();
getGrid().addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer1))
.setHeader(headerPlayer1)
.setTextAlign(ColumnTextAlign.END)
.setWidth("13em")
.setFlexGrow(1);
getGrid().addColumn((ValueProvider<CalculatedMatch, String>) this::getResultString)
.setHeader("vs.")
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("6em");
getGrid().addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer2))
.setHeader(headerPlayer2)
.setTextAlign(ColumnTextAlign.START)
.setWidth("13em")
.setFlexGrow(1);
getGrid().setWidth("32em"); // TODO: find a way to set this dynamically based on column widths
getGrid().setHeightByRows(true);
getGrid().addThemeVariants(GridVariant.LUMO_NO_BORDER,
GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES);
splitLayout.addToPrimary(matchdayView);
splitLayout.addToSecondary(matchView);
splitLayout.setWidthFull();
splitLayout.setHeightFull();
} }
private String getResultString(CalculatedMatch match) {
return StringUtils.getResultString(match.getScore1(), match.getScore2());
}
/////////////
// CONTENT //
/////////////
@Override @Override
protected void configureContent() {
getMatchdayHeader().setText(String.format("Matchday %s", matchdaySelect.getValue().toString()));
getGrid().setItems(matchService.getCalculatedMatches(matchdaySelect.getValue()));
configureButtons();
}
private void configureButtons() {
getPrevButton().setEnabled(isMatchDayParamValid(getPrevMatchdayParam()));
getPrevButton().addClickListener(getButtonClickListener(getPrevMatchdayParam()));
getNextButton().setEnabled(isMatchDayParamValid(getNextMatchdayParam()));
getNextButton().addClickListener(getButtonClickListener(getNextMatchdayParam()));
public void setParameter(BeforeEvent beforeEvent, @WildcardParameter String param) {
// String[] params = param.split("/");
// if (params.length < 2) {
// // TODO: handle this case!
// return;
// }
// String[] seasonParamAndMatchdayParam = Arrays.copyOfRange(params, 0, 2);
matchdayView.setParameter(beforeEvent, param);
// if (params.length < 3) {
// disableSplit();
// }
// matchView.setParameter(beforeEvent, params);
} }
private ComponentEventListener<ClickEvent<Button>> getButtonClickListener(String matchdayParam) {
return buttonClickEvent -> getUI().ifPresent(ui -> navigate(ui, seasonParam, matchdayParam));
}
private String getPrevMatchdayParam() {
return String.valueOf(Integer.parseInt(matchdayParam) - 1);
}
private String getNextMatchdayParam() {
return String.valueOf(Integer.parseInt(matchdayParam) + 1);
}
} }

+ 75
- 40
src/main/java/com/example/application/views/table/TableView.java View File

@ -1,69 +1,72 @@
package com.example.application.views.table; package com.example.application.views.table;
import com.example.application.data.bean.PlayerForTable; import com.example.application.data.bean.PlayerForTable;
import com.example.application.data.entity.Player;
import com.example.application.data.service.MatchdayService; import com.example.application.data.service.MatchdayService;
import com.example.application.data.service.PlayerService; import com.example.application.data.service.PlayerService;
import com.example.application.data.service.SeasonService; import com.example.application.data.service.SeasonService;
import com.example.application.utils.StringUtils; import com.example.application.utils.StringUtils;
import com.example.application.utils.VaadinUtils; import com.example.application.utils.VaadinUtils;
import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigationView;
import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigation;
import com.example.application.views.main.MainView; import com.example.application.views.main.MainView;
import com.vaadin.flow.component.dependency.CssImport; import com.vaadin.flow.component.dependency.CssImport;
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;
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.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.function.ValueProvider; import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouteAlias;
import com.vaadin.flow.router.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays;
import java.util.NoSuchElementException;
@CssImport("./views/table/table-view.css") @CssImport("./views/table/table-view.css")
@Route(value = "table", layout = MainView.class) @Route(value = "table", layout = MainView.class)
@RouteAlias(value = "", layout = MainView.class) @RouteAlias(value = "", layout = MainView.class)
@PageTitle("Schachliga DACH - Table") @PageTitle("Schachliga DACH - Table")
public class TableView extends SeasonAndMatchdayNavigationView {
public class TableView extends VerticalLayout implements HasUrlParameter<String> {
private final PlayerService playerService; private final PlayerService playerService;
private Grid<PlayerForTable> grid;
private final SeasonAndMatchdayNavigation seasonAndMatchdayNavigation;
private final Label invalidUrlLabel;
private final Grid<PlayerForTable> grid = new Grid<>();
public TableView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired PlayerService playerService) { public TableView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired PlayerService playerService) {
super(seasonService, matchdayService);
this.playerService = playerService; this.playerService = playerService;
addClassName("table-view");
}
@Override
protected String route() {
return "table";
}
this.seasonAndMatchdayNavigation = new SeasonAndMatchdayNavigation("table", seasonService, matchdayService, true);
this.seasonAndMatchdayNavigation.setRunnableToBeRunAfterSelection(this::configureContent);
this.invalidUrlLabel = seasonAndMatchdayNavigation.getInvalidUrlLabel();
@Override
protected boolean showOnlyMatchdaysWithActivity() {
return true;
addClassName("table-view");
configureLayout();
// seasonAndMatchdayNavigation.addRunnableToBeRunAfterSeasonParamChange(this::configureContent);
// seasonAndMatchdayNavigation.addRunnableToBeRunAfterMatchdayParamChange(this::configureContent);
} }
//////////// ////////////
// LAYOUT // // LAYOUT //
//////////// ////////////
private Grid<PlayerForTable> getGrid() {
if (grid == null) {
grid = new Grid<>();
}
return grid;
private void configureLayout() {
setWidthFull();
setHeightFull();
setAlignItems(FlexComponent.Alignment.CENTER);
add(seasonAndMatchdayNavigation.getSelectionLayout(), new HorizontalLayout(grid));
configureGrid();
} }
@Override
protected void configureContentLayout() {
protected void configureGrid() {
// TODO: add background color for content // TODO: add background color for content
// TODO: add diff to last matchday // TODO: add diff to last matchday
// TODO: bold matchpoints etc.
contentLayout = new HorizontalLayout(getGrid());
Label headerPlace = new Label("Place"); Label headerPlace = new Label("Place");
headerPlace.addClassName("important_table_column_header"); headerPlace.addClassName("important_table_column_header");
@ -86,55 +89,55 @@ public class TableView extends SeasonAndMatchdayNavigationView {
Label headerDiff = new Label("Diff"); Label headerDiff = new Label("Diff");
getGrid().addColumn(VaadinUtils.getBoldStringRenderer(PlayerForTable::getPlaceString))
grid.addColumn(VaadinUtils.getBoldStringRenderer(PlayerForTable::getPlaceString))
.setHeader(headerPlace) .setHeader(headerPlace)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("5em"); .setWidth("5em");
getGrid().addColumn(VaadinUtils.getPlayerRenderer(PlayerForTable::getPlayer))
grid.addColumn(VaadinUtils.getPlayerRenderer(PlayerForTable::getPlayer))
.setHeader(headerPlayer) .setHeader(headerPlayer)
.setTextAlign(ColumnTextAlign.START) .setTextAlign(ColumnTextAlign.START)
.setWidth("13em"); .setWidth("13em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatches)
grid.addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatches)
.setHeader(headerMatchesPlayed) .setHeader(headerMatchesPlayed)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("5em"); .setWidth("5em");
getGrid().addColumn(VaadinUtils.getBoldStringRenderer(player -> String.valueOf(player.getMatchPoints())))
grid.addColumn(VaadinUtils.getBoldStringRenderer(player -> String.valueOf(player.getMatchPoints())))
.setHeader(headerMatchPoints) .setHeader(headerMatchPoints)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("6em"); .setWidth("6em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesWon)
grid.addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesWon)
.setHeader(headerWon) .setHeader(headerWon)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em"); .setWidth("3em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesDrawn)
grid.addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesDrawn)
.setHeader(headerDrawn) .setHeader(headerDrawn)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em"); .setWidth("3em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesLost)
grid.addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesLost)
.setHeader(headerLost) .setHeader(headerLost)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em"); .setWidth("3em");
getGrid().addColumn((ValueProvider<PlayerForTable, String>) this::getResultString)
grid.addColumn((ValueProvider<PlayerForTable, String>) this::getResultString)
.setHeader(headerGames) .setHeader(headerGames)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("6em"); .setWidth("6em");
getGrid().addColumn((ValueProvider<PlayerForTable, String>) player -> StringUtils.getSignedString(player.getGamePointDiff()))
grid.addColumn((ValueProvider<PlayerForTable, String>) player -> StringUtils.getSignedString(player.getGamePointDiff()))
.setHeader(headerDiff) .setHeader(headerDiff)
.setTextAlign(ColumnTextAlign.CENTER) .setTextAlign(ColumnTextAlign.CENTER)
.setWidth("5em"); .setWidth("5em");
getGrid().setWidth("51em");
getGrid().setHeightByRows(true);
grid.setWidth("51em");
grid.setHeightByRows(true);
getGrid().addThemeVariants(GridVariant.LUMO_NO_BORDER,
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER,
GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES); GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES);
} }
@ -146,8 +149,40 @@ public class TableView extends SeasonAndMatchdayNavigationView {
// CONTENT // // CONTENT //
///////////// /////////////
@Override
protected void configureContent() { protected void configureContent() {
getGrid().setItems(playerService.getPlayersForTable(matchdaySelect.getValue()));
try {
grid.setItems(playerService.getPlayersForTable(seasonAndMatchdayNavigation.getSelectedMatchday().orElseThrow()));
} catch (NoSuchElementException e) {
invalidUrlLabel.setText("No season and/or matchday selected! Please select them above.");
}
}
@Override
public void setParameter(BeforeEvent event, @WildcardParameter String param) {
String[] params = param.split("/");
String seasonParam;
String matchdayParam;
switch (params.length) {
case 0:
seasonParam = "";
matchdayParam = "";
break;
case 1:
seasonParam = params[0];
matchdayParam = "";
break;
default:
seasonParam = params[0];
matchdayParam = params[1];
}
seasonAndMatchdayNavigation.setParameter(seasonParam, matchdayParam);
}
private String[] getSeasonParamAndMatchdayParam(String[] params) {
if (params.length < 2) {
String seasonParam = params.length == 1 ? params[0] : "";
params = new String[]{seasonParam, ""};
}
return Arrays.copyOfRange(params, 0, 2);
} }
} }

Loading…
Cancel
Save