From bb963850b1b91dfdf2d8a8f276eef46eee62a143 Mon Sep 17 00:00:00 2001 From: webmaster Date: Tue, 3 Nov 2026 20:04:20 +0000 Subject: [PATCH] feat(feedback): three-step progress bar + "Sign up here" link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A real
    whose current step carries aria-current="step" and whose done step is marked by a tick (never colour alone), following the page state — step 1 when seats are available, step 1 done / step 2 current when a seat is claimed, and no bar at all in the cohort-full and count-unavailable states. The bar states no count and no deadline: both come from the wire. The claimed state gains the signup link: "Sign up here", target="_blank" rel="noopener noreferrer", pointing at the directory page's own #apply form — the one place the application lives, no invented URL. Secondary to any button; the accessible text says it opens a new window. Untouched: the four states, the consent gate, the claim body, the leftKeys contract, the leak rules, the 15 questions, the corrected copy. --- feedback/index.html | 101 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/feedback/index.html b/feedback/index.html index 69a95cb..b11172d 100644 --- a/feedback/index.html +++ b/feedback/index.html @@ -145,6 +145,51 @@ .steps { margin: 0.4rem 0 0; padding-left: 1.2rem; font-size: 0.92rem; } .steps li { margin: 0 0 0.3rem; } + /* ---- Progress bar (three steps) ---- */ + .progress { margin: 0 0 1.15rem; } + .steps-bar { + list-style: none; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.3rem 0.6rem; + margin: 0; + padding: 0; + font-size: 0.85rem; + } + .steps-bar .step { display: flex; align-items: center; gap: 0.4rem; color: var(--ink-soft); } + .steps-bar .step + .step::before { content: "·"; margin-right: 0.6rem; color: var(--line-brass); } + .step-mark { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.55rem; + height: 1.55rem; + border-radius: 999px; + border: 1px solid var(--line); + background: #fff; + font-size: 0.8rem; + line-height: 1; + } + /* The current step is the brass pill; a done step carries the ✓ mark (see the script) — + the tick is the non-colour cue, so a done step is never colour alone. */ + .steps-bar .step-current { color: var(--ink); font-weight: 600; } + .steps-bar .step-current .step-mark { background: var(--brass); border-color: var(--brass); color: var(--midnight); } + .steps-bar .step-done .step-mark { background: var(--success); border-color: var(--success); color: #fff; } + .link-signup { color: var(--brass-deep); font-weight: 600; } + /* Visually hidden, still read aloud. */ + .vh { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + white-space: nowrap; + border: 0; + } + /* ---- Feedback list (cohort full) ---- */ .fb { border-top: 1px solid var(--line); padding: 1rem 0 0; margin: 1rem 0 0; } .fb:first-of-type { border-top: 0; padding-top: 0.2rem; margin-top: 0.4rem; } @@ -258,6 +303,9 @@ var C = window.O4F; var app = document.getElementById("app"); var claimedUpwork = ""; // the seat claim: an identifier, kept in memory, never in the URL + // The application form on the directory page — the anchor the page's own "Get listed" + // triggers use. There is no separate login or account page, so this is the one target. + var SIGNUP_URL = "https://odoo4projects.com/odoo-community-developers/#apply"; function el(tag, cls, text) { var n = document.createElement(tag); @@ -320,6 +368,42 @@ return b; } + // ---- Progress bar (three steps) --------------------------------------------------------- + // Which step is current follows the page state — it is not decoration. It renders only + // where there is something to do: seats available (step 1 current) and seat claimed + // (step 1 done, step 2 current, step 3 visible as what comes next). The cohort-full and + // count-unavailable states render no bar at all. The bar states no count and no deadline: + // both come from the wire and are printed elsewhere, from the wire alone. + var STEP_LABELS = ["Claim seat", "Sign up", "Fill out feedback"]; + function progressBar(current) { + var nav = el("nav", "progress"); + nav.setAttribute("aria-label", "Signup progress"); + var ol = el("ol", "steps-bar"); + STEP_LABELS.forEach(function (label, i) { + var n = i + 1; + var li = el("li", "step"); + var mark = el("span", "step-mark"); + mark.setAttribute("aria-hidden", "true"); + // A done step shows the ✓ mark; the current and upcoming steps show their number. + mark.textContent = n < current ? "\u2713" : String(n); + li.appendChild(mark); + li.appendChild(el("span", "step-label", label)); + if (n === current) { + li.className = "step step-current"; + li.setAttribute("aria-current", "step"); + } else if (n < current) { + // Done is the ✓ mark AND a word for screen readers — never colour alone. + li.className = "step step-done"; + li.appendChild(el("span", "vh", " (completed)")); + } else { + li.className = "step step-next"; + } + ol.appendChild(li); + }); + nav.appendChild(ol); + return nav; + } + // ---- The four states ----------------------------------------------------------------- // 1. Seats available. The number is read from the wire; a cap is not a remaining count. function stateAvailable(seats, total) { @@ -394,6 +478,7 @@ }); show([ + progressBar(1), panel([h, el("p", "detail", "One seat per freelancer, and there is a fixed number for this cohort.")]), panel([el("h2", "", "Claim your seat"), form]) ]); @@ -427,7 +512,21 @@ } claimed.appendChild(steps); - show([claimed, feedbackPanel()]); + // The one thing to do while a seat is held: go and finish the signup. A link, never a + // second button — the page keeps one primary action at a time, and the target is the + // application form the page's own "Get listed" triggers use. + var signup = el("p", "detail"); + var up = el("a", "link-signup"); + up.href = SIGNUP_URL; + up.target = "_blank"; + up.rel = "noopener noreferrer"; + up.appendChild(document.createTextNode("Sign up here")); + // Say it opens a new window, so a screen-reader user is not surprised by a new tab. + up.appendChild(el("span", "", " (opens in a new window)")); + signup.appendChild(up); + claimed.appendChild(signup); + + show([progressBar(2), claimed, feedbackPanel()]); } function clockTime(iso) {