recognition: page, palette modal, & dashboard palette unlock — TDD

- billboard/recognition/ view + template; recognition/<slug>/set-palette endpoint (no trailing slash)
- recognition.html: <template>-based modal (clone on open, remove on close — Selenium find_elements compatible)
- recognition-page.js: image-box → modal → swatch preview → body-click restore → OK → confirm → POST set-palette
- _palettes_for_user() replaces static PALETTES; Recognition.palette unlocks swatch + populates data-shoptalk
- _unlocked_palettes_for_user() wires dynamic unlock check into set_palette view
- _applet-palette.html: data-shoptalk from context instead of hard-coded "Placeholder"
- _recognition.scss: banner, recog-list/item, image-box, modal, palette-confirm; :not([hidden]) pattern avoids display override
- FT T2 split into T2a (banner → FYI → recog page), T2b (palette modal flow), T2c (dashboard palette applet)
- 684 ITs green; 7 FTs green

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-04-22 04:02:14 -04:00
parent 565f727aa6
commit 6d9d3d4f54
11 changed files with 683 additions and 53 deletions

View File

@@ -1,9 +1,12 @@
import json
from django.contrib.auth.decorators import login_required
from django.db.models import Max, Q
from django.http import JsonResponse
from django.shortcuts import redirect, render
from apps.applets.utils import applet_context, apply_applet_toggle
from apps.drama.models import GameEvent, ScrollPosition
from apps.drama.models import GameEvent, Recognition, ScrollPosition
from apps.epic.models import Room
from apps.epic.utils import rooms_for_user
@@ -68,6 +71,58 @@ def room_scroll(request, room_id):
})
_RECOGNITION_META = {
"stargazer": {
"title": "Stargazer",
"description": "You saved your first personal sky chart.",
"palette_options": ["palette-bardo", "palette-sheol"],
},
"schizo": {
"title": "Schizo",
"description": "The socius recognizes the line of flight.",
"palette_options": [],
},
"nomad": {
"title": "Nomad",
"description": "The socius recognizes the smooth space.",
"palette_options": [],
},
}
@login_required(login_url="/")
def recognition_set_palette(request, slug):
from django.http import Http404
try:
recognition = Recognition.objects.get(user=request.user, slug=slug)
except Recognition.DoesNotExist:
raise Http404
if request.method == "POST":
body = json.loads(request.body)
recognition.palette = body.get("palette", "")
recognition.save(update_fields=["palette"])
return JsonResponse({"ok": True})
@login_required(login_url="/")
def recognition_page(request):
qs = Recognition.objects.filter(user=request.user)
recognitions = [
{
"obj": r,
"title": _RECOGNITION_META.get(r.slug, {}).get("title", r.slug),
"description": _RECOGNITION_META.get(r.slug, {}).get("description", ""),
"palette_options": _RECOGNITION_META.get(r.slug, {}).get("palette_options", []),
}
for r in qs
]
return render(request, "apps/billboard/recognition.html", {
"recognitions": qs,
"recognition_items": recognitions,
"page_class": "page-recognition",
})
@login_required(login_url="/")
def save_scroll_position(request, room_id):
if request.method != "POST":