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

@@ -0,0 +1,49 @@
{% extends "core/base.html" %}
{% load static %}
{% block title_text %}Recognition{% endblock title_text %}
{% block header_text %}<span>Bill</span>recognition{% endblock header_text %}
{% block content %}
<div class="recognition-page">
<h2>Recognition</h2>
<ul class="recog-list">
{% for item in recognition_items %}
<li class="recog-item" data-slug="{{ item.obj.slug }}"
data-set-palette-url="{% url 'billboard:recognition_set_palette' item.obj.slug %}">
{% if item.obj.palette %}
<div class="recog-item__palette {{ item.obj.palette }}"></div>
{% else %}
<div class="recog-item__image-box">?</div>
{% endif %}
<p class="recog-item__title">{{ item.title }}</p>
<p class="recog-item__description">{{ item.description }}</p>
{% if not item.obj.palette and item.palette_options %}
<template class="recog-palette-modal-tpl">
<div class="recog-palette-modal">
{% for palette_name in item.palette_options %}
<div class="{{ palette_name }}">
<div class="recog-swatch-body"></div>
<button type="button" class="btn btn-confirm">OK</button>
</div>
{% endfor %}
<div class="recog-palette-confirm" hidden>
<p>Lock in this palette?</p>
<button type="button" class="btn btn-confirm">OK</button>
<button type="button" class="btn btn-cancel">NVM</button>
</div>
</div>
</template>
{% endif %}
</li>
{% empty %}
<li class="recog-item recog-item--empty">No recognitions yet.</li>
{% endfor %}
</ul>
</div>
<script src="{% static 'apps/billboard/recognition-page.js' %}"></script>
{% endblock %}