Skip to content

Commit

Permalink
Add user nav menu, user home and api key requests. Enhanced player ra…
Browse files Browse the repository at this point in the history
…nking
  • Loading branch information
Pelayori committed Mar 9, 2024
1 parent 8045b00 commit b0756ff
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/uniovi/configuration/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests((authorize) ->
authorize
.requestMatchers("/css/**", "/img/**", "/script/**").permitAll()
.requestMatchers("/home").authenticated()
.requestMatchers("/home/**").authenticated()
.requestMatchers("/signup/**").permitAll()
.requestMatchers("/api/**").permitAll()
.requestMatchers("/game/**").authenticated()
Expand Down
25 changes: 24 additions & 1 deletion src/main/java/com/uniovi/controllers/HomeController.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package com.uniovi.controllers;

import com.uniovi.entities.ApiKey;
import com.uniovi.entities.Player;
import com.uniovi.services.ApiKeyService;
import com.uniovi.services.PlayerService;
import com.uniovi.services.QuestionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.Random;

@Controller
public class HomeController{
private final PlayerService playerService;
private final ApiKeyService apiKeyService;

@Autowired
public HomeController(PlayerService playerService) {
public HomeController(PlayerService playerService, ApiKeyService apiKeyService) {
this.playerService = playerService;
this.apiKeyService = apiKeyService;
}

@GetMapping("/")
Expand All @@ -27,4 +34,20 @@ public String home(){
public String apiHome() {
return "api/apiHome";
}

@GetMapping("/home/apikey")
public String apiKeyHome(Authentication auth, Model model) {
Player player = playerService.getUserByUsername(auth.getName()).get();
model.addAttribute("apiKey", player.getApiKey());
return "player/apiKeyHome";
}

@GetMapping("/home/apikey/create")
public String createApiKey(Authentication auth) {
Player player = playerService.getUserByUsername(auth.getName()).get();
if (player.getApiKey() == null) {
apiKeyService.createApiKey(player);
}
return "redirect:/home/apikey";
}
}
14 changes: 13 additions & 1 deletion src/main/java/com/uniovi/entities/GameSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

Expand Down Expand Up @@ -59,7 +60,6 @@ public void addQuestion(boolean correct, int timeLeft) {
correctQuestions++;
totalQuestions++;

// TODO: Calculate score
if (correct) {
score += timeLeft + 10 /* magic number TBD */;
}
Expand Down Expand Up @@ -109,4 +109,16 @@ public boolean hasQuestionId(Long idQuestion) {
}
return false;
}

public String getDuration() {
if (createdAt != null && finishTime != null) {
Duration duration = Duration.between(createdAt, finishTime);
long hours = duration.toHours();
long minutes = duration.toMinutes() % 60;
long seconds = duration.getSeconds() % 60;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
} else {
return "00:00:00";
}
}
}
11 changes: 9 additions & 2 deletions src/main/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ navbar.login=Inicia sesión
# Buttons for authenticated users
navbar.profile=Perfil
navbar.logout=Cerrar sesión
navbar.profile.apikey=Clave de la API

# -------------------Statements for the footer.html file---------------------
footer.copyright=© ASW - Grupo 04 B
Expand All @@ -39,7 +40,10 @@ index.button=JUGAR
home.heading=Bienvenido
home.private_zone=Esta es una zona privada de la web
home.authenticated_as=Usuario autenticado como:

home.apikey.title=Clave de la API
home.apikey.description=Esta es tu clave de la API. Utilízala para acceder a los recursos de la API de WIQ.
home.apikey.missing=No tienes una clave de la API. Obtenla pulsando en este botón.
home.apikey.create=Obtener clave

# -------------------Statements for the login.html file---------------------
login.username.label=Usuario:
Expand Down Expand Up @@ -67,7 +71,10 @@ ranking.title=Ranking
ranking.position=Posición
ranking.score=Puntuación
ranking.date=Fecha
ranking.player=Player
ranking.player=Jugador
ranking.time=Tiempo
ranking.question.right=Respuestas correctas
ranking.question.wrong=Respuestas incorrectas

# -------------------Statements for the apiHome.html file---------------------
api.doc.title=Documentación de la API
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ navbar.login=Log In
# Buttons for authenticated users
navbar.profile=Profile
navbar.logout=Log Out
navbar.profile.apikey=API Key

# -------------------Statements for the footer.html file---------------------
footer.copyright=© ASW - Group 04 B
Expand All @@ -39,6 +40,10 @@ index.button=PLAY
home.heading=Welcome
home.private_zone=This is a private zone of the website
home.authenticated_as=Authenticated User as:
home.apikey.title=API Key
home.apikey.description=This is your api key. You can use it to access the WIQ's REST API.
home.apikey.missing=You don't have an API key yet. You can generate one by clicking the button below.
home.apikey.create=Obtain API Key

# -------------------Statements for the login.html file---------------------
login.username.label=Username:
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/messages_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ navbar.toSpanish=Español
# Buttons for non-authenticated users
navbar.signup=Regístrate
navbar.login=Inicia sesión
navbar.profile.apikey=Clave de la API

# Buttons for authenticated users
navbar.profile=Perfil
Expand All @@ -40,6 +41,10 @@ index.button=JUGAR
home.heading=Bienvenido
home.private_zone=Esta es una zona privada de la web
home.authenticated_as=Usuario autenticado como:
home.apikey.title=Clave de la API
home.apikey.description=Esta es tu clave de la API. Utilízala para acceder a los recursos de la API de WIQ.
home.apikey.missing=No tienes una clave de la API. Obtenla pulsando en este botón.
home.apikey.create=Obtener clave

# -------------------Statements for the login.html file---------------------
login.username.label=Usuario:
Expand Down
9 changes: 9 additions & 0 deletions src/main/resources/static/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#apiKeyDiv {
display: flex;
flex-direction: column;
margin: 0 5% 5% 5%;
}

#apiKeyDiv form {
margin: 5% 20% 0 20%;
}
2 changes: 1 addition & 1 deletion src/main/resources/static/script/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ function normalPool(o){var r=0;do{var a=Math.round(normal({mean:o.mean,dev:o.dev

const NUM_PARTICLES = 350;
const PARTICLE_SIZE = 4; // View heights
const SPEED = 20000; // Milliseconds
const SPEED = 40000; // Milliseconds

let particles = [];

Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/static/script/show_hide_password.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
$(document).ready(function() {
$(".show_hide_password a").on('click', function(event) {
event.preventDefault();
if($('.show_hide_password input').attr("type") == "text"){
$('.show_hide_password input').attr('type', 'password');
$('.show_hide_password i').addClass( "fa-eye-slash" );
$('.show_hide_password i').removeClass( "fa-eye" );
}else if($('.show_hide_password input').attr("type") == "password"){
$('.show_hide_password input').attr('type', 'text');
$('.show_hide_password i').removeClass( "fa-eye-slash" );
$('.show_hide_password i').addClass( "fa-eye" );
}
});
});
16 changes: 12 additions & 4 deletions src/main/resources/templates/fragments/nav.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,19 @@
</li>

<!-- Botones para cuando el usuario ESTÁ autenticado -->
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/home" sec:authentication="principal.username">
<i class='fas fa-user' style='font-size:16px'></i>
<span th:text="#{navbar.profile}"></span>
<li class="nav-item dropdown" sec:authorize="isAuthenticated()">
<a class="nav-link dropdown-toggle" href="#" sec:authentication="principal.username"
id="btnUser" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
</a>
<div id="userZoneDropdownMenuButton" class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" id="btnUserProfile" href="/home">
<span th:text="#{navbar.profile}"></span>
</a>
<a class="dropdown-item" id="btnUserApiKey" href="/home/apikey">
<span th:text="#{navbar.profile.apikey}"></span>
</a>
</div>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/logout">
Expand Down
32 changes: 32 additions & 0 deletions src/main/resources/templates/player/apiKeyHome.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
<head th:replace="~{fragments/head}"></head>
<body>
<nav th:replace="~{fragments/nav}"></nav>

<canvas th:replace="~{fragments/background}"></canvas>

<div class="container" style="text-align: center">
<h2 th:text="#{home.apikey.title}"></h2>
<div th:if="${apiKey != null}" class="container mx-auto" id="apiKeyDiv">
<p th:text="#{home.apikey.description}"></p>
<form>
<div class="form-group">
<div class="input-group show_hide_password">
<input class="form-control text-center" type="password" th:value="${apiKey.getKeyToken()}" readonly>
<div class="input-group-text">
<a href=""><i class="fa fa-eye-slash mx-auto" aria-hidden="true"></i></a>
</div>
</div>
</div>
</form>
</div>
<div th:unless="${apiKey != null}">
<p th:text="#{home.apikey.missing}"></p>
<a type="submit" class="btn btn-primary" th:text="#{home.apikey.create}" href="/home/apikey/create"></a>
</div>
</div>
<footer th:replace="~{fragments/footer}"></footer>
<script th:inline="javascript" th:src="@{/script/show_hide_password.js}"></script>
</body>
</html>
9 changes: 9 additions & 0 deletions src/main/resources/templates/player/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ <h3 th:text="#{home.private_zone}"></h3>
<span th:text="#{home.authenticated_as}"></span>
<b th:inline="text" sec:authentication="principal.username"></b>
</p>
<div class="row text-center">
<div class="col-6">
<a class="btn btn-primary" href="/ranking/playerRanking" th:text="#{navbar.ranking.player}"></a>
</div>
<div class="col-6">
<a class="btn btn-primary" href="/ranking/globalRanking" th:text="#{navbar.ranking.global}"></a>
</div>
</div>
</div>

<footer th:replace="~{fragments/footer}"></footer>
</body>
</html>
6 changes: 6 additions & 0 deletions src/main/resources/templates/ranking/playerRanking_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
<tr>
<th scope="col" th:text="#{ranking.position}">Posición</th>
<th scope="col" th:text="#{ranking.score}">Puntuación</th>
<th scope="col" th:text="#{ranking.time}">Tiempo empleado</th>
<th scope="col" th:text="#{ranking.question.right}">Preguntas correctas</th>
<th scope="col" th:text="#{ranking.question.wrong}">Preguntas incorrectas</th>
<th scope="col" th:text="#{ranking.date}">Fecha</th>
</tr>
</thead>
<tbody>
<tr th:each="game, iterStat : ${ranking}">
<td th:text="${iterStat.count}"></td>
<td th:text="${game.score}"></td>
<td th:text="${game.getDuration()}"></td>
<td th:text="${game.correctQuestions}"></td>
<td th:text="${game.totalQuestions - game.correctQuestions}"></td>
<td th:text="${#temporals.format(game.createdAt, 'dd-MM-yyyy HH:mm:ss')}"></td>
</tr>
</tbody>
Expand Down

0 comments on commit b0756ff

Please sign in to comment.