feat(ui): add main menu navbar and join game functionality #35

Merged
lq64 merged 2 commits from feat/30-mainmenu-navbar into main 2025-11-04 12:43:09 +01:00
11 changed files with 84 additions and 10 deletions
Showing only changes of commit 325f2e44f6 - Show all commits

View File

@@ -11,7 +11,7 @@ get {
}
params:path {
id: uZDNZA
id: BZvtJ3
}
settings {

View File

@@ -11,7 +11,7 @@ post {
}
params:path {
id: uZDNZA
id: nR1o3n
}
settings {

View File

@@ -21,7 +21,7 @@ class MainMenuController @Inject()(
// Pass the request-handling function directly to authAction (no nested Action)
def mainMenu(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
Ok("Main Menu for user: " + request.user.name)
Ok(views.html.mainmenu.navbar(Some(request.user)))
}
def index(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
@@ -37,6 +37,22 @@ class MainMenuController @Inject()(
Redirect(routes.IngameController.game(gameLobby.id))
}
def joinGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
val postData = request.body.asFormUrlEncoded
if (postData.isDefined) {
val gameId = postData.get.get("gameId").flatMap(_.headOption).getOrElse("")
val game = podManager.getGame(gameId)
game match {
case Some(g) =>
Redirect(routes.IngameController.joinGame(gameId))
case None =>
NotFound("Game not found")
}
} else {
BadRequest("Invalid form submission")
}
}
def rules(): Action[AnyContent] = {
Action { implicit request =>
Ok(views.html.mainmenu.rules())

View File

@@ -64,7 +64,7 @@ class UserController @Inject()(
if (sessionCookie.isDefined) {
sessionManager.invalidateSession(sessionCookie.get.value)
}
NoContent.discardingCookies(DiscardingCookie("sessionId"))
Redirect(routes.UserController.login()).discardingCookies(DiscardingCookie("sessionId"))
}
}

View File

@@ -70,6 +70,9 @@ class GameLobby private(
if (!sessionOpt.get.host) {
throw new NotHostException("Only the host can start the game!")
}
if (logic.getCurrentState != Lobby) {
throw new IllegalStateException("The game has already started!")
}
val playerNamesList = ListBuffer[AbstractPlayer]()
users.values.foreach { player =>
playerNamesList += PlayerFactory.createPlayer(player.name, player.id, HUMAN)

View File

@@ -17,7 +17,7 @@
@if(logic.getCurrentTrick.get.firstCard.isDefined) {
@util.WebUIUtils.cardtoImage(logic.getCurrentTrick.get.firstCard.get)
} else {
@views.html.render.card.apply("../../../public/images/cards/1B.png")("Blank Card")
@views.html.render.card.apply("images/cards/1B.png")("Blank Card")
}
</div>
</div>

View File

@@ -0,0 +1,56 @@
@(user: Option[model.users.User])
@main("Knockout Whist - Main Menu") {
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navBar" aria-controls="navBar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navBar">
<a class="navbar-brand" href="@routes.MainMenuController.mainMenu()">KnockOutWhist</a>
<div class="navbar-nav me-auto mb-2 mb-lg-0">
<ul class="navbar-nav mb-2 mb-lg-0">
@if(user.isDefined) {
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Create Game</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" aria-disabled="true">Lobbies</a>
</li>
}
<li class="nav-item">
<a class="nav-link active" href="@routes.MainMenuController.rules()">Rules</a>
</li>
</ul>
<form class="navbar-nav me-auto mb-2 mb-lg-0" method="post" action="@routes.MainMenuController.joinGame()">
<input class="form-control me-2" type="text" placeholder="Enter GameCode" name="gameId" aria-label="Join Game"/>
<button class="btn btn-outline-success" type="submit">Join</button>
</form>
</div>
@* Right side: profile dropdown if logged in, otherwise Login / Sign Up buttons *@
@if(user.isDefined) {
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" id="profileDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<img src="@routes.Assets.versioned("images/profile.png")" alt="Profile" class="rounded-circle" width="30" height="30" />
<span class="ms-2">@user.get.name</span>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="profileDropdown">
<li><a class="dropdown-item disabled" href="#" tabindex="-1" aria-disabled="true">Stats</a></li>
<li><a class="dropdown-item disabled" href="#" tabindex="-1" aria-disabled="true">Settings</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="@routes.UserController.logout()">Logout</a></li>
</ul>
</li>
</ul>
} else {
<div class="d-flex ms-auto">
<a class="btn btn-outline-primary me-2" href="@routes.UserController.login()">Login</a>
<a class="btn btn-primary" href="@routes.UserController.login()">Sign Up</a>
</div>
}
</div>
</div>
</nav>
}

View File

@@ -1,7 +1,7 @@
@()
@main("Rules") {
<div id="rules">
<div id="rules" class="game-field game-field-background">
<table>
<caption>Rules Overview and Equipment</caption>
<thead>

View File

@@ -13,6 +13,7 @@ GET /mainmenu controllers.MainMenuController.mainMenu()
GET /rules controllers.MainMenuController.rules()
POST /createGame controllers.MainMenuController.createGame()
POST /joinGame controllers.MainMenuController.joinGame()
# User authentication routes
GET /login controllers.UserController.login()
@@ -22,9 +23,7 @@ GET /logout controllers.UserController.logout()
# In-game routes
GET /game/:id controllers.IngameController.game(id: String)
POST /game/:id/join controllers.IngameController.joinGame(id: String)
GET /game/:id/join controllers.IngameController.joinGame(id: String)
POST /game/:id/start controllers.IngameController.startGame(id: String)
POST /game/:id/playCard controllers.IngameController.playCard(id: String)

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB