commit 4705cd66269d18d926ea90d5a699815c9c6ada58 Author: Ayushya Amitabh Date: Sat Feb 10 18:26:14 2024 -0500 archive diff --git a/extralarge.css b/extralarge.css new file mode 100644 index 0000000..be1fc76 --- /dev/null +++ b/extralarge.css @@ -0,0 +1,7 @@ +.input-popup.expanded { + height: 50vh; +} + +.loading-bar-container { + max-width: 25%; +} diff --git a/extrasmall.css b/extrasmall.css new file mode 100644 index 0000000..9481b4f --- /dev/null +++ b/extrasmall.css @@ -0,0 +1,475 @@ +* { + margin: 0; + padding: 0; + transition: all ease-in-out 0.2s; + color: var(--color-secondary); + user-select: none; +} + +html { + font-size: 10px; + scroll-behavior: smooth; +} +body { + display: flex; + flex-direction: column; + font-size: 10px; + padding: 3.75rem; + padding-bottom: 0 !important; + height: calc(100vh - 3.75rem); + overflow-x: hidden; + max-width: 100vw; + background: var(--color-primary); +} + +#root { + flex: 1; + display: flex; +} + +.app-container { + flex: 1; +} + +header { + display: flex; + justify-content: center; + flex-direction: column; +} + +.logo-container { + display: flex; + width: fit-content; + justify-content: center; + flex-direction: column; +} +.logo { + background-size: 100%; + width: 5rem; + height: 5rem; + display: inline-block; + background-image: url("/logo512.png"); +} +.logo-text-container { + justify-content: center; + vertical-align: middle; + flex-direction: column; + flex: 1; + margin-top: 3.5rem; +} +.logo-text-container p { + font-size: 2.4rem; +} + +.header { + font-size: 10rem; + letter-spacing: 1rem; +} + +.subheader { + font-size: 2.5rem; + margin: 0; +} + +.overline { + font-size: 2rem; + letter-spacing: 0.1rem; + margin: 0; +} + +.overline[data-button] { + border-bottom: 1px solid; + width: fit-content; + cursor: pointer; +} + +.overline[data-button] a { + text-decoration: none; +} + +.intro { + margin: 4.5rem 0; + display: inline-block; + width: fit-content; +} + +.question-container { + margin: 3.5rem 0; +} + +.question { + font-size: 2rem; +} + +.answers-container { + margin: 1rem 0; +} + +.answer { + display: inline-flex; + font-size: 1.8rem; + padding: 1.2rem 1.5rem; + margin-right: 0.75rem; + margin-bottom: 0.75rem; + border-radius: 24rem; + background-color: var(--color-secondary); + color: var(--color-primary); + outline: none !important; + border: none !important; + letter-spacing: 0.2rem; + text-transform: uppercase; + cursor: pointer; +} + +.answer.selected { + background-color: var(--color-bright); +} +.answer.unselected { + background-color: var(--color-dim); +} + +.symptom-container.subheader { + margin-top: 5rem; + margin-bottom: 2.5rem; + display: block !important; +} +.symptom-container.subheader .overline { + margin: 1rem 0; +} +.symptom-container { + margin-bottom: 5rem; +} +.symptom { + max-width: 100%; + padding: 1.5rem; + flex-direction: column; + justify-content: center; + align-items: center; + border: 1px solid transparent; + border-radius: 1rem; + transition: border-color ease-in-out 0.4s; + transition: box-shadow ease-in-out 0.3s; +} +.symptom.at-site { + background-color: var(--color-dim); + border-radius: 0; +} +.symptom.at-site:first-of-type { + border-top-left-radius: 1rem; + border-top-right-radius: 1rem; +} +.symptom.at-site:nth-of-type(3) { + border-bottom-left-radius: 1rem; + border-bottom-right-radius: 1rem; +} + +.symptom-emoji { + text-align: center; + font-size: 3rem; + padding: 0.75rem; + display: block; +} +.symptom-name { + font-size: 2rem; + text-align: center; + display: block; +} +.symptom-chance { + font-size: 1.8rem; + text-align: center; + display: block; +} +.symptom-chance:before { + content: "EXPERIENCED BY"; + font-size: 1rem; + display: block; + text-align: center; + letter-spacing: 0.1rem; +} +.symptom-chance:after { + content: "PEOPLE"; + font-size: 1rem; + display: block; + text-align: center; + letter-spacing: 0.1rem; +} + +footer { + padding: 5rem 3.75rem; + width: calc(100vw - 7.5rem); + margin-left: -3.75rem; + background-color: var(--color-secondary-light); +} +.link-container { + font-size: 1.2rem; + transition: none !important; + display: flex; + flex-direction: column; +} + +.link-container > div { + flex: 1; + display: flex; + flex-direction: column; + color: var(--color-primary); +} + +.link-container > div > .overline { + color: var(--color-primary); +} + +.link-container > div:not(:first-of-type) { + margin-top: 3rem; +} + +a { + color: var(--color); + margin-top: 0.75rem; + max-width: fit-content; + +} + +.link-container a { + font-size: 1.8rem; +} + +footer .by { + color: var(--color-primary); + margin-top: 4rem; +} + +@keyframes slideUp { + 0% { + transform: translateY(300%); + } + 100% { + transform: translateY(0); + } +} + +.input-popup { + position: sticky; + background-color: var(--color-secondary-light); + bottom: 1rem; + height: 13.5rem; + margin-bottom: 7.5rem; + transition: height ease-in-out 0.3s; + overflow: hidden; + border-radius: 2rem; + max-height: 85vh; + display: flex; + flex-direction: column; + animation: 0.5s ease-out 0s 1 slideUp; + animation-delay: 0.5s; + animation-fill-mode: forwards; + transform: translateY(200%); +} +.input-popup * { + color: var(--color-primary); +} +.input-popup.expanded { + height: 80vh; + overflow-y: auto; +} +.input-popup .input-popup-header-icon { + transform: rotate(180deg); +} +.input-popup.expanded .input-popup-header-icon { + transform: rotate(0deg); +} +.input-popup-header { + padding: 2.5rem; + position: sticky; + top: 0; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background-color: var(--color-secondary-light); + border-bottom: 1px solid transparent; +} +.input-popup.expanded .input-popup-header { + border-bottom: 1px solid var(--color-tertiary); +} +.input-popup-header-text { + flex: 1; +} +.input-popup-header-icon { + cursor: pointer; + background-color: var(--color-primary); + outline: none; + color: var(--color-secondary); + border: 1px solid transparent; + border-radius: 10rem; + height: 5rem; + width: 5rem; +} + +.input-popup-body { + padding: 2.5rem 2.5rem; + flex: 1; +} + +.input-popup-body .question-container .answers-container .answer { + background-color: var(--color-tertiary); +} +.input-popup-body .question-container .answers-container .answer.selected { + background-color: var(--color-secondary); +} + +.input-popup-submit { + padding: 2.5rem; + text-align: center; + font-size: 2rem; + letter-spacing: 0.3rem; + background-color: var(--color-secondary); + cursor: pointer; +} +.input-popup.expanded .input-popup-submit { + position: sticky; + bottom: 0; +} +.input-popup-submit.submitted { + background-color: #55c16c; +} +.input-popup-submit.errored { + background-color: #c43d34; +} + +.input-popup.expanded::-webkit-scrollbar { + width: 1.5rem; + background-color: var(--color-tertiary); + border-radius: 0 4rem 4rem 0; +} +.input-popup.expanded::-webkit-scrollbar-thumb { + width: 1rem; + background-color: var(--color-dim); + border-radius: 0 4rem 4rem 0; +} + +.tip-container { + display: flex; + flex-direction: row; + padding: 2rem 0; + margin: 2rem 0; + border-radius: 1rem; + margin-bottom: 2rem; +} +.tip-icon { + font-size: 3rem; +} +.tip .overline { + font-size: 1.6rem; +} +.tip .subheader { + font-size: 2rem; + letter-spacing: 0.2rem; + margin-top: 0.4rem; +} +.tip .header { + font-size: 6rem; + letter-spacing: 0.5rem; +} +.tip-container .overline[data-button] { + margin-top: 1rem; +} +.tip-container .overline[data-button]:first-of-type { + margin-top: 2rem; +} + +@keyframes loading { + 0% { + width: 0%; + } + 100% { + width: 100%; + } +} +.loading-bar-container { + min-height: 2rem; + background-color: var(--color-secondary-light); + border-radius: 1rem; + margin-top: 1rem; +} +.loading-bar { + position: relative; + width: 1rem; + min-height: 2rem; + background-color: #25d255; + border-radius: 1rem; + animation: 2s ease-out 0s infinite loading; +} + +.result-header-container { + display: flex; + align-items: center; + margin-top: 5rem; + flex-direction: column; + margin-bottom: 2rem; + border: 1px solid var(--color-secondary); + border-radius: 1rem; + padding: 3rem 2rem; +} + +.result-header-container .symptom-container { + margin: 0; + flex: 1; + width: 100%; +} + +.result-header-select { + width: 100%; + margin-top: 2rem; +} + +.result-header-select .overline { + font-size: 1.6rem; + margin-bottom: 0.75rem; +} + + +.result-header-select .overline.errored { + margin-bottom: 0; + margin-top: 0.75rem; + color: #c43d34; +} +.result-header-select .overline.success { + margin-bottom: 0; + margin-top: 0.75rem; +} + +.result-header-select select { + background-color: var(--color-tertiary); + border-radius: 1rem; + padding: 1rem 2rem; + outline: none; + width: 100%; +} + +@media (hover: hover) and (pointer: fine) { + .answer:hover, + .answer.unselected:hover { + background-color: var(--color-secondary-light); + } + .symptom:hover { + box-shadow: 0px 0px 100px var(--color-tertiary); + background-color: var(--color-primary); + } + .symptom.at-site:hover { + box-shadow: 0px 0px 100px var(--color-dim); + background-color: var(--color-secondary-light); + color: var(--color-primary); + } + .symptom.at-site:hover p { + color: var(--color-primary); + } + .input-popup-header-icon:hover { + background-color: var(--color-tertiary); + } + .input-popup-body .question-container .answers-container .answer:hover { + background-color: var(--color-bright); + } + .input-popup-submit:hover { + background-color: var(--color-tertiary); + } +} diff --git a/glacialindifference-bold-webfont.woff2 b/glacialindifference-bold-webfont.woff2 new file mode 100644 index 0000000..452055e Binary files /dev/null and b/glacialindifference-bold-webfont.woff2 differ diff --git a/glacialindifference-regular-webfont.woff2 b/glacialindifference-regular-webfont.woff2 new file mode 100644 index 0000000..f663610 Binary files /dev/null and b/glacialindifference-regular-webfont.woff2 differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..29901a3 --- /dev/null +++ b/index.html @@ -0,0 +1,119 @@ + + + + + + + + + + + + + Vax & Me | 404 + + +
+
+
+
+ +
+
+
+
+

