Skip to content

Commit df111e6

Browse files
authored
Merge pull request #209 from cmu-delphi/sgratzl/feedback
feat: simple feedback button/form
2 parents 74d0a80 + e89bd3c commit df111e6

File tree

12 files changed

+187
-0
lines changed

12 files changed

+187
-0
lines changed

config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,9 @@ relativeURLs = false
111111
mission = "Develop the theory and practice of epidemiological forecasting, with a long-term vision of making this technology as universally accepted and useful as weather forecasting is today."
112112
apiUrl = "https://cmu-delphi.github.io/delphi-epidata"
113113
twitter = "CmuDelphi"
114+
feedbackForm = "https://docs.google.com/forms/d/e/1FAIpQLSeIeOJtrAhdOriEyiRY7LkpQX8DZBY19dl6De8l56Q9CZhmxw/viewform?usp=pp_url&entry.1245962748="
115+
feedbackLikelihoodMobile = 0.2
116+
feedbackLikelihoodDesktop = 1
117+
feedbackDelayMin = 10 # in sec
118+
feedbackDelayMax = 100 # in sec
119+
feedbackDuration = 60 # show it for 60sec

content/covidcast/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ order: 1
77
modeTitle: Map Overview
88
icon: solid/map
99
heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg
10+
feedback: true
1011
---

content/covidcast/export.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ app_mode: export
77
order: 6
88
icon: solid/download
99
heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg
10+
feedback: true
1011
---

content/covidcast/single.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ app_mode: single
77
order: 4
88
icon: location-solid
99
heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg
10+
feedback: true
1011
---

content/covidcast/survey-results.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ app_mode: survey-results
88
order: 5
99
icon: solid/poll
1010
heroImage: /images/landing-page/hero-images/covidcast_survey.jpg
11+
feedback: true
1112
---

content/covidcast/timelapse.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ app_mode: timelapse
77
order: 2
88
icon: solid/clock
99
heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg
10+
feedback: true
1011
---

content/covidcast/top10.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ app_mode: top10
77
order: 3
88
icon: solid/list
99
heroImage: /images/landing-page/hero-images/covidcast_withfill.jpg
10+
feedback: true
1011
---
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
.feedback-modal {
2+
padding-top: 1.5em;
3+
height: calc(100vh - 100px);
4+
display: block;
5+
position: relative;
6+
}
7+
8+
.feedback-modal > iframe {
9+
width: 100%;
10+
height: 100%;
11+
}
12+
13+
.feedback-message {
14+
width: unset;
15+
16+
.uk-notification-message {
17+
background: #60a5fa;
18+
color: white;
19+
border-radius: 5px;
20+
padding: 0;
21+
font-size: 1rem;
22+
cursor: inherit;
23+
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.25);
24+
25+
> div {
26+
padding: 1.5rem 3rem 1.5rem 1.5rem;
27+
display: flex;
28+
justify-content: flex-start;
29+
align-items: center;
30+
}
31+
}
32+
33+
.uk-notification-close {
34+
color: white;
35+
right: 1rem;
36+
top: 50%;
37+
transform: translateY(-50%);
38+
display: block !important;
39+
}
40+
41+
.uk-button {
42+
background: #2563eb;
43+
border-radius: 3px;
44+
// padding: 14px 24px 14px 24px;
45+
}
46+
}
47+
48+
.feedback-text {
49+
margin-right: 1em;
50+
}
51+
52+
@media only screen and (max-width: 550px) {
53+
.feedback-message {
54+
.uk-notification-message > div {
55+
flex-direction: column;
56+
}
57+
58+
.uk-button {
59+
margin-top: 0.5em;
60+
}
61+
}
62+
}

themes/delphi/assets/css/main.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
@import "./components/card_grid";
2424
@import "./components/latest_card";
2525
@import "./components/toc";
26+
@import "./components/feedback";
2627

2728
// Page Designs
2829
@import "./pages/about";
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import UIkit from "uikit/dist/js/uikit.js";
2+
3+
(() => {
4+
const markSubmitted = () => {
5+
if (!localStorage) {
6+
return;
7+
}
8+
// expires in a month
9+
const expiresAt = new Date();
10+
expiresAt.setMonth(expiresAt.getMonth() + 1);
11+
localStorage.setItem("feedback-submitted", expiresAt.toString());
12+
13+
// send a google analytics event
14+
if (window.ga) {
15+
window.ga("send", "event", "feedback", "open", "show feedback form", "true");
16+
}
17+
};
18+
const wasRecentlySubmitted = () => {
19+
if (!localStorage) {
20+
return false;
21+
}
22+
const r = localStorage.getItem("feedback-submitted");
23+
if (!r) {
24+
return false;
25+
}
26+
const expiresAt = new Date(r);
27+
// check if expired
28+
return expiresAt > new Date();
29+
};
30+
31+
const showForm = (formLink) => {
32+
const url = `${formLink}${encodeURIComponent(location.href)}&embedded=true`;
33+
UIkit.modal.dialog(
34+
`<button class="uk-modal-close-default" type="button" uk-close></button><div class="feedback-modal"><iframe src="${url}" uk-video></iframe></div>`
35+
);
36+
};
37+
const btn = document.querySelector(".feedback-button");
38+
if (btn) {
39+
btn.addEventListener("click", (e) => {
40+
e.preventDefault();
41+
markSubmitted();
42+
showForm(btn.href);
43+
});
44+
}
45+
46+
const container = document.querySelector(".feedback-popup-container");
47+
48+
if (container && !wasRecentlySubmitted()) {
49+
const delayMin = Number.parseInt(container.dataset.delayMin, 10) * 1000;
50+
const delayMax = Number.parseInt(container.dataset.delayMax, 10) * 1000;
51+
const delay = delayMin + Math.random() * (delayMax - delayMin);
52+
53+
const feedbackLikelihoodDesktop = Number.parseFloat(container.dataset.likeDesktop);
54+
const feedbackLikelihoodMobile = Number.parseFloat(container.dataset.likeMobile);
55+
const isMobile = window.matchMedia("only screen and (max-width: 700px)").matches;
56+
57+
const showByChance =
58+
(isMobile && Math.random() <= feedbackLikelihoodMobile) ||
59+
(!isMobile && Math.random() <= feedbackLikelihoodDesktop);
60+
61+
const duration = Number.parseInt(container.dataset.duration, 10) * 1000;
62+
const formLink = container.dataset.href;
63+
64+
const showFeedbackNotification = () => {
65+
const elem = UIkit.notification({
66+
message: container.innerHTML,
67+
pos: "bottom-right",
68+
timeout: duration,
69+
clsContainer: "feedback-message uk-notification",
70+
});
71+
elem.$el.querySelector("div").addEventListener("click", (e) => {
72+
// stop auto close
73+
e.stopPropagation();
74+
e.preventDefault();
75+
});
76+
elem.$el.querySelector(".feedback-button").addEventListener("click", (e) => {
77+
e.preventDefault();
78+
elem.close(true);
79+
markSubmitted();
80+
showForm(formLink);
81+
});
82+
};
83+
// initial delay
84+
if (showByChance) {
85+
setTimeout(showFeedbackNotification, delay);
86+
}
87+
}
88+
})();

0 commit comments

Comments
 (0)