Public Access
feedback: remove identity block, heard_about and the publish consent question
- Delete the 'About you' fieldset (name, email, anonymous tick) and its validation: the claimed Upwork name now identifies the sender, and the feedback payload carries it only when a seat was claimed this visit. - Remove the heard_about question. - Remove the publish consent question. That question WAS the consent, so no feedback may be published: the cohort-full state now renders the closed-cohort line only, and the feedback-rendering branch (entry keys, publish constants, isPublishable, the .fb styles) is gone rather than left dead. The GET's 'feedback' array is no longer consumed. - recommend: first label 'Yes' -> 'Yes, good idea' (value yes unchanged).
This commit is contained in:
1 parent
bb963850b1
commit
a6ada39784
1 file changed
+27
-136
+27
-136
@@ -190,11 +190,7 @@
|
||||
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; }
|
||||
.fb p { margin: 0 0 0.5rem; }
|
||||
.fb cite { font-style: normal; font-size: 0.85rem; color: var(--brass-deep); }
|
||||
/* ---- Cohort full (no feedback is ever rendered) ---- */
|
||||
.who {
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.06em;
|
||||
@@ -211,26 +207,26 @@
|
||||
GET statusUrl (no query string)
|
||||
-> 200 { "seats_left": 7, "seats_total": 10 } <- the intended shape
|
||||
or { "free": 9 } <- what the live route answers today
|
||||
or { "free": 0,
|
||||
"feedback": [ { "text": "...", "name": "Jane", "publish": "yes" } ] }
|
||||
or { "free": 0, "feedback": [ ... ] }
|
||||
A count arrives under the documented names first, then the live route's
|
||||
"Free"/"free"; a count must be a whole number >= 0 or it is not on the wire
|
||||
(so "free": "" is skipped and the next key is read). "feedback" (aka "entries")
|
||||
carries the cohort's words: each entry is published only when its "publish" is
|
||||
"yes" (words with the name) or "name" (words without it) — "no", missing or
|
||||
unknown is never rendered. With no seats and no "feedback", the cohort-full
|
||||
state states plainly that nothing is published yet (never a blank list).
|
||||
(so "free": "" is skipped and the next key is read). A "feedback" array in the
|
||||
reply is NOT consumed by this page and nothing from it is ever rendered — the
|
||||
page reads the count only. With no seats, the cohort-full state shows the
|
||||
closed-cohort line alone: no quotes, no excerpts, no paraphrases.
|
||||
|
||||
POST submitUrl { "form": "claim", "upwork": "<their Upwork name>" }
|
||||
-> 200 { "seats_left": 6, "seats_total": 10, "held_until": "2026-11-03T18:40:00Z" }
|
||||
|
||||
POST submitUrl { "form": "feedback", "upwork": "<their Upwork name>", "name": "...",
|
||||
"email": "...", "anonymous": false, "finalized": "yes", ... ,
|
||||
"publish": "yes" | "name" | "no" }
|
||||
POST submitUrl { "form": "feedback", "upwork": "<their claimed Upwork name>",
|
||||
"finalized": "yes", ... }
|
||||
"upwork" is present ONLY when a seat was claimed this visit — the claimed Upwork
|
||||
name, held in memory, sent to this route and nowhere else. With no claim the
|
||||
submission carries no name and is anonymous.
|
||||
-> any 2xx = receipt shown; a non-2xx is reported as a failure, never as success.
|
||||
|
||||
An unexpected shape is named, never guessed: a count that did not come off the wire is
|
||||
not printed, and an entry whose "publish" is missing or unknown is not rendered. -->
|
||||
not printed. -->
|
||||
<script>
|
||||
window.O4F = {
|
||||
// The one route that answers the count + feedback (read-only).
|
||||
@@ -242,16 +238,6 @@
|
||||
// shape still wins if it ever arrives. "free": "" is skipped by pick() below.
|
||||
leftKeys: ["seats_left", "seatsLeft", "Free", "free"],
|
||||
totalKeys: ["seats_total", "seatsTotal"],
|
||||
feedbackKeys: ["feedback", "entries"],
|
||||
// One feedback entry. A missing "" means "not on the wire", never a value.
|
||||
entryTextKeys: ["text", "feedback", "message"],
|
||||
entryNameKeys: ["name", "Name"],
|
||||
entryPublishKeys: ["publish", "consent"],
|
||||
// The three publish answers, character for character as the form sends them. ONLY "yes"
|
||||
// and "name" are rendered; anything else — "no", missing, unknown — is never rendered.
|
||||
PUBLISH_YES: "yes",
|
||||
PUBLISH_NAME: "name",
|
||||
PUBLISH_NO: "no",
|
||||
// The POST reply key that carries the seat deadline (ISO 8601).
|
||||
heldUntilKeys: ["held_until", "heldUntil", "deadline"]
|
||||
};
|
||||
@@ -540,27 +526,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Cohort full — consented feedback only.
|
||||
function stateFull(entries) {
|
||||
var shown = entries.filter(isPublishable);
|
||||
// 3. Cohort full — the closed-cohort line only. No feedback is rendered: the consent
|
||||
// question (the old "publish" field) is gone, so there is no record of anyone agreeing
|
||||
// to have their words shown in public, and anonymous words are still the author's
|
||||
// words. This state names no quotes, no excerpts and no paraphrases.
|
||||
function stateFull() {
|
||||
var box = panel([el("h2", "", "This cohort is full")]);
|
||||
box.appendChild(el("p", "detail", "All seats for this cohort have been claimed, so it is closed. Thank you — here is what the freelancers who took part told us."));
|
||||
if (shown.length) {
|
||||
shown.forEach(function (e) {
|
||||
var fig = el("figure", "fb");
|
||||
fig.appendChild(el("p", "", String(pick(e, C.entryTextKeys) || "")));
|
||||
var nm = pick(e, C.entryNameKeys);
|
||||
var consent = pick(e, C.entryPublishKeys);
|
||||
if (consent === C.PUBLISH_YES && nm) {
|
||||
fig.appendChild(el("cite", "", "— " + String(nm)));
|
||||
} else {
|
||||
fig.appendChild(el("cite", "", "— published without a name"));
|
||||
}
|
||||
box.appendChild(fig);
|
||||
});
|
||||
} else {
|
||||
box.appendChild(el("p", "detail", "No feedback has been published yet."));
|
||||
}
|
||||
box.appendChild(el("p", "detail", "All seats for this cohort have been claimed, so it is closed. We are not accepting new freelancers into this group."));
|
||||
show([box]);
|
||||
}
|
||||
|
||||
@@ -586,11 +558,6 @@
|
||||
show([panel(nodes)]);
|
||||
}
|
||||
|
||||
function isPublishable(entry) {
|
||||
var c = pick(entry, C.entryPublishKeys);
|
||||
return c === C.PUBLISH_YES || c === C.PUBLISH_NAME;
|
||||
}
|
||||
|
||||
// ---- The feedback form ----------------------------------------------------------------
|
||||
// The n8n form's questions, with the copy corrected, plus the fields the owner asked for.
|
||||
var FIELDS = [
|
||||
@@ -605,10 +572,8 @@
|
||||
{ id: "design_feedback", label: "What can we improve about the profile design?", type: "textarea", required: true },
|
||||
{ id: "will_stay", label: "Will you stay on the platform?", type: "select", options: [["yes", "Yes"], ["no", "No"]], required: true },
|
||||
{ id: "improve_offer", label: "How can we improve what we offer you?", type: "textarea", required: true },
|
||||
{ id: "recommend", label: "Would you recommend this platform to a freelance colleague?", type: "select", options: [["yes", "Yes"], ["maybe", "Maybe"], ["no", "Preferably not"]], required: true },
|
||||
{ id: "heard_about", label: "Where did you first hear about this platform?", type: "text", required: true },
|
||||
{ id: "expect_next", label: "What do you expect to happen next — and how soon?", type: "textarea", required: true },
|
||||
{ id: "publish", label: "May we publish your feedback with your name?", type: "radio", options: [["yes", "Yes, with my name"], ["name", "Yes, but not with my name"], ["no", "No"]], required: true }
|
||||
{ id: "recommend", label: "Would you recommend this platform to a freelance colleague?", type: "select", options: [["yes", "Yes, good idea"], ["maybe", "Maybe"], ["no", "Preferably not"]], required: true },
|
||||
{ id: "expect_next", label: "What do you expect to happen next — and how soon?", type: "textarea", required: true }
|
||||
];
|
||||
|
||||
function fieldBlock(spec) {
|
||||
@@ -663,47 +628,10 @@
|
||||
function feedbackPanel() {
|
||||
var box = el("div", "panel");
|
||||
box.appendChild(el("h2", "", "How did it go?"));
|
||||
box.appendChild(el("p", "detail", "Answer in your own words. Nothing here is published unless you say so, and you can ask us to leave your name off."));
|
||||
box.appendChild(el("p", "detail", "Answer in your own words. Your answers come to us — nothing here is published."));
|
||||
var form = el("form");
|
||||
form.setAttribute("novalidate", "novalidate");
|
||||
|
||||
// Name + email, or an explicit anonymous tick.
|
||||
var ident = el("fieldset");
|
||||
ident.setAttribute("data-field", "identity");
|
||||
ident.appendChild(el("legend", "", "About you"));
|
||||
var nf = el("div", "field");
|
||||
var nl = el("label", "", "Your name");
|
||||
nl.setAttribute("for", "fb-name");
|
||||
var ni = el("input");
|
||||
ni.type = "text";
|
||||
ni.id = "fb-name";
|
||||
ni.name = "name";
|
||||
ni.autocomplete = "name";
|
||||
nf.appendChild(nl); nf.appendChild(ni);
|
||||
ident.appendChild(nf);
|
||||
var ef = el("div", "field");
|
||||
var elab = el("label", "", "Your email");
|
||||
elab.setAttribute("for", "fb-email");
|
||||
var ei = el("input");
|
||||
ei.type = "email";
|
||||
ei.id = "fb-email";
|
||||
ei.name = "email";
|
||||
ei.autocomplete = "email";
|
||||
ef.appendChild(elab); ef.appendChild(ei);
|
||||
ident.appendChild(ef);
|
||||
var anonLab = el("label", "choice");
|
||||
var anon = el("input");
|
||||
anon.type = "checkbox";
|
||||
anon.id = "fb-anonymous";
|
||||
anon.name = "anonymous";
|
||||
anonLab.appendChild(anon);
|
||||
anonLab.appendChild(el("span", "", "Send this anonymously — do not record my name or email."));
|
||||
ident.appendChild(anonLab);
|
||||
var idErr = el("p", "err", "");
|
||||
idErr.hidden = true;
|
||||
ident.appendChild(idErr);
|
||||
form.appendChild(ident);
|
||||
|
||||
FIELDS.forEach(function (spec) { form.appendChild(fieldBlock(spec)); });
|
||||
|
||||
var line = status("", "");
|
||||
@@ -712,23 +640,6 @@
|
||||
form.appendChild(btn);
|
||||
form.appendChild(line);
|
||||
|
||||
// The anonymous tick hides the name/email and removes "with my name" as an option.
|
||||
function syncAnon() {
|
||||
var on = anon.checked;
|
||||
nf.hidden = on;
|
||||
ef.hidden = on;
|
||||
var yesName = form.querySelector('input[name="publish"][value="yes"]');
|
||||
if (yesName) {
|
||||
yesName.disabled = on;
|
||||
if (on && yesName.checked) {
|
||||
var alt = form.querySelector('input[name="publish"][value="name"]');
|
||||
if (alt) alt.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
anon.addEventListener("change", syncAnon);
|
||||
syncAnon();
|
||||
|
||||
form.addEventListener("submit", function (ev) {
|
||||
ev.preventDefault();
|
||||
var bad = false;
|
||||
@@ -753,22 +664,6 @@
|
||||
err.hidden = true;
|
||||
}
|
||||
});
|
||||
var anonymous = anon.checked;
|
||||
if (!anonymous) {
|
||||
var nm = ni.value.trim(), em = ei.value.trim();
|
||||
if (!nm || !em) {
|
||||
ident.setAttribute("data-bad", "1");
|
||||
idErr.textContent = "Add your name and email — or tick “Send this anonymously”.";
|
||||
idErr.hidden = false;
|
||||
bad = true;
|
||||
} else {
|
||||
ident.removeAttribute("data-bad");
|
||||
idErr.hidden = true;
|
||||
}
|
||||
} else {
|
||||
ident.removeAttribute("data-bad");
|
||||
idErr.hidden = true;
|
||||
}
|
||||
if (bad) {
|
||||
line.className = "status state-warn";
|
||||
line.textContent = "Some answers are missing — see the notes above.";
|
||||
@@ -777,14 +672,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the body. An anonymous sender sends no name and no email.
|
||||
var body = {
|
||||
form: "feedback",
|
||||
upwork: claimedUpwork,
|
||||
name: anonymous ? "" : ni.value.trim(),
|
||||
email: anonymous ? "" : ei.value.trim(),
|
||||
anonymous: anonymous
|
||||
};
|
||||
// Build the body. The claimed Upwork name rides along ONLY when a seat was claimed
|
||||
// this visit — state the page already holds. With no claim the submission carries no
|
||||
// name and is anonymous (nothing is invented).
|
||||
var body = { form: "feedback" };
|
||||
if (claimedUpwork) body.upwork = claimedUpwork;
|
||||
FIELDS.forEach(function (spec) {
|
||||
if (spec.type === "radio") {
|
||||
var checked = form.querySelector('input[name="' + spec.id + '"]:checked');
|
||||
@@ -838,14 +730,13 @@
|
||||
}
|
||||
var seats = count(res.body, C.leftKeys);
|
||||
var total = count(res.body, C.totalKeys);
|
||||
var entries = pick(res.body, C.feedbackKeys);
|
||||
if (seats === null) {
|
||||
// No count came off the wire: print no number.
|
||||
stateUnavailable(null, "the reply carried no seat count");
|
||||
return;
|
||||
}
|
||||
if (seats === 0) {
|
||||
stateFull(Array.isArray(entries) ? entries : []);
|
||||
stateFull();
|
||||
return;
|
||||
}
|
||||
stateAvailable(seats, total);
|
||||
|
||||
Reference in new issue
Block a user