sky wheel: ubiquitous DEL btn — applet & PICK SKY parity w. Dashsky; PICK SKY clears client-only state (no User-model touch) — TDD

My Sky applet (.../dashboard/_partials/_applet-my-sky.html): adds <button id="id_applet_sky_delete_btn" class="btn btn-danger"> at the wheel center, gated on user.sky_chart_data. Click → window.showGuard("Forget sky?") → on OK, fetch POSTs sky_delete (clears every sky_* field on User), removes the 'sky-form:dashboard:sky' localStorage entry that would otherwise rehydrate the post-reload form via _restoreForm(), then reloads — applet's form-render branch is server-template-gated on chart_data so the page comes back form-only.

PICK SKY in-room overlay (.../gameboard/_partials/_sky_overlay.html): adds <button id="id_sky_delete_btn"> at the wheel center. The wheel here is purely a live preview — sky_save fires only on SAVE SKY click w. action='confirm', so there's no draft Character to delete & we do NOT touch the Character/User model. The DEL handler clears the SVG, resets form fields (including lat/lon/tz/tzHint), nulls _lastChartData, disables the SAVE SKY btn, & purges the LS_KEY entry that would otherwise rehydrate on next overlay open / page refresh. Mirrors the user's spec ("shouldn't be targeting the user model anyway, only the character/seat model" — and there's currently no character/seat draft in the PICK SKY flow).

Both handlers defer the window.showGuard readiness check to click-time rather than gating the listener bind itself: window.showGuard is assigned by a base.html script that lives BELOW the content block, so an `if (window.showGuard)` gate at script-execute time would skip the bind entirely (we hit this writing the applet handler — manifested as portal class never receiving 'active' on click).

SCSS: extends the existing #id_sky_delete_form absolute-center rule onto the two new btn IDs (#id_sky_delete_btn, #id_applet_sky_delete_btn). #id_applet_my_sky picks up position:relative as the absolute anchor for the applet btn.

FTs: MySkyAppletDelTest (applet → DEL → guard → OK → reload, asserts User cleared + LS purged + form re-renders) & PickSkyDelTest (overlay → fill form → wheel paints → DEL → guard → OK, asserts SVG empty + form blank + LS purged). Both red before the wiring, green after; full sky suite (46 tests) green.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-08 14:34:41 -04:00
parent 283b417341
commit e9bceaab62
5 changed files with 256 additions and 4 deletions

View File

@@ -71,6 +71,9 @@
<svg id="id_my_sky_svg" class="sky-svg"
{% if not request.user.sky_chart_data %}style="display:none;"{% endif %}></svg>
{% if request.user.sky_chart_data %}
<button type="button" id="id_applet_sky_delete_btn"
class="btn btn-danger"
data-delete-url="{% url 'sky_delete' %}">DEL</button>
{{ request.user.sky_chart_data|json_script:"id_my_sky_data" }}
{% endif %}
</section>
@@ -98,6 +101,38 @@
SkyWheel.preload().then(function () { SkyWheel.draw(svgEl, stale); });
});
// DEL btn — guard portal → POST sky_delete + clear LS + reload. Reload is
// the simplest swap back to the form rendering (the form-rendering branch
// is server-template-gated on user.sky_chart_data). window.showGuard is
// assigned by the script in base.html that lives BELOW the content block,
// so the readiness check is deferred to click-time rather than gating the
// listener bind itself.
var delBtn = document.getElementById('id_applet_sky_delete_btn');
if (delBtn) {
var DELETE_URL = delBtn.dataset.deleteUrl;
function _csrf() {
var m = document.cookie.match(/csrftoken=([^;]+)/);
return m ? m[1] : '';
}
delBtn.addEventListener('click', function () {
if (!window.showGuard) return;
window.showGuard(delBtn, 'Forget sky?', function () {
fetch(DELETE_URL, {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-CSRFToken': _csrf() },
}).then(function (r) {
if (!r.ok) return;
// The form-render branch's _restoreForm() will re-seed
// fields from this LS entry on the next render — clear it
// so the post-reload form lands genuinely empty.
try { localStorage.removeItem('sky-form:dashboard:sky'); } catch (_) {}
window.location.reload();
});
});
});
}
{% else %}
// No sky saved yet — wire up the entry form.

View File

@@ -89,6 +89,8 @@
{# ── Wheel column ─────────────────────────────────────── #}
<div class="sky-wheel-col">
<svg id="id_sky_svg" class="sky-svg"></svg>
<button type="button" id="id_sky_delete_btn"
class="btn btn-danger">DEL</button>
</div>
</div>{# /.sky-modal-body #}
@@ -399,6 +401,34 @@
window.location.reload();
}
// ── DEL btn — clears wheel + form + localStorage (no server hit) ────────
// PICK SKY's wheel is a live preview; un-saved data lives only in LS_KEY.
// Match the My Sky applet / Dashsky pattern but skip the server delete —
// there's no Character draft created during preview (sky_save fires only
// on SAVE SKY click w. action='confirm').
// window.showGuard is assigned in a base.html script that loads BELOW the
// content block — defer the readiness check to click-time so the listener
// bind happens regardless of inline-script execution order.
const delBtn = document.getElementById('id_sky_delete_btn');
if (delBtn) {
delBtn.addEventListener('click', () => {
if (!window.showGuard) return;
window.showGuard(delBtn, 'Forget sky?', () => {
while (svgEl.firstChild) svgEl.removeChild(svgEl.firstChild);
form.reset();
latInput.value = '';
lonInput.value = '';
tzInput.value = '';
tzHint.textContent = '';
_lastChartData = null;
confirmBtn.disabled = true;
setStatus('');
try { localStorage.removeItem(LS_KEY); } catch (_) {}
});
});
}
// ── CSRF ──────────────────────────────────────────────────────────────────
function _getCsrf() {