Feedback: space/bottom-align Send button; submit No + comment as one row
Comment area is now a flex row that bottom-aligns the Send button with a 16px gap. The No vote is held until the comment is sent so both land on one Google Form row; a No left without a comment is still recorded once via sendBeacon on page-leave. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fd4cd73b51
commit
f174bb5f68
|
|
@ -511,16 +511,20 @@
|
||||||
.guide-feedback-comment {
|
.guide-feedback-comment {
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
.guide-feedback-comment-label {
|
.guide-feedback-comment-label {
|
||||||
display: block;
|
flex-basis: 100%;
|
||||||
margin-bottom: 6px;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #344054;
|
color: #344054;
|
||||||
}
|
}
|
||||||
.guide-feedback-comment-input {
|
.guide-feedback-comment-input {
|
||||||
width: 100%;
|
flex: 1 1 300px;
|
||||||
max-width: 520px;
|
max-width: 520px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border: 1.5px solid #D0D5DD;
|
border: 1.5px solid #D0D5DD;
|
||||||
|
|
@ -535,9 +539,9 @@
|
||||||
box-shadow: 0 0 0 3px rgba(115, 96, 242, .15);
|
box-shadow: 0 0 0 3px rgba(115, 96, 242, .15);
|
||||||
}
|
}
|
||||||
.guide-feedback-comment-send {
|
.guide-feedback-comment-send {
|
||||||
display: inline-block;
|
flex: 0 0 auto;
|
||||||
margin-top: 8px;
|
margin: 0;
|
||||||
padding: 8px 20px;
|
padding: 11px 24px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #7360F2;
|
background: #7360F2;
|
||||||
|
|
@ -557,7 +561,8 @@
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
.guide-feedback-comment-thanks {
|
.guide-feedback-comment-thanks {
|
||||||
margin: 8px 0 0;
|
flex-basis: 100%;
|
||||||
|
margin: 0;
|
||||||
color: #12B76A;
|
color: #12B76A;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@
|
||||||
* and (on "No") a short comment box is offered. Voting is locked for the current
|
* and (on "No") a short comment box is offered. Voting is locked for the current
|
||||||
* page view, so to give feedback again the visitor reloads the page.
|
* page view, so to give feedback again the visitor reloads the page.
|
||||||
*
|
*
|
||||||
|
* "Yes" is submitted immediately. "No" is submitted together with the comment as
|
||||||
|
* a single row when the visitor clicks Send; if they leave without commenting,
|
||||||
|
* the "No" is still recorded once via sendBeacon on page-leave. This avoids the
|
||||||
|
* "No" and the comment landing on two separate rows.
|
||||||
|
*
|
||||||
* Votes are sent to a Google Form (see setup notes at the bottom). If the form
|
* Votes are sent to a Google Form (see setup notes at the bottom). If the form
|
||||||
* is not configured, the widget still works locally and "No" still routes the
|
* is not configured, the widget still works locally and "No" still routes the
|
||||||
* visitor to email / the Viber community.
|
* visitor to email / the Viber community.
|
||||||
|
|
@ -19,46 +24,46 @@
|
||||||
commentField: 'entry.1634915934' // Question 3 (Comment)
|
commentField: 'entry.1634915934' // Question 3 (Comment)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// "No" votes awaiting submission (visitor clicked No but has not sent yet).
|
||||||
|
var pending = [];
|
||||||
|
|
||||||
function pageLabel(slug) {
|
function pageLabel(slug) {
|
||||||
var lang = (document.documentElement.getAttribute('lang') || '').toLowerCase();
|
var lang = (document.documentElement.getAttribute('lang') || '').toLowerCase();
|
||||||
return lang ? slug + ' (' + lang + ')' : slug;
|
return lang ? slug + ' (' + lang + ')' : slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
function postToForm(fields) {
|
function buildParams(slug, value, comment) {
|
||||||
|
var params = new URLSearchParams();
|
||||||
|
params.append(GOOGLE_FORM.pageField, pageLabel(slug));
|
||||||
|
if (value) { params.append(GOOGLE_FORM.helpfulField, value); }
|
||||||
|
if (comment && GOOGLE_FORM.commentField) { params.append(GOOGLE_FORM.commentField, comment); }
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit(slug, value, comment) {
|
||||||
if (!GOOGLE_FORM.action) { return; }
|
if (!GOOGLE_FORM.action) { return; }
|
||||||
try {
|
try {
|
||||||
var body = new URLSearchParams();
|
|
||||||
for (var key in fields) {
|
|
||||||
if (fields.hasOwnProperty(key) && key) { body.append(key, fields[key]); }
|
|
||||||
}
|
|
||||||
// no-cors: Google Forms returns no CORS headers, so the response cannot be
|
// no-cors: Google Forms returns no CORS headers, so the response cannot be
|
||||||
// read, but the submission is recorded.
|
// read, but the submission is recorded.
|
||||||
fetch(GOOGLE_FORM.action, { method: 'POST', mode: 'no-cors', body: body });
|
fetch(GOOGLE_FORM.action, { method: 'POST', mode: 'no-cors', body: buildParams(slug, value, comment) });
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
|
||||||
|
|
||||||
function sendVote(slug, value) {
|
|
||||||
var label = pageLabel(slug);
|
|
||||||
var fields = {};
|
|
||||||
fields[GOOGLE_FORM.pageField] = label;
|
|
||||||
fields[GOOGLE_FORM.helpfulField] = value;
|
|
||||||
postToForm(fields);
|
|
||||||
if (typeof window.plausible === 'function') {
|
if (typeof window.plausible === 'function') {
|
||||||
try { window.plausible('Guide feedback', { props: { page: label, value: value } }); } catch (e) {}
|
try { window.plausible('Guide feedback', { props: { page: pageLabel(slug), value: value || 'comment' } }); } catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendComment(slug, text) {
|
function flushPending() {
|
||||||
var label = pageLabel(slug);
|
for (var i = 0; i < pending.length; i++) {
|
||||||
var fields = {};
|
var slug = pending[i];
|
||||||
fields[GOOGLE_FORM.pageField] = label;
|
if (GOOGLE_FORM.action && navigator.sendBeacon) {
|
||||||
// helpful field is left out so comment rows do not inflate the Yes/No counts.
|
try { navigator.sendBeacon(GOOGLE_FORM.action, buildParams(slug, 'no', '')); } catch (e) {}
|
||||||
if (GOOGLE_FORM.commentField) { fields[GOOGLE_FORM.commentField] = text; }
|
} else {
|
||||||
postToForm(fields);
|
submit(slug, 'no', '');
|
||||||
if (typeof window.plausible === 'function') {
|
}
|
||||||
try { window.plausible('Guide feedback comment', { props: { page: label } }); } catch (e) {}
|
|
||||||
}
|
}
|
||||||
|
pending = [];
|
||||||
}
|
}
|
||||||
|
window.addEventListener('pagehide', flushPending);
|
||||||
|
|
||||||
function setup(fb) {
|
function setup(fb) {
|
||||||
if (fb.getAttribute('data-ready')) { return; }
|
if (fb.getAttribute('data-ready')) { return; }
|
||||||
|
|
@ -73,6 +78,14 @@
|
||||||
var commentSend = fb.querySelector('.guide-feedback-comment-send');
|
var commentSend = fb.querySelector('.guide-feedback-comment-send');
|
||||||
var commentThanks = fb.querySelector('.guide-feedback-comment-thanks');
|
var commentThanks = fb.querySelector('.guide-feedback-comment-thanks');
|
||||||
var voted = false;
|
var voted = false;
|
||||||
|
var noPending = false;
|
||||||
|
|
||||||
|
function clearPending() {
|
||||||
|
if (!noPending) { return; }
|
||||||
|
noPending = false;
|
||||||
|
var idx = pending.indexOf(slug);
|
||||||
|
if (idx > -1) { pending.splice(idx, 1); }
|
||||||
|
}
|
||||||
|
|
||||||
function onVote() {
|
function onVote() {
|
||||||
/* jshint validthis:true */
|
/* jshint validthis:true */
|
||||||
|
|
@ -81,10 +94,14 @@
|
||||||
var value = this.getAttribute('data-vote');
|
var value = this.getAttribute('data-vote');
|
||||||
for (var i = 0; i < buttons.length; i++) { buttons[i].disabled = true; }
|
for (var i = 0; i < buttons.length; i++) { buttons[i].disabled = true; }
|
||||||
this.classList.add('is-selected');
|
this.classList.add('is-selected');
|
||||||
sendVote(slug, value);
|
|
||||||
if (value === 'yes') {
|
if (value === 'yes') {
|
||||||
|
submit(slug, 'yes', '');
|
||||||
if (yesMsg) { yesMsg.hidden = false; }
|
if (yesMsg) { yesMsg.hidden = false; }
|
||||||
} else {
|
} else {
|
||||||
|
// Hold the "No" until the comment is sent (or the page is left), so both
|
||||||
|
// land on the same row.
|
||||||
|
noPending = true;
|
||||||
|
pending.push(slug);
|
||||||
if (noMsg) { noMsg.hidden = false; }
|
if (noMsg) { noMsg.hidden = false; }
|
||||||
if (comment) { comment.hidden = false; }
|
if (comment) { comment.hidden = false; }
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +115,8 @@
|
||||||
commentSend.addEventListener('click', function () {
|
commentSend.addEventListener('click', function () {
|
||||||
var text = commentInput ? commentInput.value.replace(/^\s+|\s+$/g, '') : '';
|
var text = commentInput ? commentInput.value.replace(/^\s+|\s+$/g, '') : '';
|
||||||
if (!text) { if (commentInput) { commentInput.focus(); } return; }
|
if (!text) { if (commentInput) { commentInput.focus(); } return; }
|
||||||
sendComment(slug, text);
|
clearPending();
|
||||||
|
submit(slug, 'no', text);
|
||||||
commentSend.disabled = true;
|
commentSend.disabled = true;
|
||||||
if (commentInput) { commentInput.disabled = true; }
|
if (commentInput) { commentInput.disabled = true; }
|
||||||
if (commentThanks) { commentThanks.hidden = false; }
|
if (commentThanks) { commentThanks.hidden = false; }
|
||||||
|
|
@ -122,17 +140,13 @@
|
||||||
* ---------------------------------------------------------------------------
|
* ---------------------------------------------------------------------------
|
||||||
* Google Form collection
|
* Google Form collection
|
||||||
* ---------------------------------------------------------------------------
|
* ---------------------------------------------------------------------------
|
||||||
* The form currently has two questions wired: Page and Helpful.
|
* Three questions are wired: Page, Helpful, Comment (entry IDs above).
|
||||||
* To capture the "what was missing" comments, add a THIRD question to the same
|
|
||||||
* Google Form:
|
|
||||||
* - Type: Paragraph
|
|
||||||
* - Title: Comment
|
|
||||||
* Then open the three-dot menu > "Get pre-filled link", type any text in all
|
|
||||||
* three boxes, get the link, and read off the new entry.NNNN for the Comment
|
|
||||||
* box. Paste it into GOOGLE_FORM.commentField above.
|
|
||||||
*
|
|
||||||
* The "Page" value sent is the slug plus language, e.g.
|
* The "Page" value sent is the slug plus language, e.g.
|
||||||
* "student-residence-permit (mk)". Comment submissions omit the Helpful answer
|
* "student-residence-permit (mk)".
|
||||||
* so they do not affect the Yes/No counts; filter the sheet by a non-empty
|
*
|
||||||
* Comment column to read them.
|
* Rows you will see:
|
||||||
|
* - Yes vote -> Helpful = yes, Comment empty
|
||||||
|
* - No with a comment -> Helpful = no, Comment = the text (one row)
|
||||||
|
* - No without comment -> Helpful = no, Comment empty (sent on leave)
|
||||||
|
* A "No" produces exactly one row either way.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue