Confirmations pin to the viewport; open ballots mark their rows
Twice the feedback was the bug: the success banner lives at the top of the page and the user acts at the bottom of a 136-row list — so a working "pick edition" read as broken (it worked both times; the data proves it). showToast() pins transient confirmations to the viewport bottom (role=status, carries buttons, auto-dismisses), and pick- edition/edit-saved use it. Better: state stops being transient at all — any row whose edition ballot is open shows a persistent "ballot in Review" link where its button was, so the answer to "where did it go?" lives on the row forever. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
edb72514f4
commit
c94e8bc695
+2
-2
@@ -17,7 +17,7 @@ Cards Against Humanity,50381,Cards Against Humanity,2009,boardgame,auto,,,versio
|
||||
Joking Hazard,193621,Joking Hazard,2016,boardgame,auto,,,version_unknown,"[{""bgg_id"": 193621, ""name"": ""Joking Hazard"", ""year"": 2016, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],IMG_4501.jpeg;IMG_4502.jpeg,,
|
||||
SKIP-BO,1269,Skip-Bo,1967,boardgame,auto,,,version_unknown,"[{""bgg_id"": 1269, ""name"": ""Skip-Bo"", ""year"": 1967, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],IMG_4501.jpeg;IMG_4542.jpeg,,
|
||||
EXPLODING KITTENS,172225,Exploding Kittens,2015,boardgame,auto,,,version_unknown,"[{""bgg_id"": 172225, ""name"": ""Exploding Kittens"", ""year"": 2015, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],IMG_4501.jpeg;IMG_4505.jpeg,,
|
||||
Wiz-War,589,Wiz-War,1983,boardgame,auto,,,version_unknown,"[{""bgg_id"": 589, ""name"": ""Wiz-War"", ""year"": 1983, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],IMG_4502.jpeg,,
|
||||
Wiz-War,589,Wiz-War,1983,boardgame,auto,,,version_ambiguous,"[{""bgg_id"": 589, ""name"": ""Wiz-War"", ""year"": 1983, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]","[{""version_id"": 188937, ""name"": ""First edition"", ""year"": 1985, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 360421, ""name"": ""Second edition"", ""year"": 1985, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 360420, ""name"": ""Third edition"", ""year"": 1986, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 29214, ""name"": ""Fourth edition"", ""year"": 1987, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 26114, ""name"": ""Fifth edition"", ""year"": 1991, ""publishers"": [""Chessex""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 27352, ""name"": ""Chessex sixth edition"", ""year"": 1993, ""publishers"": [""Chessex""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 28612, ""name"": ""Chessex classic edition 1997"", ""year"": 1997, ""publishers"": [""Chessex""], ""languages"": [""English""], ""score"": 1}]",IMG_4502.jpeg,,
|
||||
SCRAWL,202982,Scrawl,2016,boardgame,auto,,,version_unknown,"[{""bgg_id"": 202982, ""name"": ""Scrawl"", ""year"": 2016, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],IMG_4502.jpeg,,
|
||||
FLAT TOP,2529,Flat Top,1977,boardgame,auto,,,version_unknown,"[{""bgg_id"": 2529, ""name"": ""Flat Top"", ""year"": 1977, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],IMG_4502.jpeg,,
|
||||
Diplomacy,483,Diplomacy,1959,boardgame,auto,,,version_unknown,"[{""bgg_id"": 483, ""name"": ""Diplomacy"", ""year"": 1959, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]",[],IMG_4502.jpeg,,
|
||||
@@ -83,7 +83,7 @@ Poetry for Neanderthals,312786,Poetry for Neanderthals,2020,boardgame,auto,,,ver
|
||||
Elite Sportz Equipment Table Top Curling,,,,,local,,,,[],[],IMG_4526.jpeg,,
|
||||
Simon's Cat Card Game,188614,Simon's Cat Card Game,2016,boardgame,auto,,,version_ambiguous,"[{""bgg_id"": 188614, ""name"": ""Simon's Cat Card Game"", ""year"": 2016, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": 1429, ""rank"": 12119}, {""bgg_id"": 330317, ""name"": ""NOM: Simon's Cat Card Game"", ""year"": 2021, ""type"": ""boardgame"", ""exact"": false, ""fuzzy"": 91.3, ""owned"": 70, ""rank"": null}]","[{""version_id"": 292307, ""name"": ""English edition, first printing"", ""year"": 2016, ""publishers"": [""Steve Jackson Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 779792, ""name"": ""English edition, second printing"", ""year"": 2016, ""publishers"": [""Steve Jackson Games""], ""languages"": [""English""], ""score"": 3}]",IMG_4526.jpeg,,
|
||||
The Red Dragon Inn: Smorgasbox,273240,The Red Dragon Inn Smorgasbox,2019,boardgame,auto,,,version_ambiguous,"[{""bgg_id"": 273240, ""name"": ""The Red Dragon Inn Smorgasbox"", ""year"": 2019, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]","[{""version_id"": 446786, ""name"": ""Collector's edition"", ""year"": 2019, ""publishers"": [""SlugFest Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 446788, ""name"": ""Eco-Collector's edition"", ""year"": 2019, ""publishers"": [""SlugFest Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 446787, ""name"": ""Eco-Kickstarter edition"", ""year"": 2019, ""publishers"": [""SlugFest Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 446069, ""name"": ""Kickstarter edition"", ""year"": 2019, ""publishers"": [""SlugFest Games""], ""languages"": [""English""], ""score"": 3}]",IMG_4527.jpeg,,
|
||||
WIZ-WAR,589,Wiz-War,1983,boardgame,auto,,,version_ambiguous,"[{""bgg_id"": 589, ""name"": ""Wiz-War"", ""year"": 1983, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]","[{""version_id"": 26114, ""name"": ""Fifth edition"", ""year"": 1991, ""publishers"": [""Chessex""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 29214, ""name"": ""Fourth edition"", ""year"": 1987, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 360420, ""name"": ""Third edition"", ""year"": 1986, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 3}]",IMG_4528.jpeg,,
|
||||
WIZ-WAR,589,Wiz-War,1983,boardgame,auto,,,version_ambiguous,"[{""bgg_id"": 589, ""name"": ""Wiz-War"", ""year"": 1983, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]","[{""version_id"": 360420, ""name"": ""Third edition"", ""year"": 1986, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 29214, ""name"": ""Fourth edition"", ""year"": 1987, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 26114, ""name"": ""Fifth edition"", ""year"": 1991, ""publishers"": [""Chessex""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 188937, ""name"": ""First edition"", ""year"": 1985, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 360421, ""name"": ""Second edition"", ""year"": 1985, ""publishers"": [""Jolly Games""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 27352, ""name"": ""Chessex sixth edition"", ""year"": 1993, ""publishers"": [""Chessex""], ""languages"": [""English""], ""score"": 1}, {""version_id"": 28612, ""name"": ""Chessex classic edition 1997"", ""year"": 1997, ""publishers"": [""Chessex""], ""languages"": [""English""], ""score"": 1}]",IMG_4528.jpeg,,
|
||||
RISK THE LORD OF THE RINGS: The Middle-earth Conquest Game,4324,Risk: The Lord of the Rings,2002,boardgame,auto,26859,English edition 2002,version_approved,"[{""bgg_id"": 4324, ""name"": ""Risk: The Lord of the Rings"", ""year"": 2002, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 57.1, ""owned"": null, ""rank"": null}]","[{""version_id"": 33264, ""name"": ""English (UK) edition 2002"", ""year"": 2002, ""publishers"": [""Hasbro"", ""Parker Brothers""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 26859, ""name"": ""English edition 2002"", ""year"": 2002, ""publishers"": [""Hasbro"", ""Parker Brothers""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 26863, ""name"": ""English/French edition"", ""year"": 2002, ""publishers"": [""Parker Brothers""], ""languages"": [""English"", ""French""], ""score"": 3}, {""version_id"": 26865, ""name"": ""Danish edition"", ""year"": 2002, ""publishers"": [""Parker Brothers""], ""languages"": [""Danish""], ""score"": 2}, {""version_id"": 26861, ""name"": ""Dutch edition"", ""year"": 2002, ""publishers"": [""Parker Brothers""], ""languages"": [""Dutch""], ""score"": 2}, {""version_id"": 77144, ""name"": ""French edition 2002"", ""year"": 2002, ""publishers"": [""Hasbro"", ""Parker Brothers""], ""languages"": [""French""], ""score"": 2}, {""version_id"": 26862, ""name"": ""German edition"", ""year"": 2002, ""publishers"": [""Parker Brothers""], ""languages"": [""German""], ""score"": 2}, {""version_id"": 75135, ""name"": ""Norwegian edition"", ""year"": 2002, ""publishers"": [""Parker Brothers""], ""languages"": [""Norwegian""], ""score"": 2}]",IMG_4529.jpeg,,
|
||||
Ouija,,,,,local,,,,[],[],IMG_4529.jpeg,,
|
||||
Munchkin Big Box,419687,Munchkin Big Box,2025,boardgame,auto,711400,English edition,version_auto,"[{""bgg_id"": 419687, ""name"": ""Munchkin Big Box"", ""year"": 2025, ""type"": ""boardgame"", ""exact"": true, ""fuzzy"": 100.0, ""owned"": null, ""rank"": null}]","[{""version_id"": 711400, ""name"": ""English edition"", ""year"": 2025, ""publishers"": [""Steve Jackson Games""], ""languages"": [""English""], ""score"": 3}, {""version_id"": 777457, ""name"": ""Russian edition"", ""year"": 2025, ""publishers"": [""Hobby World"", ""Steve Jackson Games""], ""languages"": [""Russian""], ""score"": 2}]",IMG_4530.jpeg,,
|
||||
|
||||
|
@@ -473,6 +473,21 @@ button.danger { color: var(--stop-ink); border-color: var(--stop); background: #
|
||||
border: var(--line); border-radius: var(--radius); background: #fff;
|
||||
}
|
||||
|
||||
/* -- toast: viewport-pinned transient confirmations --------------------- */
|
||||
#toast {
|
||||
position: fixed; bottom: 1.1rem; left: 50%; transform: translateX(-50%);
|
||||
z-index: 20; max-width: min(92vw, 34rem);
|
||||
background: var(--navy); color: #fff;
|
||||
border: var(--line); border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-raised);
|
||||
padding: .7rem 1rem; font-size: .9rem; line-height: 1.45;
|
||||
}
|
||||
#toast a { color: var(--gold); }
|
||||
#toast button {
|
||||
font-size: .8rem; padding: .25rem .7rem; margin-left: .5rem;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* -- responsive -------------------------------------------------------- */
|
||||
|
||||
/* The rail collapses to a top bar: brand + hamburger; the nav drops
|
||||
|
||||
@@ -13,6 +13,24 @@ function showBanner(html) {
|
||||
document.getElementById("banner").innerHTML = html;
|
||||
}
|
||||
|
||||
/* Transient confirmation pinned to the viewport: the #banner region sits
|
||||
* at the top of the page, which a user deep in a long list never sees.
|
||||
* The toast may carry buttons — wire them before the timer clears it. */
|
||||
function showToast(html, ms = 8000) {
|
||||
let el = document.getElementById("toast");
|
||||
if (!el) {
|
||||
el = document.createElement("div");
|
||||
el.id = "toast";
|
||||
el.setAttribute("role", "status");
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
el.innerHTML = html;
|
||||
el.hidden = false;
|
||||
clearTimeout(showToast._timer);
|
||||
showToast._timer = setTimeout(() => { el.hidden = true; }, ms);
|
||||
return el;
|
||||
}
|
||||
|
||||
function errorBanner(detail) {
|
||||
// an HTTP status means the server answered — the problem is its data
|
||||
const httpish = /^\d{3}$/.test(String(detail));
|
||||
|
||||
@@ -89,6 +89,10 @@ function render() {
|
||||
title="one line, several boxes? make each photo its own copy">
|
||||
split into copies</button>`
|
||||
: ""}
|
||||
${c.version_status === "version_ambiguous"
|
||||
? `<a class="linkbtn ballot" href="/review#editions"
|
||||
title="this copy's edition ballot is open">ballot in Review</a>`
|
||||
: ""}
|
||||
${c.bgg_id && ["auto", "approved"].includes(c.status)
|
||||
&& ["version_unknown", "version_error", ""].includes(c.version_status || "")
|
||||
? `<button class="pickedition" data-title="${esc(c.title_raw)}"
|
||||
@@ -139,9 +143,8 @@ document.getElementById("catbody").addEventListener("click", async e => {
|
||||
row_ix: pe.dataset.rowix === "" ? null : Number(pe.dataset.rowix),
|
||||
});
|
||||
if (res) {
|
||||
showBanner(`<div class="banner">edition ballot ready for
|
||||
<b>${esc(pe.dataset.title)}</b> —
|
||||
<a href="/review#editions">pick it in Review</a></div>`);
|
||||
showToast(`edition ballot ready for <b>${esc(pe.dataset.title)}</b> —
|
||||
<a href="/review#editions">pick it in Review</a>`);
|
||||
GATE.reset(); refresh().catch(() => {});
|
||||
}
|
||||
return;
|
||||
@@ -214,14 +217,13 @@ document.getElementById("catbody").addEventListener("submit", async e => {
|
||||
EDITING = null; GATE.reset(); refresh().catch(() => {});
|
||||
// an edit re-queues any matched row: without a resolve run the line
|
||||
// sits "awaiting resolve" — say so, and offer the run right here
|
||||
showBanner(`<div class="banner">saved — a previously matched title
|
||||
re-matches on the next resolve run.
|
||||
<button id="resolvenow">run resolve now</button></div>`);
|
||||
const btn = document.getElementById("resolvenow");
|
||||
const toast = showToast(`saved — a previously matched title re-matches
|
||||
on the next resolve run. <button id="resolvenow">run resolve now</button>`);
|
||||
const btn = toast.querySelector("#resolvenow");
|
||||
if (btn) btn.addEventListener("click", async () => {
|
||||
const r = await apiPost("/api/run/resolve");
|
||||
if (r) showBanner(`<div class="banner">resolve running — progress on
|
||||
the <a href="/">Pipeline</a> page; this list updates itself</div>`);
|
||||
if (r) showToast(`resolve running — progress on the
|
||||
<a href="/">Pipeline</a> page; this list updates itself`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user