Files
python-tdd/src/templates/apps/billboard/bud.html
Disco DeDisco f5ee83be0a bud page sea-btn cascade: live-invite window + accept-on-GET + glow handoff; my-buds tooltip clamp + row hover/lock — TDD
Fixes the Bill Bud invite cascade so the sea sub-btn actually lights + leads
to the bud's my_sea, and gives the My Buds row tooltip viewport clamping +
hover/lock styling.

SeaInvite.invitee_access_open (gameboard/models.py): new invitee-facing
access window — a non-terminal invite (PENDING/ACCEPTED) within 24h of being
proffered OR within 24h of the invitee's last gate token deposit. Re-arms on
each deposit; DECLINED/LEFT/EXPIRED stay shut. Distinct from is_expired
(which only models the PENDING lapse). 8 UTs.

bud_page (billboard/views.py): sea_btn_active / sea_first_draw_pending now key
on invitee_access_open across PENDING + ACCEPTED, not PENDING-only. Old design
darkened the btn the instant the user accepted, so they could never reach the
bud's sea from here post-accept — that was the red .fa-ban the user saw. ITs
updated: accepted-within-window now lights; added stale-accepted-dark +
recent-deposit-relights cases.

my_sea_visit (gameboard/views.py): accept-on-GET — a still-pending, non-expired
invite from the owner to the visitor is accepted implicitly on arrival (the
sea-btn cascade + @mailman post-attribution anchor both land here, so the click
IS the acceptance). Previously PENDING → 403, so the cascade dead-ended. ITs:
pending-invitee now auto-accepts (200); expired-pending still 403s; stranger
still 403s.

bud.html: burger → sea_btn glow-handoff machine (the my_sea.html cascade minus
the spread-modal stage) so the glow rides the affordance chain to the click
target; active sea click clears glow, preserves .active, navigates.

my-buds-tooltip.js: clamp the position:fixed #id_tooltip_portal to the viewport
on row-lock — same 1rem-inset shape as game-kit.js / sky-wheel.js / wallet.js
(measure after .active, clamp left, prefer above / flip below). Reset on clear.

_billboard.scss: .bud-entry hover + .row-locked highlight (rows aren't
.row-3col so the existing rule missed them) — fill --secUser, flip the
--terUser handle to --quiUser, trailing title to readable --priUser.

520 billboard+gameboard ITs/UTs green; affected sea-btn-cascade FT green.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:36:25 -04:00

98 lines
4.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "core/base.html" %}
{% load static %}
{% load lyric_extras %}
{% block title_text %}Billbud{% endblock title_text %}
{% block header_text %}<span>Bill</span><span>bud</span>{% endblock header_text %}
{% block content %}
{# note.js exposes window.Brief — needed by the auto-fired @mailman Brief #}
{# when this page is the destination of an invite cascade. #}
<script src="{% static 'apps/dashboard/note.js' %}"></script>
<div class="bud-page">
<header class="bud-page-header">
<h3>{{ bud|at_handle }} the {{ bud.active_title_display }}</h3>
<p class="bud-page-email">{{ bud.email }}</p>
</header>
<form id="id_bud_shoptalk_form"
method="POST"
action="{% url 'billboard:save_bud_shoptalk' bud.id %}"
class="bud-shoptalk-form">
{% csrf_token %}
<label for="id_shoptalk" class="bud-shoptalk-label">Shoptalk</label>
<textarea id="id_shoptalk"
name="shoptalk"
maxlength="160"
class="form-control bud-shoptalk-input"
placeholder="Your personal note about this bud…">{{ shoptalk_text }}</textarea>
</form>
{# 4-btn apparatus mirrors my_sea.html: kit (from base.html) + bud + #}
{# gear + burger. Apparatus loads bud-btn.js (defines window. #}
{# bindBudBtn — does NOT auto-bind); the inline script below opens #}
{# the panel + stubs OK to disabled per the sprint plan. #}
{% include "apps/billboard/_partials/_bud_apparatus.html" with aria_label="Bud" include_suggestions=False %}
<script>
(function () {
var btn = document.getElementById('id_bud_btn');
var panel = document.getElementById('id_bud_panel');
var ok = document.getElementById('id_bud_ok');
if (!btn || !panel || !ok) return;
// bud-of-bud flow isn't wired yet — stub OK as disabled w. × text.
ok.classList.add('btn-disabled');
ok.textContent = '×';
ok.setAttribute('disabled', 'disabled');
btn.addEventListener('click', function () {
var open = document.documentElement.classList.toggle('bud-open');
btn.classList.toggle('active', open);
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && document.documentElement.classList.contains('bud-open')) {
document.documentElement.classList.remove('bud-open');
btn.classList.remove('active');
}
});
}());
</script>
{% include "apps/billboard/_partials/_bud_gear.html" %}
{# Burger fan — sea_btn_active + sea_first_draw_pending drive the #}
{# server-side .active / .glow-handoff classes (same vars my_sea + #}
{# room read). burger-btn.js auto-binds on DOMContentLoaded. #}
{% include "apps/gameboard/_partials/_burger.html" %}
</div>
<script src="{% static 'apps/epic/burger-btn.js' %}"></script>
{% if pending_invite %}
<script>
(function () {
var burger = document.getElementById('id_burger_btn');
var sea = document.getElementById('id_sea_btn');
if (!burger || !sea) return;
// Glow-handoff machine — the bud-page counterpart of my_sea.html's
// burger → sea_btn cascade, minus the spread-modal stage (the active
// sea sub-btn navigates away instead of opening a modal). The burger
// carries .glow-handoff server-side (sea_first_draw_pending); opening
// it hands the glow off to the sea sub-btn so the nudge rides the
// affordance chain to the click target.
burger.addEventListener('click', function () {
if (!burger.classList.contains('glow-handoff')) return;
burger.classList.remove('glow-handoff');
sea.classList.add('glow-handoff');
});
// Active sea sub-btn click → the bud's spectator my-sea (which accepts
// the invite on GET). Clears the glow but PRESERVES .active. Fires in
// the target phase, BEFORE burger-btn.js's delegated fan-close.
sea.addEventListener('click', function () {
if (!sea.classList.contains('active')) return;
sea.classList.remove('glow-handoff');
window.location.href = '/gameboard/my-sea/visit/{{ bud.id }}/';
});
}());
</script>
{% endif %}
{% endblock content %}