diff --git a/.env b/.env index 54bcebfe..b980c4a7 100644 --- a/.env +++ b/.env @@ -1,6 +1,6 @@ SKIP_PREFLIGHT_CHECK=true EXTEND_ESLINT=true -REACT_APP_VERSION=$npm_package_version -REACT_APP_BUILT_AT=10/26/2020 - 1:33 PM +REACT_APP_VERSION=$npm_package_version +REACT_APP_BUILT_AT=12/29/2020 - 12:26 AM REACT_APP_BASE_URL=https://api-dev.allclear.app REACT_APP_ROBOTS_DIRECTIVE=noindex, nofollow diff --git a/package.json b/package.json index 5d842eab..5801be00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "allclear-pwa-2", - "version": "1.11.0", + "version": "1.11.1", "license": "AGPL-3.0-only", "scripts": { "build": "node stampBuild.js && react-app-rewired build", @@ -28,14 +28,14 @@ "@material-ui/core": "latest", "@material-ui/icons": "latest", "@material-ui/lab": "^4.0.0-alpha.48", - "airtable": "^0.8.1", + "airtable": "^0.10.1", "autosuggest-highlight": "^3.1.1", "axios": "^0.19.2", "clipboardy": "^2.3.0", "clsx": "latest", "formik": "^2.1.4", "google-map-react": "^1.1.7", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "logrocket": "^1.0.7", "logrocket-react": "^4.0.1", "material-ui-phone-number": "^2.2.6", diff --git a/src/Router.js b/src/Router.js index b2f6e4f9..8d20d004 100644 --- a/src/Router.js +++ b/src/Router.js @@ -34,6 +34,7 @@ import StateListPage from '@pages/state-list.page'; import StatePage from '@pages/state.page'; import CityPage from '@pages/city.page'; import AddTestCenterPage from '@pages/add-test-center.page'; +import EditTestCenterPage from '@pages/edit-test-center.page'; import MetadataService from '@services/metadata.service'; export const history = createBrowserHistory(); @@ -113,6 +114,7 @@ export default function Router() { {/* /:city/:test-center-name */} + {/* SEO */} diff --git a/src/components/experiences/feedback-experiences.js b/src/components/experiences/feedback-experiences.js index 92b0cb77..3b624965 100644 --- a/src/components/experiences/feedback-experiences.js +++ b/src/components/experiences/feedback-experiences.js @@ -90,10 +90,10 @@ export default function FeedbackExperiences(props) { const Content = styled(DialogContent)` overflow-y: visible; - padding: 0 24px; + padding: ${((window.innerWidth < 960) ? '0px' : '0 24px')}; text-align: center; letter-spacing: -0.41px; - font-size: 20px; + font-size: ${((window.innerWidth < 960) ? '16px' : '20px')}; `; const Actions = styled(DialogActions)` @@ -106,15 +106,18 @@ const Actions = styled(DialogActions)` const Box = styled(DialogActions)` flex-direction: row; align-self: center; + ${((window.innerWidth < 960) ? 'margin: 3px' : '')} `; const TagButton = styled(PrimaryButton)` border-color: #007AFF; border-radius: 20px; - height: 60%; - width: 50%; + ${((window.innerWidth < 960) ? 'font-size: 10px' : '')}; + height: ${((window.innerWidth) < 960 ? '100%' : '65%')}; + ${((window.innerWidth < 960) ? 'min-width: 127px' : '')}; + ${((window.innerWidth < 960) ? 'height: 40px' : '')}; + ${((window.innerWidth < 960) ? 'padding: 3px' : '')}; .MuiButton-label { - width: 100%; align-items: center; justify-content: center; display: flex; @@ -130,7 +133,7 @@ const NextButton = styled(PrimaryButton)` `; const SpaceButton = styled(PrimaryButton)` - margin-top: 15px; + margin-top: 20px; align-self: center; width: 30%; `; \ No newline at end of file diff --git a/src/components/experiences/initial-experiences.js b/src/components/experiences/initial-experiences.js index e6f13268..3e512f87 100644 --- a/src/components/experiences/initial-experiences.js +++ b/src/components/experiences/initial-experiences.js @@ -33,11 +33,11 @@ export default function InitialExperiences(props) { handleFeedback(true)}> - + Positive handleFeedback(false)}> - + Negative @@ -59,10 +59,10 @@ export default function InitialExperiences(props) { const Content = styled(DialogContent)` overflow-y: visible; - padding: 0 24px; + padding: ${((window.innerWidth < 960) ? '0px' : '0 24px')}; text-align: center; letter-spacing: -0.41px; - font-size: 20px; + font-size: ${((window.innerWidth < 960) ? '16px' : '20px')}; `; const Actions = styled(DialogActions)` @@ -72,6 +72,7 @@ const Actions = styled(DialogActions)` const RateButton = styled(PrimaryButton)` border-color: #007AFF; + ${((window.innerWidth < 960) ? 'min-width: 110px' : '')}; .MuiButton-label { width: 100%; height: 55%; @@ -92,7 +93,7 @@ const NextButton = styled(PrimaryButton)` `; const SpaceButton = styled(PrimaryButton)` - margin-top: 40px; + margin-top: 30px; align-self: center; width: 30%; `; \ No newline at end of file diff --git a/src/components/experiences/submit-experiences.js b/src/components/experiences/submit-experiences.js index c271ab74..c89c8bda 100644 --- a/src/components/experiences/submit-experiences.js +++ b/src/components/experiences/submit-experiences.js @@ -34,10 +34,10 @@ export default function SubmitExperiences(props) { const Content = styled(DialogContent)` overflow-y: hidden; margin-bottom: 15px; - padding: 0 24px; + padding: ${((window.innerWidth < 960) ? '0px' : '0 24px')}; text-align: center; letter-spacing: -0.41px; - font-size: 20px; + font-size: ${((window.innerWidth < 960) ? '16px' : '20px')}; `; const Actions = styled(DialogActions)` @@ -47,7 +47,7 @@ const Actions = styled(DialogActions)` const DoneButton = styled(PrimaryButton)` border-color: #007AFF; - margin-bottom: 30%; + margin-bottom: ${((window.innerWidth < 960) ? '0%' : '30%')}; `; const Title = styled(DialogTitle)` @@ -56,7 +56,7 @@ const Title = styled(DialogTitle)` flex-direction: column; align-items: center; text-align: center; - font-size: 24px; - margin: 15px; + font-size: ${((window.innerWidth < 960) ? '18px' : '24px')}; + margin: ${((window.innerWidth < 960) ? '0px' : '15px')}; } `; \ No newline at end of file diff --git a/src/components/general/modals/form-modal.js b/src/components/general/modals/form-modal.js index 51d37209..234cf0a4 100644 --- a/src/components/general/modals/form-modal.js +++ b/src/components/general/modals/form-modal.js @@ -1,16 +1,13 @@ import React, { useState } from 'react'; -import { useHistory } from 'react-router-dom'; import styled from 'styled-components'; import { Dialog, DialogTitle, DialogContent, DialogActions, IconButton } from '@material-ui/core'; import CloseIcon from '@material-ui/icons/Close'; import { isNullOrUndefined } from '@util/general.helpers'; -import PrimaryButton from '@general/buttons/primary-button'; import ModalService from '@services/modal.service'; export default function FormModal(props) { - const history = useHistory(); const modalService = ModalService.getInstance(); modalService.registerModal(props.modalName, toggleModal); @@ -48,12 +45,7 @@ export default function FormModal(props) { )} - history.push('/sign-in')}> - Login - - history.push('/sign-up')}> - Create Account - + {props.actions} @@ -87,7 +79,7 @@ const Content = styled(DialogContent)` padding: 0 24px; text-align: center; letter-spacing: -0.41px; - font-size: 16px; + font-size: 18px; `; const Actions = styled(DialogActions)` @@ -101,11 +93,3 @@ const CloseButton = styled(IconButton)` margin: 20px; `; -const LoginButton = styled(PrimaryButton)` - box-shadow: 0px 0px 10px 5px lightGrey; -`; - -const CreateAccountButton = styled(PrimaryButton)` - box-shadow: 0px 0px 10px 5px lightGrey; - margin-top: 10px; -`; diff --git a/src/components/general/modals/share-experiences-modal.js b/src/components/general/modals/share-experiences-modal.js index 511ecb32..04a4d06a 100644 --- a/src/components/general/modals/share-experiences-modal.js +++ b/src/components/general/modals/share-experiences-modal.js @@ -59,6 +59,8 @@ export default function ShareExperiencesModal(props) { setOpen(isOpen); if (isOpen === true) { setScroll(scrollType); + } else if(currentScreen === 3){ + window.location.reload(); } } @@ -83,9 +85,9 @@ export default function ShareExperiencesModal(props) { }) .catch((err) => { setExperienceResult({ - title: 'Please try again tomorrow.', - content: `To help ensure authenticity, you'll need to wait until tomorrow - to share your Experience at ${props.testTitle} again. Thank you!` + title: 'An error has occured.', + content: `An error has occured while trying to share your Experience. + Please try again later.` }); setCurrentScreen(3); }); @@ -144,10 +146,9 @@ const ShareExperienceContainer = styled(Dialog)` .MuiPaper-rounded { border-radius: 30px; padding: 25px; - width: 70%; - height: 60%; + height: ${((window.innerWidth < 960) ? '450px' : '70%')}; flex: 1; - justify-content: space-between; + justify-content: ${((window.innerWidth < 960) ? 'space-evenly' : 'space-between')}; } `; @@ -157,21 +158,22 @@ const Title = styled(DialogTitle)` flex-direction: column; align-items: center; text-align: center; - font-size: 24px; - margin: 15px; + font-size: ${((window.innerWidth < 960) ? '18px' : '24px')}; + margin: ${((window.innerWidth < 960) ? '5px' : '15px')}; } + padding: 0px 20px; `; const CloseButton = styled(IconButton)` position: absolute; top: 0; right: 0; - margin: 20px; + margin:${((window.innerWidth < 960) ? '10px' : '20px')}; `; const BackButton = styled(IconButton)` position: absolute; top: 0; left: 0; - margin: 20px; + margin: ${((window.innerWidth < 960) ? '10px' : '20px')}; `; \ No newline at end of file diff --git a/src/components/general/pin-location.js b/src/components/general/pin-location.js index f60e69d7..fccb2bf8 100644 --- a/src/components/general/pin-location.js +++ b/src/components/general/pin-location.js @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useHistory } from 'react-router-dom'; import styled, { css, keyframes } from 'styled-components'; import { IconButton } from '@material-ui/core'; @@ -8,10 +9,12 @@ import FormModal from '@general/modals/form-modal'; import PeopleService from '@services/people.service'; import ModalService from '@services/modal.service'; +import PrimaryButton from '@general/buttons/primary-button'; export default function PinLocation({ location }) { const peopleService = PeopleService.getInstance(); const modalService = ModalService.getInstance(); + const history = useHistory(); const initialLocationState = { location, @@ -49,6 +52,16 @@ export default function PinLocation({ location }) { modalName="promptLogin" contentText="You need to be logged in to AllClear to save pinned locations. Please login or create an account to continue." icon={} + actions = { + <> + history.push('/sign-in')}> + Login + + history.push('/sign-up')}> + Create Account + + + } /> ); @@ -106,3 +119,12 @@ const pinAnimation = keyframes` transform: scale(1, 1) rotate(-15deg) translateY(0); } `; + +const LoginButton = styled(PrimaryButton)` + box-shadow: 0px 0px 10px 5px lightGrey; +`; + +const CreateAccountButton = styled(PrimaryButton)` + box-shadow: 0px 0px 10px 5px lightGrey; + margin-top: 10px; +`; diff --git a/src/components/general/share-experiences.js b/src/components/general/share-experiences.js index 152e6315..e5b4c1e0 100644 --- a/src/components/general/share-experiences.js +++ b/src/components/general/share-experiences.js @@ -1,25 +1,37 @@ import React, { useContext } from 'react'; import Button from '@material-ui/core/Button'; import Tooltip from '@material-ui/core/Tooltip'; +import styled from 'styled-components'; + +import { useHistory } from 'react-router-dom'; import FormModal from '@general/modals/form-modal.js'; import ShareExperiencesModal from '@general/modals/share-experiences-modal'; +import PrimaryButton from '@general/buttons/primary-button'; import ModalService from '@services/modal.service'; +import ExperienceService from '@services/experience.service'; import { AppContext } from '@contexts/app.context'; export default function ShareExperience(props) { const { appState } = useContext(AppContext); const isLoggedIn = appState.sessionId ? true : false; + const experienceService = ExperienceService.getInstance(); + + const history = useHistory(); const modalService = ModalService.getInstance(); const handleShareExperience = () => { - if(!isLoggedIn){ - modalService.toggleModal('promptLoginExperiences', true); + if(!isLoggedIn){ + modalService.toggleModal('promptLoginExperiences', true); } else { - modalService.toggleModal('promptShareExperiences'+props.testTitle, true); + experienceService.limit(appState.sessionId, props.facilityId).then((result, i) => { + modalService.toggleModal('promptShareExperiences'+props.testTitle, true); + }).catch((error) => { + modalService.toggleModal('promptRateLimit', true); + }); } }; @@ -36,8 +48,35 @@ export default function ShareExperience(props) { + history.push('/sign-in')}> + Login + + history.push('/sign-up')}> + Create Account + + + } + /> + + modalService.toggleModal('promptRateLimit', false)} + > + Done + + + } /> + +