+ 404 +

+
+ This page doesn't exist. +
+
+
+
+
+ + + diff --git a/index.js b/index.js new file mode 100644 index 0000000..10d6c08 --- /dev/null +++ b/index.js @@ -0,0 +1,988 @@ +import React from "react"; +import ReactDOM from "react-dom"; +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 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) => ( + + ))} +
+
+ ); + }; + 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 ( +
+
+
+
+
+
+
+ Getting vaccinated? +
+

+ Find out what to expect. +

+
+
+
+ {loading ? ( +
+

+ {loadingMessage} +

+
+
+
+
+ ) : null} + {!loading ? ( +
+

+ Select Vaccine +

+
+ {Object.values(VAX_NAMES).map((v) => ( + + ))} +
+
+ ) : null} + {this.shouldShowDoses() ? ( +
+

+ Select Dose +

+
+ {Object.values(VAX_DOSES).map((v) => ( + + ))} +
+
+ ) : null} + + {this.shouldShowAgeGroup() ? ( +
+

+ Select Age Group +

+
+ {Object.values(VAX_AGES[name]).map((v) => ( + + ))} +
+
+ ) : null} + + {this.shouldShowResults() ? ( +
+
+

Here's what to expect.

+
+ Share ↗ +
+
+ Download Chart ↗ +
+
+
+
+ Use data from: +
+ +
+ 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 +
+
+ See Data Sources ↗ +
+
+ + What is efficacy? ↗ + +
+
+
+ ) : 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. +

+
+ +
+ {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} +
+ ); + } +} + +ReactDOM.render( + + + , + document.querySelector("#root") +); diff --git a/large.css b/large.css new file mode 100644 index 0000000..9913a9f --- /dev/null +++ b/large.css @@ -0,0 +1,30 @@ +body { + padding: 15rem; + padding-top: 10rem; + height: calc(100vh - 10rem); +} + +.logo { + width: 10rem; + height: 10rem; +} +.symptom { + max-width: 25rem; + height: 25rem; +} + +footer { + padding: 5rem 15rem; + width: calc(100vw - 30rem); + margin-left: -15rem; +} +.input-popup { + height: 10rem; +} +.input-popup.expanded { + height: 55vh; +} + +.loading-bar-container { + max-width: 35%; +} diff --git a/logo512.png b/logo512.png new file mode 100644 index 0000000..eaa8e23 Binary files /dev/null and b/logo512.png differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..201170f --- /dev/null +++ b/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "Vax&Me", + "name": "Vax And Me", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/medium.css b/medium.css new file mode 100644 index 0000000..d55f661 --- /dev/null +++ b/medium.css @@ -0,0 +1,135 @@ +body { + padding: 7.5rem; + padding-top: 5rem; + height: calc(100vh - 5rem); +} + +.logo-container { + flex-direction: row; + align-items: center; + justify-content: flex-start; +} +.logo { + width: 7.5rem; + height: 7.5rem; +} +.logo-text-container { + margin-top: 0; + margin-left: 1.5rem; + border-left: 1px solid var(--color-secondary); + padding-left: 1.5rem; +} +.intro .overline { + font-size: 3rem; +} +.intro .subheader { + font-size: 4rem; +} + +.header { + font-size: 15rem; + letter-spacing: 1.5rem; +} + +.overline[data-button] { + display: inline-block; +} +.overline[data-button]:not(:first-of-type) { + margin-left: 1.5rem; +} + +.symptom { + width: 100%; + max-width: 20rem; + height: 20rem; + display: inline-flex; + margin-bottom: 0.75rem; +} +.symptom-emoji { + font-size: 3.5rem; + padding: 0.75rem; +} +.symptom-name { + font-size: 2.5rem; + text-align: center; +} +.symptom-chance { + font-size: 2.4rem; + text-align: center; +} +.symptom-chance:before { + font-size: 1.5rem; +} +.symptom-chance:after { + font-size: 1.5rem; +} +.symptom.at-site:first-of-type { + border-radius: 0; + border-top-left-radius: 1rem; + border-bottom-left-radius: 1rem; +} +.symptom.at-site:nth-of-type(3) { + border-radius: 0; + border-top-right-radius: 1rem; + border-bottom-right-radius: 1rem; +} + +footer { + padding: 5rem 7.5rem; + width: calc(100vw - 15rem); + margin-left: -7.5rem; +} +.link-container { + font-size: 1.7rem; + flex-direction: row; +} +.link-container > div { + flex-direction: column; + flex: 1; +} +.link-container > div:not(:first-of-type) { + margin-left: 3rem; + margin-top: 0; +} + +.input-popup { + height: 10rem; +} + +.input-popup.expanded { + height: 55vh; +} + +.loading-bar-container { + max-width: 50%; +} + +.tip-container .overline[data-button] { + margin-top: 2rem; + margin-left: 0; +} +.tip-container .overline[data-button]:last-of-type { + margin-left: 1.2rem; +} +.tip { + padding-left: 2rem; +} + +.result-header-container { + flex-direction: row; +} + +.result-header-container .symptom-container { + width: fit-content; +} + +.result-header-select { + margin-top: 0rem; + width: fit-content; + padding-left: 3rem; + border-left: 1px solid var(--color-secondary); +} + +.result-header-select select { + width: fit-content; +} diff --git a/small.css b/small.css new file mode 100644 index 0000000..9d1a25d --- /dev/null +++ b/small.css @@ -0,0 +1,7 @@ +.input-popup { + height: 11.5rem; +} + +.input-popup.expanded { + height: 85vh; +} diff --git a/smallmedium.css b/smallmedium.css new file mode 100644 index 0000000..19d457d --- /dev/null +++ b/smallmedium.css @@ -0,0 +1,7 @@ +.input-popup { + height: 11rem; +} + +.input-popup.expanded { + height: 65vh; +} diff --git a/variables-dark.css b/variables-dark.css new file mode 100644 index 0000000..19b0da5 --- /dev/null +++ b/variables-dark.css @@ -0,0 +1,9 @@ +:root { + --color-primary: black; + --color-secondary: white; + --color-secondary-light: rgb(185, 185, 185); + --color-tertiary: rgb(105, 105, 105); + --color-bright: #636363; + --color-dim: #352f2f; + font-size: 10px; +} diff --git a/variables.css b/variables.css new file mode 100644 index 0000000..a013437 --- /dev/null +++ b/variables.css @@ -0,0 +1,9 @@ +:root { + --color-primary: white; + --color-secondary: black; + --color-secondary-light: rgb(56, 56, 56); + --color-tertiary: rgba(185, 185, 185, 0.459); + --color-bright: #575757; + --color-dim: #aeadb0; + font-size: 10px; +}