diff --git a/src/apps/applets/migrations/0008_seed_my_sea_applet.py b/src/apps/applets/migrations/0008_seed_my_sea_applet.py new file mode 100644 index 0000000..305c8eb --- /dev/null +++ b/src/apps/applets/migrations/0008_seed_my_sea_applet.py @@ -0,0 +1,42 @@ +"""Seed the My Sea applet — Sprint 3 of the My Sea roadmap. + +The applet itself is just a shell for now (header + horizontal scroll +container w. empty-state placeholder). Sprints 4+ will fill in the +sig-select / sea-select / gatekeeper phases that render in the dedicated +`my_sea.html` page reachable via the applet's header link. + +Grid: 12 cols × 4 rows — wide horizontal banner so the latest draw's +10-card Celtic Cross spread can render left-to-right in the applet +aperture, scrollable like My Palette. +""" +from django.db import migrations + + +def seed(apps, schema_editor): + Applet = apps.get_model("applets", "Applet") + Applet.objects.update_or_create( + slug="my-sea", + defaults={ + "name": "My Sea", + "context": "gameboard", + "default_visible": True, + "grid_cols": 12, + "grid_rows": 4, + }, + ) + + +def unseed(apps, schema_editor): + Applet = apps.get_model("applets", "Applet") + Applet.objects.filter(slug="my-sea").delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ("applets", "0007_rename_my_buddies_to_my_buds"), + ] + + operations = [ + migrations.RunPython(seed, unseed), + ] diff --git a/src/apps/gameboard/tests/integrated/test_views.py b/src/apps/gameboard/tests/integrated/test_views.py index 3475454..5ce9a9b 100644 --- a/src/apps/gameboard/tests/integrated/test_views.py +++ b/src/apps/gameboard/tests/integrated/test_views.py @@ -34,6 +34,23 @@ class GameboardViewTest(TestCase): def test_gameboard_shows_new_game_applet(self): [_] = self.parsed.cssselect("#id_applet_new_game") + + def test_gameboard_shows_my_sea_applet(self): + # Sprint 3 of the My Sea roadmap — applet shell only; sigs/sea/draw + # flow lands in later sprints. Seeded via migration 0008. + [_] = self.parsed.cssselect("#id_applet_my_sea") + + def test_my_sea_applet_renders_empty_state_for_new_user(self): + # A fresh user has no saved draws → the scroll container hosts a + # single placeholder line ("No draws yet."), no card cells. + [empty] = self.parsed.cssselect("#id_applet_my_sea .my-sea-empty") + self.assertIn("No draws yet", empty.text_content()) + cards = self.parsed.cssselect("#id_applet_my_sea .my-sea-card") + self.assertEqual(len(cards), 0) + + def test_my_sea_applet_header_links_to_my_sea_page(self): + [link] = self.parsed.cssselect("#id_applet_my_sea h2 a") + self.assertEqual(link.get("href"), reverse("my_sea")) def test_gameboard_shows_game_kit(self): [_] = self.parsed.cssselect("#id_game_kit") @@ -442,3 +459,31 @@ class TarotFanViewTest(TestCase): def test_returns_403_for_locked_deck(self): response = self.client.get(reverse("tarot_fan", kwargs={"deck_id": self.fiorentine.pk})) self.assertEqual(response.status_code, 403) + + +class MySeaViewTest(TestCase): + """Sprint 3 of the My Sea roadmap — standalone page is a shell only. + Sigs / sea-select / gatekeeper phase content lands in later sprints.""" + + def setUp(self): + self.user = User.objects.create(email="sea@test.io") + self.client.force_login(self.user) + + def test_my_sea_requires_login(self): + self.client.logout() + response = self.client.get(reverse("my_sea")) + self.assertRedirects( + response, "/?next=/gameboard/my-sea/", fetch_redirect_response=False + ) + + def test_my_sea_renders_200(self): + response = self.client.get(reverse("my_sea")) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "apps/gameboard/my_sea.html") + + def test_my_sea_uses_gameboard_page_class(self): + # `page_class` drives the body class for the landscape layout aperture + # — My Sea inherits the gameboard's aperture (same nav/footer rails). + response = self.client.get(reverse("my_sea")) + self.assertIn("page-gameboard", response.content.decode()) + self.assertIn("page-my-sea", response.content.decode()) diff --git a/src/apps/gameboard/urls.py b/src/apps/gameboard/urls.py index e96cac9..e4e013b 100644 --- a/src/apps/gameboard/urls.py +++ b/src/apps/gameboard/urls.py @@ -13,5 +13,6 @@ urlpatterns = [ path('game-kit/', views.game_kit, name='game_kit'), path('game-kit/toggle-sections', views.toggle_game_kit_sections, name='toggle_game_kit_sections'), path('game-kit/deck//', views.tarot_fan, name='tarot_fan'), + path('my-sea/', views.my_sea, name='my_sea'), ] diff --git a/src/apps/gameboard/views.py b/src/apps/gameboard/views.py index 5e7e718..671e303 100644 --- a/src/apps/gameboard/views.py +++ b/src/apps/gameboard/views.py @@ -166,6 +166,19 @@ def toggle_game_kit_sections(request): return redirect("game_kit") +@login_required(login_url="/") +def my_sea(request): + """Shell view for the My Sea standalone page. + + Sprint 3 scaffolding only — the page will host the gatekeeper / + sig-select / sea-select phase reskin for solo-user draws in later + sprints. For now it just renders the empty skeleton. + """ + return render(request, "apps/gameboard/my_sea.html", { + "page_class": "page-gameboard page-my-sea", + }) + + @login_required(login_url="/") def tarot_fan(request, deck_id): from apps.epic.models import TarotCard diff --git a/src/templates/apps/gameboard/_partials/_applet-my-sea.html b/src/templates/apps/gameboard/_partials/_applet-my-sea.html new file mode 100644 index 0000000..ea119f7 --- /dev/null +++ b/src/templates/apps/gameboard/_partials/_applet-my-sea.html @@ -0,0 +1,18 @@ +
+

My Sea

+
+ {% if latest_draw_cards %} + {% for card in latest_draw_cards %} +
+ {{ card.corner_rank }} + {% if card.suit_icon %}{% endif %} +
+ {% endfor %} + {% else %} +

No draws yet.

+ {% endif %} +
+
diff --git a/src/templates/apps/gameboard/my_sea.html b/src/templates/apps/gameboard/my_sea.html new file mode 100644 index 0000000..629bce7 --- /dev/null +++ b/src/templates/apps/gameboard/my_sea.html @@ -0,0 +1,13 @@ +{% extends "core/base.html" %} +{% load static %} + +{% block title_text %}My Sea{% endblock title_text %} +{% block header_text %}MySea{% endblock header_text %} + +{% block content %} +
+ {# Sprint 3 shell only — gatekeeper / sig-select / sea-select phases #} + {# will land here in later sprints of the My Sea roadmap. #} +

Your sea is calm. Draws will appear here.

+
+{% endblock content %}