Compare commits

..
3 Commits
Author SHA1 Message Date
ayushya 47319c270c Archive 2024-02-27 23:10:08 -05:00
Ayushya Amitabh 365f8bb6e6 wget fetch all sources 2024-02-10 18:37:47 -05:00
Ayushya Amitabh 4705cd6626 archive 2024-02-10 18:26:14 -05:00
43 changed files with 2217 additions and 22622 deletions
Vendored
BIN
View File
Binary file not shown.
-5
View File
@@ -1,5 +0,0 @@
{
"projects": {
"default": "vaxandme"
}
}
-28
View File
@@ -1,28 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
*/node_modules
/.pnp
.pnp.js
.firebase
# testing
/coverage
# production
*/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.cache
*.log
functions/lib/
functions/src/backup.json
+159 -159
View File
@@ -1,159 +1,159 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Learn about the expected side effects of COVID-19 Vaccines" content="Learn about the expected side effects of COVID-19 Vaccines"
/> />
<link rel="apple-touch-icon" href="/logo192.png" /> <link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="manifest.json" />
<style> <style>
@import "/styles/variables.css"; @import "styles/variables.css";
@import "/styles/variables-dark.css" screen and @import "styles/variables-dark.css" screen and
(prefers-color-scheme: dark); (prefers-color-scheme: dark);
@import "/styles/extrasmall.css"; @import "styles/extrasmall.css";
@import "/styles/small.css" only screen and (min-width: 370px) and @import "styles/small.css" only screen and (min-width: 370px) and
(max-width: 767px); (max-width: 767px);
@import "/styles/smallmedium.css" only screen and (min-width: 768px) and @import "styles/smallmedium.css" only screen and (min-width: 768px) and
(max-width: 1023px); (max-width: 1023px);
@import "/styles/medium.css" only screen and (min-width: 1024px); @import "styles/medium.css" only screen and (min-width: 1024px);
@import "/styles/large.css" only screen and (min-width: 1366px); @import "styles/large.css" only screen and (min-width: 1366px);
@import "/styles/extralarge.css" only screen and (min-width: 1921px); @import "styles/extralarge.css" only screen and (min-width: 1921px);
@font-face { @font-face {
font-family: "GlacialBold"; font-family: "GlacialBold";
src: url("/fonts/glacialindifference-bold-webfont.woff2") src: url("fonts/glacialindifference-bold-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-bold-webfont.woff") format("woff"); url("fonts/glacialindifference-bold-webfont.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "GlacialItalic"; font-family: "GlacialItalic";
src: url("/fonts/glacialindifference-italic-webfont.woff2") src: url("fonts/glacialindifference-italic-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-italic-webfont.woff") format("woff"); url("fonts/glacialindifference-italic-webfont.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "Glacial"; font-family: "Glacial";
src: url("/fonts/glacialindifference-regular-webfont.woff2") src: url("fonts/glacialindifference-regular-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-regular-webfont.woff") format("woff"); url("fonts/glacialindifference-regular-webfont.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
[data-font="sans"] { [data-font="sans"] {
font-family: "Glacial", sans-serif; font-family: "Glacial", sans-serif;
font-display: swap; font-display: swap;
} }
[data-font="sans-bold"] { [data-font="sans-bold"] {
font-family: "GlacialBold", sans-serif; font-family: "GlacialBold", sans-serif;
font-display: swap; font-display: swap;
} }
[data-font="sans-italic"] { [data-font="sans-italic"] {
font-family: "GlacialItalic", sans-serif; font-family: "GlacialItalic", sans-serif;
font-style: italic; font-style: italic;
font-display: swap; font-display: swap;
} }
</style> </style>
<title>Vax & Me | Copyright</title> <title>Vax & Me | Copyright</title>
</head> </head>
<body> <body>
<div id="root"> <div id="root">
<div class="app-container"> <div class="app-container">
<header> <header>
<div class="logo-container"> <div class="logo-container">
<div class="logo"></div> <div class="logo"></div>
<div class="logo-text-container"> <div class="logo-text-container">
<p data-font="sans-bold">COPYRIGHT</p> <p data-font="sans-bold">COPYRIGHT</p>
</div> </div>
</div> </div>
</header> </header>
<main> <main>
<div class="intro"> <div class="intro">
<h3 class="subheader" data-font="sans-bold">© 2021</h3> <h3 class="subheader" data-font="sans-bold">© 2021</h3>
<h6 style="display: inline-block;" class="overline" data-font="sans"> <h6 style="display: inline-block;" class="overline" data-font="sans">
Alba Rajanibala Alba Rajanibala
</h6> </h6>
<h6 style="display: inline-block;" class="overline" data-font="sans"> &amp; Ayushya Amitabh <h6 style="display: inline-block;" class="overline" data-font="sans"> &amp; Ayushya Amitabh
</h6> </h6>
</div> </div>
<div class="question-container"> <div class="question-container">
<p class="overline" data-font="sans"> <p class="overline" data-font="sans">
Unauthorized copying of this website via any medium is strictly Unauthorized copying of this website via any medium is strictly
prohibited. Designed and developed by Alba Rajanibala and Ayushya prohibited. Designed and developed by Alba Rajanibala and Ayushya
Amitabh. Amitabh.
</p> </p>
</div> </div>
<div class="question-container"> <div class="question-container">
<p class="overline" data-font="sans"> <p class="overline" data-font="sans">
All content on this website, unless otherwise cited is the All content on this website, unless otherwise cited is the
intellectual property of Alba Rajanibala and Ayushya Amitabh, and intellectual property of Alba Rajanibala and Ayushya Amitabh, and
may not be copied, edited, or reproduced without permission. may not be copied, edited, or reproduced without permission.
</p> </p>
</div> </div>
<div class="question-container"> <div class="question-container">
<p class="overline" data-font="sans-italic"> <p class="overline" data-font="sans-italic">
Data shown is based on FDA clinical trials. Data shown is based on FDA clinical trials.
</p> </p>
<div class="answers-container"> <div class="answers-container">
<a <a
style="display: block" style="display: block"
class="question" class="question"
data-font="sans-italic" data-font="sans-italic"
href="https://www.fda.gov/media/144416/download" href="https://www.fda.gov/media/144416/download"
>PFizer-BioNTech</a >PFizer-BioNTech</a
> >
<a <a
style="display: block" style="display: block"
class="question" class="question"
data-font="sans-italic" data-font="sans-italic"
href="https://www.fda.gov/media/144637/download" href="https://www.fda.gov/media/144637/download"
>Moderna</a >Moderna</a
> >
<a <a
style="display: block" style="display: block"
class="question" class="question"
data-font="sans-italic" data-font="sans-italic"
href="https://www.fda.gov/media/146304/download" href="https://www.fda.gov/media/146304/download"
>J&amp;J Janssen</a >J&amp;J Janssen</a
> >
</div> </div>
</div> </div>
</main> </main>
</div> </div>
</div> </div>
<footer data-font="sans"> <footer data-font="sans">
<div class="link-container"> <div class="link-container">
<div> <div>
<h6 class="overline" data-font="sans">FDA Data Sources</h6> <h6 class="overline" data-font="sans">FDA Data Sources</h6>
<a href="https://www.fda.gov/media/144416/download">PFizer-BioNTech</a> <a href="https://www.fda.gov/media/144416/download">PFizer-BioNTech</a>
<a href="https://www.fda.gov/media/144637/download">Moderna</a> <a href="https://www.fda.gov/media/144637/download">Moderna</a>
<a href="https://www.fda.gov/media/146304/download">J&J Janssen</a> <a href="https://www.fda.gov/media/146304/download">J&J Janssen</a>
</div> </div>
<div> <div>
<h6 class="overline" data-font="sans">Links</h6> <h6 class="overline" data-font="sans">Links</h6>
<a href="/vaxandme_chart.pdf">Download Chart</a> <a href="vaxandme_chart.pdf">Download Chart</a>
<a href="/">Home</a> <a href="index.html">Home</a>
</div> </div>
<div> <div>
<h6 class="overline" data-font="sans">Legal</h6> <h6 class="overline" data-font="sans">Legal</h6>
<a href="/copyright">Copyright</a> <a href="copyright.html">Copyright</a>
<a href="/privacy">Privacy</a> <a href="privacy.html">Privacy</a>
</div> </div>
</div> </div>
<div class="overline by" data-font="sans"> <div class="overline by" data-font="sans">
Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh
</div> </div>
</footer> </footer>
</body> </body>
</html> </html>

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

-69
View File
@@ -1,69 +0,0 @@
{
"hosting": {
"public": "website/build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "/copyright",
"destination": "/copyright.html"
},
{
"source": "/privacy",
"destination": "/privacy.html"
},
{
"source": "/jj",
"destination": "/index.html"
},
{
"source": "/pb",
"destination": "/index.html"
},
{
"source": "/md",
"destination": "/index.html"
},
{
"source": "/jj/**",
"destination": "/index.html"
},
{
"source": "/pb/**",
"destination": "/index.html"
},
{
"source": "/md/**",
"destination": "/index.html"
},
{
"source": "**",
"destination": "/404.html"
}
]
},
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
],
"source": "functions"
},
"emulators": {
"functions": {
"port": 5001
},
"firestore": {
"port": 8080
},
"hosting": {
"port": 5000
},
"ui": {
"enabled": true
}
}
}
-28
View File
@@ -1,28 +0,0 @@
module.exports = {
root: true,
env: {
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"google",
"plugin:@typescript-eslint/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["tsconfig.json", "tsconfig.dev.json"],
sourceType: "module",
tsconfigRootDir: __dirname,
},
ignorePatterns: [
"/lib/**/*", // Ignore built files.
],
plugins: ["@typescript-eslint", "import"],
rules: {
quotes: ["error", "double"],
},
};
-12
View File
@@ -1,12 +0,0 @@
{
"type": "service_account",
"project_id": "vaxandme",
"private_key_id": "878a8ed429f84ef5fb42555a34bd71d53ba612b2",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDnYRdGS8aHi8aY\nDmcIPlPVN0HSDMjg046n1LmR1f1eRS/28m+C94pP6Pn2WvzRJIGK/8XKXARfAiPD\noVfUaXUAPD0DVbgHMn6/1pvCCF0wwlbXZNsIjDg9vLQiGvnsNcgoDXOhfliqw0ca\nTx3oUdgPCzOzkWw4wGMZaJrNK4w0jwuffNgA746jCG3RX9vo/m+aucerYqPPlSfs\nAjJpmpXXCwdHNx0EnEpGQc3bdnbN53hikEJ+J2xCESqaEmTryi1vXDv+i+YCrZn6\nLD+P1etLvtoU0pKMMCcVLk7VmCPMT8F2RB0L21aFmGLuy8fpBDMIYXtjMp+FbKBf\nAne0zKX1AgMBAAECggEAcn4nQ7gsWZdPYp/B2SDqX9fTgKql3iahcSgA2YrPsM3A\nscx+ZIq0TN2GjyKv9pe/aHYvO/azjQXkh6H3cZrrc/3a3SMWCK2ybaA2+9kisAWC\nblwwTHcCP354jmXV5M4qKLObU77PBlTSAtFChdq19J/A6ircJ+r/Pqv16e+SFdD/\n4ie64DR5Uxj9vUN8WFrfDfapwxwnGLgJCOrS7fecC7xDzi5iLR9mZD1ak2a8B+B6\nwNrzGafVz285B0fVtYbXiEGNElbPBd/hvioK0zsHdYxZYmiaZvyWqABejRk6jW6R\n6OTy7fCk6h7LG23+uh9Ruh8jKEE9nX6g/vYaKOqv0wKBgQD6nKvshaCQD4G5s5gK\nwQFYUFJbmMd547BKtzaFXKzUcldZaL9da4AhINoxu70uCl/nDJmTACEcifU/juiO\nM0XsZ0kufoomW2DyxVKBshBoF/OJoEUngDiCDdJFMUsa6zeiZHVi9uFmTY4c4nf8\nozJxxpcbmQRXEdv9kafBEw83DwKBgQDsWpC/t7QCqCytXWqnkVM5QIvrIxXqIHGR\nzHkS6bmUYv2IF6ynzCgAbnRNDh0X2PIvBk6gJDfyC9lN9LvLurn/CVpEK85Q6hRy\nVO4BegYEaFOfDueiAB1RXvGbEvchZp7+DpN2tgi2lRbuEDPWcySFb7YVkyHo/aVO\nfDHjbSmyuwKBgQDB7ORY1Rpm/6jZozB++dTpmtXwDSwmlNsBJf7peco/Zu9Sv2x9\nhMcUW5CcNXNxohON/cEE8G+u3s5JTXOFPgXYpiAJ+185yTIfCjQLKlfpxMbuc3ev\n1ldjxAA3DKrafLODVQtE9ld6y36xuUkx1LMfOZYKhADIw4ghEknmbLI0XwKBgQCJ\njHtI16QPF8hIbGExe8WvP/0trpGGW7M8Yn7jntm9nY5msUBttsZg2ecdIOWVh1AK\nReVkFS/p0TN5xr+z5MtxvQOnQMclF9UmZH7OpTDzR7PCkW4EPYTSYirBUNw5t/s3\nZ0wyfeW8PC/IA+3nE8NhVI3nKGVHrrWRY321GUZ3GQKBgQDzLC96HVGbIQgfGRhW\nu1mcgNKFdQcy8Yu8ffttnuIIPGljwWwLpfbROD+Fe0CtqJ4XXL0Js7XfoPYoxMMH\nc16GysIG9/TYioBhBVfyf94eekylVjdYO0aY7d5Zjw1EuO/LVdPMlSZuosv3q10n\nyB55dJWP6pkR6tLqeNCuJ8+9qQ==\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-meacv@vaxandme.iam.gserviceaccount.com",
"client_id": "110649794888712776390",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-meacv%40vaxandme.iam.gserviceaccount.com"
}
-184
View File
@@ -1,184 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPastResult = exports.saveResult = void 0;
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const firebaseSecret = require("./firebaseSecret.json");
const params = {
type: firebaseSecret.type,
projectId: firebaseSecret.project_id,
privateKeyId: firebaseSecret.private_key_id,
privateKey: firebaseSecret.private_key,
clientEmail: firebaseSecret.client_email,
clientId: firebaseSecret.client_id,
authUri: firebaseSecret.auth_uri,
tokenUri: firebaseSecret.token_uri,
authProviderX509CertUrl: firebaseSecret.auth_provider_x509_cert_url,
clientC509CertUrl: firebaseSecret.client_x509_cert_url,
};
admin.initializeApp({
credential: admin.credential.cert(params),
});
const serializeGaId = (_gaid) => {
if (_gaid) {
return _gaid.split(".").join("|");
}
return "";
};
exports.saveResult = functions.https.onCall(async (data, context) => {
if (!!data && !!context) {
const { gid } = data;
const docId = serializeGaId(gid);
const docData = Object.assign({}, data);
delete docData.gid;
const existingData = await admin
.firestore()
.collection("results")
.doc(docId)
.get()
.then((doc) => doc.data());
if (existingData !== undefined && existingData !== docData &&
existingData.name === docData.name) {
// DATA EXISTS FOR USER
// BUT DIFF DATA SENT FOR UPDATE
await admin.firestore().collection("results").doc(docId).set(docData);
delete docData.age;
delete docData.name;
delete docData.dose;
const updateDoc = {};
Object.keys(docData).forEach((symptom) => {
// IF WAS TRUE, NOW FALSE = -1
// IF WAS TRUE, NOW TRUE = 0
// IF WAS FALSE, NOW TRUE = +1
// IF WAS FALSE, NOW FALSE = 0
let incrementValue = 0;
if (existingData[symptom] === true && docData[symptom] === false) {
incrementValue = -1;
}
else if (existingData[symptom] === false &&
docData[symptom] === true) {
incrementValue = 1;
}
if (data.dose) {
updateDoc[`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(incrementValue);
}
else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(incrementValue);
}
});
admin
.firestore()
.collection("stats")
.doc(data.name)
.update(updateDoc);
}
else if (existingData !== undefined && existingData !== docData &&
existingData.name !== docData.name) {
// UNDO DATA FOR OLD VAX
// DECREMENT VALUES FOR EACH SYMPTOM ON OLD VAX
const undoDoc = {
count: admin.firestore.FieldValue.increment(-1),
};
Object.keys(existingData).forEach((symptom) => {
if (symptom !== "name" && symptom !== "dose" &&
symptom !== "age") {
if (existingData[symptom] === true) {
if (existingData.dose) {
undoDoc[`${existingData.dose}.${existingData.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(-1);
}
else {
undoDoc[`${existingData.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(-1);
}
}
}
});
admin
.firestore()
.collection("stats")
.doc(existingData.name)
.update(undoDoc);
// PROCESS NEW DATA FOR USE
// SAVE USER DATA
await admin.firestore().collection("results").doc(docId).set(docData);
delete docData.age;
delete docData.name;
delete docData.dose;
// INCREMENT TOTAL RESPONSE COUNT
const updateDoc = {
count: admin.firestore.FieldValue.increment(1),
};
// INCREMENT VALUES FOR EACH SYMPTOM
Object.keys(docData).forEach((symptom) => {
if (docData[symptom] === true) {
if (data.dose) {
updateDoc[`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
}
else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
}
}
});
admin
.firestore()
.collection("stats")
.doc(data.name)
.update(updateDoc);
}
else if (existingData === undefined) {
// DATA DOESN'T EXIST FOR USER
// SAVE USER DATA
await admin.firestore().collection("results").doc(docId).set(docData);
delete docData.age;
delete docData.name;
delete docData.dose;
// INCREMENT TOTAL RESPONSE COUNT
const updateDoc = {
count: admin.firestore.FieldValue.increment(1),
};
// INCREMENT VALUES FOR EACH SYMPTOM
Object.keys(docData).forEach((symptom) => {
if (docData[symptom] === true) {
if (data.dose) {
updateDoc[`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
}
else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
}
}
});
admin
.firestore()
.collection("stats")
.doc(data.name)
.update(updateDoc);
}
}
});
exports.getPastResult = functions.https.onCall(async (data, context) => {
if (!!data && !!context) {
const { gid } = data;
const existingData = await admin
.firestore()
.collection("results")
.doc(serializeGaId(gid))
.get()
.then((doc) => doc.data());
if (existingData) {
if (existingData["NAUSEA / VOMITING"]) {
existingData["NAUSEA OR VOMITING"] =
existingData["NAUSEA / VOMITING"];
}
return existingData;
}
}
return undefined;
});
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
-3243
View File
File diff suppressed because it is too large Load Diff
-31
View File
@@ -1,31 +0,0 @@
{
"name": "functions",
"scripts": {
"lint": "eslint --ext .js,.ts .",
"build": "tsc",
"serve": "npm run devenv && npm run build && $env:NODE_ENV=DEV && firebase emulators:start",
"devenv": "SET NODE_ENV=dev",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "12"
},
"main": "lib/index.js",
"dependencies": {
"firebase-admin": "^9.2.0",
"firebase-functions": "^3.11.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^3.9.1",
"@typescript-eslint/parser": "^3.8.0",
"eslint": "^7.6.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-import": "^2.22.0",
"firebase-functions-test": "^0.2.0",
"typescript": "^3.8.0"
},
"private": true
}
-12
View File
@@ -1,12 +0,0 @@
{
"type": "service_account",
"project_id": "vaxandme",
"private_key_id": "878a8ed429f84ef5fb42555a34bd71d53ba612b2",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDnYRdGS8aHi8aY\nDmcIPlPVN0HSDMjg046n1LmR1f1eRS/28m+C94pP6Pn2WvzRJIGK/8XKXARfAiPD\noVfUaXUAPD0DVbgHMn6/1pvCCF0wwlbXZNsIjDg9vLQiGvnsNcgoDXOhfliqw0ca\nTx3oUdgPCzOzkWw4wGMZaJrNK4w0jwuffNgA746jCG3RX9vo/m+aucerYqPPlSfs\nAjJpmpXXCwdHNx0EnEpGQc3bdnbN53hikEJ+J2xCESqaEmTryi1vXDv+i+YCrZn6\nLD+P1etLvtoU0pKMMCcVLk7VmCPMT8F2RB0L21aFmGLuy8fpBDMIYXtjMp+FbKBf\nAne0zKX1AgMBAAECggEAcn4nQ7gsWZdPYp/B2SDqX9fTgKql3iahcSgA2YrPsM3A\nscx+ZIq0TN2GjyKv9pe/aHYvO/azjQXkh6H3cZrrc/3a3SMWCK2ybaA2+9kisAWC\nblwwTHcCP354jmXV5M4qKLObU77PBlTSAtFChdq19J/A6ircJ+r/Pqv16e+SFdD/\n4ie64DR5Uxj9vUN8WFrfDfapwxwnGLgJCOrS7fecC7xDzi5iLR9mZD1ak2a8B+B6\nwNrzGafVz285B0fVtYbXiEGNElbPBd/hvioK0zsHdYxZYmiaZvyWqABejRk6jW6R\n6OTy7fCk6h7LG23+uh9Ruh8jKEE9nX6g/vYaKOqv0wKBgQD6nKvshaCQD4G5s5gK\nwQFYUFJbmMd547BKtzaFXKzUcldZaL9da4AhINoxu70uCl/nDJmTACEcifU/juiO\nM0XsZ0kufoomW2DyxVKBshBoF/OJoEUngDiCDdJFMUsa6zeiZHVi9uFmTY4c4nf8\nozJxxpcbmQRXEdv9kafBEw83DwKBgQDsWpC/t7QCqCytXWqnkVM5QIvrIxXqIHGR\nzHkS6bmUYv2IF6ynzCgAbnRNDh0X2PIvBk6gJDfyC9lN9LvLurn/CVpEK85Q6hRy\nVO4BegYEaFOfDueiAB1RXvGbEvchZp7+DpN2tgi2lRbuEDPWcySFb7YVkyHo/aVO\nfDHjbSmyuwKBgQDB7ORY1Rpm/6jZozB++dTpmtXwDSwmlNsBJf7peco/Zu9Sv2x9\nhMcUW5CcNXNxohON/cEE8G+u3s5JTXOFPgXYpiAJ+185yTIfCjQLKlfpxMbuc3ev\n1ldjxAA3DKrafLODVQtE9ld6y36xuUkx1LMfOZYKhADIw4ghEknmbLI0XwKBgQCJ\njHtI16QPF8hIbGExe8WvP/0trpGGW7M8Yn7jntm9nY5msUBttsZg2ecdIOWVh1AK\nReVkFS/p0TN5xr+z5MtxvQOnQMclF9UmZH7OpTDzR7PCkW4EPYTSYirBUNw5t/s3\nZ0wyfeW8PC/IA+3nE8NhVI3nKGVHrrWRY321GUZ3GQKBgQDzLC96HVGbIQgfGRhW\nu1mcgNKFdQcy8Yu8ffttnuIIPGljwWwLpfbROD+Fe0CtqJ4XXL0Js7XfoPYoxMMH\nc16GysIG9/TYioBhBVfyf94eekylVjdYO0aY7d5Zjw1EuO/LVdPMlSZuosv3q10n\nyB55dJWP6pkR6tLqeNCuJ8+9qQ==\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-meacv@vaxandme.iam.gserviceaccount.com",
"client_id": "110649794888712776390",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-meacv%40vaxandme.iam.gserviceaccount.com"
}
-218
View File
@@ -1,218 +0,0 @@
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import * as firebaseSecret from "./firebaseSecret.json";
const params = {
type: firebaseSecret.type,
projectId: firebaseSecret.project_id,
privateKeyId: firebaseSecret.private_key_id,
privateKey: firebaseSecret.private_key,
clientEmail: firebaseSecret.client_email,
clientId: firebaseSecret.client_id,
authUri: firebaseSecret.auth_uri,
tokenUri: firebaseSecret.token_uri,
authProviderX509CertUrl: firebaseSecret.auth_provider_x509_cert_url,
clientC509CertUrl: firebaseSecret.client_x509_cert_url,
};
admin.initializeApp({
credential: admin.credential.cert(params),
});
interface RequestData {
gid: string;
}
interface SaveResultRequestData extends RequestData {
name: string;
age: string;
dose: string;
[x: string]: string;
}
const serializeGaId = (_gaid: string): string => {
if (_gaid) {
return _gaid.split(".").join("|");
}
return "";
};
if (process.env.NODE_ENV && process.env.NODE_ENV === "dev") {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const backup = require("./backup.json");
const BackupResults : Record<string, Record<string,
boolean | Record<string, unknown> | string>> =
backup.results;
const BackupStats : Record<string,
Record<string, unknown> |
Record<string, number> |
Record<string, Record<string, number> |
Record<string, Record<string, Record<string, number>>>>> =
backup.stats;
Object.keys(BackupResults).forEach((key : string) => {
admin.firestore().collection("results").doc(key).set(BackupResults[key]);
});
Object.keys(BackupStats).forEach((key : string) => {
admin.firestore().collection("stats").doc(key).set(BackupStats[key]);
});
}
export const saveResult = functions.https.onCall(
async (data: SaveResultRequestData, context) => {
if (!!data && !!context) {
const {gid} = data;
const docId: string = serializeGaId(gid);
const docData: Record<string, string | boolean> =
Object.assign({}, data);
delete docData.gid;
const existingData = await admin
.firestore()
.collection("results")
.doc(docId)
.get()
.then((doc) => doc.data());
if (existingData !== undefined && existingData !== docData &&
existingData.name === docData.name) {
// DATA EXISTS FOR USER
// BUT DIFF DATA SENT FOR UPDATE
await admin.firestore().collection("results").doc(docId).set(docData);
delete docData.age;
delete docData.name;
delete docData.dose;
const updateDoc : Record<string, admin.firestore.FieldValue> = {};
Object.keys(docData).forEach((symptom : string) => {
// IF WAS TRUE, NOW FALSE = -1
// IF WAS TRUE, NOW TRUE = 0
// IF WAS FALSE, NOW TRUE = +1
// IF WAS FALSE, NOW FALSE = 0
let incrementValue = 0;
if (existingData[symptom] === true && docData[symptom] === false) {
incrementValue = -1;
} else if (existingData[symptom] === false &&
docData[symptom] === true) {
incrementValue = 1;
}
if (data.dose) {
updateDoc[
`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(incrementValue);
} else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(incrementValue);
}
});
admin
.firestore()
.collection("stats")
.doc(data.name)
.update(updateDoc);
} else if (existingData !== undefined && existingData !== docData &&
existingData.name !== docData.name) {
// UNDO DATA FOR OLD VAX
// DECREMENT VALUES FOR EACH SYMPTOM ON OLD VAX
const undoDoc : Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(-1),
};
Object.keys(existingData).forEach((symptom : string) => {
if (symptom !== "name" && symptom !== "dose" &&
symptom !== "age" ) {
if (existingData[symptom] === true) {
if (existingData.dose) {
undoDoc[
`${existingData.dose}.${existingData.age}.${
symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(-1);
} else {
undoDoc[`${existingData.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(-1);
}
}
}
});
admin
.firestore()
.collection("stats")
.doc(existingData.name)
.update(undoDoc);
// PROCESS NEW DATA FOR USE
// SAVE USER DATA
await admin.firestore().collection("results").doc(docId).set(docData);
delete docData.age;
delete docData.name;
delete docData.dose;
// INCREMENT TOTAL RESPONSE COUNT
const updateDoc : Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(1),
};
// INCREMENT VALUES FOR EACH SYMPTOM
Object.keys(docData).forEach((symptom : string) => {
if (docData[symptom] === true) {
if (data.dose) {
updateDoc[
`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
} else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
}
}
});
admin
.firestore()
.collection("stats")
.doc(data.name)
.update(updateDoc);
} else if (existingData === undefined) {
// DATA DOESN'T EXIST FOR USER
// SAVE USER DATA
await admin.firestore().collection("results").doc(docId).set(docData);
delete docData.age;
delete docData.name;
delete docData.dose;
// INCREMENT TOTAL RESPONSE COUNT
const updateDoc : Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(1),
};
// INCREMENT VALUES FOR EACH SYMPTOM
Object.keys(docData).forEach((symptom : string) => {
if (docData[symptom] === true) {
if (data.dose) {
updateDoc[
`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
} else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] =
admin.firestore.FieldValue.increment(1);
}
}
});
admin
.firestore()
.collection("stats")
.doc(data.name)
.update(updateDoc);
}
}
});
export const getPastResult = functions.https.onCall(async (data, context) => {
if (!!data && !!context) {
const {gid} = data;
const existingData : admin.firestore.DocumentData | undefined = await admin
.firestore()
.collection("results")
.doc(serializeGaId(gid))
.get()
.then((doc) => doc.data());
if (existingData) {
if (existingData["NAUSEA / VOMITING"]) {
existingData["NAUSEA OR VOMITING"] =
existingData["NAUSEA / VOMITING"];
}
return existingData;
}
}
return undefined;
});
-5
View File
@@ -1,5 +0,0 @@
{
"include": [
".eslintrc.js"
]
}
-16
View File
@@ -1,16 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017",
"resolveJsonModule": true
},
"compileOnSave": true,
"include": [
"src"
]
}
-1
View File
@@ -1 +0,0 @@
Web / API server started at http://localhost:4000
+124
View File
@@ -0,0 +1,124 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Learn about the expected side effects of COVID-19 Vaccines" />
<link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="manifest.json" />
<style>
@import "styles/variables.css";
@import "styles/variables-dark.css" screen and (prefers-color-scheme: dark);
@import "styles/extrasmall.css";
@import "styles/small.css" only screen and (min-width: 370px) and (max-width: 767px);
@import "styles/smallmedium.css" only screen and (min-width: 768px) and (max-width: 1023px);
@import "styles/medium.css" only screen and (min-width: 1024px);
@import "styles/large.css" only screen and (min-width: 1366px);
@import "styles/extralarge.css" only screen and (min-width: 1921px);
@font-face {
font-family: "GlacialBold";
src: url("fonts/glacialindifference-bold-webfont.woff2") format("woff2"),
url("fonts/glacialindifference-bold-webfont.woff") format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "GlacialItalic";
src: url("fonts/glacialindifference-italic-webfont.woff2") format("woff2"),
url("fonts/glacialindifference-italic-webfont.woff") format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Glacial";
src: url("fonts/glacialindifference-regular-webfont.woff2") format("woff2"),
url("fonts/glacialindifference-regular-webfont.woff") format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
[data-font="sans"] {
font-family: "Glacial", sans-serif;
font-display: swap;
}
[data-font="sans-bold"] {
font-family: "GlacialBold", sans-serif;
font-display: swap;
}
[data-font="sans-italic"] {
font-family: "GlacialItalic", sans-serif;
font-style: italic;
font-display: swap;
}
.logo {
background-image: url("logo512.png");
}
header#archived {
background: rgb(149 40 40 / 48%);
border-radius: 2rem;
position: fixed;
padding: 1rem 2rem;
font-size: 1.5rem;
letter-spacing: 1.5px;
text-transform: uppercase;
z-index: 99;
backdrop-filter: blur(7px);
font-family: "GlacialBold";
left: 15rem;
right: 15rem;
top: 3rem;
}
</style>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-7VZCY223NG"></script>
<script>function gtag() { dataLayer.push(arguments) } window.dataLayer = window.dataLayer || [], gtag("js", new Date), gtag("config", "G-7VZCY223NG")</script>
<title>Vax & Me</title>
</head>
<body><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-K4XHBR6" height="0" width="0"
style="display:none;visibility:hidden"></iframe></noscript>
<header id="archived">
<div>⚠️ Project has been archived.</div>
</header>
<div id="root"></div>
<footer data-font="sans">
<div class="link-container">
<div id="data-sources">
<h6 class="overline" data-font="sans">FDA Data Sources</h6><a id="fda-pfizer"
href="https://www.fda.gov/media/144416/download">PFizer-BioNTech</a> <a id="fda-moderna"
href="https://www.fda.gov/media/144637/download">Moderna</a> <a id="fda-j&j"
href="https://www.fda.gov/media/146304/download">J&J Janssen</a>
</div>
<div>
<h6 class="overline" data-font="sans">Links</h6><a href="vaxandme_chart.pdf">Download Chart</a> <a
href="index.html">Home</a> <a target="_blank"
href="https://www.gavi.org/vaccineswork/what-difference-between-efficacy-and-effectiveness"
rel="noreferrer">What is efficacy? ↗</a>
</div>
<div>
<h6 class="overline" data-font="sans">Legal</h6><a href="copyright.html">Copyright</a> <a
href="privacy.html">Privacy</a>
</div>
</div>
<div class="overline by" data-font="sans">Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh
</div>
</footer>
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel" src="index.js"></script>
</body>
</html>
+989
View File
@@ -0,0 +1,989 @@
'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 (
<div className="question-container" key={`answer-dose-${answerDose}`}>
{!!answerDose ? (
<h6 className="overline" data-font="sans">
{answerDose}
</h6>
) : null}
<p className="question" data-font="sans">
{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)"}
</p>
<div className="answers-container">
{Object.keys(result).map((symptom) => (
<button
key={`ans-${answerDose}-${symptom}`}
data-font="sans-bold"
className={`answer${
!!answerDose
? answers[answerDose] && answers[answerDose][symptom]
? " selected"
: ""
: answers[symptom]
? " selected"
: ""
}`}
onClick={() => this.updateAnswers(symptom, answerDose)}
>
{symptom}
</button>
))}
</div>
</div>
);
};
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 (
<div className="app-container">
<header>
<div className="logo-container">
<div className="logo"> </div>
</div>
<div className="intro">
<h6 className="overline" data-font="sans">
Getting vaccinated?
</h6>
<h3 className="subheader" data-font="sans-bold">
Find out what to expect.
</h3>
</div>
</header>
<main>
{loading ? (
<div className="loading-container">
<p className="question" data-font="sans">
{loadingMessage}
</p>
<div className="loading-bar-container">
<div className="loading-bar"></div>
</div>
</div>
) : null}
{!loading ? (
<div className="question-container">
<p className="question" data-font="sans">
Select Vaccine
</p>
<div className="answers-container">
{Object.values(VAX_NAMES).map((v) => (
<button
key={`name-group-${v}`}
onClick={() =>
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}
</button>
))}
</div>
</div>
) : null}
{this.shouldShowDoses() ? (
<div className="question-container">
<p className="question" data-font="sans">
Select Dose
</p>
<div className="answers-container">
{Object.values(VAX_DOSES).map((v) => (
<button
key={`dose-group-${v}`}
onClick={() =>
this.setState({
dose: v,
age: "",
showInputPopup: false,
})
}
data-font="sans-bold"
className={`dose answer${dose === v ? " selected" : ""}${
dose && dose !== v ? " unselected" : ""
}`}
>
{v}
</button>
))}
</div>
</div>
) : null}
{this.shouldShowAgeGroup() ? (
<div className="question-container">
<p className="question" data-font="sans">
Select Age Group
</p>
<div className="answers-container">
{Object.values(VAX_AGES[name]).map((v) => (
<button
key={`age-group-${v}`}
onClick={() =>
this.setState({
age: v,
showInputPopup: false,
})
}
data-font="sans-bold"
className={`age answer${age === v ? " selected" : ""}${
age && age !== v ? " unselected" : ""
}`}
>
{v}
</button>
))}
</div>
</div>
) : null}
{this.shouldShowResults() ? (
<div className="result-header-container">
<div className="symptom-container subheader">
<h3 data-font="sans-bold">Here's what to expect.</h3>
<h6
id="share-result"
onClick={this.copyShareLink}
className="overline"
data-font="sans"
data-button
>
Share ↗
</h6>
<h6 className="overline" data-font="sans" data-button>
<a href="/vaxandme_chart.pdf">Download Chart ↗</a>
</h6>
</div>
<div className="result-header-select">
<h6 className="overline" data-font="sans">
Use data from:
</h6>
<select
disabled={Object.keys(stats).length < 1}
onChange={(e) =>
this.setState({ dataSource: e.target.value })
}
value={dataSource}
>
{Object.keys(DATA_CHOICES).map((dataKey) => (
<option key={dataKey}>{DATA_CHOICES[dataKey]}</option>
))}
</select>
<h6 className="overline success" data-font="sans">
Based on
{` ${this.getSampleSize()
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} `}
individual experiences
</h6>
{Object.keys(stats).length < 1 ? (
<h6 className="overline errored" data-font="sans">
Failed to get stats from Vax&amp;Me
</h6>
) : null}
</div>
</div>
) : null}
{this.shouldShowResults() ? (
<div className="tip-container">
{/* <div className="tip-icon">&#9432;</div> */}
<div className="tip">
<h6 className="subheader" data-font="sans">
NET EFFICACY
</h6>
<h3 className="header" data-font="sans-bold">
{VAX_EFFICACY[name][age]}%
</h3>
<h6
className="overline"
style={{ wordBreak: "break-word" }}
data-font="sans"
>
of vaccine trial participants who took the <u>{name}</u>{" "}
vaccine (in the <u>{age}</u> age group) did not get COVID-19
</h6>
<h6 className="overline" data-font="sans" data-button>
<a href="#data-sources">See Data Sources ↗</a>
</h6>
<h6 className="overline" data-font="sans" data-button>
<a
target="_blank"
href="https://www.gavi.org/vaccineswork/what-difference-between-efficacy-and-effectiveness"
rel="noreferrer"
>
What is efficacy? ↗
</a>
</h6>
</div>
</div>
) : null}
{this.shouldShowResults() ? (
<div className="symptom-container">
{Object.keys(resultAtSite).map((symptom) => (
<div className="symptom at-site" key={`symptom-${symptom}`}>
<span className="symptom-emoji">{ICONS[symptom]}</span>
<p data-font="sans" className="symptom-name">
{symptom}
</p>
<p data-font="sans-bold" className="symptom-chance">
{this.getStatForSymptom(symptom)}/10
</p>
</div>
))}
{Object.keys(result).map((symptom) => (
<div className="symptom" key={`symptom-${symptom}`}>
<span className="symptom-emoji">{ICONS[symptom]}</span>
<p data-font="sans" className="symptom-name">
{symptom}
</p>
<p data-font="sans-bold" className="symptom-chance">
{this.getStatForSymptom(symptom)}/10
</p>
</div>
))}
</div>
) : null}
</main>
{this.shouldShowResults() ? (
<div className={`input-popup${showInputPopup ? " expanded" : ""}`}>
<div
className="input-popup-header"
onClick={() => this.setState({ showInputPopup: !showInputPopup })}
>
<div className="input-popup-header-text">
<h6 className="overline" data-font="sans">
Already vaxxed?
</h6>
<h3 className="subheader" data-font="sans-bold">
Tell us how it went.
</h3>
</div>
<button
data-font="sans"
className="subheader input-popup-header-icon"
onClick={() =>
this.setState({ showInputPopup: !showInputPopup })
}
>
&#9660;
</button>
</div>
{showInputPopup ? (
<div className="input-popup-body">
<p className="question" data-font="sans">
I am in age group <strong>{age}</strong> and got the
<strong> {name}</strong> vaccine. I experienced:
</p>
{this.shouldShowDoses()
? Object.keys(VAX_DOSES).map((dose) =>
this.getAnswerOptions(VAX_DOSES[dose])
)
: this.getAnswerOptions()}
<div className="question-container">
<p className="question" data-font="sans">
You can comeback and update your experience at any time!
</p>
<p className="question" data-font="sans">
(Just use the same device)
</p>
</div>
</div>
) : null}
{showInputPopup ? (
<div
onClick={this.submitAnswer}
className="input-popup-submit"
data-font="sans-bold"
>
SUBMIT
</div>
) : null}
</div>
) : null}
</div>
);
}
}
const domContainer = document.querySelector('#root');
const root = ReactDOM.createRoot(domContainer);
root.render(React.createElement(App));

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+147 -147
View File
@@ -1,147 +1,147 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Learn about the expected side effects of COVID-19 Vaccines" content="Learn about the expected side effects of COVID-19 Vaccines"
/> />
<link rel="apple-touch-icon" href="/logo192.png" /> <link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="manifest.json" />
<style> <style>
@import "/styles/variables.css"; @import "styles/variables.css";
@import "/styles/variables-dark.css" screen and @import "styles/variables-dark.css" screen and
(prefers-color-scheme: dark); (prefers-color-scheme: dark);
@import "/styles/extrasmall.css"; @import "styles/extrasmall.css";
@import "/styles/small.css" only screen and (min-width: 370px) and @import "styles/small.css" only screen and (min-width: 370px) and
(max-width: 767px); (max-width: 767px);
@import "/styles/smallmedium.css" only screen and (min-width: 768px) and @import "styles/smallmedium.css" only screen and (min-width: 768px) and
(max-width: 1023px); (max-width: 1023px);
@import "/styles/medium.css" only screen and (min-width: 1024px); @import "styles/medium.css" only screen and (min-width: 1024px);
@import "/styles/large.css" only screen and (min-width: 1366px); @import "styles/large.css" only screen and (min-width: 1366px);
@import "/styles/extralarge.css" only screen and (min-width: 1921px); @import "styles/extralarge.css" only screen and (min-width: 1921px);
@font-face { @font-face {
font-family: "GlacialBold"; font-family: "GlacialBold";
src: url("/fonts/glacialindifference-bold-webfont.woff2") src: url("fonts/glacialindifference-bold-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-bold-webfont.woff") format("woff"); url("fonts/glacialindifference-bold-webfont.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "GlacialItalic"; font-family: "GlacialItalic";
src: url("/fonts/glacialindifference-italic-webfont.woff2") src: url("fonts/glacialindifference-italic-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-italic-webfont.woff") format("woff"); url("fonts/glacialindifference-italic-webfont.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "Glacial"; font-family: "Glacial";
src: url("/fonts/glacialindifference-regular-webfont.woff2") src: url("fonts/glacialindifference-regular-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-regular-webfont.woff") format("woff"); url("fonts/glacialindifference-regular-webfont.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
[data-font="sans"] { [data-font="sans"] {
font-family: "Glacial", sans-serif; font-family: "Glacial", sans-serif;
font-display: swap; font-display: swap;
} }
[data-font="sans-bold"] { [data-font="sans-bold"] {
font-family: "GlacialBold", sans-serif; font-family: "GlacialBold", sans-serif;
font-display: swap; font-display: swap;
} }
[data-font="sans-italic"] { [data-font="sans-italic"] {
font-family: "GlacialItalic", sans-serif; font-family: "GlacialItalic", sans-serif;
font-style: italic; font-style: italic;
font-display: swap; font-display: swap;
} }
</style> </style>
<title>Vax & Me | Privacy</title> <title>Vax & Me | Privacy</title>
</head> </head>
<body> <body>
<div id="root"> <div id="root">
<div class="app-container"> <div class="app-container">
<header> <header>
<div class="logo-container"> <div class="logo-container">
<div class="logo"></div> <div class="logo"></div>
<div class="logo-text-container"> <div class="logo-text-container">
<p data-font="sans-bold">PRIVACY</p> <p data-font="sans-bold">PRIVACY</p>
</div> </div>
</div> </div>
</header> </header>
<main> <main>
<div class="intro"> <div class="intro">
<h6 class="overline" data-font="sans">What's collected</h6> <h6 class="overline" data-font="sans">What's collected</h6>
<h6 class="overline" data-font="sans">&amp; How it's used</h6> <h6 class="overline" data-font="sans">&amp; How it's used</h6>
</div> </div>
<div class="question-container"> <div class="question-container">
<h6 class="subheader" data-font="sans-bold"> <h6 class="subheader" data-font="sans-bold">
USER PERSONAL INFORMATION USER PERSONAL INFORMATION
</h6> </h6>
<div class="answers-container"> <div class="answers-container">
<p class="overline" data-font="sans"> <p class="overline" data-font="sans">
No personally indentifiable information or user data is No personally indentifiable information or user data is
collected. Vax & Me only uses Google Analytics on it's website collected. Vax & Me only uses Google Analytics on it's website
to study website performance, and error prevention. to study website performance, and error prevention.
</p> </p>
</div> </div>
</div> </div>
<div class="question-container"> <div class="question-container">
<h6 class="subheader" data-font="sans-bold">USER ACTIVITY</h6> <h6 class="subheader" data-font="sans-bold">USER ACTIVITY</h6>
<div class="answers-container"> <div class="answers-container">
<p class="overline" data-font="sans"> <p class="overline" data-font="sans">
Vax & Me monitors some click, and keystrokes on it's website to Vax & Me monitors some click, and keystrokes on it's website to
detect functionality errors. detect functionality errors.
</p> </p>
</div> </div>
</div> </div>
<div class="question-container"> <div class="question-container">
<h6 class="subheader" data-font="sans-bold">USER DATA</h6> <h6 class="subheader" data-font="sans-bold">USER DATA</h6>
<div class="answers-container"> <div class="answers-container">
<p class="overline" data-font="sans"> <p class="overline" data-font="sans">
Volunteered data is linked to Google Analytics Client ID - which Volunteered data is linked to Google Analytics Client ID - which
is unique to a device and web-browser combination. This is is unique to a device and web-browser combination. This is
required to prevent data duplicacy and is core to the required to prevent data duplicacy and is core to the
functionality of Vax & Me. functionality of Vax & Me.
</p> </p>
</div> </div>
</div> </div>
</main> </main>
</div> </div>
</div> </div>
<footer data-font="sans"> <footer data-font="sans">
<div class="link-container"> <div class="link-container">
<div> <div>
<h6 class="overline" data-font="sans">FDA Data Sources</h6> <h6 class="overline" data-font="sans">FDA Data Sources</h6>
<a href="https://www.fda.gov/media/144416/download" <a href="https://www.fda.gov/media/144416/download"
>PFizer-BioNTech</a >PFizer-BioNTech</a
> >
<a href="https://www.fda.gov/media/144637/download">Moderna</a> <a href="https://www.fda.gov/media/144637/download">Moderna</a>
<a href="https://www.fda.gov/media/146304/download">J&J Janssen</a> <a href="https://www.fda.gov/media/146304/download">J&J Janssen</a>
</div> </div>
<div> <div>
<h6 class="overline" data-font="sans">Links</h6> <h6 class="overline" data-font="sans">Links</h6>
<a href="/vaxandme_chart.pdf">Download Chart</a> <a href="vaxandme_chart.pdf">Download Chart</a>
<a href="/">Home</a> <a href="index.html">Home</a>
</div> </div>
<div> <div>
<h6 class="overline" data-font="sans">Legal</h6> <h6 class="overline" data-font="sans">Legal</h6>
<a href="/copyright">Copyright</a> <a href="copyright.html">Copyright</a>
<a href="/privacy">Privacy</a> <a href="privacy.html">Privacy</a>
</div> </div>
</div> </div>
<div class="overline by" data-font="sans"> <div class="overline by" data-font="sans">
Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh
</div> </div>
</footer> </footer>
</body> </body>
</html> </html>
+119 -119
View File
@@ -1,119 +1,119 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Learn about the expected side effects of COVID-19 Vaccines" content="Learn about the expected side effects of COVID-19 Vaccines"
/> />
<link rel="apple-touch-icon" href="/logo192.png" /> <link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="manifest.json" />
<style> <style>
@import "/styles/variables.css"; @import "styles/variables.css";
@import "/styles/variables-dark.css" screen and @import "styles/variables-dark.css" screen and
(prefers-color-scheme: dark); (prefers-color-scheme: dark);
@import "/styles/extrasmall.css"; @import "styles/extrasmall.css";
@import "/styles/small.css" only screen and (min-width: 370px) @import "styles/small.css" only screen and (min-width: 370px)
and (max-width: 767px); and (max-width: 767px);
@import "/styles/smallmedium.css" only screen and @import "styles/smallmedium.css" only screen and
(min-width: 768px) and (max-width: 1023px); (min-width: 768px) and (max-width: 1023px);
@import "/styles/medium.css" only screen and @import "styles/medium.css" only screen and
(min-width: 1024px); (min-width: 1024px);
@import "/styles/large.css" only screen and @import "styles/large.css" only screen and
(min-width: 1366px); (min-width: 1366px);
@import "/styles/extralarge.css" only screen and @import "styles/extralarge.css" only screen and
(min-width: 1921px); (min-width: 1921px);
@font-face { @font-face {
font-family: "GlacialBold"; font-family: "GlacialBold";
src: url("/fonts/glacialindifference-bold-webfont.woff2") src: url("fonts/glacialindifference-bold-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-bold-webfont.woff") format("woff"); url("fonts/glacialindifference-bold-webfont.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "GlacialItalic"; font-family: "GlacialItalic";
src: url("/fonts/glacialindifference-italic-webfont.woff2") src: url("fonts/glacialindifference-italic-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-italic-webfont.woff") url("fonts/glacialindifference-italic-webfont.woff")
format("woff"); format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "Glacial"; font-family: "Glacial";
src: url("/fonts/glacialindifference-regular-webfont.woff2") src: url("fonts/glacialindifference-regular-webfont.woff2")
format("woff2"), format("woff2"),
url("/fonts/glacialindifference-regular-webfont.woff") url("fonts/glacialindifference-regular-webfont.woff")
format("woff"); format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
[data-font="sans"] { [data-font="sans"] {
font-family: "Glacial", sans-serif; font-family: "Glacial", sans-serif;
font-display: swap; font-display: swap;
} }
[data-font="sans-bold"] { [data-font="sans-bold"] {
font-family: "GlacialBold", sans-serif; font-family: "GlacialBold", sans-serif;
font-display: swap; font-display: swap;
} }
[data-font="sans-italic"] { [data-font="sans-italic"] {
font-family: "GlacialItalic", sans-serif; font-family: "GlacialItalic", sans-serif;
font-style: italic; font-style: italic;
font-display: swap; font-display: swap;
} }
</style> </style>
<title>Vax & Me | 404</title> <title>Vax & Me | 404</title>
</head> </head>
<body> <body>
<div id="root"> <div id="root">
<div class="app-container"> <div class="app-container">
<header> <header>
<div class="logo-container"> <div class="logo-container">
<div class="logo"> </div> <div class="logo"> </div>
</div> </div>
</header> </header>
<main> <main>
<div class="intro"> <div class="intro">
<h1 class="header" data-font="sans-bold"> <h1 class="header" data-font="sans-bold">
404 404
</h1> </h1>
<h6 class="overline" data-font="sans"> <h6 class="overline" data-font="sans">
This page doesn't exist. This page doesn't exist.
</h6> </h6>
</div> </div>
</main> </main>
</div> </div>
</div> </div>
<footer data-font="sans"> <footer data-font="sans">
<div class="link-container"> <div class="link-container">
<div> <div>
<h6 class="overline" data-font="sans">FDA Data Sources</h6> <h6 class="overline" data-font="sans">FDA Data Sources</h6>
<a href="https://www.fda.gov/media/144416/download">PFizer-BioNTech</a> <a href="https://www.fda.gov/media/144416/download">PFizer-BioNTech</a>
<a href="https://www.fda.gov/media/144637/download">Moderna</a> <a href="https://www.fda.gov/media/144637/download">Moderna</a>
<a href="https://www.fda.gov/media/146304/download">J&J Janssen</a> <a href="https://www.fda.gov/media/146304/download">J&J Janssen</a>
</div> </div>
<div> <div>
<h6 class="overline" data-font="sans">Links</h6> <h6 class="overline" data-font="sans">Links</h6>
<a href="/vaxandme_chart.pdf">Download Chart</a> <a href="vaxandme_chart.pdf">Download Chart</a>
<a href="/">Home</a> <a href="index.html">Home</a>
</div> </div>
<div> <div>
<h6 class="overline" data-font="sans">Legal</h6> <h6 class="overline" data-font="sans">Legal</h6>
<a href="/copyright">Copyright</a> <a href="copyright.html">Copyright</a>
<a href="/privacy">Privacy</a> <a href="privacy.html">Privacy</a>
</div> </div>
</div> </div>
<div class="overline by" data-font="sans"> <div class="overline by" data-font="sans">
Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh
</div> </div>
</footer> </footer>
</body> </body>
</html> </html>
@@ -1,7 +1,7 @@
.input-popup.expanded { .input-popup.expanded {
height: 40vh; height: 50vh;
} }
.loading-bar-container { .loading-bar-container {
max-width: 25%; max-width: 25%;
} }
@@ -1,384 +1,475 @@
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
transition: all ease-in-out 0.2s; transition: all ease-in-out 0.2s;
color: var(--color-secondary); color: var(--color-secondary);
user-select: none; user-select: none;
} }
html { html {
font-size: 10px; font-size: 10px;
} scroll-behavior: smooth;
body { }
display: flex; body {
flex-direction: column; display: flex;
font-size: 10px; flex-direction: column;
padding: 3.75rem; font-size: 10px;
padding-bottom: 0 !important; padding: 3.75rem;
height: calc(100vh - 3.75rem); padding-bottom: 0 !important;
overflow-x: hidden; height: calc(100vh - 3.75rem);
max-width: 100vw; overflow-x: hidden;
background: var(--color-primary); max-width: 100vw;
} background: var(--color-primary);
}
#root {
flex: 1; #root {
display: flex; flex: 1;
} display: flex;
}
.app-container {
flex: 1; .app-container {
} flex: 1;
}
header {
display: flex; header {
justify-content: center; display: flex;
flex-direction: column; justify-content: center;
} flex-direction: column;
}
.logo-container {
display: flex; .logo-container {
width: fit-content; display: flex;
justify-content: center; width: fit-content;
flex-direction: column; justify-content: center;
} flex-direction: column;
.logo { }
background-size: 100%; .logo {
width: 5rem; background-size: 100%;
height: 5rem; width: 5rem;
display: inline-block; height: 5rem;
background-image: url("/logo512.png"); display: inline-block;
} background-image: url("../logo512.png");
.logo-text-container { }
justify-content: center; .logo-text-container {
vertical-align: middle; justify-content: center;
flex-direction: column; vertical-align: middle;
flex: 1; flex-direction: column;
margin-top: 3.5rem; flex: 1;
} margin-top: 3.5rem;
.logo-text-container p { }
font-size: 2.4rem; .logo-text-container p {
} font-size: 2.4rem;
}
.header {
font-size: 10rem; .header {
letter-spacing: 1rem; font-size: 10rem;
} letter-spacing: 1rem;
}
.subheader {
font-size: 2.5rem; .subheader {
margin: 0; font-size: 2.5rem;
} margin: 0;
}
.overline {
font-size: 2rem; .overline {
letter-spacing: 0.1rem; font-size: 2rem;
margin: 0; letter-spacing: 0.1rem;
} margin: 0;
}
.overline[data-button] {
border-bottom: 1px solid; .overline[data-button] {
width: fit-content; border-bottom: 1px solid;
cursor: pointer; width: fit-content;
} cursor: pointer;
}
.overline[data-button] a {
text-decoration: none; .overline[data-button] a {
} text-decoration: none;
}
.intro {
margin: 4.5rem 0; .intro {
display: inline-block; margin: 4.5rem 0;
width: fit-content; display: inline-block;
} width: fit-content;
}
.question-container {
margin: 3.5rem 0; .question-container {
} margin: 3.5rem 0;
}
.question {
font-size: 2rem; .question {
} font-size: 2rem;
}
.answers-container {
margin: 1rem 0; .answers-container {
} margin: 1rem 0;
}
.answer {
display: inline-flex; .answer {
font-size: 1.8rem; display: inline-flex;
padding: 1.2rem 1.5rem; font-size: 1.8rem;
margin-right: 0.75rem; padding: 1.2rem 1.5rem;
margin-bottom: 0.75rem; margin-right: 0.75rem;
border-radius: 24rem; margin-bottom: 0.75rem;
background-color: var(--color-secondary); border-radius: 24rem;
color: var(--color-primary); background-color: var(--color-secondary);
outline: none !important; color: var(--color-primary);
border: none !important; outline: none !important;
letter-spacing: 0.2rem; border: none !important;
text-transform: uppercase; letter-spacing: 0.2rem;
cursor: pointer; text-transform: uppercase;
} cursor: pointer;
}
.answer.selected {
background-color: var(--color-bright); .answer.selected {
} background-color: var(--color-bright);
.answer.unselected { }
background-color: var(--color-dim); .answer.unselected {
} background-color: var(--color-dim);
}
.symptom-container.subheader {
margin-top: 5rem; .symptom-container.subheader {
margin-bottom: 2.5rem; margin-top: 5rem;
display: block !important; margin-bottom: 2.5rem;
} display: block !important;
.symptom-container.subheader .overline { }
margin: 1rem 0; .symptom-container.subheader .overline {
} margin: 1rem 0;
.symptom-container { }
margin-bottom: 5rem; .symptom-container {
} margin-bottom: 5rem;
.symptom { }
max-width: 100%; .symptom {
padding: 1.5rem; max-width: 100%;
flex-direction: column; padding: 1.5rem;
justify-content: center; flex-direction: column;
align-items: center; justify-content: center;
border: 1px solid transparent; align-items: center;
border-radius: 1rem; border: 1px solid transparent;
transition: border-color ease-in-out 0.4s; border-radius: 1rem;
transition: box-shadow ease-in-out 0.3s; transition: border-color ease-in-out 0.4s;
} transition: box-shadow ease-in-out 0.3s;
.symptom.at-site { }
background-color: var(--color-dim); .symptom.at-site {
border-radius: 0; background-color: var(--color-dim);
} border-radius: 0;
.symptom.at-site:first-of-type { }
border-top-left-radius: 1rem; .symptom.at-site:first-of-type {
border-top-right-radius: 1rem; border-top-left-radius: 1rem;
} border-top-right-radius: 1rem;
.symptom.at-site:nth-of-type(3) { }
border-bottom-left-radius: 1rem; .symptom.at-site:nth-of-type(3) {
border-bottom-right-radius: 1rem; border-bottom-left-radius: 1rem;
} border-bottom-right-radius: 1rem;
}
.symptom-emoji {
text-align: center; .symptom-emoji {
font-size: 3rem; text-align: center;
padding: 0.75rem; font-size: 3rem;
display: block; padding: 0.75rem;
} display: block;
.symptom-name { }
font-size: 2rem; .symptom-name {
text-align: center; font-size: 2rem;
display: block; text-align: center;
} display: block;
.symptom-chance { }
font-size: 1.8rem; .symptom-chance {
text-align: center; font-size: 1.8rem;
display: block; text-align: center;
} display: block;
.symptom-chance:before { }
content: "EXPERIENCED BY"; .symptom-chance:before {
font-size: 1rem; content: "EXPERIENCED BY";
display: block; font-size: 1rem;
text-align: center; display: block;
letter-spacing: 0.1rem; text-align: center;
} letter-spacing: 0.1rem;
.symptom-chance:after { }
content: "PEOPLE"; .symptom-chance:after {
font-size: 1rem; content: "PEOPLE";
display: block; font-size: 1rem;
text-align: center; display: block;
letter-spacing: 0.1rem; text-align: center;
} letter-spacing: 0.1rem;
}
footer {
padding: 5rem 3.75rem; footer {
width: calc(100vw - 7.5rem); padding: 5rem 3.75rem;
margin-left: -3.75rem; width: calc(100vw - 7.5rem);
background-color: var(--color-secondary-light); margin-left: -3.75rem;
} background-color: var(--color-secondary-light);
.link-container { }
font-size: 1.2rem; .link-container {
transition: none !important; font-size: 1.2rem;
display: flex; transition: none !important;
flex-direction: column; display: flex;
} flex-direction: column;
}
.link-container > div {
flex: 1; .link-container > div {
display: flex; flex: 1;
flex-direction: column; display: flex;
color: var(--color-primary); flex-direction: column;
} color: var(--color-primary);
}
.link-container > div > .overline {
color: var(--color-primary); .link-container > div > .overline {
} color: var(--color-primary);
}
.link-container > div:not(:first-of-type) {
margin-top: 3rem; .link-container > div:not(:first-of-type) {
} margin-top: 3rem;
}
a {
color: var(--color); a {
margin-top: 0.75rem; color: var(--color);
max-width: fit-content; margin-top: 0.75rem;
max-width: fit-content;
}
}
.link-container a {
font-size: 1.8rem; .link-container a {
} font-size: 1.8rem;
}
footer .by {
color: var(--color-primary); footer .by {
margin-top: 4rem; color: var(--color-primary);
} margin-top: 4rem;
}
@keyframes slideUp {
0% { @keyframes slideUp {
transform: translateY(300%); 0% {
} transform: translateY(300%);
100% { }
transform: translateY(0); 100% {
} transform: translateY(0);
} }
}
.input-popup {
position: sticky; .input-popup {
background-color: var(--color-secondary-light); position: sticky;
bottom: 1rem; background-color: var(--color-secondary-light);
height: 13.5rem; bottom: 1rem;
margin-bottom: 7.5rem; height: 13.5rem;
transition: height ease-in-out 0.3s; margin-bottom: 7.5rem;
overflow: hidden; transition: height ease-in-out 0.3s;
border-radius: 2rem; overflow: hidden;
max-height: 85vh; border-radius: 2rem;
display: flex; max-height: 85vh;
flex-direction: column; display: flex;
animation: 0.5s ease-out 0s 1 slideUp; flex-direction: column;
animation-delay: 0.5s; animation: 0.5s ease-out 0s 1 slideUp;
animation-fill-mode: forwards; animation-delay: 0.5s;
transform: translateY(200%); animation-fill-mode: forwards;
} transform: translateY(200%);
.input-popup * { }
color: var(--color-primary); .input-popup * {
} color: var(--color-primary);
.input-popup.expanded { }
height: 80vh; .input-popup.expanded {
overflow-y: auto; height: 80vh;
} overflow-y: auto;
.input-popup .input-popup-header-icon { }
transform: rotate(180deg); .input-popup .input-popup-header-icon {
} transform: rotate(180deg);
.input-popup.expanded .input-popup-header-icon { }
transform: rotate(0deg); .input-popup.expanded .input-popup-header-icon {
} transform: rotate(0deg);
.input-popup-header { }
padding: 2.5rem; .input-popup-header {
position: sticky; padding: 2.5rem;
top: 0; position: sticky;
display: flex; top: 0;
flex-direction: row; display: flex;
justify-content: center; flex-direction: row;
align-items: center; justify-content: center;
background-color: var(--color-secondary-light); align-items: center;
border-bottom: 1px solid transparent; 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.expanded .input-popup-header {
} border-bottom: 1px solid var(--color-tertiary);
.input-popup-header-text { }
flex: 1; .input-popup-header-text {
} flex: 1;
.input-popup-header-icon { }
cursor: pointer; .input-popup-header-icon {
background-color: var(--color-secondary); cursor: pointer;
outline: none; background-color: var(--color-primary);
color: var(--color-primary); outline: none;
border: 1px solid transparent; color: var(--color-secondary);
border-radius: 10rem; border: 1px solid transparent;
height: 5rem; border-radius: 10rem;
width: 5rem; height: 5rem;
} width: 5rem;
}
.input-popup-body {
padding: 2.5rem 2.5rem; .input-popup-body {
flex: 1; 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 {
} background-color: var(--color-tertiary);
.input-popup-body .question-container .answers-container .answer.selected { }
background-color: var(--color-secondary); .input-popup-body .question-container .answers-container .answer.selected {
} background-color: var(--color-secondary);
}
.input-popup-submit {
padding: 2.5rem; .input-popup-submit {
text-align: center; padding: 2.5rem;
font-size: 2rem; text-align: center;
letter-spacing: 0.3rem; font-size: 2rem;
background-color: var(--color-secondary); letter-spacing: 0.3rem;
cursor: pointer; background-color: var(--color-secondary);
} cursor: pointer;
.input-popup.expanded .input-popup-submit { }
position: sticky; .input-popup.expanded .input-popup-submit {
bottom: 0; position: sticky;
} bottom: 0;
.input-popup-submit.submitted { }
background-color: #55c16c; .input-popup-submit.submitted {
} background-color: #55c16c;
}
@keyframes loading { .input-popup-submit.errored {
0% { background-color: #c43d34;
width: 0%; }
}
100% { .input-popup.expanded::-webkit-scrollbar {
width: 100%; width: 1.5rem;
} background-color: var(--color-tertiary);
} border-radius: 0 4rem 4rem 0;
.loading-bar-container { }
min-height: 2rem; .input-popup.expanded::-webkit-scrollbar-thumb {
background-color: var(--color-secondary-light); width: 1rem;
border-radius: 1rem; background-color: var(--color-dim);
margin-top: 1rem; border-radius: 0 4rem 4rem 0;
} }
.loading-bar {
position: relative; .tip-container {
width: 1rem; display: flex;
min-height: 2rem; flex-direction: row;
background-color: #25d255; padding: 2rem 0;
border-radius: 1rem; margin: 2rem 0;
animation: 2s ease-out 0s infinite loading; border-radius: 1rem;
} margin-bottom: 2rem;
}
@media (hover: hover) and (pointer: fine) { .tip-icon {
.answer:hover, font-size: 3rem;
.answer.unselected:hover { }
background-color: var(--color-secondary-light); .tip .overline {
} font-size: 1.6rem;
.symptom:hover { }
box-shadow: 0px 0px 100px var(--color-tertiary); .tip .subheader {
background-color: var(--color-primary); font-size: 2rem;
} letter-spacing: 0.2rem;
.symptom.at-site:hover { margin-top: 0.4rem;
box-shadow: 0px 0px 100px var(--color-dim); }
background-color: var(--color-secondary-light); .tip .header {
color: var(--color-primary); font-size: 6rem;
} letter-spacing: 0.5rem;
.symptom.at-site:hover p { }
color: var(--color-primary); .tip-container .overline[data-button] {
} margin-top: 1rem;
.input-popup-header-icon:hover { }
background-color: var(--color-tertiary); .tip-container .overline[data-button]:first-of-type {
} margin-top: 2rem;
.input-popup-body .question-container .answers-container .answer:hover { }
background-color: var(--color-bright);
} @keyframes loading {
.input-popup-submit:hover { 0% {
background-color: var(--color-tertiary); 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);
}
}
@@ -1,30 +1,30 @@
body { body {
padding: 15rem; padding: 15rem;
padding-top: 10rem; padding-top: 10rem;
height: calc(100vh - 10rem); height: calc(100vh - 10rem);
} }
.logo { .logo {
width: 10rem; width: 10rem;
height: 10rem; height: 10rem;
} }
.symptom { .symptom {
max-width: 25rem; max-width: 25rem;
height: 25rem; height: 25rem;
} }
footer { footer {
padding: 5rem 15rem; padding: 5rem 15rem;
width: calc(100vw - 30rem); width: calc(100vw - 30rem);
margin-left: -15rem; margin-left: -15rem;
} }
.input-popup { .input-popup {
height: 10rem; height: 10rem;
} }
.input-popup.expanded { .input-popup.expanded {
height: 50vh; height: 55vh;
} }
.loading-bar-container { .loading-bar-container {
max-width: 35%; max-width: 35%;
} }
@@ -1,105 +1,135 @@
body { body {
padding: 7.5rem; padding: 7.5rem;
padding-top: 5rem; padding-top: 5rem;
height: calc(100vh - 5rem); height: calc(100vh - 5rem);
} }
.logo-container { .logo-container {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
} }
.logo { .logo {
width: 7.5rem; width: 7.5rem;
height: 7.5rem; height: 7.5rem;
} }
.logo-text-container { .logo-text-container {
margin-top: 0; margin-top: 0;
margin-left: 1.5rem; margin-left: 1.5rem;
border-left: 1px solid var(--color-secondary); border-left: 1px solid var(--color-secondary);
padding-left: 1.5rem; padding-left: 1.5rem;
} }
.intro .overline { .intro .overline {
font-size: 3rem; font-size: 3rem;
} }
.intro .subheader { .intro .subheader {
font-size: 4rem; font-size: 4rem;
} }
.header { .header {
font-size: 15rem; font-size: 15rem;
letter-spacing: 1.5rem; letter-spacing: 1.5rem;
} }
.overline[data-button] { .overline[data-button] {
display: inline-block; display: inline-block;
} }
.overline[data-button]:not(:first-of-type) { .overline[data-button]:not(:first-of-type) {
margin-left: 1.5rem; margin-left: 1.5rem;
} }
.symptom { .symptom {
width: 100%; width: 100%;
max-width: 20rem; max-width: 20rem;
height: 20rem; height: 20rem;
display: inline-flex; display: inline-flex;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.symptom-emoji { .symptom-emoji {
font-size: 3.5rem; font-size: 3.5rem;
padding: 0.75rem; padding: 0.75rem;
} }
.symptom-name { .symptom-name {
font-size: 2.5rem; font-size: 2.5rem;
text-align: center; text-align: center;
} }
.symptom-chance { .symptom-chance {
font-size: 2.4rem; font-size: 2.4rem;
text-align: center; text-align: center;
} }
.symptom-chance:before { .symptom-chance:before {
font-size: 1.5rem; font-size: 1.5rem;
} }
.symptom-chance:after { .symptom-chance:after {
font-size: 1.5rem; font-size: 1.5rem;
} }
.symptom.at-site:first-of-type { .symptom.at-site:first-of-type {
border-radius: 0; border-radius: 0;
border-top-left-radius: 1rem; border-top-left-radius: 1rem;
border-bottom-left-radius: 1rem; border-bottom-left-radius: 1rem;
} }
.symptom.at-site:nth-of-type(3) { .symptom.at-site:nth-of-type(3) {
border-radius: 0; border-radius: 0;
border-top-right-radius: 1rem; border-top-right-radius: 1rem;
border-bottom-right-radius: 1rem; border-bottom-right-radius: 1rem;
} }
footer { footer {
padding: 5rem 7.5rem; padding: 5rem 7.5rem;
width: calc(100vw - 15rem); width: calc(100vw - 15rem);
margin-left: -7.5rem; margin-left: -7.5rem;
} }
.link-container { .link-container {
font-size: 1.7rem; font-size: 1.7rem;
flex-direction: row; flex-direction: row;
} }
.link-container > div { .link-container > div {
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
} }
.link-container > div:not(:first-of-type) { .link-container > div:not(:first-of-type) {
margin-left: 3rem; margin-left: 3rem;
margin-top: 0; margin-top: 0;
} }
.input-popup { .input-popup {
height: 10rem; height: 10rem;
} }
.input-popup.expanded { .input-popup.expanded {
height: 55vh; height: 55vh;
} }
.loading-bar-container { .loading-bar-container {
max-width: 50%; 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;
}
@@ -1,7 +1,7 @@
.input-popup { .input-popup {
height: 11.5rem; height: 11.5rem;
} }
.input-popup.expanded { .input-popup.expanded {
height: 85vh; height: 85vh;
} }
@@ -1,7 +1,7 @@
.input-popup { .input-popup {
height: 11rem; height: 11rem;
} }
.input-popup.expanded { .input-popup.expanded {
height: 65vh; height: 65vh;
} }
@@ -1,9 +1,9 @@
:root { :root {
--color-primary: black; --color-primary: black;
--color-secondary: white; --color-secondary: white;
--color-secondary-light: rgb(185, 185, 185); --color-secondary-light: rgb(185, 185, 185);
--color-tertiary: rgb(105, 105, 105); --color-tertiary: rgb(105, 105, 105);
--color-bright: #636363; --color-bright: #636363;
--color-dim: #352f2f; --color-dim: #352f2f;
font-size: 10px; font-size: 10px;
} }
@@ -1,9 +1,9 @@
:root { :root {
--color-primary: white; --color-primary: white;
--color-secondary: black; --color-secondary: black;
--color-secondary-light: rgb(56, 56, 56); --color-secondary-light: rgb(56, 56, 56);
--color-tertiary: rgba(185, 185, 185, 0.459); --color-tertiary: rgba(185, 185, 185, 0.459);
--color-bright: #575757; --color-bright: #575757;
--color-dim: #aeadb0; --color-dim: #aeadb0;
font-size: 10px; font-size: 10px;
} }
-16937
View File
File diff suppressed because it is too large Load Diff
-38
View File
@@ -1,38 +0,0 @@
{
"name": "vaxandme",
"version": "0.1.0",
"private": true,
"dependencies": {
"firebase": "^8.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"deploy": "firebase deploy --only hosting",
"serve" : "firebase serve --only hosting"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
-142
View File
@@ -1,142 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google Tag Manager -->
<script>
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l != "dataLayer" ? "&l=" + l : "";
j.async = true;
j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
f.parentNode.insertBefore(j, f);
})(window, document, "script", "dataLayer", "GTM-K4XHBR6");
</script>
<!-- End Google Tag Manager -->
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Learn about the expected side effects of COVID-19 Vaccines"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<style>
@import "%PUBLIC_URL%/styles/variables.css";
@import "%PUBLIC_URL%/styles/variables-dark.css" screen and
(prefers-color-scheme: dark);
@import "%PUBLIC_URL%/styles/extrasmall.css";
@import "%PUBLIC_URL%/styles/small.css" only screen and (min-width: 370px)
and (max-width: 767px);
@import "%PUBLIC_URL%/styles/smallmedium.css" only screen and
(min-width: 768px) and (max-width: 1023px);
@import "%PUBLIC_URL%/styles/medium.css" only screen and
(min-width: 1024px);
@import "%PUBLIC_URL%/styles/large.css" only screen and
(min-width: 1366px);
@import "%PUBLIC_URL%/styles/extralarge.css" only screen and
(min-width: 1921px);
@font-face {
font-family: "GlacialBold";
src: url("%PUBLIC_URL%/fonts/glacialindifference-bold-webfont.woff2")
format("woff2"),
url("/fonts/glacialindifference-bold-webfont.woff") format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "GlacialItalic";
src: url("%PUBLIC_URL%/fonts/glacialindifference-italic-webfont.woff2")
format("woff2"),
url("%PUBLIC_URL%/fonts/glacialindifference-italic-webfont.woff")
format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Glacial";
src: url("%PUBLIC_URL%/fonts/glacialindifference-regular-webfont.woff2")
format("woff2"),
url("%PUBLIC_URL%/fonts/glacialindifference-regular-webfont.woff")
format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
[data-font="sans"] {
font-family: "Glacial", sans-serif;
font-display: swap;
}
[data-font="sans-bold"] {
font-family: "GlacialBold", sans-serif;
font-display: swap;
}
[data-font="sans-italic"] {
font-family: "GlacialItalic", sans-serif;
font-style: italic;
font-display: swap;
}
.logo {
background-image: url("%PUBLIC_URL%/logo512.png");
}
</style>
<!-- Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=G-7VZCY223NG"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "G-7VZCY223NG");
</script>
<title>Vax & Me</title>
</head>
<body>
<!-- Google Tag Manager (noscript) -->
<noscript>
<iframe
src="https://www.googletagmanager.com/ns.html?id=GTM-K4XHBR6"
height="0"
width="0"
style="display: none; visibility: hidden"
>
</iframe>
</noscript>
<!-- End Google Tag Manager (noscript) -->
<div id="root"></div>
<footer data-font="sans">
<div class="link-container">
<div>
<h6 class="overline" data-font="sans">FDA Data Sources</h6>
<a href="https://www.fda.gov/media/144416/download">PFizer-BioNTech</a>
<a href="https://www.fda.gov/media/144637/download">Moderna</a>
<a href="https://www.fda.gov/media/146304/download">J&J Janssen</a>
</div>
<div>
<h6 class="overline" data-font="sans">Links</h6>
<a href="%PUBLIC_URL%/vaxandme_chart.pdf">Download Chart</a>
<a href="/">Home</a>
</div>
<div>
<h6 class="overline" data-font="sans">Legal</h6>
<a href="/copyright">Copyright</a>
<a href="/privacy">Privacy</a>
</div>
</div>
<div class="overline by" data-font="sans">
Designed &amp; Developed with ❤ by Alba Rajanibala and Ayushya Amitabh
</div>
</footer>
</body>
</html>
-669
View File
@@ -1,669 +0,0 @@
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.startsWith("localhost")) {
firebase.functions().useEmulator("localhost", 5001);
}
const fb_functions = firebase.functions();
const saveResult = fb_functions.httpsCallable("saveResult");
const getPastResult = fb_functions.httpsCallable("getPastResult");
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 PB_DOSE1_YOUNGER_RESULT = {
"PAIN AT SITE": "8",
"RED AT SITE": "1",
"SWELLING AT SITE": "1",
FATIGUE: "5",
HEADACHE: "4",
"NEED PAIN MED": "3",
"MUSCLE PAIN": "2",
CHILLS: "1",
DIRRHEA: "1",
"JOINT PAIN": "1",
FEVER: "<1",
VOMITING: "<1",
};
const PB_DOSE2_YOUNGER_RESULT = {
"PAIN AT SITE": "8",
"RED AT SITE": "1",
"SWELLING AT SITE": "1",
FATIGUE: "6",
HEADACHE: "5",
"NEED PAIN MED": "5",
"MUSCLE PAIN": "4",
CHILLS: "4",
"JOINT PAIN": "2",
FEVER: "2",
DIRRHEA: "1",
VOMITING: "<1",
};
const PB_DOSE1_OLDER_RESULT = {
"PAIN AT SITE": "7",
"RED AT SITE": "1",
"SWELLING AT SITE": "1",
FATIGUE: "3",
HEADACHE: "3",
"NEED PAIN MED": "2",
"MUSCLE PAIN": "1",
CHILLS: "1",
DIRRHEA: "1",
"JOINT PAIN": "1",
FEVER: "<1",
VOMITING: "<1",
};
const PB_DOSE2_OLDER_RESULT = {
"PAIN AT SITE": "7",
"RED AT SITE": "1",
"SWELLING AT SITE": "1",
FATIGUE: "5",
HEADACHE: "4",
"NEED PAIN MED": "4",
"MUSCLE PAIN": "3",
CHILLS: "2",
"JOINT PAIN": "2",
DIRRHEA: "1",
FEVER: "1",
VOMITING: "<1",
};
const MD_DOSE1_YOUNGER_RESULT = {
"PAIN AT SITE": "9",
"RED AT SITE": "<1",
"SWELLING AT SITE": "<1",
HEADACHE: "4",
FATIGUE: "4",
"MUSCLE PAIN": "2",
"JOINT PAIN": "2",
"NAUSEA OR VOMITING": "1",
FEVER: "1",
CHILLS: "1",
};
const MD_DOSE2_YOUNGER_RESULT = {
"PAIN AT SITE": "9",
"RED AT SITE": "1",
"SWELLING AT SITE": "1",
FATIGUE: "7",
HEADACHE: "6",
"JOINT PAIN": "5",
CHILLS: "5",
FEVER: "2",
"NAUSEA OR VOMITING": "2",
"MUSCLE PAIN": "1",
};
const MD_DOSE1_OLDER_RESULT = {
"PAIN AT SITE": "7",
"RED AT SITE": "<1",
"SWELLING AT SITE": "<1",
HEADACHE: "3",
FATIGUE: "3",
"MUSCLE PAIN": "2",
"JOINT PAIN": "2",
"NAUSEA OR VOMITING": "1",
CHILLS: "1",
FEVER: "<1",
};
const MD_DOSE2_OLDER_RESULT = {
"PAIN AT SITE": "8",
"RED AT SITE": "1",
"SWELLING AT SITE": "1",
FATIGUE: "6",
HEADACHE: "5",
"MUSCLE PAIN": "5",
"JOINT PAIN": "4",
CHILLS: "3",
"NAUSEA OR VOMITING": "1",
FEVER: "1",
};
const JJ_YOUNGER_RESULT = {
"PAIN AT SITE": "6",
"RED AT SITE": "1",
"SWELLING AT SITE": "1",
HEADACHE: "4",
FATIGUE: "4",
"MUSCLE PAIN": "4",
"NEED PAIN MED": "3",
"NAUSEA OR VOMITING": "2",
FEVER: "1",
};
const JJ_OLDER_RESULT = {
"PAIN AT SITE": "3",
"RED AT SITE": "<1",
"SWELLING AT SITE": "<1",
HEADACHE: "3",
FATIGUE: "3",
"MUSCLE PAIN": "2",
"NAUSEA OR VOMITING": "1",
"NEED PAIN MED": "1",
FEVER: "<1",
};
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: "🥶",
DIRRHEA: "💩",
"JOINT PAIN": "🦵",
FEVER: "🤒",
VOMITING: "🤮",
"NAUSEA OR VOMITING": "🤮",
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
dose: "",
age: "",
gid: "",
showInputPopup: false,
answers: {},
loading: true,
};
}
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;
}
}
}
if (!window.localStorage.getItem("hasExistingAnswer")) {
this.setState({ loading: false });
}
if (window.gtag) {
window.gtag("get", "G-7VZCY223NG", "client_id", (client_id) => {
if (!!client_id) {
this.setState({ gid: client_id });
getPastResult({ gid: client_id })
.then((v) => {
const { data } = v;
if (!!data) {
const { name, age, dose } = data;
this.setState({ name, age, dose, showInputPopup: true });
delete data.name;
delete data.age;
delete data.dose;
this.setState({ 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 });
}
}
shouldShowAgeGroup = () => {
const { name, dose } = this.state;
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 } = this.state;
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 } = this.state;
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) => {
const { answers } = this.state;
const newAnswers = Object.assign({}, answers);
if (answers[symptom]) {
newAnswers[symptom] = !answers[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 = Object.assign({}, this.getResults());
const symptoms = {};
Object.keys(result).forEach((symptom) => {
symptoms[symptom] = false;
});
const submitAnswers = Object.assign({}, symptoms, answers, {
name,
dose,
age,
gid,
});
saveResult(submitAnswers)
.then(() => {
const inputPopupSubmit = document.querySelector(".input-popup-submit");
if (!inputPopupSubmit.classList.contains("submitted")) {
inputPopupSubmit.classList.add("submitted");
}
inputPopupSubmit.innerHTML = "THANK YOU!";
setTimeout(() => {
inputPopupSubmit.innerHTML = "UPDATE ANSWER";
}, 1500);
})
.catch((err) => console.error("ERROR Saving Answer", err));
};
render() {
const { name, dose, age, showInputPopup, answers, loading } = 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 (
<div className="app-container">
<header>
<div className="logo-container">
<div className="logo"> </div>
</div>
<div className="intro">
<h6 className="overline" data-font="sans">
Getting vaccinated?
</h6>
<h3 className="subheader" data-font="sans-bold">
Find out what to expect.
</h3>
</div>
</header>
<main>
{loading ? (
<div className="loading-container">
<p className="question" data-font="sans">
Checking for existing answers...
</p>
<div className="loading-bar-container">
<div className="loading-bar"></div>
</div>
</div>
) : null}
{!loading ? (
<div className="question-container">
<p className="question" data-font="sans-italic">
Select Vaccine
</p>
<div className="answers-container">
{Object.values(VAX_NAMES).map((v) => (
<button
key={`name-group-${v}`}
onClick={() =>
this.setState({
name: v,
dose: "",
age: "",
answers: {},
showInputPopup: false,
})
}
data-font="sans-bold"
className={`vaccine answer${name === v ? " selected" : ""}${
name && name !== v ? " unselected" : ""
}`}
>
{v}
</button>
))}
</div>
</div>
) : null}
{name && (name === VAX_NAMES.PB || name === VAX_NAMES.MD) ? (
<div className="question-container">
<p className="question" data-font="sans-italic">
Select Dose
</p>
<div className="answers-container">
{Object.values(VAX_DOSES).map((v) => (
<button
key={`dose-group-${v}`}
onClick={() =>
this.setState({
dose: v,
age: "",
answers: {},
showInputPopup: false,
})
}
data-font="sans-bold"
className={`dose answer${dose === v ? " selected" : ""}${
dose && dose !== v ? " unselected" : ""
}`}
>
{v}
</button>
))}
</div>
</div>
) : null}
{this.shouldShowAgeGroup() ? (
<div className="question-container">
<p className="question" data-font="sans-italic">
Select Age Group
</p>
<div className="answers-container">
{Object.values(VAX_AGES[name]).map((v) => (
<button
key={`age-group-${v}`}
onClick={() =>
this.setState({
age: v,
answers: {},
showInputPopup: false,
})
}
data-font="sans-bold"
className={`age answer${age === v ? " selected" : ""}${
age && age !== v ? " unselected" : ""
}`}
>
{v}
</button>
))}
</div>
</div>
) : null}
{this.shouldShowResults() ? (
<div className="symptom-container subheader">
<h3 data-font="sans-bold">Here's what to expect.</h3>
<h6
id="share-result"
onClick={this.copyShareLink}
className="overline"
data-font="sans"
data-button
>
Share ↗
</h6>
<h6 className="overline" data-font="sans" data-button>
<a href="/vaxandme_chart.pdf">Download Chart ↗</a>
</h6>
</div>
) : null}
{this.shouldShowResults() ? (
<div className="symptom-container">
{Object.keys(resultAtSite).map((symptom) => (
<div className="symptom at-site" key={`symptom-${symptom}`}>
<span className="symptom-emoji">{ICONS[symptom]}</span>
<p data-font="sans" className="symptom-name">
{symptom}
</p>
<p data-font="sans-bold" className="symptom-chance">
{resultAtSite[symptom]}/10
</p>
</div>
))}
{Object.keys(result).map((symptom) => (
<div className="symptom" key={`symptom-${symptom}`}>
<span className="symptom-emoji">{ICONS[symptom]}</span>
<p data-font="sans" className="symptom-name">
{symptom}
</p>
<p data-font="sans-bold" className="symptom-chance">
{result[symptom]}/10
</p>
</div>
))}
</div>
) : null}
</main>
{this.shouldShowResults() ? (
<div className={`input-popup${showInputPopup ? " expanded" : ""}`}>
<div
className="input-popup-header"
onClick={() => this.setState({ showInputPopup: !showInputPopup })}
>
<div className="input-popup-header-text">
<h6 className="overline" data-font="sans">
Already vaxxed?
</h6>
<h3 className="subheader" data-font="sans-bold">
Tell us how it went.
</h3>
</div>
<button
data-font="sans"
className="subheader input-popup-header-icon"
onClick={() =>
this.setState({ showInputPopup: !showInputPopup })
}
>
&#9660;
</button>
</div>
<div className="input-popup-body">
<p className="question" data-font="sans">
I am in age group <strong>{age}</strong> and got
<strong>{!!dose ? ` ${dose} ` : ""}</strong>
{!!dose ? `of` : ""} the
<strong> {name}</strong> vaccine. I experienced:
</p>
<div className="question-container">
<p className="question" data-font="sans-italic">
(Select all side effects that you experienced)
</p>
<div className="answers-container">
{Object.keys(resultAtSite).map((symptom) => (
<button
key={`ans-${symptom}`}
data-font="sans-bold"
className={`answer${answers[symptom] ? " selected" : ""}`}
onClick={() => this.updateAnswers(symptom)}
>
{symptom}
</button>
))}
{Object.keys(result).map((symptom) => (
<button
key={`ans-${symptom}`}
data-font="sans-bold"
className={`answer${answers[symptom] ? " selected" : ""}`}
onClick={() => this.updateAnswers(symptom)}
>
{symptom}
</button>
))}
</div>
</div>
<div className="question-container">
<p className="question" data-font="sans">
You can comeback and update your experience later!
</p>
<p className="question" data-font="sans-italic">
(Just use the same device)
</p>
</div>
</div>
<div
onClick={this.submitAnswer}
className="input-popup-submit"
data-font="sans-bold"
>
SUBMIT
</div>
</div>
) : null}
</div>
);
}
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.querySelector("#root")
);