'use strict';
//import firebase from "firebase/app";
//import "firebase/functions";
//
//const FIREBASE_CONFIG = {
// apiKey: "AIzaSyDKw8HXWRn3dtkIRETv-BxtwnklZIpIkxU",
// authDomain: "vaxandme.firebaseapp.com",
// projectId: "vaxandme",
// storageBucket: "vaxandme.appspot.com",
// messagingSenderId: "614738281692",
// appId: "1:614738281692:web:7a89e0af76675e4f0ea019",
// measurementId: "G-N016TVJ6JC",
//};
//
//firebase.initializeApp(FIREBASE_CONFIG);
//
//if (window.location.host !== "www.vaxand.me") {
// firebase.functions().useEmulator(window.location.host.split(":")[0], 5001);
//}
//
//const fb_functions = firebase.functions();
//const saveResult = fb_functions.httpsCallable("saveResultV1");
//const getPastResult = fb_functions.httpsCallable("getPastResultV1");
//const getStats = fb_functions.httpsCallable("getStatsV1");
const saveResult = () => new Promise((resolve, reject) => reject());
const getPastResult = () => new Promise((resolve, reject) => reject());
const getStats = () => new Promise((resolve, reject) => reject());
const VAX_NAMES = {
PB: "Pfizer-BioNTech",
MD: "Moderna",
JJ: "J&J Janssen",
};
const VAX_DOSES = {
ONE: "Dose 1",
TWO: "Dose 2",
};
const VAX_AGES = {
"Pfizer-BioNTech": {
YOUNGER: "18 - 55",
OLDER: "56 & Older",
},
Moderna: {
YOUNGER: "18 - 64",
OLDER: "65 & Older",
},
"J&J Janssen": {
YOUNGER: "18 - 59",
OLDER: "60 & Older",
},
};
const VAX_EFFICACY = {
"Pfizer-BioNTech": {
"18 - 55": 95.3,
"56 & Older": 93.8,
},
Moderna: {
"18 - 64": 95.6,
"65 & Older": 86.4,
},
"J&J Janssen": {
"18 - 59": 66.1,
"60 & Older": 66.2,
},
};
const PB_DOSE1_YOUNGER_RESULT = {
"PAIN AT SITE": 1904,
"RED AT SITE": 104,
"SWELLING AT SITE": 132,
FATIGUE: 1085,
HEADACHE: 959,
"NEED PAIN MED": 638,
"MUSCLE PAIN": 487,
CHILLS: 321,
DIARRHEA: 255,
"JOINT PAIN": 251,
FEVER: 85,
VOMITING: 28,
};
const PB_DOSE2_YOUNGER_RESULT = {
"PAIN AT SITE": 1632,
"RED AT SITE": 123,
"SWELLING AT SITE": 132,
FATIGUE: 1247,
HEADACHE: 1085,
"NEED PAIN MED": 945,
"MUSCLE PAIN": 783,
CHILLS: 737,
"JOINT PAIN": 459,
FEVER: 331,
DIARRHEA: 219,
VOMITING: 40,
};
const PB_DOSE1_OLDER_RESULT = {
"PAIN AT SITE": 1282,
"RED AT SITE": 85,
"SWELLING AT SITE": 118,
FATIGUE: 615,
HEADACHE: 454,
"NEED PAIN MED": 358,
"MUSCLE PAIN": 251,
CHILLS: 113,
DIARRHEA: 147,
"JOINT PAIN": 155,
FEVER: 26,
VOMITING: 9,
};
const PB_DOSE2_OLDER_RESULT = {
"PAIN AT SITE": 1098,
"RED AT SITE": 120,
"SWELLING AT SITE": 124,
FATIGUE: 839,
HEADACHE: 647,
"NEED PAIN MED": 625,
"MUSCLE PAIN": 477,
CHILLS: 377,
"JOINT PAIN": 313,
DIARRHEA: 137,
FEVER: 181,
VOMITING: 11,
};
const MD_DOSE1_YOUNGER_RESULT = {
"PAIN AT SITE": 9908,
"RED AT SITE": 344,
"SWELLING AT SITE": 2089,
HEADACHE: 4030,
FATIGUE: 4384,
"MUSCLE PAIN": 2699,
"JOINT PAIN": 1893,
"NAUSEA OR VOMITING": 1068,
FEVER: 105,
CHILLS: 1051,
};
const MD_DOSE2_YOUNGER_RESULT = {
"PAIN AT SITE": 9873,
"RED AT SITE": 982,
"SWELLING AT SITE": 3164,
FATIGUE: 7430,
HEADACHE: 6898,
"JOINT PAIN": 4993,
CHILLS: 5341,
FEVER: 1908,
"NAUSEA OR VOMITING": 2348,
"MUSCLE PAIN": 6769,
};
const MD_DOSE1_OLDER_RESULT = {
"PAIN AT SITE": 2782,
"RED AT SITE": 86,
"SWELLING AT SITE": 396,
HEADACHE: 921,
FATIGUE: 1251,
"MUSCLE PAIN": 742,
"JOINT PAIN": 618,
"NAUSEA OR VOMITING": 194,
CHILLS: 202,
FEVER: 10,
};
const MD_DOSE2_OLDER_RESULT = {
"PAIN AT SITE": 3070,
"RED AT SITE": 275,
"SWELLING AT SITE": 715,
FATIGUE: 2152,
HEADACHE: 1704,
"MUSCLE PAIN": 1739,
"JOINT PAIN": 1291,
CHILLS: 1141,
"NAUSEA OR VOMITING": 437,
FEVER: 370,
};
const JJ_YOUNGER_RESULT = {
"PAIN AT SITE": 1193,
"RED AT SITE": 184,
"SWELLING AT SITE": 142,
HEADACHE: 905,
FATIGUE: 891,
"MUSCLE PAIN": 796,
"NEED PAIN MED": 538,
"NAUSEA OR VOMITING": 315,
FEVER: 261,
};
const JJ_OLDER_RESULT = {
"PAIN AT SITE": 439,
"RED AT SITE": 61,
"SWELLING AT SITE": 36,
HEADACHE: 401,
FATIGUE: 392,
"MUSCLE PAIN": 317,
"NAUSEA OR VOMITING": 162,
"NEED PAIN MED": 130,
FEVER: 41,
};
const VAX_COUNTS = {
"Pfizer-BioNTech": {
"Dose 1": {
"18 - 55": 2291,
"56 & Older": 1802,
},
"Dose 2": {
"18 - 55": 2098,
"56 & Older": 1660,
},
},
Moderna: {
"Dose 1": {
"18 - 64": 11406,
"65 & Older": 3762,
},
"Dose 2": {
"18 - 64": 10985,
"65 & Older": 3692,
},
},
"J&J Janssen": {
"18 - 59": 2036,
"60 & Older": 1320,
},
};
const VAX_RESULTS = {
"Pfizer-BioNTech": {
"Dose 1": {
"18 - 55": PB_DOSE1_YOUNGER_RESULT,
"56 & Older": PB_DOSE1_OLDER_RESULT,
},
"Dose 2": {
"18 - 55": PB_DOSE2_YOUNGER_RESULT,
"56 & Older": PB_DOSE2_OLDER_RESULT,
},
},
Moderna: {
"Dose 1": {
"18 - 64": MD_DOSE1_YOUNGER_RESULT,
"65 & Older": MD_DOSE1_OLDER_RESULT,
},
"Dose 2": {
"18 - 64": MD_DOSE2_YOUNGER_RESULT,
"65 & Older": MD_DOSE2_OLDER_RESULT,
},
},
"J&J Janssen": {
"18 - 59": JJ_YOUNGER_RESULT,
"60 & Older": JJ_OLDER_RESULT,
},
};
const ICONS = {
"PAIN AT SITE": "💪",
"RED AT SITE": "🔴",
"SWELLING AT SITE": "💢",
FATIGUE: "🥱",
HEADACHE: "🤕",
"NEED PAIN MED": "💊",
"MUSCLE PAIN": "😫",
CHILLS: "🥶",
DIARRHEA: "💩",
"JOINT PAIN": "🦵",
FEVER: "🤒",
VOMITING: "🤮",
"NAUSEA OR VOMITING": "🤮",
};
const DATA_CHOICES = {
VNM: "Vax&Me Only",
VNM_FDA: "Vax&Me + FDA",
FDA: "FDA Only",
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
dose: "",
age: "",
gid: "",
showInputPopup: false,
answers: {},
fetchedResult: {},
stats: {},
loading: true,
dataSource: DATA_CHOICES.FDA,
loadingMessage: "Gettings stats...",
};
}
componentDidMount() {
const params = window.location.pathname
.toUpperCase()
.split("/")
.filter((v) => v.length > 0);
if (params) {
if (params[0] && VAX_NAMES[params[0]]) {
const name = VAX_NAMES[params[0]];
this.setState({ name });
switch (name) {
case VAX_NAMES.PB:
case VAX_NAMES.MD:
if (params[1] && VAX_DOSES[params[1]]) {
const dose = VAX_DOSES[params[1]];
this.setState({ dose });
if (params[2] && VAX_AGES[name][params[2]]) {
const age = VAX_AGES[name][params[2]];
this.setState({ age });
}
}
break;
case VAX_NAMES.JJ:
if (params[1] && VAX_AGES[name][params[1]]) {
const age = VAX_AGES[name][params[1]];
this.setState({ age });
}
break;
default:
break;
}
}
}
getStats()
.then((v) => {
// GET & SET STATS
this.setState({
stats: v.data,
loadingMessage: "Checking for existing answers...",
dataSource: DATA_CHOICES.VNM_FDA,
});
// HAS GID?
if (window.gtag) {
// GET GID
window.gtag("get", "G-7VZCY223NG", "client_id", (client_id) => {
// GOT GID?
if (!!client_id) {
// SAVE GID
this.setState({ gid: client_id });
// GET PAST ANSWER FOR GID
getPastResult({ gid: client_id })
.then((v) => {
const { data } = v;
if (!!data) {
const { name, age, dose } = data;
this.setState({
name,
age,
dose,
showInputPopup: true,
fetchedResult: data,
answers: data,
loading: false,
});
const inputPopupSubmit = document.querySelector(
".input-popup-submit"
);
if (inputPopupSubmit) {
inputPopupSubmit.classList.add("submitted");
inputPopupSubmit.innerHTML = "UPDATE ANSWER";
}
window.localStorage.setItem("hasExistingAnswer", true);
} else {
this.setState({ loading: false });
}
})
.catch((err) => {
console.error("ERROR Getting Past Answer", err);
this.setState({ loading: false });
});
} else {
this.setState({ loading: false });
}
});
} else {
this.setState({ loading: false });
}
})
.catch((err) => {
console.error("ERROR GETTING STATS", err);
this.setState({ loading: false });
});
}
shouldShowDoses = () => {
const { name, loading } = this.state;
if (loading) {
return false;
}
return name && (name === VAX_NAMES.PB || name === VAX_NAMES.MD);
};
shouldShowAgeGroup = () => {
const { name, dose, loading } = this.state;
if (loading) {
return false;
}
switch (name) {
case VAX_NAMES.JJ:
return true;
case VAX_NAMES.MD:
case VAX_NAMES.PB:
return !!dose;
default:
return false;
}
};
shouldShowResults = () => {
const { name, dose, age, loading } = this.state;
if (loading) {
return false;
}
switch (name) {
case VAX_NAMES.JJ:
return !!age;
case VAX_NAMES.MD:
case VAX_NAMES.PB:
return !!dose && !!age;
default:
return false;
}
};
getResults = () => {
const { name, dose, age, loading } = this.state;
if (loading) {
return null;
}
switch (name) {
case VAX_NAMES.JJ:
return VAX_RESULTS[name][age];
case VAX_NAMES.MD:
case VAX_NAMES.PB:
return VAX_RESULTS[name][dose][age];
default:
return null;
}
};
copyShareLink = () => {
const { name, dose, age } = this.state;
let SHARE_URL = window.location.host;
const serialize_name = Object.keys(VAX_NAMES).filter(
(k) => VAX_NAMES[k] === name
)[0];
const serialize_dose = Object.keys(VAX_DOSES).filter(
(k) => VAX_DOSES[k] === dose
)[0];
const serialize_age = Object.keys(VAX_AGES[name]).filter(
(k) => VAX_AGES[name][k] === age
)[0];
if (serialize_name) {
SHARE_URL += `/${serialize_name}`;
}
if (serialize_dose) {
SHARE_URL += `/${serialize_dose}`;
}
if (serialize_age) {
SHARE_URL += `/${serialize_age}`;
}
const copyTextInput = document.createElement("input");
document.body.appendChild(copyTextInput);
copyTextInput.value = SHARE_URL.toLowerCase();
copyTextInput.select();
document.execCommand("copy");
copyTextInput.remove();
document.querySelector("#share-result").innerHTML = "Copied Link";
setTimeout(() => {
document.querySelector("#share-result").innerHTML = "Share ↗";
}, 1500);
};
updateAnswers = (symptom, dose) => {
const { answers } = this.state;
const newAnswers = Object.assign({}, answers);
if (!!dose) {
if (!newAnswers[dose]) {
newAnswers[dose] = {};
}
if (newAnswers[dose][symptom]) {
newAnswers[dose][symptom] = !newAnswers[dose][symptom];
} else {
newAnswers[dose][symptom] = true;
}
} else {
if (newAnswers[symptom]) {
newAnswers[symptom] = !newAnswers[symptom];
} else {
newAnswers[symptom] = true;
}
}
this.setState({ answers: newAnswers });
};
submitAnswer = () => {
document.querySelector(".input-popup-submit").innerHTML = "SENDING...";
const { name, dose, age, answers, gid } = this.state;
const result = this.getResults();
const symptoms = {};
delete answers.name;
delete answers.dose;
delete answers.age;
if (!!dose) {
symptoms[VAX_DOSES.ONE] = {};
symptoms[VAX_DOSES.TWO] = {};
Object.keys(VAX_DOSES).forEach((dose) => {
Object.keys(result).forEach((symptom) => {
symptoms[VAX_DOSES[dose]][symptom] = false;
if (answers[VAX_DOSES[dose]] && answers[VAX_DOSES[dose]][symptom]) {
symptoms[VAX_DOSES[dose]][symptom] = true;
}
});
});
} else {
Object.keys(result).forEach((symptom) => {
symptoms[symptom] = false;
if (answers[symptom]) {
symptoms[symptom] = true;
}
});
}
const submitAnswers = Object.assign({}, symptoms, {
name,
dose,
age,
gid,
});
saveResult(submitAnswers)
.then(() => {
delete submitAnswers.gid;
this.setState({ fetchedResult: submitAnswers });
const inputPopupSubmit = document.querySelector(".input-popup-submit");
if (inputPopupSubmit.classList.contains("errored")) {
inputPopupSubmit.classList.remove("errored");
}
if (!inputPopupSubmit.classList.contains("submitted")) {
inputPopupSubmit.classList.add("submitted");
}
inputPopupSubmit.innerHTML = "THANK YOU!";
setTimeout(() => {
inputPopupSubmit.innerHTML = "UPDATE ANSWER";
}, 1500);
})
.catch((err) => {
const inputPopupSubmit = document.querySelector(".input-popup-submit");
if (!inputPopupSubmit.classList.contains("errored")) {
inputPopupSubmit.classList.add("errored");
}
if (inputPopupSubmit.classList.contains("submitted")) {
inputPopupSubmit.classList.remove("submitted");
}
inputPopupSubmit.innerHTML = "UH OH...UNABLE TO SEND";
setTimeout(() => {
inputPopupSubmit.innerHTML = "TRY AGAIN";
}, 1500);
console.error("ERROR Saving Answer", err);
});
};
getAnswerOptions = (answerDose) => {
const { answers, dose } = this.state;
const result = this.getResults();
return (
{!!answerDose ? (
{answerDose}
) : null}
{dose === VAX_DOSES.ONE && answerDose === VAX_DOSES.TWO
? "(You can comeback later to tell us about Dose 2)"
: "(Select all side effects that you experienced)"}
{Object.keys(result).map((symptom) => (
this.updateAnswers(symptom, answerDose)}
>
{symptom}
))}
);
};
getStatForSymptom = (symptom) => {
const { dataSource, name, dose, age, stats } = this.state;
switch (dataSource) {
case DATA_CHOICES.FDA:
if (!!dose) {
return Math.ceil(
(VAX_RESULTS[name][dose][age][symptom] /
VAX_COUNTS[name][dose][age]) *
10
);
} else {
return Math.ceil(
(VAX_RESULTS[name][age][symptom] / VAX_COUNTS[name][age]) * 10
);
}
case DATA_CHOICES.VNM:
if (Object.keys(stats).length > 0) {
if (!!dose) {
return Math.ceil(
(stats[name][dose][age][symptom] / stats[name][dose][age].count) *
10
);
} else {
return Math.ceil(
(stats[name][age][symptom] / stats[name][age].count) * 10
);
}
} else {
return 0;
}
case DATA_CHOICES.VNM_FDA:
let symptomCount = 0;
let totalCount = 0;
if (Object.keys(stats).length > 0) {
if (!!dose) {
symptomCount =
VAX_RESULTS[name][dose][age][symptom] +
stats[name][dose][age][symptom];
totalCount =
VAX_COUNTS[name][dose][age] + stats[name][dose][age].count;
} else {
symptomCount =
VAX_RESULTS[name][age][symptom] + stats[name][age][symptom];
totalCount = VAX_COUNTS[name][age] + stats[name][age].count;
}
} else {
symptomCount = VAX_RESULTS[name][dose][age][symptom];
totalCount = VAX_COUNTS[name][dose][age];
}
return Math.ceil((symptomCount / totalCount) * 10);
default:
return 1;
}
};
getSampleSize = () => {
const { dataSource, name, dose, age, stats } = this.state;
switch (dataSource) {
case DATA_CHOICES.FDA:
if (!!dose) {
return VAX_COUNTS[name][dose][age];
} else {
return VAX_COUNTS[name][age];
}
case DATA_CHOICES.VNM:
if (Object.keys(stats).length > 0) {
if (!!dose) {
return stats[name][dose][age].count;
} else {
return stats[name][age].count;
}
} else {
return 0;
}
case DATA_CHOICES.VNM_FDA:
let totalCount = 0;
if (Object.keys(stats).length > 0) {
if (!!dose) {
totalCount =
VAX_COUNTS[name][dose][age] + stats[name][dose][age].count;
} else {
totalCount = VAX_COUNTS[name][age] + stats[name][age].count;
}
} else {
totalCount = VAX_COUNTS[name][dose][age];
}
return totalCount;
default:
return 0;
}
};
render() {
const {
name,
dose,
age,
showInputPopup,
loading,
fetchedResult,
dataSource,
loadingMessage,
stats,
} = this.state;
let result = {};
let resultAtSite = {};
if (this.shouldShowResults()) {
result = Object.assign({}, this.getResults());
Object.keys(result).forEach((s) => {
if (s.includes("AT SITE")) {
resultAtSite[s] = result[s];
delete result[s];
}
});
}
return (
{loading ? (
) : null}
{!loading ? (
Select Vaccine
{Object.values(VAX_NAMES).map((v) => (
this.setState({
name: v,
dose: "",
age: "",
answers: v === fetchedResult.name ? fetchedResult : {},
showInputPopup: false,
})
}
data-font="sans-bold"
className={`vaccine answer${name === v ? " selected" : ""}${
name && name !== v ? " unselected" : ""
}`}
>
{v}
))}
) : null}
{this.shouldShowDoses() ? (
Select Dose
{Object.values(VAX_DOSES).map((v) => (
this.setState({
dose: v,
age: "",
showInputPopup: false,
})
}
data-font="sans-bold"
className={`dose answer${dose === v ? " selected" : ""}${
dose && dose !== v ? " unselected" : ""
}`}
>
{v}
))}
) : null}
{this.shouldShowAgeGroup() ? (
Select Age Group
{Object.values(VAX_AGES[name]).map((v) => (
this.setState({
age: v,
showInputPopup: false,
})
}
data-font="sans-bold"
className={`age answer${age === v ? " selected" : ""}${
age && age !== v ? " unselected" : ""
}`}
>
{v}
))}
) : null}
{this.shouldShowResults() ? (
Here's what to expect.
Share ↗
Use data from:
this.setState({ dataSource: e.target.value })
}
value={dataSource}
>
{Object.keys(DATA_CHOICES).map((dataKey) => (
{DATA_CHOICES[dataKey]}
))}
Based on
{` ${this.getSampleSize()
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} `}
individual experiences
{Object.keys(stats).length < 1 ? (
Failed to get stats from Vax&Me
) : null}
) : null}
{this.shouldShowResults() ? (
{/*
ⓘ
*/}
NET EFFICACY
{VAX_EFFICACY[name][age]}%
of vaccine trial participants who took the {name} {" "}
vaccine (in the {age} age group) did not get COVID-19
) : null}
{this.shouldShowResults() ? (
{Object.keys(resultAtSite).map((symptom) => (
{ICONS[symptom]}
{symptom}
{this.getStatForSymptom(symptom)}/10
))}
{Object.keys(result).map((symptom) => (
{ICONS[symptom]}
{symptom}
{this.getStatForSymptom(symptom)}/10
))}
) : null}
{this.shouldShowResults() ? (
this.setState({ showInputPopup: !showInputPopup })}
>
Already vaxxed?
Tell us how it went.
this.setState({ showInputPopup: !showInputPopup })
}
>
▼
{showInputPopup ? (
I am in age group {age} and got the
{name} vaccine. I experienced:
{this.shouldShowDoses()
? Object.keys(VAX_DOSES).map((dose) =>
this.getAnswerOptions(VAX_DOSES[dose])
)
: this.getAnswerOptions()}
You can comeback and update your experience at any time!
(Just use the same device)
) : null}
{showInputPopup ? (
SUBMIT
) : null}
) : null}
);
}
}
const domContainer = document.querySelector('#root');
const root = ReactDOM.createRoot(domContainer);
root.render(React.createElement(App));