{props.title}

+

{props.content}

+
+ +
+
+

Test Center Profile

+ props.onChange('name', event.target.value)} + /> + props.onChange('address', event.target.value)} + /> + + + + props.onChange('notes', event.target.value)} + /> + +

Were you able to get tested at this location?

+ + {props.state.gotTested.map((option) => ( + } + label={option.displayName} + onChange={(event) => props.onChange('gotTested', option.value)} + /> + ))} + +
+
+
+

Test Center Offerings

+
+

Select all offerings that exist at this test center:

+ + {props.state.offerings.map((option, index) => ( + // TODO: Make checkbox partial + + {`Select + + + + ))} + +
+
+

Select all screening methods used at this test center:

+ + {props.state.screening.map((option, index) => ( + // TODO: Make checkbox partial + + {`Select + + + + ))} + +
+
+ + Cancel + + Submit Test Center + + +
+
+ + + ); +} + +const StyledHeader = styled(Header)` + .color-block { + display: none; + } +`; + +const H3 = styled.h3` +margin-bottom: 20px; +font-size: 24px; +`; + +const H4 = styled.h4` +margin-bottom: 20px; +font-size: 16px; +`; + +const Article = styled.article` +padding: 44px 0; +color: #fff; + +&:first-child { + border-bottom: 1px solid rgba(255, 255, 255, 0.4); +} + +> * { + width: 100%; + &:not(:last-child) { + margin-bottom: 20px; + } +} +`; + +const StyledRadio = styled(Radio)` + color: #fff; + &.Mui-checked { + color: #fff; + } +`; + +const Checkboxes = styled.section` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(172px, 1fr)); + column-gap: 17px; + row-gap: 17px; + margin-bottom: 40px; +`; + +const Checkbox = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + min-height: 122.78px; + text-align: center; + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.5); + transition: background-color ease 0.5s; + cursor: pointer; + + * { + cursor: inherit; + } + + &:hover { + background-color: rgba(0, 0, 0, 0.1); + } + + &.checked { + border-color: ${(props) => props.theme.palette.secondary.main}; + background-color: ${(props) => props.theme.palette.secondary.main}; + } +`; + +const ButtonContainer = styled.div` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + column-gap: 32px; + row-gap: 32px; + margin-bottom: 61px; + + button { + font-size: 17px; + } +`; + +const CancelButton = styled(PrimaryButton)` + color: #fff; + background-color: rgba(255, 255, 255, 0.1); +`; + +const SubmitButton = styled(PrimaryButton)` + color: ${(props) => props.theme.palette.primary.main}; + background-color: #fff; + + &:hover { + background-color: #e1efff; + } +`; \ No newline at end of file diff --git a/src/components/map-components/testing-location-list-item.js b/src/components/map-components/testing-location-list-item.js index 11fba57f..5ff6612d 100644 --- a/src/components/map-components/testing-location-list-item.js +++ b/src/components/map-components/testing-location-list-item.js @@ -16,7 +16,7 @@ import PeopleIcon from '@material-ui/icons/People'; import LocalMallIcon from '@material-ui/icons/LocalMall'; import EventIcon from '@material-ui/icons/Event'; import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord'; -import {boolToEng, isNullOrUndefined, getFeedbackButtonURL, isTaggableLocation, sortExperiencesTags} from '@util/general.helpers'; +import {boolToEng, isNullOrUndefined, isTaggableLocation, sortExperiencesTags} from '@util/general.helpers'; import ExternalItemLinks from './external-item-links'; import CustomizedExpansionPanel, {ExpansionPanelSummary, ExpansionPanelDetails} from './expansion-panel'; import {Link} from 'react-router-dom'; @@ -153,8 +153,6 @@ export default function TestingLocationListItem(props) { ); - const changeURL = getFeedbackButtonURL(props); - function buildExperienceSection(experiencesData) { const {positives, negatives, total, tags} = experiencesData; @@ -375,9 +373,7 @@ export default function TestingLocationListItem(props) { )} View Full Test Center Detail
- - Suggest Change To Test Center Information - + Suggest Change to Test Center Information

Last update: {updatedAt.toLocaleString()}

diff --git a/src/constants/add-test-center.constants.js b/src/constants/add-test-center.constants.js index 6c796894..1d7372b3 100644 --- a/src/constants/add-test-center.constants.js +++ b/src/constants/add-test-center.constants.js @@ -34,31 +34,37 @@ export const OFFERINGS = [ displayName: 'Antibody Test', icon: DiagnosticTest, key: 'ii', + value: false }, { displayName: 'Nasal Swab Test', icon: Swab, key: 'rp', + value: false }, { displayName: 'Drivethrough', icon: Car, key: 'driveThru', + value: false }, { displayName: 'Plasma Donation', icon: Donate, key: 'canDonatePlasma', + value: false }, { displayName: 'Free or Low-Cost Testing', icon: DollarSign, key: 'freeOrLowCost', + value: false }, { displayName: 'Online Scheduling', icon: Monitor, - key: 'teleScreeningAvailable', + key: 'telescreeningAvailable', + value: false }, ]; @@ -67,31 +73,37 @@ export const SCREENING_METHODS = [ displayName: 'Accepts Insurance', icon: InsuranceCard, key: 'acceptsInsurance', + value: false }, { displayName: 'Government ID Required', icon: Id, key: 'governmentIdRequired', + value: false }, { displayName: 'Referral Required', icon: Users, key: 'referralRequired', + value: false }, { displayName: 'Appointment Required', icon: Calendar, key: 'appointmentRequired', + value: false }, { displayName: 'First Responders Prioritized', icon: Transportation, key: 'firstResponderFriendly', + value: false }, { displayName: 'Minimum Age', icon: AlertTriangle, key: 'minimumAge', + value: false }, ]; diff --git a/src/constants/experiences.constants.js b/src/constants/experiences.constants.js index b242d389..3d4e232a 100644 --- a/src/constants/experiences.constants.js +++ b/src/constants/experiences.constants.js @@ -132,38 +132,38 @@ export const NEGATIVE_TAGS = [ { id: 'ph', name: 'Poor Hygiene', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'su', name: 'Seemed Understaffed', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'ct', name: 'Could Not Get Tested', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'lt', name: 'Long Wait Time', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'ca', name: 'Confusing Process', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'oc', name: 'Overly Crowded', - icon: , - whiteIcon: + icon: , + whiteIcon: }, ]; @@ -171,37 +171,37 @@ export const NEGATIVE_TAGS = [ { id: 'st', name: 'Short Wait Time', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'gh', name: 'Good Hygiene', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'fs', name: 'Friendly Staff', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'pp', name: 'PPE Provided', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'se', name: 'Social Distancing', - icon: , - whiteIcon: + icon: , + whiteIcon: }, { id: 'gm', name: 'Good Bedside Manner', - icon: , - whiteIcon: + icon: , + whiteIcon: }, ]; \ No newline at end of file diff --git a/src/pages/add-test-center.page.js b/src/pages/add-test-center.page.js index e901d5ea..ec672995 100644 --- a/src/pages/add-test-center.page.js +++ b/src/pages/add-test-center.page.js @@ -2,17 +2,6 @@ import React, { Component } from 'react'; import { bindAll, cloneDeep } from 'lodash'; import styled from 'styled-components'; -import { - Container, - TextField, - MenuItem, - Select, - RadioGroup, - Radio, - FormControlLabel, - FormControl, -} from '@material-ui/core'; - import { AppContext } from '@contexts/app.context'; import { GOT_TESTED_OPTIONS, @@ -20,12 +9,11 @@ import { SCREENING_METHODS, POST_DATA_STATE, } from '@constants/add-test-center.constants'; -import Header from '@general/headers/header'; -import PrimaryButton from '@general/buttons/primary-button'; -import SnackbarMessage from '@general/alerts/snackbar-message'; import TypesService from '@services/types.service.js'; import FacilitateService from '@services/facilitate.service.js'; +import TestCenterUpdate from '@components/general/test-center-update'; + export default class AddTestCenterPage extends Component { static contextType = AppContext; @@ -137,159 +125,27 @@ export default class AddTestCenterPage extends Component { this.setState({ snackbarOpen: false }); } + onChange(item, value) { + this.setState({ postData: { ...this.state.postData, [item]: value } }); + } + render() { return ( - <>
- -

Submit New Test Center

-

- Complete the following form to propose a new test center within AllClear. All submissions will be reviewed - by the AllClear team within 24 hours. -

-
- -
-
-

Test Center Profile

- - this.setState({ postData: { ...this.state.postData, name: event.target.value } }) - } - /> - - this.setState({ postData: { ...this.state.postData, address: event.target.value } }) - } - /> - - - - - this.setState({ postData: { ...this.state.postData, notes: event.target.value } }) - } - /> - -

Were you able to get tested at this location?

- - {this.state.gotTested.map((option) => ( - } - label={option.displayName} - onChange={(event) => - this.setState({ postData: { ...this.state.postData, gotTested: option.value } }) - } - /> - ))} - -
-
-
-

Test Center Offerings

-
-

Select all offerings that exist at this test center:

- - {this.state.offerings.map((option, index) => ( - // TODO: Make checkbox partial - - {`Select - - - - ))} - -
-
-

Select all screening methods used at this test center:

- - {this.state.screening.map((option, index) => ( - // TODO: Make checkbox partial - - {`Select - - - - ))} - -
-
- - Cancel - - Submit Test Center - - -
-
+ this.onCheckboxSelected.bind(this, option)} + handleSnackbarClose={() => this.handleSnackbarClose} + handleSubmit={(e) => this.handleSubmit(e)} + onCancelClicked={() => this.onCancelClicked} + onChange={(item, value) => this.onChange(item, value)} + />
- - ); } } @@ -303,102 +159,3 @@ const Section = styled.section` color: #fff; } `; - -const H3 = styled.h3` - margin-bottom: 20px; - font-size: 24px; -`; - -const H4 = styled.h4` - margin-bottom: 20px; - font-size: 16px; -`; - -const StyledHeader = styled(Header)` - .color-block { - display: none; - } -`; - -const Article = styled.article` - padding: 44px 0; - color: #fff; - - &:first-child { - border-bottom: 1px solid rgba(255, 255, 255, 0.4); - } - - > * { - width: 100%; - &:not(:last-child) { - margin-bottom: 20px; - } - } -`; - -const Checkboxes = styled.section` - display: grid; - grid-template-columns: repeat(auto-fill, minmax(172px, 1fr)); - column-gap: 17px; - row-gap: 17px; - margin-bottom: 40px; -`; - -const Checkbox = styled.div` - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - min-height: 122.78px; - text-align: center; - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.5); - transition: background-color ease 0.5s; - cursor: pointer; - - * { - cursor: inherit; - } - - &:hover { - background-color: rgba(0, 0, 0, 0.1); - } - - &.checked { - border-color: ${(props) => props.theme.palette.secondary.main}; - background-color: ${(props) => props.theme.palette.secondary.main}; - } -`; - -const StyledRadio = styled(Radio)` - color: #fff; - &.Mui-checked { - color: #fff; - } -`; - -const ButtonContainer = styled.div` - display: grid; - grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); - column-gap: 32px; - row-gap: 32px; - margin-bottom: 61px; - - button { - font-size: 17px; - } -`; - -const CancelButton = styled(PrimaryButton)` - color: #fff; - background-color: rgba(255, 255, 255, 0.1); -`; - -const SubmitButton = styled(PrimaryButton)` - color: ${(props) => props.theme.palette.primary.main}; - background-color: #fff; - - &:hover { - background-color: #e1efff; - } -`; diff --git a/src/pages/edit-test-center.page.js b/src/pages/edit-test-center.page.js new file mode 100644 index 00000000..88a91c8c --- /dev/null +++ b/src/pages/edit-test-center.page.js @@ -0,0 +1,230 @@ +import React, { Component } from 'react'; +import { bindAll, cloneDeep } from 'lodash'; +import styled from 'styled-components'; + +import FacilityService from '@services/facility.service.js'; + +import { AppContext } from '@contexts/app.context'; +import { + GOT_TESTED_OPTIONS, + OFFERINGS, + SCREENING_METHODS, + POST_DATA_STATE, +} from '@constants/add-test-center.constants'; +import TypesService from '@services/types.service.js'; +import FacilitateService from '@services/facilitate.service.js'; +import TestCenterUpdate from '@components/general/test-center-update'; + +export default class EditTestCenterPage extends Component { + static contextType = AppContext; + + initialState = { + postData: POST_DATA_STATE, + gotTested: GOT_TESTED_OPTIONS, + offerings: OFFERINGS, + screening: SCREENING_METHODS, + snackbarOpen: false, + snackbarSeverity: '', + snackbarMessage: '', + }; + + constructor(props) { + super(props); + bindAll(this, ['onCheckboxSelected', 'onCancelClicked', 'handleSubmit', 'handleSnackbarClose']); + this.typesService = TypesService.getInstance(); + this.facilitateService = FacilitateService.getInstance(); + this.facilityService = FacilityService.getInstance(); + this.state = cloneDeep(this.initialState); + this.id = props.match.params.id; + } + + async componentDidMount() { + + try { + const testCenterTypes = await this.typesService.getFacilities(); + const testCenterDetails = await this.facilityService.getFacility(this.id); + + if (testCenterTypes?.length) { + this.setState({ testCenterTypes }); + } + + let allTestTypes = []; + if(testCenterDetails.data.testTypes) { + testCenterDetails.data.testTypes.map((testType) => { + allTestTypes.push({id: testType.id}); + }); + } + + this.setState((prevState) => { + const offeringsArr = prevState.offerings.map((item, j) => { + if(allTestTypes.some((e) => e.id === item.key) || testCenterDetails.data[item.key]){ + item.value= true; + } + return item; + }); + + const screeningArr = prevState.screening.map((item, j) => { + if(testCenterDetails.data[item.key]){ + item.value=true; + } + return item; + }); + return { + ...prevState, + postData: { + id: this.id, + name: testCenterDetails.data.name, + address: testCenterDetails.data.address, + driveThru: testCenterDetails.data.driveThru, + telescreeningAvailable: testCenterDetails.data.telescreeningAvailable, + freeOrLowCost: testCenterDetails.data.freeOrLowCost, + acceptsInsurance: testCenterDetails.data.acceptsInsurance, + governmentIdRequired: testCenterDetails.data.governmentIdRequired, + referralRequired: testCenterDetails.data.referralRequired, + firstResponderFriendly: testCenterDetails.data.firstResponderFriendly, + canDonatePlasma: testCenterDetails.data.canDonatePlasma, + appointmentRequired: testCenterDetails.data.appointmentRequired, + notes: '', + gotTested: undefined, + type: testCenterDetails.data.typeId ? { id: testCenterDetails.data.typeId } : { id: 'none' }, + testTypes: allTestTypes, + minimumAge: testCenterDetails.data.minimumAge ? '18' : undefined, + }, + offerings: offeringsArr, + screening: screeningArr + }; + }); + } catch(err) { + // display error if test center id is invalid + this.setState({ + ...this.initialState, + snackbarMessage: 'Test Center does not exist.', + snackbarSeverity: 'error', + snackbarOpen: true, + }); + } + } + + onCheckboxSelected(selected) { + selected.value = !selected.value; + if (selected.key === 'ii' || selected.key === 'rp') { + if (selected.value) { + // add to testTypes array + this.setState((prevState) => ({ + postData: { + ...prevState.postData, + testTypes: [...prevState.postData.testTypes, { id: selected.key }], + }, + })); + } else { + // remove from testTypes array + this.setState((prevState) => ({ + postData: { + ...prevState.postData, + testTypes: prevState.postData.testTypes.filter((key) => key.id !== selected.key), + }, + })); + } + } else if (selected.key === 'minimumAge') { + if (selected.value) { + // converting minimumAge boolean to actual minimum age value + this.setState((prevState) => ({ + postData: { ...prevState.postData, [selected.key]: 18 }, + })); + } else { + this.setState((prevState) => ({ + postData: { ...prevState.postData, [selected.key]: undefined }, + })); + } + } else { + this.setState((prevState) => ({ + postData: { ...prevState.postData, [selected.key]: selected.value }, + })); + } + } + + onCancelClicked() { + this.setState(cloneDeep(this.initialState), () => { + if (this.props.history.length > 2) { + // if history is not empty, go back: + this.props.history.goBack(); + } else { + // else go to Map page + this.props.history.push(`/map/selection=${this.id}`); + } + }); + } + + async handleSubmit(e) { + e.preventDefault(); + + if (!this.state.postData.name || !this.state.postData.address) { + this.setState({ + snackbarMessage: 'Please complete required fields', + snackbarSeverity: 'error', + snackbarOpen: true, + }); + } else { + const response = await this.facilitateService.changeFacilityByCitizen({ value: this.state.postData }); + if (response.error) { + this.setState({ + snackbarMessage: 'An error occured. Please try again later.', + snackbarSeverity: 'error', + snackbarOpen: true, + }); + } else { + this.setState((prevState) => ({ + ...prevState, + snackbarMessage: 'Success! The edited Test Center has been submitted', + snackbarSeverity: 'success', + snackbarOpen: true, + })); + } + } + } + + handleSnackbarClose() { + this.setState({ snackbarOpen: false }); + + // return to map if test center is invalid + if(this.state.snackbarMessage === 'Test Center does not exist.'){ + this.onCancelClicked(); + } + } + + onChange(item, value) { + this.setState({ postData: { ...this.state.postData, [item]: value } }); + } + + render() { + return ( + <> +
+ this.onCheckboxSelected.bind(this, option)} + handleSnackbarClose={() => this.handleSnackbarClose} + handleSubmit={(e) => this.handleSubmit(e)} + onCancelClicked={() => this.onCancelClicked} + onChange={(item, value) => this.onChange(item, value)} + /> +
+ + ); + } +} + +const Section = styled.section` + background: ${(props) => props.theme.palette.gradient.mobile}; + ${(props) => props.theme.breakpoints.up('md')} { + background: ${(props) => props.theme.palette.gradient.desktop}; + } + svg { + color: #fff; + } +`; diff --git a/src/pages/test-center.page.js b/src/pages/test-center.page.js index f348d5d1..bfad901a 100644 --- a/src/pages/test-center.page.js +++ b/src/pages/test-center.page.js @@ -5,6 +5,7 @@ import { withRouter } from 'react-router'; import { Container, Button } from '@material-ui/core'; import PhoneIcon from '@material-ui/icons/Phone'; import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'; +import { Link } from 'react-router-dom'; // components import LinkButton from '@general/buttons/link-button'; @@ -20,7 +21,6 @@ import { triggerShareAction, getShareActionSnackbar } from '@util/social.helpers import { getFacilityDetailsMap, convertToReadableDate, - getFeedbackButtonURL, isTaggableLocation, applyCovidTag } from '@util/general.helpers'; @@ -63,7 +63,6 @@ class TestCenterPage extends Component { const facility = res.data; this.loading = false; this.facilityDetailsMap = getFacilityDetailsMap(facility); - this.feedbackURL = getFeedbackButtonURL(facility); facility.lastUpdated = convertToReadableDate(facility.updatedAt); return facility; }) @@ -213,7 +212,7 @@ class TestCenterPage extends Component {
Want to help us improve our data? - + - +
)} diff --git a/src/services/experience.service.js b/src/services/experience.service.js index ad97de83..ba238c3c 100644 --- a/src/services/experience.service.js +++ b/src/services/experience.service.js @@ -47,4 +47,13 @@ export default class ExperienceService { params: {facilityId} }); } + + limit(sessionId, facilityId){ + return Axios({ + method: 'GET', + url:`${this.baseURL}/limit`, + headers: { 'X-AllClear-SessionID': sessionId }, + params: {facilityId} + }); + } } diff --git a/src/services/facilitate.service.js b/src/services/facilitate.service.js index e607dac8..be93b473 100644 --- a/src/services/facilitate.service.js +++ b/src/services/facilitate.service.js @@ -41,4 +41,21 @@ export default class FacilitateService { return error; } } + + async changeFacilityByCitizen(body) { + try { + const response = await Axios({ + method: 'PUT', + url: `${this.baseURL}/citizen`, + data: body, + }); + return response; + } catch (e) { + const error = { + error: true, + ...e.response, + }; + return error; + } + } } diff --git a/src/util/general.helpers.js b/src/util/general.helpers.js index 69c43923..e346a64d 100644 --- a/src/util/general.helpers.js +++ b/src/util/general.helpers.js @@ -141,16 +141,6 @@ export function convertToReadableDate(dateStr) { return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`; } -export function getFeedbackButtonURL(facility) { - return ` - https://airtable.com/shrVJrPQs4qQkcW4o?prefill_Name=${facility.name || facility.title || ''} - &prefill_Phone number=${facility.phone || ''} - &prefill_Hours=${facility.service_time === undefined ? '' : facility.service_time} - &prefill_This location was drive through=${(facility.driveThru.toString() === 'true' ? 'Drive Through' : '')} - &prefill_This location required an appointment=${boolToEng(facility.appointmentRequired) || ''} - &prefill_Address=${facility.description || facility.address || ''}`; -} - export function applyCovidTag({name, city, state, address, lastUpdated, text, url, type}) { const tag = { '@context': 'https://schema.org', @@ -185,4 +175,14 @@ export function sortExperiencesTags(tagA, tagB){ } else { return tagB.count - tagA.count; } -} \ No newline at end of file +} + +/*export function getFeedbackButtonURL(facility) { + return ` + https://airtable.com/shrVJrPQs4qQkcW4o?prefill_Name=${facility.name || facility.title || ''} + &prefill_Phone number=${facility.phone || ''} + &prefill_Hours=${facility.service_time === undefined ? '' : facility.service_time} + &prefill_This location was drive through=${(facility.driveThru.toString() === 'true' ? 'Drive Through' : '')} + &prefill_This location required an appointment=${boolToEng(facility.appointmentRequired) || ''} + &prefill_Address=${facility.description || facility.address || ''}`; +}*/ \ No newline at end of file