msos/guide-feedback.js

153 lines
5.7 KiB
JavaScript

/*
* Student Welcome Guide - "Was this page helpful?" feedback.
* On a vote the chosen button is highlighted and locked, a thank-you appears,
* 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.
*
* "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
* is not configured, the widget still works locally and "No" still routes the
* visitor to email / the Viber community.
*/
(function () {
'use strict';
/* Google Form collection. Fill in all four values to turn it on. */
var GOOGLE_FORM = {
action: 'https://docs.google.com/forms/d/e/1FAIpQLSc4i6fdIpR3D8nuEJ_yvxlHUBRRO-8Z-vP4WF-qtk17uzkYog/formResponse',
pageField: 'entry.1926483387', // Question 1 (Page)
helpfulField: 'entry.971202016', // Question 2 (Helpful)
commentField: 'entry.1634915934' // Question 3 (Comment)
};
// "No" votes awaiting submission (visitor clicked No but has not sent yet).
var pending = [];
function pageLabel(slug) {
var lang = (document.documentElement.getAttribute('lang') || '').toLowerCase();
return lang ? slug + ' (' + lang + ')' : slug;
}
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; }
try {
// no-cors: Google Forms returns no CORS headers, so the response cannot be
// read, but the submission is recorded.
fetch(GOOGLE_FORM.action, { method: 'POST', mode: 'no-cors', body: buildParams(slug, value, comment) });
} catch (e) {}
if (typeof window.plausible === 'function') {
try { window.plausible('Guide feedback', { props: { page: pageLabel(slug), value: value || 'comment' } }); } catch (e) {}
}
}
function flushPending() {
for (var i = 0; i < pending.length; i++) {
var slug = pending[i];
if (GOOGLE_FORM.action && navigator.sendBeacon) {
try { navigator.sendBeacon(GOOGLE_FORM.action, buildParams(slug, 'no', '')); } catch (e) {}
} else {
submit(slug, 'no', '');
}
}
pending = [];
}
window.addEventListener('pagehide', flushPending);
function setup(fb) {
if (fb.getAttribute('data-ready')) { return; }
fb.setAttribute('data-ready', '1');
var slug = fb.getAttribute('data-slug') || location.pathname;
var buttons = fb.querySelectorAll('[data-vote]');
var yesMsg = fb.querySelector('.guide-feedback-yes');
var noMsg = fb.querySelector('.guide-feedback-no');
var comment = fb.querySelector('.guide-feedback-comment');
var commentInput = fb.querySelector('.guide-feedback-comment-input');
var commentSend = fb.querySelector('.guide-feedback-comment-send');
var commentThanks = fb.querySelector('.guide-feedback-comment-thanks');
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() {
/* jshint validthis:true */
if (voted) { return; }
voted = true;
var value = this.getAttribute('data-vote');
for (var i = 0; i < buttons.length; i++) { buttons[i].disabled = true; }
this.classList.add('is-selected');
if (value === 'yes') {
submit(slug, 'yes', '');
if (yesMsg) { yesMsg.hidden = false; }
} 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 (comment) { comment.hidden = false; }
}
}
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', onVote);
}
if (commentSend) {
commentSend.addEventListener('click', function () {
var text = commentInput ? commentInput.value.replace(/^\s+|\s+$/g, '') : '';
if (!text) { if (commentInput) { commentInput.focus(); } return; }
clearPending();
submit(slug, 'no', text);
commentSend.disabled = true;
if (commentInput) { commentInput.disabled = true; }
if (commentThanks) { commentThanks.hidden = false; }
});
}
}
function init() {
var widgets = document.querySelectorAll('.guide-feedback');
for (var i = 0; i < widgets.length; i++) { setup(widgets[i]); }
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
/*
* ---------------------------------------------------------------------------
* Google Form collection
* ---------------------------------------------------------------------------
* Three questions are wired: Page, Helpful, Comment (entry IDs above).
* The "Page" value sent is the slug plus language, e.g.
* "student-residence-permit (mk)".
*
* 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.
*/