From cfffc9934c4eab276bafb9a45822c2b9081773be Mon Sep 17 00:00:00 2001 From: Asger Geel Weirsoee Date: Fri, 27 Feb 2026 22:26:55 +0100 Subject: [PATCH] feat(ui): add MVP host/player web screens --- docs/UI_SMOKE.md | 18 +++++++++ lobby/templates/lobby/host_screen.html | 34 +++++++++++++++++ lobby/templates/lobby/player_screen.html | 26 +++++++++++++ lobby/tests.py | 47 ++++++++++++++++++++++++ lobby/ui_views.py | 14 +++++++ lobby/urls.py | 5 ++- lobby/views.py | 17 +++++++++ 7 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 docs/UI_SMOKE.md create mode 100644 lobby/templates/lobby/host_screen.html create mode 100644 lobby/templates/lobby/player_screen.html create mode 100644 lobby/ui_views.py diff --git a/docs/UI_SMOKE.md b/docs/UI_SMOKE.md new file mode 100644 index 0000000..3c42fac --- /dev/null +++ b/docs/UI_SMOKE.md @@ -0,0 +1,18 @@ +# UI smoke (MVP) + +## Forudsætning +- Host er logget ind i Django. +- Mindst én aktiv kategori med spørgsmål findes. + +## Flow +1. Åbn host-siden på /lobby/ui/host og tryk Opret session. +2. Åbn player-siden i 3 faner/enheder på /lobby/ui/player. +3. Join alle spillere med sessionkode og nickname. +4. Host: vælg kategori, Start runde, Vis spørgsmål. +5. Spillere: brug round_question_id og submit løgn. +6. Host: Mix svar. +7. Spillere: submit gæt. +8. Host: Beregn score og Vis scoreboard. +9. Host: Næste runde eller Afslut spil. + +Resultat: En fuld runde kan køres uden rå API-kald fra terminal. diff --git a/lobby/templates/lobby/host_screen.html b/lobby/templates/lobby/host_screen.html new file mode 100644 index 0000000..25df4d6 --- /dev/null +++ b/lobby/templates/lobby/host_screen.html @@ -0,0 +1,34 @@ + +WPP Host + +

Host panel (MVP)

+

Kræver login som host-bruger.

+ + + + + + + + + + + + +
Klar.
+ + diff --git a/lobby/templates/lobby/player_screen.html b/lobby/templates/lobby/player_screen.html new file mode 100644 index 0000000..9be36f0 --- /dev/null +++ b/lobby/templates/lobby/player_screen.html @@ -0,0 +1,26 @@ + +WPP Player + +

Player panel (MVP)

+ + + + + + + + + + +
Klar.
+ + diff --git a/lobby/tests.py b/lobby/tests.py index 16d75c2..68338d1 100644 --- a/lobby/tests.py +++ b/lobby/tests.py @@ -664,3 +664,50 @@ class RevealRoundFlowTests(TestCase): self.assertEqual(response.status_code, 400) self.assertEqual(response.json()["error"], "Next round can only start from reveal phase") + +class UiScreenTests(TestCase): + def setUp(self): + self.host = User.objects.create_user(username="host_ui", password="secret123") + + def test_host_screen_requires_login(self): + response = self.client.get(reverse("lobby:host_screen")) + self.assertEqual(response.status_code, 302) + + def test_host_screen_renders_for_logged_in_user(self): + self.client.login(username="host_ui", password="secret123") + response = self.client.get(reverse("lobby:host_screen")) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Host panel") + + def test_player_screen_is_public(self): + response = self.client.get(reverse("lobby:player_screen")) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Player panel") + + +class SessionDetailRoundQuestionTests(TestCase): + def setUp(self): + self.host = User.objects.create_user(username="host_detail", password="secret123") + self.session = GameSession.objects.create(host=self.host, code="ABCDE1", status=GameSession.Status.LIE) + self.category = Category.objects.create(name="Historie", slug="historie-2", is_active=True) + self.question = Question.objects.create( + category=self.category, + prompt="Hvem opfandt pæren?", + correct_answer="Edison", + is_active=True, + ) + + def test_session_detail_includes_current_round_question_when_available(self): + round_question = RoundQuestion.objects.create( + session=self.session, + round_number=1, + question=self.question, + correct_answer=self.question.correct_answer, + ) + + response = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})) + + self.assertEqual(response.status_code, 200) + payload = response.json() + self.assertEqual(payload["round_question"]["id"], round_question.id) + self.assertEqual(payload["round_question"]["prompt"], self.question.prompt) diff --git a/lobby/ui_views.py b/lobby/ui_views.py new file mode 100644 index 0000000..9912276 --- /dev/null +++ b/lobby/ui_views.py @@ -0,0 +1,14 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import render + +from fupogfakta.models import Category + + +@login_required +def host_screen(request): + categories = Category.objects.filter(is_active=True).order_by("name") + return render(request, "lobby/host_screen.html", {"categories": categories}) + + +def player_screen(request): + return render(request, "lobby/player_screen.html") diff --git a/lobby/urls.py b/lobby/urls.py index 558b9f1..4acd074 100644 --- a/lobby/urls.py +++ b/lobby/urls.py @@ -1,10 +1,12 @@ from django.urls import path -from . import views +from . import ui_views, views app_name = "lobby" urlpatterns = [ + path("ui/host", ui_views.host_screen, name="host_screen"), + path("ui/player", ui_views.player_screen, name="player_screen"), path("sessions/create", views.create_session, name="create_session"), path("sessions/join", views.join_session, name="join_session"), path("sessions/", views.session_detail, name="session_detail"), @@ -34,4 +36,3 @@ urlpatterns = [ path("sessions//finish", views.finish_game, name="finish_game"), path("sessions//rounds/next", views.start_next_round, name="start_next_round"), ] - diff --git a/lobby/views.py b/lobby/views.py index 4d3cf8b..da3cc25 100644 --- a/lobby/views.py +++ b/lobby/views.py @@ -133,6 +133,22 @@ def session_detail(request: HttpRequest, code: str) -> JsonResponse: ) ) + current_round_question = ( + RoundQuestion.objects.filter(session=session, round_number=session.current_round) + .select_related("question") + .order_by("-id") + .first() + ) + + round_question_payload = None + if current_round_question: + round_question_payload = { + "id": current_round_question.id, + "round_number": current_round_question.round_number, + "prompt": current_round_question.question.prompt, + "shown_at": current_round_question.shown_at.isoformat(), + } + return JsonResponse( { "session": { @@ -143,6 +159,7 @@ def session_detail(request: HttpRequest, code: str) -> JsonResponse: "players_count": len(players), }, "players": players, + "round_question": round_question_payload, } )