diff --git a/package.json b/package.json index 2a3a6b0..5286af9 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,6 @@ "version": "0.1.0", "private": true, "dependencies": { - "@testing-library/jest-dom": "^5.11.10", - "@testing-library/react": "^11.2.6", - "@testing-library/user-event": "^12.8.3", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3", diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..f2d38d2 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,3 @@ +RewriteEngine On + +DirectoryIndex index.html \ No newline at end of file diff --git a/public/index.html b/public/index.html index 8b2f61b..9a43605 100644 --- a/public/index.html +++ b/public/index.html @@ -1,6 +1,20 @@ + + + @@ -17,8 +31,10 @@ @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: 768px) - and (max-width: 1024px); + @import "%PUBLIC_URL%/styles/small.css" only screen and (min-width: 400px) + 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 @@ -71,9 +87,48 @@ background-image: url("%PUBLIC_URL%/logo512.png"); } + + + Vax & Me + + +
+ diff --git a/public/styles/extralarge.css b/public/styles/extralarge.css index e69de29..129144a 100644 --- a/public/styles/extralarge.css +++ b/public/styles/extralarge.css @@ -0,0 +1,3 @@ +.input-popup.expanded { + height: 40vh; +} diff --git a/public/styles/extrasmall.css b/public/styles/extrasmall.css index 0074cfb..c12ad0c 100644 --- a/public/styles/extrasmall.css +++ b/public/styles/extrasmall.css @@ -2,6 +2,8 @@ margin: 0; padding: 0; transition: all ease-in-out 0.2s; + color: var(--color-secondary); + user-select: none; } html { @@ -9,9 +11,14 @@ html { } body { display: flex; + flex-direction: column; font-size: 10px; padding: 3.75rem; - height: calc(100vh - 7.5rem); + padding-bottom: 0 !important; + height: calc(100vh - 3.75rem); + overflow-x: hidden; + max-width: 100vw; + background: var(--color-primary); } #root { @@ -19,18 +26,19 @@ body { display: flex; } -#root > div { +.app-container { flex: 1; - display: flex; - flex-direction: column; } -#root > div > main { - flex: 1; +header { + display: flex; + justify-content: center; + flex-direction: column; } .logo-container { display: flex; + width: fit-content; align-items: center; justify-content: center; } @@ -41,18 +49,16 @@ body { display: inline-block; } .logo-text-container { - display: inline-flex; - align-items: center; + display: none; + justify-content: center; vertical-align: middle; + flex-direction: column; flex: 1; margin-left: 1.5rem; } .logo-text-container p { font-size: 2.4rem; } -.logo-text-container p:last-of-type { - margin-left: 0.75rem; -} .subheader { font-size: 2.5rem; @@ -65,8 +71,24 @@ body { margin: 0; } +.overline[data-button] { + border-bottom: 1px solid; + width: fit-content; + cursor: pointer; +} + +.overline[data-button] a { + text-decoration: none; +} + .intro { margin: 4.5rem 0; + display: inline-block; + width: fit-content; +} + +.question-container { + margin: 3.5rem 0; } .question { @@ -93,11 +115,6 @@ body { cursor: pointer; } -.answer:hover, -.answer:focus { - background-color: var(--color-secondary-light); -} - .answer.selected { background-color: var(--color-bright); } @@ -107,10 +124,15 @@ body { .symptom-container.subheader { margin-top: 5rem; - margin-bottom: 1rem; + margin-bottom: 2.5rem; display: block !important; } - +.symptom-container.subheader .overline { + margin: 1rem 0; +} +.symptom-container { + margin-bottom: 5rem; +} .symptom { max-width: 100%; padding: 1.5rem; @@ -151,14 +173,6 @@ body { text-align: center; display: block; } -.symptom:hover { - box-shadow: 0px 0px 100px var(--color-dim); - background-color: var(--color-primary); -} -.symptom.at-site:hover { - background-color: var(--color-bright); - color: var(--color-primary); -} .symptom-chance:before { content: "EXPERIENCED BY"; font-size: 1rem; @@ -176,5 +190,152 @@ body { footer { font-size: 1.2rem; - padding: 1.5rem; + padding: 5rem 3.75rem; + width: calc(100vw - 7.5rem); + margin-left: -3.75rem; + background-color: var(--color-secondary-light); + transition: none !important; + display: flex; + flex-direction: column; +} + +footer > div { + flex: 1; + display: flex; + flex-direction: column; + color: var(--color-primary); +} + +footer > div > .overline { + color: var(--color-primary); +} + +footer > div:not(:first-of-type) { + margin-top: 3rem; +} + +a { + color: var(--color); + margin-top: 0.75rem; + max-width: fit-content; +} + +@keyframes slideUp { + 0% { + transform: translateY(300%); + } + 100% { + transform: translateY(0); + } +} + +.input-popup { + position: sticky; + background-color: var(--color-secondary-light); + bottom: 1rem; + height: 13.5rem; + margin-bottom: 7.5rem; + transition: height ease-in-out 0.3s; + overflow: hidden; + border-radius: 2rem; + max-height: 85vh; + display: flex; + flex-direction: column; + animation: 0.5s ease-out 0s 1 slideUp; + animation-delay: 1s; + animation-fill-mode: forwards; + transform: translateY(300%); +} +.input-popup * { + color: var(--color-primary); +} +.input-popup.expanded { + height: 80vh; + overflow-y: auto; +} +.input-popup .input-popup-header-icon { + transform: rotate(180deg); +} +.input-popup.expanded .input-popup-header-icon { + transform: rotate(0deg); +} +.input-popup-header { + padding: 2.5rem; + position: sticky; + top: 0; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background-color: var(--color-secondary-light); + border-bottom: 1px solid var(--color-tertiary); +} +.input-popup-header-text { + flex: 1; +} +.input-popup-header-icon { + cursor: pointer; + background-color: var(--color-secondary); + outline: none; + color: var(--color-primary); + border: 1px solid transparent; + border-radius: 10rem; + height: 5rem; + width: 5rem; +} + +.input-popup-body { + padding: 2.5rem 2.5rem; + flex: 1; +} + +.input-popup-body .question-container .answers-container .answer { + background-color: var(--color-tertiary); +} +.input-popup-body .question-container .answers-container .answer.selected { + background-color: var(--color-secondary); +} + +.input-popup-submit { + padding: 2.5rem; + text-align: center; + font-size: 2rem; + letter-spacing: 0.3rem; + background-color: var(--color-secondary); + cursor: pointer; +} +.input-popup.expanded .input-popup-submit { + position: sticky; + bottom: 0; +} +.input-popup-submit.submitted { + background-color: #55c16c; +} + +@media (hover: hover) and (pointer: fine) { + .answer:hover, + .answer.unselected:hover { + background-color: var(--color-secondary-light); + } + .symptom:hover { + box-shadow: 0px 0px 100px var(--color-tertiary); + background-color: var(--color-primary); + } + .symptom.at-site:hover { + box-shadow: 0px 0px 100px var(--color-dim); + background-color: var(--color-secondary-light); + color: var(--color-primary); + } + .symptom.at-site:hover p { + color: var(--color-primary); + } + .input-popup-header-icon:hover { + background-color: var(--color-tertiary); + } + .input-popup-body .question-container .answers-container .answer:hover { + background-color: var(--color-bright); + } + .input-popup-submit:hover { + background-color: var(--color-tertiary); + } } diff --git a/public/styles/large.css b/public/styles/large.css index 5a4adbb..692e33c 100644 --- a/public/styles/large.css +++ b/public/styles/large.css @@ -1,5 +1,26 @@ body { padding: 15rem; padding-top: 10rem; - height: calc(100vh - 25rem); + height: calc(100vh - 10rem); +} + +.logo { + width: 10rem; + height: 10rem; +} +.symptom { + max-width: 25rem; + height: 25rem; +} + +footer { + padding: 5rem 15rem; + width: calc(100vw - 30rem); + margin-left: -15rem; +} +.input-popup { + height: 10rem; +} +.input-popup.expanded { + height: 50vh; } diff --git a/public/styles/medium.css b/public/styles/medium.css index 90ee65b..a816806 100644 --- a/public/styles/medium.css +++ b/public/styles/medium.css @@ -1,26 +1,33 @@ body { padding: 7.5rem; padding-top: 5rem; - height: calc(100vh - 12.5rem); + height: calc(100vh - 5rem); } .logo { width: 7.5rem; height: 7.5rem; } -.logo-text-container { - flex-direction: column; - justify-content: center; - align-items: flex-start; + +.intro .overline { + font-size: 3rem; } -.logo-text-container p:last-of-type { - margin-left: 0; +.intro .subheader { + font-size: 4rem; +} + +.overline[data-button] { + display: inline-block; +} +.overline[data-button]:not(:first-of-type) { + margin-left: 1.5rem; } .symptom { width: 100%; max-width: 20rem; - display: inline-block; + height: 20rem; + display: inline-flex; margin-bottom: 0.75rem; } .symptom-emoji { @@ -51,7 +58,31 @@ body { border-top-right-radius: 1rem; border-bottom-right-radius: 1rem; } + footer { font-size: 1.7rem; - padding: 1.5rem; + padding: 5rem 7.5rem; + width: calc(100vw - 15rem); + margin-left: -7.5rem; + flex-direction: row; +} +footer > div { + flex-direction: column; + flex: 1; +} + +footer > div:not(:first-of-type) { + margin-left: 3rem; + margin-top: 0; +} +footer > div:last-of-type { + flex: 3; + align-items: flex-end; +} +.input-popup { + height: 10rem; +} + +.input-popup.expanded { + height: 55vh; } diff --git a/public/styles/small.css b/public/styles/small.css index e69de29..9f12264 100644 --- a/public/styles/small.css +++ b/public/styles/small.css @@ -0,0 +1,7 @@ +.input-popup { + height: 10rem; +} + +.input-popup.expanded { + height: 85vh; +} diff --git a/public/styles/smallmedium.css b/public/styles/smallmedium.css new file mode 100644 index 0000000..3edc919 --- /dev/null +++ b/public/styles/smallmedium.css @@ -0,0 +1,7 @@ +.input-popup { + height: 10rem; +} + +.input-popup.expanded { + height: 65vh; +} diff --git a/public/styles/variables-dark.css b/public/styles/variables-dark.css index e69de29..b61d949 100644 --- a/public/styles/variables-dark.css +++ b/public/styles/variables-dark.css @@ -0,0 +1,9 @@ +:root { + --color-primary: black; + --color-secondary: white; + --color-secondary-light: rgb(185, 185, 185); + --color-tertiary: rgb(105, 105, 105); + --color-bright: #636363; + --color-dim: #352f2f; + font-size: 10px; +} diff --git a/public/styles/variables.css b/public/styles/variables.css index 9a7e11f..5c98d57 100644 --- a/public/styles/variables.css +++ b/public/styles/variables.css @@ -1,9 +1,9 @@ :root { --color-primary: white; --color-secondary: black; - --color-secondary-light: rgb(87, 87, 87); - --color-tertiary: #f7f6f6; - --color-bright: rgb(25, 48, 122); - --color-dim: rgb(138, 154, 209); + --color-secondary-light: rgb(56, 56, 56); + --color-tertiary: rgba(185, 185, 185, 0.459); + --color-bright: #575757; + --color-dim: #aeadb0; font-size: 10px; } diff --git a/public/vaxandme_chart.pdf b/public/vaxandme_chart.pdf new file mode 100644 index 0000000..6fd08ef Binary files /dev/null and b/public/vaxandme_chart.pdf differ diff --git a/src/index.js b/src/index.js index 70eb578..d80aae7 100644 --- a/src/index.js +++ b/src/index.js @@ -131,7 +131,6 @@ const MD_DOSE2_OLDER_RESULT = { "NAUSEA / VOMITING": "1", FEVER: "1", }; - const JJ_YOUNGER_RESULT = { "PAIN AT SITE": "6", "RED AT SITE": "1", @@ -185,9 +184,9 @@ const VAX_RESULTS = { const ICONS = { "PAIN AT SITE": "💪", "RED AT SITE": "🔴", - "SWELLING AT SITE": "⭕", + "SWELLING AT SITE": "💢", FATIGUE: "🥱", - HEADACHE: "🤯", + HEADACHE: "🤕", "NEED PAIN MED": "💊", "MUSCLE PAIN": "😫", CHILLS: "🥶", @@ -205,8 +204,50 @@ class App extends React.Component { name: "", dose: "", age: "", + gid: "", + showInputPopup: false, + answers: {}, }; } + 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.gtag) { + window.gtag("get", "G-7VZCY223NG", "client_id", (client_id) => { + this.setState({ gid: client_id }); + }); + } + } shouldShowAgeGroup = () => { const { name, dose } = this.state; switch (name) { @@ -243,13 +284,81 @@ class App extends React.Component { return null; } }; - render() { + 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, + }); + setTimeout(() => { + document.querySelector(".input-popup-submit").innerHTML = "THANK YOU!"; + document.querySelector(".input-popup-submit").classList.add("submitted"); + setTimeout(() => { + document.querySelector(".input-popup-submit").innerHTML = + "UPDATE ANSWER"; + }, 1500); + }, 1500); + console.log(submitAnswers); + }; + render() { + const { name, dose, age, showInputPopup, answers } = this.state; let result = {}; let resultAtSite = {}; if (this.shouldShowResults()) { result = Object.assign({}, this.getResults()); - Object.keys(result).map((s) => { + Object.keys(result).forEach((s) => { if (s.includes("AT SITE")) { resultAtSite[s] = result[s]; delete result[s]; @@ -257,24 +366,25 @@ class App extends React.Component { }); } return ( -
-
-
-
-

VAX

-

& ME

+
+
+
+
+
+

VAX

+

& ME

+
-
-
Getting vaccinated?

- Find out what to expect + Find out what to expect.

- + +

Select Vaccine @@ -285,8 +395,8 @@ class App extends React.Component { key={`name-group-${v}`} onClick={() => this.setState({ name: v, dose: "", age: "" })} data-font="sans-bold" - className={`answer ${name === v ? "selected" : ""} ${ - name && name !== v ? "unselected" : "" + className={`vaccine answer${name === v ? " selected" : ""}${ + name && name !== v ? " unselected" : "" }`} > {v} @@ -306,8 +416,8 @@ class App extends React.Component { key={`dose-group-${v}`} onClick={() => this.setState({ dose: v, age: "" })} data-font="sans-bold" - className={`answer ${dose === v ? "selected" : ""} ${ - dose && dose !== v ? "unselected" : "" + className={`dose answer${dose === v ? " selected" : ""}${ + dose && dose !== v ? " unselected" : "" }`} > {v} @@ -328,8 +438,8 @@ class App extends React.Component { key={`age-group-${v}`} onClick={() => this.setState({ age: v })} data-font="sans-bold" - className={`answer ${age === v ? "selected" : ""} ${ - age && age !== v ? "unselected" : "" + className={`age answer${age === v ? " selected" : ""}${ + age && age !== v ? " unselected" : "" }`} > {v} @@ -340,9 +450,21 @@ class App extends React.Component { ) : null} {this.shouldShowResults() ? ( -

- Here's what to expect -

+
+

Here's what to expect.

+
+ Share ↗ +
+
+ Download Chart ↗ +
+
) : null} {this.shouldShowResults() ? ( @@ -372,16 +494,74 @@ class App extends React.Component {
) : null}
- + ) : null}
); }