From 22b4d0bfc0b961bf1676e61369c8f53238e2b003 Mon Sep 17 00:00:00 2001 From: jlarson Date: Wed, 27 Mar 2024 08:22:43 -0500 Subject: [PATCH 1/2] Added Sign-in and sign-out functionality to the base --- src/App.tsx | 6 +- src/components/BottomStickySection/index.scss | 2 +- src/components/Errors/NetworkError/index.scss | 2 +- .../BorderedInput/index.scss | 6 +- .../ConfirmationPageContent/index.scss | 4 +- .../GeneralUIElements/GrayInput/index.scss | 5 +- .../GrayInput/responsive.scss | 3 +- .../UnderlinedInput/index.scss | 8 +- src/components/InputWrapper/index.scss | 35 ++ src/components/InputWrapper/index.test.tsx | 8 + src/components/InputWrapper/index.tsx | 23 + src/components/InputWrapper/responsive.scss | 19 + src/components/SignUpPageStep/desktop.scss | 17 + src/components/SignUpPageStep/index.scss | 34 + src/components/SignUpPageStep/index.test.tsx | 10 + src/components/SignUpPageStep/index.tsx | 236 +++++++ src/pages/Home/Post/index.scss | 8 - src/pages/Home/Post/index.test.tsx | 9 - src/pages/Home/Post/index.tsx | 114 ---- src/pages/Home/index.scss | 23 +- src/pages/Home/index.tsx | 4 +- src/pages/Home/responsive.scss | 2 +- src/pages/Home/route-helpers.tsx | 26 - src/pages/PrivacyPolicy/index.scss | 10 + src/pages/PrivacyPolicy/index.test.tsx | 9 + src/pages/PrivacyPolicy/index.tsx | 19 + src/pages/SignIn/SignInEmail/index.scss | 12 + src/pages/SignIn/SignInEmail/index.test.tsx | 9 + src/pages/SignIn/SignInEmail/index.tsx | 103 +++ src/pages/SignUp/index.scss | 46 ++ src/pages/SignUp/index.test.tsx | 9 + src/pages/SignUp/index.tsx | 108 ++++ src/pages/SignUp/responsive.scss | 21 + src/pages/Splash/cih.scss | 7 + src/pages/Splash/nha.scss | 7 + src/pages/TermsOfUse/index.scss | 10 + src/pages/TermsOfUse/index.test.tsx | 8 + src/pages/TermsOfUse/index.tsx | 17 + src/theme/elements.scss | 44 +- src/theme/variables.scss | 585 +++++++++--------- 40 files changed, 1104 insertions(+), 524 deletions(-) create mode 100644 src/components/InputWrapper/index.scss create mode 100644 src/components/InputWrapper/index.test.tsx create mode 100644 src/components/InputWrapper/index.tsx create mode 100644 src/components/InputWrapper/responsive.scss create mode 100644 src/components/SignUpPageStep/desktop.scss create mode 100644 src/components/SignUpPageStep/index.scss create mode 100644 src/components/SignUpPageStep/index.test.tsx create mode 100644 src/components/SignUpPageStep/index.tsx delete mode 100644 src/pages/Home/Post/index.scss delete mode 100644 src/pages/Home/Post/index.test.tsx delete mode 100644 src/pages/Home/Post/index.tsx delete mode 100644 src/pages/Home/route-helpers.tsx create mode 100644 src/pages/PrivacyPolicy/index.scss create mode 100644 src/pages/PrivacyPolicy/index.test.tsx create mode 100644 src/pages/PrivacyPolicy/index.tsx create mode 100644 src/pages/SignIn/SignInEmail/index.scss create mode 100644 src/pages/SignIn/SignInEmail/index.test.tsx create mode 100644 src/pages/SignIn/SignInEmail/index.tsx create mode 100644 src/pages/SignUp/index.scss create mode 100644 src/pages/SignUp/index.test.tsx create mode 100644 src/pages/SignUp/index.tsx create mode 100644 src/pages/SignUp/responsive.scss create mode 100644 src/pages/Splash/cih.scss create mode 100644 src/pages/Splash/nha.scss create mode 100644 src/pages/TermsOfUse/index.scss create mode 100644 src/pages/TermsOfUse/index.test.tsx create mode 100644 src/pages/TermsOfUse/index.tsx diff --git a/src/App.tsx b/src/App.tsx index 1279f4dc..8237c5d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,6 +11,8 @@ import {BrowserRouter, Redirect, useHistory} from "react-router-dom"; /* Theme variables */ import './theme/main.scss' import Splash from "./pages/Splash"; +import SignInEmail from "./pages/SignIn/SignInEmail"; +import SignUp from "./pages/SignUp"; interface StateProps { @@ -30,7 +32,9 @@ const ReactApp: React.FC = ({tokenData}) => {
}/> - + }/> + +
diff --git a/src/components/BottomStickySection/index.scss b/src/components/BottomStickySection/index.scss index d5c6ffb9..9df36620 100644 --- a/src/components/BottomStickySection/index.scss +++ b/src/components/BottomStickySection/index.scss @@ -8,7 +8,7 @@ padding: 6px; margin: 0; } - ion-button { + button { margin: 0; } } diff --git a/src/components/Errors/NetworkError/index.scss b/src/components/Errors/NetworkError/index.scss index bd834cc3..b8fb8353 100644 --- a/src/components/Errors/NetworkError/index.scss +++ b/src/components/Errors/NetworkError/index.scss @@ -5,7 +5,7 @@ height: 70%; text-align: center; padding: 0 10%; - ion-icon { + img { display: block; margin: 0 auto; font-size: 96px; diff --git a/src/components/GeneralUIElements/BorderedInput/index.scss b/src/components/GeneralUIElements/BorderedInput/index.scss index e2d503b3..6f267add 100644 --- a/src/components/GeneralUIElements/BorderedInput/index.scss +++ b/src/components/GeneralUIElements/BorderedInput/index.scss @@ -1,9 +1,7 @@ .bordered-input { margin: 0 0 12px; - ion-datetime, - ion-textarea, - ion-input { + input, textarea { --padding-start: 12px; - border: 2px var(--ion-color-primary) solid; + border: 2px var(--color-primary) solid; } } diff --git a/src/components/GeneralUIElements/ConfirmationPageContent/index.scss b/src/components/GeneralUIElements/ConfirmationPageContent/index.scss index 5cd1d357..1fc2976b 100644 --- a/src/components/GeneralUIElements/ConfirmationPageContent/index.scss +++ b/src/components/GeneralUIElements/ConfirmationPageContent/index.scss @@ -9,11 +9,11 @@ color: black; } h3 { - color: var(--ion-color-medium); + color: var(--color-medium); font-weight: bold; } hr { - background: var(--ion-color-light-shade); + background: var(--color-light-shade); height: 1px; max-width: 35vw; margin: 0 auto; diff --git a/src/components/GeneralUIElements/GrayInput/index.scss b/src/components/GeneralUIElements/GrayInput/index.scss index cf23a4eb..6858e3ee 100644 --- a/src/components/GeneralUIElements/GrayInput/index.scss +++ b/src/components/GeneralUIElements/GrayInput/index.scss @@ -1,9 +1,8 @@ .gray-input { - ion-datetime, - ion-input { + input { --padding-start: 3.125vw; border: transparent; - background: var(--ion-color-light); + background: var(--color-light); border-radius: 10px; } } diff --git a/src/components/GeneralUIElements/GrayInput/responsive.scss b/src/components/GeneralUIElements/GrayInput/responsive.scss index 14e85c9d..eaa84317 100644 --- a/src/components/GeneralUIElements/GrayInput/responsive.scss +++ b/src/components/GeneralUIElements/GrayInput/responsive.scss @@ -1,6 +1,5 @@ .gray-input { - ion-datetime, - ion-input { + input { @media screen and (min-width: 576px) { --padding-start: 18px; } diff --git a/src/components/GeneralUIElements/UnderlinedInput/index.scss b/src/components/GeneralUIElements/UnderlinedInput/index.scss index 508e24a1..1c7baf3b 100644 --- a/src/components/GeneralUIElements/UnderlinedInput/index.scss +++ b/src/components/GeneralUIElements/UnderlinedInput/index.scss @@ -1,12 +1,10 @@ .underlined-input { - ion-datetime, - ion-textarea, - ion-input { + input { --padding-start: 10px; border: transparent; - border-bottom: 3px var(--ion-color-medium) solid; + border-bottom: 3px var(--color-medium) solid; } - ion-label { + label { font-weight: 600; } } diff --git a/src/components/InputWrapper/index.scss b/src/components/InputWrapper/index.scss new file mode 100644 index 00000000..fa6c58d3 --- /dev/null +++ b/src/components/InputWrapper/index.scss @@ -0,0 +1,35 @@ +.input-wrapper { + margin: 8vw 0 5vw; + &.rounded{ + ion-input{ + border-radius: 5px; + } + } + &.dark{ + ion-input{ + border-color: var(--ion-color-dark); + } + } + ion-label { + padding-left: 5px; + padding-bottom: 4px; + margin-bottom: 0; + display: block; + } + ion-input { + border: 1px var(--ion-color-primary) solid; + --padding-start: 3.125vw; + } + .subtext { + padding-top: 5px; + padding-left: 5px; + } +} +.plt-android { + .input-wrapper { + ion-label { + padding-bottom: 3.5vw; + } + } +} +@import "responsive"; diff --git a/src/components/InputWrapper/index.test.tsx b/src/components/InputWrapper/index.test.tsx new file mode 100644 index 00000000..532ffb0d --- /dev/null +++ b/src/components/InputWrapper/index.test.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import Input from './index'; + +test('renders Input without crashing', () => { + const { baseElement } = render(); + expect(baseElement).toBeDefined(); +}); \ No newline at end of file diff --git a/src/components/InputWrapper/index.tsx b/src/components/InputWrapper/index.tsx new file mode 100644 index 00000000..5284ef01 --- /dev/null +++ b/src/components/InputWrapper/index.tsx @@ -0,0 +1,23 @@ +import React, {PropsWithChildren} from 'react' +import './index.scss'; + +interface Props extends PropsWithChildren { + label: string + subtext?: string + error?: string + color?: string + rounded?: boolean +} + +const InputWrapper: React.FC = ({ label, error, color, rounded, subtext, children }) => { + const wrapperClasses = (color ? ' ' + color : '') + (rounded ? ' rounded' : '') + return ( +
+ + {children} + {subtext &&

{subtext}

} +
+ ) +} + +export default InputWrapper diff --git a/src/components/InputWrapper/responsive.scss b/src/components/InputWrapper/responsive.scss new file mode 100644 index 00000000..a8113b0c --- /dev/null +++ b/src/components/InputWrapper/responsive.scss @@ -0,0 +1,19 @@ +.input-wrapper { + @media screen and (min-width: 600px) { + margin: 48px 0 30px; + } + ion-input { + @media screen and (min-width: 576px) { + --padding-start: 18px; + } + } +} +.plt-android { + .input-wrapper { + ion-label { + @media screen and (min-width: 600px) { + padding-bottom: 21px; + } + } + } +} diff --git a/src/components/SignUpPageStep/desktop.scss b/src/components/SignUpPageStep/desktop.scss new file mode 100644 index 00000000..2efe52d8 --- /dev/null +++ b/src/components/SignUpPageStep/desktop.scss @@ -0,0 +1,17 @@ +.sign-up-page { + ion-content { + .flex-wrapper { + .sign-up-disclaimer-wrapper { + .sign-up-disclaimer { + ion-icon { + @media screen and (min-width: 640px){ + font-size: 64px; + width: 160px; + margin: 0 16px 0 0; + } + } + } + } + } + } +} diff --git a/src/components/SignUpPageStep/index.scss b/src/components/SignUpPageStep/index.scss new file mode 100644 index 00000000..fad84ae4 --- /dev/null +++ b/src/components/SignUpPageStep/index.scss @@ -0,0 +1,34 @@ +.sign-up-page { + ion-content { + --padding-top: 0; + .flex-wrapper { + display: flex; + flex-direction: column; + height: 100%; + .sign-up-disclaimer-wrapper { + flex-grow: 1; + align-items: flex-end; + + .sign-up-disclaimer { + display: flex; + clear: both; + margin-bottom: var(--ion-safe-area-bottom); + ion-icon { + font-size: 10vw; + width: 25vw; + margin: 0 2.5vw 0 0; + display: block; + flex-grow: 1; + } + p { + margin: 0; + } + } + .age-notice { + text-align: center; + } + } + } + } +} +@import "desktop"; diff --git a/src/components/SignUpPageStep/index.test.tsx b/src/components/SignUpPageStep/index.test.tsx new file mode 100644 index 00000000..c7996d77 --- /dev/null +++ b/src/components/SignUpPageStep/index.test.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import SignUpPageStep from './index'; +import { shallow } from 'enzyme'; +import {MemoryRouter} from 'react-router'; + +test('renders AuthHeader without crashing', () => { + const baseElement = shallow(); + expect(baseElement).toBeDefined(); +}); diff --git a/src/components/SignUpPageStep/index.tsx b/src/components/SignUpPageStep/index.tsx new file mode 100644 index 00000000..960f8f9a --- /dev/null +++ b/src/components/SignUpPageStep/index.tsx @@ -0,0 +1,236 @@ + +import AuthRequests from '../../services/requests/AuthRequests'; +import {RequestError} from '../../models/request-error'; +import React, {PropsWithChildren, useEffect, useState} from 'react'; +import ServerAlert from '../ServerAlert'; +import SignUpContextProvider, { + SignUpContext, + SignUpContextStateConsumer, + SignUpData +} from '../../contexts/signin/SignUpContext'; +import {ObjectSchema} from 'yup'; +import {useFormik} from 'formik'; +import './index.scss'; +import {useHistory} from 'react-router-dom'; + +export interface FormInformation { + setForm: (form: any) => void, + validationSchema: ObjectSchema, + onValidate: (submission: any) => string|undefined, +} + +export interface SignUpPageStepSharedProps { + header?: string, + formInformation?: FormInformation, + onNextStep?: () => void, + className?: string, + bypassSubmit?: boolean, + inputs?: HTMLInputElement[] +} + +export interface SignUpPageStepContentProps extends SignUpPageStepSharedProps { + form?: any + signUpContext: SignUpContextStateConsumer, + onSubmit: (formData: SignUpData) => void, + disclaimer?: string, +} + +const SignUpPageStepContent: React.FC> = ({className, onSubmit, form, bypassSubmit, disclaimer, header, signUpContext, children}) => { + + return ( +
+
+
+ {header &&

{header}

} + {children} + {!bypassSubmit && +
form.handleSubmit() : () => onSubmit(signUpContext.data)} + className={form && form.isValid ? 'valid' : ''} + > + Next +
+ } +
+ {disclaimer && +
+ +
+

{disclaimer}

+
+

+ Must be 17 years or older to participate +

+
+ } +
+
+ ) +} + +interface SignUpPageStepContentWithFormProps extends SignUpPageStepContentProps { + formInformation: FormInformation, + setValidationError: (error: string) => void, +} + +const SignUpPageStepContentWithForm : React.FC = ({onSubmit, formInformation, setValidationError, inputs, signUpContext, ...rest}) => { + + const form: any = useFormik({ + initialValues: signUpContext.data, + initialErrors: {first_name: signUpContext.data.first_name}, + validationSchema: formInformation.validationSchema, + onSubmit: onSubmit, + }); + + useEffect(() => { + if (!form.isValid) { + + const currentError = formInformation.onValidate(form); + + setValidationError(currentError ? currentError : ''); + } + }, [form.isSubmitting, form.isValid]) + + useEffect( () => formInformation.setForm(form), [form.values]); + + const enterPressed = (event: any) => { + if (event.keyCode === 13 && !form.isSubmitting) { + form.handleSubmit(); + } + } + + useEffect(() => { + if (inputs) { + inputs.forEach(i => { + i.removeEventListener('keydown', enterPressed); + i.addEventListener('keydown', enterPressed) + }) + } + }, [inputs]) + + return ( + + ) +} + +export interface SignUpPageStepProps extends SignUpPageStepSharedProps { + cancelOnBack: boolean, + arrowColor?: string, + disclaimer?: string, + showArrow?: boolean, +} + +const SignUpPageStep : React.FC> = ({ arrowColor, showArrow, formInformation, onNextStep, cancelOnBack, ...rest}) => { + + const [validationError, setValidationError] = useState('') + const [requestError, setRequestError] = useState(undefined); + const history = useHistory(); + if (arrowColor) { + document.documentElement.style.setProperty("--arrow-color", arrowColor); + } + else { + document.documentElement.style.setProperty("--arrow-color", "var(--ion-color-light)"); + } + + const onSubmit = (formData: SignUpData, setData: (data: SignUpData) => void) => { + setData(formData); + if ( formData.phone && !formData.email ) { + duplicatePhoneCheck(formData.phone) + } else if ( formData.email ) { + duplicateEmailCheck(formData.email).then(() => { + setValidationError(''); + processSignUp(formData); + }) + } + } + + const cancel = () => { + if (cancelOnBack) { + window.location.replace('/welcome/splash') + } else { + history.goBack(); + } + } + + const duplicatePhoneCheck = async (phone: string) => { + try { + await AuthRequests.signUp({phone: phone} as SignUpData) + } catch (error: any) { + if ( error.data.errors.phone ) { + setValidationError('Phone number is already in use') + } + else if ( onNextStep ){ + onNextStep() + } + } + } + + const duplicateEmailCheck = async (email: string) => { + try { + await AuthRequests.signUp({email: email} as SignUpData) + } catch (error: any) { + if (error.data.errors.email) { + setValidationError('Email number is already in use') + return Promise.reject(); + } + return Promise.resolve(); + } + + } + + const processSignUp = async (signUpData: SignUpData) => { + + try { + await AuthRequests.signUp(signUpData) + const redirectUrl = localStorage.getItem('login_redirect'); + localStorage.removeItem('login_redirect'); + if (redirectUrl) { + history.push(redirectUrl); + } else { + history.push('/home') + } + } catch (error) { + setRequestError(error as RequestError) + } + } + + return ( +
+
cancel()}>Cancel
+ + + {signUpContext => ( + + {formInformation ? + onSubmit(data, signUpContext.setData)} + {...rest} + /> : + onSubmit(data, signUpContext.setData)} + {...rest} + /> + } + {requestError && + setRequestError(undefined)} + /> + } + + )} + + +
+ )} + +export default SignUpPageStep; diff --git a/src/pages/Home/Post/index.scss b/src/pages/Home/Post/index.scss deleted file mode 100644 index 4448403b..00000000 --- a/src/pages/Home/Post/index.scss +++ /dev/null @@ -1,8 +0,0 @@ -.post-details-content-wrapper { - border-radius: 20px; - overflow: hidden; - perspective: 1px; - @media (pointer: fine) { - --padding-bottom: 36px !important; - } -} diff --git a/src/pages/Home/Post/index.test.tsx b/src/pages/Home/Post/index.test.tsx deleted file mode 100644 index c5aea1bb..00000000 --- a/src/pages/Home/Post/index.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import PostPage from './index'; -import { shallow } from 'enzyme'; -import {MemoryRouter} from 'react-router'; - -test('renders without crashing', () => { - const baseElement = shallow(); - expect(baseElement).toBeDefined(); -}); diff --git a/src/pages/Home/Post/index.tsx b/src/pages/Home/Post/index.tsx deleted file mode 100644 index 46c6a715..00000000 --- a/src/pages/Home/Post/index.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, {useEffect, useState} from 'react' -import './index.scss'; -import MeContextProvider, {MeContext, MeContextStateConsumer} from '../../../contexts/MeContext'; -import {PostContext, PostContextConsumerState, PostContextProvider} from '../../../contexts/PostContext'; -import Post from '../../../models/post/post'; -import Location from '../../../models/location/location'; -import User from "../../../models/user/user"; -import PostResponse from "../../../models/post/post-response"; -import {useHistory} from 'react-router-dom'; -import {useLocation, useParams} from 'react-router'; - -interface PostPageContentProps { - post: Post, - me: User, - url: string - setPost: (post: Post) => void, - inFocus: boolean, -} - -const PostPageContent: React.FC = ({post, setPost, me, url, inFocus}) => { - - const navigate = useHistory(); - - return ( -
- {/*TODO Post Output*/} -
- ) -} - -interface SeenPostsReadyProps extends PostLoadedProps { - meContext: MeContextStateConsumer, -} - -const SeenPostsReady: React.FC = ({meContext, postContext, inFocus}) => { - - const location = useLocation() - - - const setPost = (post: Post) => { - postContext.setPost(post); - } - - return ( - - ) -} - -interface PostLoadedProps { - inFocus: boolean, - postContext: PostContextConsumerState, -} - -const PostLoaded: React.FC = (props) => { - - - return ( - - - {meContext => - - } - - - ) -} - -type RouteParams = { - postId: string, -} - -const PostPage: React.FC = () => { - const [inFocus, setInFocus] = useState(false); - - const {postId} = useParams(); - - useEffect(() => { - setInFocus(true); - - return () => { - setInFocus(false); - } - }, []); - - return ( -
-
Page Header
-
- - - {postContext => - - } - - -
-
- ) -} - -export default PostPage diff --git a/src/pages/Home/index.scss b/src/pages/Home/index.scss index a93f8f37..b030dd98 100644 --- a/src/pages/Home/index.scss +++ b/src/pages/Home/index.scss @@ -1,25 +1,4 @@ -.home-splitpane { - max-width: 1024px; - margin: 0 auto; - --ion-safe-area-bottom: 15px; - .home-tabs-content.ion-page-hidden { - display: flex !important; - } - .home-tabs { - --border: 0; - padding: 1vw; - margin-bottom: var(--ion-safe-area-bottom); - .profile-image { - width: 8vw; - height: 8vw; - } - ion-icon { - font-size: 7.5vw; - &.primary-tab{ - --color: var(--ion-color-primary); - } - } - } +#home-page { } @import "./responsive"; diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index f47febcb..1c92c0ce 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -8,9 +8,9 @@ interface HomeProps { const Home: React.FC = (props) => { return ( -
+

Home Page

-
+ ) } diff --git a/src/pages/Home/responsive.scss b/src/pages/Home/responsive.scss index e18b5457..ed5fa1a3 100644 --- a/src/pages/Home/responsive.scss +++ b/src/pages/Home/responsive.scss @@ -9,7 +9,7 @@ height: 40px; } - ion-icon { + .icon { font-size: 38px; } } diff --git a/src/pages/Home/route-helpers.tsx b/src/pages/Home/route-helpers.tsx deleted file mode 100644 index 16af0ffc..00000000 --- a/src/pages/Home/route-helpers.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, {ReactElement} from 'react'; -import PostPage from './Post'; -import {Route} from 'react-router'; - -interface SubRoute { - routePath: string, - page: ReactElement, -} -const SubRoutes: SubRoute[] = [ - { - routePath: '/post/:postId', - page: , - }, -] - - -export const buildSubRoutes = (baseUrl: string): JSX.Element[] => { - return SubRoutes.map(i => - i.page} - /> - ) -} diff --git a/src/pages/PrivacyPolicy/index.scss b/src/pages/PrivacyPolicy/index.scss new file mode 100644 index 00000000..3de8dd74 --- /dev/null +++ b/src/pages/PrivacyPolicy/index.scss @@ -0,0 +1,10 @@ +#welcome-section { + .privacy-page { + ion-content { + --background: white; + h3 { + font-size: 22px; + } + } + } +} diff --git a/src/pages/PrivacyPolicy/index.test.tsx b/src/pages/PrivacyPolicy/index.test.tsx new file mode 100644 index 00000000..051bf8a1 --- /dev/null +++ b/src/pages/PrivacyPolicy/index.test.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import PrivacyPolicy from './index'; +import {MemoryRouter} from 'react-router'; + +test('renders without crashing', () => { + const { baseElement } = render(); + expect(baseElement).toBeDefined(); +}); diff --git a/src/pages/PrivacyPolicy/index.tsx b/src/pages/PrivacyPolicy/index.tsx new file mode 100644 index 00000000..821c1b10 --- /dev/null +++ b/src/pages/PrivacyPolicy/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import PrivacyPolicyText from '../../components/PrivacyPolicyText'; +import './index.scss'; + +const PrivacyPolicy: React.FC = () => { + + return ( +
+
+ Privacy Policy +
+ +
+ ); +} + +export default PrivacyPolicy; diff --git a/src/pages/SignIn/SignInEmail/index.scss b/src/pages/SignIn/SignInEmail/index.scss new file mode 100644 index 00000000..5320698a --- /dev/null +++ b/src/pages/SignIn/SignInEmail/index.scss @@ -0,0 +1,12 @@ +.welcome-section { + .sign-up-page { + ion-router-link { + display: block; + font-weight: normal; + align-self: flex-start; + color: var(--ion-color-secondary); + margin-bottom: 6.25vw; + padding-left: 1.875vw; + } + } +} diff --git a/src/pages/SignIn/SignInEmail/index.test.tsx b/src/pages/SignIn/SignInEmail/index.test.tsx new file mode 100644 index 00000000..63131496 --- /dev/null +++ b/src/pages/SignIn/SignInEmail/index.test.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import SignInEmail from './index'; +import { shallow } from 'enzyme'; +import { MemoryRouter } from 'react-router-dom'; + +test('renders without crashing', () => { + const baseElement = shallow(); + expect(baseElement).toBeDefined(); +}); diff --git a/src/pages/SignIn/SignInEmail/index.tsx b/src/pages/SignIn/SignInEmail/index.tsx new file mode 100644 index 00000000..912945b6 --- /dev/null +++ b/src/pages/SignIn/SignInEmail/index.tsx @@ -0,0 +1,103 @@ +import React, {useState} from 'react'; +import { useFormik } from 'formik'; +import * as Yup from 'yup'; + +import AuthRequests, { LoginReq } from '../../../services/requests/AuthRequests'; +import InputWrapper from '../../../components/InputWrapper'; +import './index.scss'; +import {Link, useHistory} from 'react-router-dom'; + +interface SignInEmailProps { } + +const SignInEmail: React.FC = ({}) => { + const [credentialError, setCredentialError] = useState(false); + const history = useHistory(); + + const SignupSchema = Yup.object().shape({ + emailOrPhone: Yup.string().required('Email or Phone is required'), + password: Yup.string().required('Password is required') + }) + + const form = useFormik({ + initialValues: { + emailOrPhone: '', + password: '' + }, + validationSchema: SignupSchema, + onSubmit: (values) => submit(values) + }) + + const submit = (values: { emailOrPhone: string; password: string }) => { + const { emailOrPhone, password } = values + let userReq: LoginReq = { password } + const isPhoneNumber = /^\d+$/.test(emailOrPhone) + if (isPhoneNumber) { + userReq.phone = emailOrPhone + } else { + userReq.email = emailOrPhone + } + + AuthRequests.signIn(userReq).then(async () => { + const redirectUrl = localStorage.getItem('login_redirect'); + localStorage.removeItem('login_redirect'); + + if (redirectUrl) { + history.push(redirectUrl); + } + else { + history.push('/') + } + }).catch(error => { + if (error.status && error.status == 401) { + setCredentialError(true); + } + }) + } + + const locationOptions: PositionOptions = { + maximumAge: 5 * 60 * 60 * 1000, + enableHighAccuracy: false + } + + return ( +
+
Cancel
+
+

Sign In

+ {credentialError && +

Invalid Email or Password

+ } + + + + + + + + forgot password? + + +
+
+ ) +} + +export default SignInEmail diff --git a/src/pages/SignUp/index.scss b/src/pages/SignUp/index.scss new file mode 100644 index 00000000..a15eb1bd --- /dev/null +++ b/src/pages/SignUp/index.scss @@ -0,0 +1,46 @@ +#welcome-section { + .sign-up-page { + .email-signup-step { + h3 { + margin: 54px 6px 30px; + color: var(--ion-color-dark); + text-align: left; + font-weight: normal; + } + .email-box { + border: none; + border-bottom: 3px black solid; + margin: 0 6px 16px 6px; + --padding-start: 0; + ion-input { + color: var(--ion-color-medium); + --placeholder-color: var(--ion-color-light-shade); + --placeholder-font-weight: normal; + --placeholder-opacity: 1; + font-style: italic; + font-size: 7vw; + } + } + .disclaimer-checkbox { + border: none; + margin: 48px 6px 0; + --padding-start: 0; + ion-checkbox { + vertical-align: top; + margin-right: 5vw; + padding-left: 0; + } + .disclaimer { + display: inline-block; + font-style: italic; + color: var(--ion-color-medium); + text-align: left; + padding-bottom: 32px; + margin: 0; + } + } + } + } +} + +@import "responsive"; diff --git a/src/pages/SignUp/index.test.tsx b/src/pages/SignUp/index.test.tsx new file mode 100644 index 00000000..8a1ca0c4 --- /dev/null +++ b/src/pages/SignUp/index.test.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import SignUpignUp from './index'; +import { shallow } from 'enzyme'; +import { MemoryRouter } from 'react-router'; + +test('renders without crashing', () => { + const baseElement = shallow(); + expect(baseElement).toBeDefined(); +}); diff --git a/src/pages/SignUp/index.tsx b/src/pages/SignUp/index.tsx new file mode 100644 index 00000000..ba5d9022 --- /dev/null +++ b/src/pages/SignUp/index.tsx @@ -0,0 +1,108 @@ +import React, {useRef, useState} from 'react' +import * as Yup from 'yup' +import './index.scss' +import SignUpPageStep, {FormInformation} from "../../components/SignUpPageStep"; +import {useHistory} from 'react-router-dom'; +import PhoneNumberInput from "../../components/PhoneNumberInput"; + + +const SignUp: React.FC = () => { + const [form, setForm] = useState(undefined); + const history = useHistory(); + const emailInputRef = useRef(null) + const firstNameInputRef = useRef(null) + const lastNameInputRef = useRef(null) + const passwordInputRef = useRef(null) + const SignUpSchema = Yup.object().shape({ + first_name: Yup.string().trim().required('First Name is required'), + last_name: Yup.string().trim().required('Last Name is required'), + email: Yup.string().email('Invalid Email').required('Email is required'), + password: Yup.string().trim().required('Password is required'), + }) + + const validateForm = (submission: any) : string|undefined => { + if (submission.errors.email && submission.touched.email) { + return submission.errors.email + } + else if (submission.errors.first_name && submission.touched.first_name) { + return submission.errors.first_name; + } + else if (submission.errors.last_name && submission.touched.last_name) { + return submission.errors.last_name; + } + return undefined + } + + const formInformation: FormInformation = { + onValidate: validateForm, + validationSchema: SignUpSchema, + setForm, + } + + const onNextStep = () => { + history.push('/welcome/sign-up/date-of-birth'); + } + + return ( + i)as HTMLInputElement[]} + > + {form && +
+
+ + +
+ + +
+ } +
+ ) +} + +export default SignUp; diff --git a/src/pages/SignUp/responsive.scss b/src/pages/SignUp/responsive.scss new file mode 100644 index 00000000..ee375826 --- /dev/null +++ b/src/pages/SignUp/responsive.scss @@ -0,0 +1,21 @@ +#welcome-section { + .sign-up-page { + .email-signup-step { + + @media screen and (min-width: 514px) { + .email-box { + ion-input { + font-size: 36px; + } + } + } + .disclaimer-checkbox { + @media screen and (min-width: 480px) { + ion-checkbox { + margin-right: 24px; + } + } + } + } + } +} diff --git a/src/pages/Splash/cih.scss b/src/pages/Splash/cih.scss new file mode 100644 index 00000000..76ef9331 --- /dev/null +++ b/src/pages/Splash/cih.scss @@ -0,0 +1,7 @@ +#splash-page { + h2 { + color: red; + } +} + +@import "responsive"; diff --git a/src/pages/Splash/nha.scss b/src/pages/Splash/nha.scss new file mode 100644 index 00000000..80977ae5 --- /dev/null +++ b/src/pages/Splash/nha.scss @@ -0,0 +1,7 @@ +#splash-page { + h2 { + color: blue; + } +} + +@import "responsive"; diff --git a/src/pages/TermsOfUse/index.scss b/src/pages/TermsOfUse/index.scss new file mode 100644 index 00000000..0c39a173 --- /dev/null +++ b/src/pages/TermsOfUse/index.scss @@ -0,0 +1,10 @@ +#welcome-section { + .terms-page { + ion-content { + --background: white; + h3 { + font-size: 22px; + } + } + } +} diff --git a/src/pages/TermsOfUse/index.test.tsx b/src/pages/TermsOfUse/index.test.tsx new file mode 100644 index 00000000..e2327ee2 --- /dev/null +++ b/src/pages/TermsOfUse/index.test.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import TermsOfUse from './index'; + +test('renders without crashing', () => { + const { baseElement } = render(); + expect(baseElement).toBeDefined(); +}); diff --git a/src/pages/TermsOfUse/index.tsx b/src/pages/TermsOfUse/index.tsx new file mode 100644 index 00000000..f08ea300 --- /dev/null +++ b/src/pages/TermsOfUse/index.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import TermsOfUseText from '../../components/TermsOfUseText'; +import './index.scss'; + +const TermsOfUse: React.FC = () => { + + return ( +
+
+ Terms of Use +
+ +
+ ); +} + +export default TermsOfUse; diff --git a/src/theme/elements.scss b/src/theme/elements.scss index 76467d9f..b74b751e 100644 --- a/src/theme/elements.scss +++ b/src/theme/elements.scss @@ -1,5 +1,5 @@ h3 { - color: var(--ion-color-primary-shade); + color: var(--color-primary-shade); text-align: center; font-weight: bold; } @@ -15,55 +15,51 @@ h6 { } } .barlow { - color: var(--ion-color-primary); + color: var(--color-primary); font-family: 'barlow', Arial, Helvetica, sans-serif !important; font-style: italic; font-weight: 900; } -ion-content { +section { --padding-bottom: 10px; --padding-end: 20px; --padding-start: 20px; --padding-top: 20px; } -ion-button { +button { text-decoration: none; width: 100%; max-height: 40px; - --background: var(--ion-color-primary); + --background: var(--color-primary); --color: white; --border-radius: 8px; &.secondary-button-outlined { border-radius: 10px; - border: 2px var(--ion-color-medium-tint) solid; + border: 2px var(--color-medium-tint) solid; --background: transparent; - --color: var(--ion-color-primary); - --background-activated: var(--ion-color-light); + --color: var(--color-primary); + --background-activated: var(--color-light); padding: 0; } } -ion-button.outlined { +button.outlined { border-radius: 5px; --background: white; - color: var(--ion-color-primary); - border: 2px solid var(--ion-color-primary); + color: var(--color-primary); + border: 2px solid var(--color-primary); } .error { - color: var(--ion-color-danger); + color: var(--color-danger); } -ion-select { - border: 1px solid var(--ion-color-light-shade); +select { + border: 1px solid var(--color-light-shade); border-radius: 5px; } -ion-item { - --detail-icon-opacity: 1; -} -ion-item, p { - color: var(--ion-color-medium); + color: var(--color-medium); } -ion-alert{ +.alert{ .alert-wrapper { --max-height: 75vh; --max-width: 80vw; @@ -83,11 +79,3 @@ a { text-decoration: none; font-weight: bold; } -.plt-android { - ion-toolbar ion-back-button { - display: none; - } -} -ion-app { - background: white; -} diff --git a/src/theme/variables.scss b/src/theme/variables.scss index fba8fc14..07965679 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -22,81 +22,81 @@ :root { /** primary **/ - --ion-color-primary: #fe9024; - --ion-color-primary-rgb: 56, 128, 255; - --ion-color-primary-contrast: #ffffff; - --ion-color-primary-contrast-rgb: 255, 255, 255; - --ion-color-primary-shade: #E87819; - --ion-color-primary-tint: #f58d34; - --ion-color-primary-light: #FFEED4; + --color-primary: lightblue; + --color-primary-rgb: 56, 128, 255; + --color-primary-contrast: #ffffff; + --color-primary-contrast-rgb: 255, 255, 255; + --color-primary-shade: darken(lightblue, 10%); + --color-primary-tint: darken(lightblue, 20%); + --color-primary-light: lighten(lightblue, 10%); /** secondary **/ - --ion-color-secondary: #3298b3; - --ion-color-secondary-rgb: 61, 194, 255; - --ion-color-secondary-contrast: #ffffff; - --ion-color-secondary-contrast-rgb: 255, 255, 255; - --ion-color-secondary-shade: #3eacc9; - --ion-color-secondary-tint: #3298b3; + --color-secondary: lightgreen; + --color-secondary-rgb: 61, 194, 255; + --color-secondary-contrast: #ffffff; + --color-secondary-contrast-rgb: 255, 255, 255; + --color-secondary-shade: darken(lightgreen, 10%); + --color-secondary-tint: lighten(lightgreen, 10%); /** tertiary **/ - --ion-color-tertiary: #5260ff; - --ion-color-tertiary-rgb: 82, 96, 255; - --ion-color-tertiary-contrast: #ffffff; - --ion-color-tertiary-contrast-rgb: 255, 255, 255; - --ion-color-tertiary-shade: #4854e0; - --ion-color-tertiary-tint: #6370ff; + --color-tertiary: darkgreen; + --color-tertiary-rgb: 82, 96, 255; + --color-tertiary-contrast: #ffffff; + --color-tertiary-contrast-rgb: 255, 255, 255; + --color-tertiary-shade: darken(darkgreen, 10%); + --color-tertiary-tint: lighten(darkgreen, 10%); /** quaternary **/ - --ion-color-quaternary: #481514; + --color-quaternary: #481514; /** success **/ - --ion-color-success: #52ab58; - --ion-color-success-rgb: 45, 211, 111; - --ion-color-success-contrast: #ffffff; - --ion-color-success-contrast-rgb: 255, 255, 255; - --ion-color-success-shade: rgba(0, 128, 55, 0.95); - --ion-color-success-tint: #3fa46a; + --color-success: #52ab58; + --color-success-rgb: 45, 211, 111; + --color-success-contrast: #ffffff; + --color-success-contrast-rgb: 255, 255, 255; + --color-success-shade: rgba(0, 128, 55, 0.95); + --color-success-tint: #3fa46a; /** warning **/ - --ion-color-warning: #ffc409; - --ion-color-warning-rgb: 255, 196, 9; - --ion-color-warning-contrast: #000000; - --ion-color-warning-contrast-rgb: 0, 0, 0; - --ion-color-warning-shade: #e0ac08; - --ion-color-warning-tint: #ffca22; + --color-warning: #ffc409; + --color-warning-rgb: 255, 196, 9; + --color-warning-contrast: #000000; + --color-warning-contrast-rgb: 0, 0, 0; + --color-warning-shade: #e0ac08; + --color-warning-tint: #ffca22; /** danger **/ - --ion-color-danger: #eb445a; - --ion-color-danger-rgb: 235, 68, 90; - --ion-color-danger-contrast: #ffffff; - --ion-color-danger-contrast-rgb: 255, 255, 255; - --ion-color-danger-shade: #cf3c4f; - --ion-color-danger-tint: #ed576b; + --color-danger: #eb445a; + --color-danger-rgb: 235, 68, 90; + --color-danger-contrast: #ffffff; + --color-danger-contrast-rgb: 255, 255, 255; + --color-danger-shade: #cf3c4f; + --color-danger-tint: #ed576b; /** dark **/ - --ion-color-dark: #222428; - --ion-color-dark-rgb: 34, 36, 40; - --ion-color-dark-contrast: #ffffff; - --ion-color-dark-contrast-rgb: 255, 255, 255; - --ion-color-dark-shade: #1e2023; - --ion-color-dark-tint: #383a3e; + --color-dark: #222428; + --color-dark-rgb: 34, 36, 40; + --color-dark-contrast: #ffffff; + --color-dark-contrast-rgb: 255, 255, 255; + --color-dark-shade: #1e2023; + --color-dark-tint: #383a3e; /** medium **/ - --ion-color-medium: #545454; - --ion-color-medium-rgb: 146, 148, 156; - --ion-color-medium-contrast: #ffffff; - --ion-color-medium-contrast-rgb: 255, 255, 255; - --ion-color-medium-shade: #888b8e; - --ion-color-medium-tint: #9d9fa6; + --color-medium: #545454; + --color-medium-rgb: 146, 148, 156; + --color-medium-contrast: #ffffff; + --color-medium-contrast-rgb: 255, 255, 255; + --color-medium-shade: #888b8e; + --color-medium-tint: #9d9fa6; /** light **/ - --ion-color-light: #f5f7f4; - --ion-color-light-rgb: 244, 245, 248; - --ion-color-light-contrast: var(--ion-color-primary); - --ion-color-light-contrast-rgb: 0, 0, 0; - --ion-color-light-shade: #d7d8da; - --ion-color-light-tint: #f5f6f9; + --color-light: #f5f7f4; + --color-light-rgb: 244, 245, 248; + --color-light-contrast: var(--color-primary); + --color-light-contrast-rgb: 0, 0, 0; + --color-light-shade: #d7d8da; + --color-light-tint: #f5f6f9; --welcome-background: #F6EAD7; --color-violet: #5E17EB; @@ -107,122 +107,117 @@ * Additional Ionic Colors * ---------------------------------------------------------------------------- * In order to add colors to be used within Ionic components, - * the color should be added as a class with the convention `.ion-color-{COLOR}` - * where `{COLOR}` is the color to be used on the Ionic component. - * For more information on adding new colors, please see - * https://ionicframework.com/docs/theming/colors#adding-colors - * - * To generate the code for a new color, check out our new color creator: - * https://ionicframework.com/docs/theming/colors#new-color-creator + * the color should be added as a class with the convention `.color-{COLOR}` + * where `{COLOR}` is the color to be used on the component. */ :root { - --ion-color-favorite: #69bb7b; - --ion-color-favorite-rgb: 105,187,123; - --ion-color-favorite-contrast: #ffffff; - --ion-color-favorite-contrast-rgb: 255,255,255; - --ion-color-favorite-shade: #5ca56c; - --ion-color-favorite-tint: #78c288; - - --ion-color-twitter: #1da1f4; - --ion-color-twitter-rgb: 29,161,244; - --ion-color-twitter-contrast: #ffffff; - --ion-color-twitter-contrast-rgb: 255,255,255; - --ion-color-twitter-shade: #1a8ed7; - --ion-color-twitter-tint: #34aaf5; - - --ion-color-instagram: #5956d8; - --ion-color-instagram-rgb: 89,86,216; - --ion-color-instagram-contrast: #ffffff; - --ion-color-instagram-contrast-rgb: 255,255,255; - --ion-color-instagram-shade: #4e4cbe; - --ion-color-instagram-tint: #6a67dc; - - --ion-color-vimeo: #23b6ea; - --ion-color-vimeo-rgb: 35,182,234; - --ion-color-vimeo-contrast: #ffffff; - --ion-color-vimeo-contrast-rgb: 255,255,255; - --ion-color-vimeo-shade: #1fa0ce; - --ion-color-vimeo-tint: #39bdec; - - --ion-color-facebook: #3b5998; - --ion-color-facebook-rgb: 59,89,152; - --ion-color-facebook-contrast: #ffffff; - --ion-color-facebook-contrast-rgb: 255,255,255; - --ion-color-facebook-shade: #344e86; - --ion-color-facebook-tint: #4f6aa2; + --color-favorite: #69bb7b; + --color-favorite-rgb: 105,187,123; + --color-favorite-contrast: #ffffff; + --color-favorite-contrast-rgb: 255,255,255; + --color-favorite-shade: #5ca56c; + --color-favorite-tint: #78c288; + + --color-twitter: #1da1f4; + --color-twitter-rgb: 29,161,244; + --color-twitter-contrast: #ffffff; + --color-twitter-contrast-rgb: 255,255,255; + --color-twitter-shade: #1a8ed7; + --color-twitter-tint: #34aaf5; + + --color-instagram: #5956d8; + --color-instagram-rgb: 89,86,216; + --color-instagram-contrast: #ffffff; + --color-instagram-contrast-rgb: 255,255,255; + --color-instagram-shade: #4e4cbe; + --color-instagram-tint: #6a67dc; + + --color-vimeo: #23b6ea; + --color-vimeo-rgb: 35,182,234; + --color-vimeo-contrast: #ffffff; + --color-vimeo-contrast-rgb: 255,255,255; + --color-vimeo-shade: #1fa0ce; + --color-vimeo-tint: #39bdec; + + --color-facebook: #3b5998; + --color-facebook-rgb: 59,89,152; + --color-facebook-contrast: #ffffff; + --color-facebook-contrast-rgb: 255,255,255; + --color-facebook-shade: #344e86; + --color-facebook-tint: #4f6aa2; } -.ion-color-favorite { - --ion-color-base: var(--ion-color-favorite); - --ion-color-base-rgb: var(--ion-color-favorite-rgb); - --ion-color-contrast: var(--ion-color-favorite-contrast); - --ion-color-contrast-rgb: var(--ion-color-favorite-contrast-rgb); - --ion-color-shade: var(--ion-color-favorite-shade); - --ion-color-tint: var(--ion-color-favorite-tint); +.color-favorite { + --color-base: var(--color-favorite); + --color-base-rgb: var(--color-favorite-rgb); + --color-contrast: var(--color-favorite-contrast); + --color-contrast-rgb: var(--color-favorite-contrast-rgb); + --color-shade: var(--color-favorite-shade); + --color-tint: var(--color-favorite-tint); } -.ion-color-twitter { - --ion-color-base: var(--ion-color-twitter); - --ion-color-base-rgb: var(--ion-color-twitter-rgb); - --ion-color-contrast: var(--ion-color-twitter-contrast); - --ion-color-contrast-rgb: var(--ion-color-twitter-contrast-rgb); - --ion-color-shade: var(--ion-color-twitter-shade); - --ion-color-tint: var(--ion-color-twitter-tint); +.color-twitter { + --color-base: var(--color-twitter); + --color-base-rgb: var(--color-twitter-rgb); + --color-contrast: var(--color-twitter-contrast); + --color-contrast-rgb: var(--color-twitter-contrast-rgb); + --color-shade: var(--color-twitter-shade); + --color-tint: var(--color-twitter-tint); } -.ion-color-google { - --ion-color-base: var(--ion-color-google); - --ion-color-base-rgb: var(--ion-color-google-rgb); - --ion-color-contrast: var(--ion-color-google-contrast); - --ion-color-contrast-rgb: var(--ion-color-google-contrast-rgb); - --ion-color-shade: var(--ion-color-google-shade); - --ion-color-tint: var(--ion-color-google-tint); +.color-google { + --color-base: var(--color-google); + --color-base-rgb: var(--color-google-rgb); + --color-contrast: var(--color-google-contrast); + --color-contrast-rgb: var(--color-google-contrast-rgb); + --color-shade: var(--color-google-shade); + --color-tint: var(--color-google-tint); } -.ion-color-instagram { - --ion-color-base: var(--ion-color-instagram); - --ion-color-base-rgb: var(--ion-color-instagram-rgb); - --ion-color-contrast: var(--ion-color-instagram-contrast); - --ion-color-contrast-rgb: var(--ion-color-instagram-contrast-rgb); - --ion-color-shade: var(--ion-color-instagram-shade); - --ion-color-tint: var(--ion-color-instagram-tint); +.color-instagram { + --color-base: var(--color-instagram); + --color-base-rgb: var(--color-instagram-rgb); + --color-contrast: var(--color-instagram-contrast); + --color-contrast-rgb: var(--color-instagram-contrast-rgb); + --color-shade: var(--color-instagram-shade); + --color-tint: var(--color-instagram-tint); } -.ion-color-vimeo { - --ion-color-base: var(--ion-color-vimeo); - --ion-color-base-rgb: var(--ion-color-vimeo-rgb); - --ion-color-contrast: var(--ion-color-vimeo-contrast); - --ion-color-contrast-rgb: var(--ion-color-vimeo-contrast-rgb); - --ion-color-shade: var(--ion-color-vimeo-shade); - --ion-color-tint: var(--ion-color-vimeo-tint); +.color-vimeo { + --color-base: var(--color-vimeo); + --color-base-rgb: var(--color-vimeo-rgb); + --color-contrast: var(--color-vimeo-contrast); + --color-contrast-rgb: var(--color-vimeo-contrast-rgb); + --color-shade: var(--color-vimeo-shade); + --color-tint: var(--color-vimeo-tint); } -.ion-color-facebook { - --ion-color-base: var(--ion-color-facebook); - --ion-color-base-rgb: var(--ion-color-facebook-rgb); - --ion-color-contrast: var(--ion-color-facebook-contrast); - --ion-color-contrast-rgb: var(--ion-color-facebook-contrast-rgb); - --ion-color-shade: var(--ion-color-facebook-shade); - --ion-color-tint: var(--ion-color-facebook-tint); +.color-facebook { + --color-base: var(--color-facebook); + --color-base-rgb: var(--color-facebook-rgb); + --color-contrast: var(--color-facebook-contrast); + --color-contrast-rgb: var(--color-facebook-contrast-rgb); + --color-shade: var(--color-facebook-shade); + --color-tint: var(--color-facebook-tint); } -.ion-color-github { - --ion-color-base: #211F1F; - --ion-color-base-rgb: 33,31,31; - --ion-color-contrast: #ffffff; - --ion-color-contrast-rgb: 255,255,255; - --ion-color-shade: #1d1b1b; - --ion-color-tint: #373535; +.color-github { + --color-base: #211F1F; + --color-base-rgb: 33,31,31; + --color-contrast: #ffffff; + --color-contrast-rgb: 255,255,255; + --color-shade: #1d1b1b; + --color-tint: #373535; } -.ion-color-instagram { - --ion-color-base: #9537BC; - --ion-color-base-rgb: 149,55,188; - --ion-color-contrast: #ffffff; - --ion-color-contrast-rgb: 255,255,255; - --ion-color-shade: #8330a5; - --ion-color-tint: #a04bc3; +.color-instagram { + --color-base: #9537BC; + --color-base-rgb: 149,55,188; + --color-contrast: #ffffff; + --color-contrast-rgb: 255,255,255; + --color-shade: #8330a5; + --color-tint: #a04bc3; } /* @@ -235,20 +230,20 @@ */ :root { - --ion-headings-font-weight: 300; - --ion-font-family: 'Omnes', "Helvetica Neue", "Arial", sans-serif; - - --ion-toolbar-background: white; - --ion-tab-bar-background: white; - --ion-color-angular: #ac282b; - --ion-color-communication: #8e8d93; - --ion-color-tooling: #fe4c52; - --ion-color-services: #fd8b2d; - --ion-color-design: #fed035; - --ion-color-workshop: #69bb7b; - --ion-color-food: #3bc7c4; - --ion-color-documentation: #b16be3; - --ion-color-navigation: #6600cc; + --headings-font-weight: 300; + --font-family: 'Omnes', "Helvetica Neue", "Arial", sans-serif; + + --toolbar-background: white; + --tab-bar-background: white; + --color-angular: #ac282b; + --color-communication: #8e8d93; + --color-tooling: #fe4c52; + --color-services: #fd8b2d; + --color-design: #fed035; + --color-workshop: #69bb7b; + --color-food: #3bc7c4; + --color-documentation: #b16be3; + --color-navigation: #6600cc; } /* @@ -285,68 +280,68 @@ */ body.dark { - --ion-color-primary: #428cff; - --ion-color-primary-rgb: 66,140,255; - --ion-color-primary-contrast: #ffffff; - --ion-color-primary-contrast-rgb: 255,255,255; - --ion-color-primary-shade: #3a7be0; - --ion-color-primary-tint: #5598ff; - - --ion-color-secondary: #50c8ff; - --ion-color-secondary-rgb: 80,200,255; - --ion-color-secondary-contrast: #ffffff; - --ion-color-secondary-contrast-rgb: 255,255,255; - --ion-color-secondary-shade: #46b0e0; - --ion-color-secondary-tint: #62ceff; - - --ion-color-tertiary: #6a64ff; - --ion-color-tertiary-rgb: 106,100,255; - --ion-color-tertiary-contrast: #ffffff; - --ion-color-tertiary-contrast-rgb: 255,255,255; - --ion-color-tertiary-shade: #5d58e0; - --ion-color-tertiary-tint: #7974ff; - - --ion-color-success: #2fdf75; - --ion-color-success-rgb: 47,223,117; - --ion-color-success-contrast: #000000; - --ion-color-success-contrast-rgb: 0,0,0; - --ion-color-success-shade: #29c467; - --ion-color-success-tint: #44e283; - - --ion-color-warning: #ffd534; - --ion-color-warning-rgb: 255,213,52; - --ion-color-warning-contrast: #000000; - --ion-color-warning-contrast-rgb: 0,0,0; - --ion-color-warning-shade: #e0bb2e; - --ion-color-warning-tint: #ffd948; - - --ion-color-danger: #ff4961; - --ion-color-danger-rgb: 255,73,97; - --ion-color-danger-contrast: #ffffff; - --ion-color-danger-contrast-rgb: 255,255,255; - --ion-color-danger-shade: #e04055; - --ion-color-danger-tint: #ff5b71; - - --ion-color-dark: #f4f5f8; - --ion-color-dark-rgb: 244,245,248; - --ion-color-dark-contrast: #000000; - --ion-color-dark-contrast-rgb: 0,0,0; - --ion-color-dark-shade: #d7d8da; - --ion-color-dark-tint: #f5f6f9; - - --ion-color-medium: #989aa2; - --ion-color-medium-rgb: 152,154,162; - --ion-color-medium-contrast: #000000; - --ion-color-medium-contrast-rgb: 0,0,0; - --ion-color-medium-shade: #86888f; - --ion-color-medium-tint: #a2a4ab; - - --ion-color-light: #222428; - --ion-color-light-rgb: 34,36,40; - --ion-color-light-contrast: #ffffff; - --ion-color-light-contrast-rgb: 255,255,255; - --ion-color-light-shade: #1e2023; - --ion-color-light-tint: #383a3e; + --color-primary: #428cff; + --color-primary-rgb: 66,140,255; + --color-primary-contrast: #ffffff; + --color-primary-contrast-rgb: 255,255,255; + --color-primary-shade: #3a7be0; + --color-primary-tint: #5598ff; + + --color-secondary: #50c8ff; + --color-secondary-rgb: 80,200,255; + --color-secondary-contrast: #ffffff; + --color-secondary-contrast-rgb: 255,255,255; + --color-secondary-shade: #46b0e0; + --color-secondary-tint: #62ceff; + + --color-tertiary: #6a64ff; + --color-tertiary-rgb: 106,100,255; + --color-tertiary-contrast: #ffffff; + --color-tertiary-contrast-rgb: 255,255,255; + --color-tertiary-shade: #5d58e0; + --color-tertiary-tint: #7974ff; + + --color-success: #2fdf75; + --color-success-rgb: 47,223,117; + --color-success-contrast: #000000; + --color-success-contrast-rgb: 0,0,0; + --color-success-shade: #29c467; + --color-success-tint: #44e283; + + --color-warning: #ffd534; + --color-warning-rgb: 255,213,52; + --color-warning-contrast: #000000; + --color-warning-contrast-rgb: 0,0,0; + --color-warning-shade: #e0bb2e; + --color-warning-tint: #ffd948; + + --color-danger: #ff4961; + --color-danger-rgb: 255,73,97; + --color-danger-contrast: #ffffff; + --color-danger-contrast-rgb: 255,255,255; + --color-danger-shade: #e04055; + --color-danger-tint: #ff5b71; + + --color-dark: #f4f5f8; + --color-dark-rgb: 244,245,248; + --color-dark-contrast: #000000; + --color-dark-contrast-rgb: 0,0,0; + --color-dark-shade: #d7d8da; + --color-dark-tint: #f5f6f9; + + --color-medium: #989aa2; + --color-medium-rgb: 152,154,162; + --color-medium-contrast: #000000; + --color-medium-contrast-rgb: 0,0,0; + --color-medium-shade: #86888f; + --color-medium-tint: #a2a4ab; + + --color-light: #222428; + --color-light-rgb: 34,36,40; + --color-light-contrast: #ffffff; + --color-light-contrast-rgb: 255,255,255; + --color-light-shade: #1e2023; + --color-light-tint: #383a3e; } /* @@ -355,37 +350,37 @@ body.dark { */ .ios body.dark { - --ion-background-color: #000000; - --ion-background-color-rgb: 0,0,0; - - --ion-text-color: #ffffff; - --ion-text-color-rgb: 255,255,255; - - --ion-color-step-50: #0d0d0d; - --ion-color-step-100: #1a1a1a; - --ion-color-step-150: #262626; - --ion-color-step-200: #333333; - --ion-color-step-250: #404040; - --ion-color-step-300: #4d4d4d; - --ion-color-step-350: #595959; - --ion-color-step-400: #666666; - --ion-color-step-450: #737373; - --ion-color-step-500: #808080; - --ion-color-step-550: #8c8c8c; - --ion-color-step-600: #999999; - --ion-color-step-650: #a6a6a6; - --ion-color-step-700: #b3b3b3; - --ion-color-step-750: #bfbfbf; - --ion-color-step-800: #cccccc; - --ion-color-step-850: #d9d9d9; - --ion-color-step-900: #e6e6e6; - --ion-color-step-950: #f2f2f2; - - --ion-toolbar-background: #0d0d0d; - - --ion-item-background: #000000; - - --ion-card-background: #1c1c1d; + --background-color: #000000; + --background-color-rgb: 0,0,0; + + --text-color: #ffffff; + --text-color-rgb: 255,255,255; + + --color-step-50: #0d0d0d; + --color-step-100: #1a1a1a; + --color-step-150: #262626; + --color-step-200: #333333; + --color-step-250: #404040; + --color-step-300: #4d4d4d; + --color-step-350: #595959; + --color-step-400: #666666; + --color-step-450: #737373; + --color-step-500: #808080; + --color-step-550: #8c8c8c; + --color-step-600: #999999; + --color-step-650: #a6a6a6; + --color-step-700: #b3b3b3; + --color-step-750: #bfbfbf; + --color-step-800: #cccccc; + --color-step-850: #d9d9d9; + --color-step-900: #e6e6e6; + --color-step-950: #f2f2f2; + + --toolbar-background: #0d0d0d; + + --item-background: #000000; + + --card-background: #1c1c1d; } @@ -395,39 +390,39 @@ body.dark { */ .md body.dark { - --ion-background-color: #121212; - --ion-background-color-rgb: 18,18,18; - - --ion-text-color: #ffffff; - --ion-text-color-rgb: 255,255,255; - - --ion-border-color: #222222; - - --ion-color-step-50: #1e1e1e; - --ion-color-step-100: #2a2a2a; - --ion-color-step-150: #363636; - --ion-color-step-200: #414141; - --ion-color-step-250: #4d4d4d; - --ion-color-step-300: #595959; - --ion-color-step-350: #656565; - --ion-color-step-400: #717171; - --ion-color-step-450: #7d7d7d; - --ion-color-step-500: #898989; - --ion-color-step-550: #949494; - --ion-color-step-600: #a0a0a0; - --ion-color-step-650: #acacac; - --ion-color-step-700: #b8b8b8; - --ion-color-step-750: #c4c4c4; - --ion-color-step-800: #d0d0d0; - --ion-color-step-850: #dbdbdb; - --ion-color-step-900: #e7e7e7; - --ion-color-step-950: #f3f3f3; - - --ion-item-background: #1e1e1e; - - --ion-toolbar-background: #1f1f1f; - - --ion-tab-bar-background: #1f1f1f; - - --ion-card-background: #1e1e1e; + --background-color: #121212; + --background-color-rgb: 18,18,18; + + --text-color: #ffffff; + --text-color-rgb: 255,255,255; + + --border-color: #222222; + + --color-step-50: #1e1e1e; + --color-step-100: #2a2a2a; + --color-step-150: #363636; + --color-step-200: #414141; + --color-step-250: #4d4d4d; + --color-step-300: #595959; + --color-step-350: #656565; + --color-step-400: #717171; + --color-step-450: #7d7d7d; + --color-step-500: #898989; + --color-step-550: #949494; + --color-step-600: #a0a0a0; + --color-step-650: #acacac; + --color-step-700: #b8b8b8; + --color-step-750: #c4c4c4; + --color-step-800: #d0d0d0; + --color-step-850: #dbdbdb; + --color-step-900: #e7e7e7; + --color-step-950: #f3f3f3; + + --item-background: #1e1e1e; + + --toolbar-background: #1f1f1f; + + --tab-bar-background: #1f1f1f; + + --card-background: #1e1e1e; } From db342f75135c1904bd0b481ff35b6a9866d908fa Mon Sep 17 00:00:00 2001 From: jlarson Date: Thu, 28 Mar 2024 14:14:37 -0500 Subject: [PATCH 2/2] Refactored out additional Ionic references, combined SignUpStep and the SignUp page --- src/App.tsx | 2 +- src/components/InputWrapper/index.scss | 14 +- src/components/InputWrapper/responsive.scss | 4 +- src/components/SignUpPageStep/desktop.scss | 17 -- src/components/SignUpPageStep/index.scss | 34 --- src/components/SignUpPageStep/index.test.tsx | 10 - src/components/SignUpPageStep/index.tsx | 236 ----------------- src/pages/PrivacyPolicy/index.scss | 2 +- src/pages/SignIn/{SignInEmail => }/index.scss | 4 +- .../SignIn/{SignInEmail => }/index.test.tsx | 4 +- src/pages/SignIn/{SignInEmail => }/index.tsx | 10 +- src/pages/SignUp/index.scss | 12 +- src/pages/SignUp/index.tsx | 239 +++++++++++++----- src/pages/SignUp/responsive.scss | 4 +- src/pages/TermsOfUse/index.scss | 2 +- 15 files changed, 209 insertions(+), 385 deletions(-) delete mode 100644 src/components/SignUpPageStep/desktop.scss delete mode 100644 src/components/SignUpPageStep/index.scss delete mode 100644 src/components/SignUpPageStep/index.test.tsx delete mode 100644 src/components/SignUpPageStep/index.tsx rename src/pages/SignIn/{SignInEmail => }/index.scss (74%) rename src/pages/SignIn/{SignInEmail => }/index.test.tsx (63%) rename src/pages/SignIn/{SignInEmail => }/index.tsx (90%) diff --git a/src/App.tsx b/src/App.tsx index 8237c5d8..2179c6b6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,7 +11,7 @@ import {BrowserRouter, Redirect, useHistory} from "react-router-dom"; /* Theme variables */ import './theme/main.scss' import Splash from "./pages/Splash"; -import SignInEmail from "./pages/SignIn/SignInEmail"; +import SignInEmail from "./pages/SignIn"; import SignUp from "./pages/SignUp"; diff --git a/src/components/InputWrapper/index.scss b/src/components/InputWrapper/index.scss index fa6c58d3..d6c571a0 100644 --- a/src/components/InputWrapper/index.scss +++ b/src/components/InputWrapper/index.scss @@ -1,23 +1,23 @@ .input-wrapper { margin: 8vw 0 5vw; &.rounded{ - ion-input{ + input{ border-radius: 5px; } } &.dark{ - ion-input{ - border-color: var(--ion-color-dark); + input{ + border-color: var(--color-dark); } } - ion-label { + label { padding-left: 5px; padding-bottom: 4px; margin-bottom: 0; display: block; } - ion-input { - border: 1px var(--ion-color-primary) solid; + input { + border: 1px var(--color-primary) solid; --padding-start: 3.125vw; } .subtext { @@ -27,7 +27,7 @@ } .plt-android { .input-wrapper { - ion-label { + label { padding-bottom: 3.5vw; } } diff --git a/src/components/InputWrapper/responsive.scss b/src/components/InputWrapper/responsive.scss index a8113b0c..4fab5b0b 100644 --- a/src/components/InputWrapper/responsive.scss +++ b/src/components/InputWrapper/responsive.scss @@ -2,7 +2,7 @@ @media screen and (min-width: 600px) { margin: 48px 0 30px; } - ion-input { + input { @media screen and (min-width: 576px) { --padding-start: 18px; } @@ -10,7 +10,7 @@ } .plt-android { .input-wrapper { - ion-label { + label { @media screen and (min-width: 600px) { padding-bottom: 21px; } diff --git a/src/components/SignUpPageStep/desktop.scss b/src/components/SignUpPageStep/desktop.scss deleted file mode 100644 index 2efe52d8..00000000 --- a/src/components/SignUpPageStep/desktop.scss +++ /dev/null @@ -1,17 +0,0 @@ -.sign-up-page { - ion-content { - .flex-wrapper { - .sign-up-disclaimer-wrapper { - .sign-up-disclaimer { - ion-icon { - @media screen and (min-width: 640px){ - font-size: 64px; - width: 160px; - margin: 0 16px 0 0; - } - } - } - } - } - } -} diff --git a/src/components/SignUpPageStep/index.scss b/src/components/SignUpPageStep/index.scss deleted file mode 100644 index fad84ae4..00000000 --- a/src/components/SignUpPageStep/index.scss +++ /dev/null @@ -1,34 +0,0 @@ -.sign-up-page { - ion-content { - --padding-top: 0; - .flex-wrapper { - display: flex; - flex-direction: column; - height: 100%; - .sign-up-disclaimer-wrapper { - flex-grow: 1; - align-items: flex-end; - - .sign-up-disclaimer { - display: flex; - clear: both; - margin-bottom: var(--ion-safe-area-bottom); - ion-icon { - font-size: 10vw; - width: 25vw; - margin: 0 2.5vw 0 0; - display: block; - flex-grow: 1; - } - p { - margin: 0; - } - } - .age-notice { - text-align: center; - } - } - } - } -} -@import "desktop"; diff --git a/src/components/SignUpPageStep/index.test.tsx b/src/components/SignUpPageStep/index.test.tsx deleted file mode 100644 index c7996d77..00000000 --- a/src/components/SignUpPageStep/index.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import SignUpPageStep from './index'; -import { shallow } from 'enzyme'; -import {MemoryRouter} from 'react-router'; - -test('renders AuthHeader without crashing', () => { - const baseElement = shallow(); - expect(baseElement).toBeDefined(); -}); diff --git a/src/components/SignUpPageStep/index.tsx b/src/components/SignUpPageStep/index.tsx deleted file mode 100644 index 960f8f9a..00000000 --- a/src/components/SignUpPageStep/index.tsx +++ /dev/null @@ -1,236 +0,0 @@ - -import AuthRequests from '../../services/requests/AuthRequests'; -import {RequestError} from '../../models/request-error'; -import React, {PropsWithChildren, useEffect, useState} from 'react'; -import ServerAlert from '../ServerAlert'; -import SignUpContextProvider, { - SignUpContext, - SignUpContextStateConsumer, - SignUpData -} from '../../contexts/signin/SignUpContext'; -import {ObjectSchema} from 'yup'; -import {useFormik} from 'formik'; -import './index.scss'; -import {useHistory} from 'react-router-dom'; - -export interface FormInformation { - setForm: (form: any) => void, - validationSchema: ObjectSchema, - onValidate: (submission: any) => string|undefined, -} - -export interface SignUpPageStepSharedProps { - header?: string, - formInformation?: FormInformation, - onNextStep?: () => void, - className?: string, - bypassSubmit?: boolean, - inputs?: HTMLInputElement[] -} - -export interface SignUpPageStepContentProps extends SignUpPageStepSharedProps { - form?: any - signUpContext: SignUpContextStateConsumer, - onSubmit: (formData: SignUpData) => void, - disclaimer?: string, -} - -const SignUpPageStepContent: React.FC> = ({className, onSubmit, form, bypassSubmit, disclaimer, header, signUpContext, children}) => { - - return ( -
-
-
- {header &&

{header}

} - {children} - {!bypassSubmit && -
form.handleSubmit() : () => onSubmit(signUpContext.data)} - className={form && form.isValid ? 'valid' : ''} - > - Next -
- } -
- {disclaimer && -
- -
-

{disclaimer}

-
-

- Must be 17 years or older to participate -

-
- } -
-
- ) -} - -interface SignUpPageStepContentWithFormProps extends SignUpPageStepContentProps { - formInformation: FormInformation, - setValidationError: (error: string) => void, -} - -const SignUpPageStepContentWithForm : React.FC = ({onSubmit, formInformation, setValidationError, inputs, signUpContext, ...rest}) => { - - const form: any = useFormik({ - initialValues: signUpContext.data, - initialErrors: {first_name: signUpContext.data.first_name}, - validationSchema: formInformation.validationSchema, - onSubmit: onSubmit, - }); - - useEffect(() => { - if (!form.isValid) { - - const currentError = formInformation.onValidate(form); - - setValidationError(currentError ? currentError : ''); - } - }, [form.isSubmitting, form.isValid]) - - useEffect( () => formInformation.setForm(form), [form.values]); - - const enterPressed = (event: any) => { - if (event.keyCode === 13 && !form.isSubmitting) { - form.handleSubmit(); - } - } - - useEffect(() => { - if (inputs) { - inputs.forEach(i => { - i.removeEventListener('keydown', enterPressed); - i.addEventListener('keydown', enterPressed) - }) - } - }, [inputs]) - - return ( - - ) -} - -export interface SignUpPageStepProps extends SignUpPageStepSharedProps { - cancelOnBack: boolean, - arrowColor?: string, - disclaimer?: string, - showArrow?: boolean, -} - -const SignUpPageStep : React.FC> = ({ arrowColor, showArrow, formInformation, onNextStep, cancelOnBack, ...rest}) => { - - const [validationError, setValidationError] = useState('') - const [requestError, setRequestError] = useState(undefined); - const history = useHistory(); - if (arrowColor) { - document.documentElement.style.setProperty("--arrow-color", arrowColor); - } - else { - document.documentElement.style.setProperty("--arrow-color", "var(--ion-color-light)"); - } - - const onSubmit = (formData: SignUpData, setData: (data: SignUpData) => void) => { - setData(formData); - if ( formData.phone && !formData.email ) { - duplicatePhoneCheck(formData.phone) - } else if ( formData.email ) { - duplicateEmailCheck(formData.email).then(() => { - setValidationError(''); - processSignUp(formData); - }) - } - } - - const cancel = () => { - if (cancelOnBack) { - window.location.replace('/welcome/splash') - } else { - history.goBack(); - } - } - - const duplicatePhoneCheck = async (phone: string) => { - try { - await AuthRequests.signUp({phone: phone} as SignUpData) - } catch (error: any) { - if ( error.data.errors.phone ) { - setValidationError('Phone number is already in use') - } - else if ( onNextStep ){ - onNextStep() - } - } - } - - const duplicateEmailCheck = async (email: string) => { - try { - await AuthRequests.signUp({email: email} as SignUpData) - } catch (error: any) { - if (error.data.errors.email) { - setValidationError('Email number is already in use') - return Promise.reject(); - } - return Promise.resolve(); - } - - } - - const processSignUp = async (signUpData: SignUpData) => { - - try { - await AuthRequests.signUp(signUpData) - const redirectUrl = localStorage.getItem('login_redirect'); - localStorage.removeItem('login_redirect'); - if (redirectUrl) { - history.push(redirectUrl); - } else { - history.push('/home') - } - } catch (error) { - setRequestError(error as RequestError) - } - } - - return ( -
-
cancel()}>Cancel
- - - {signUpContext => ( - - {formInformation ? - onSubmit(data, signUpContext.setData)} - {...rest} - /> : - onSubmit(data, signUpContext.setData)} - {...rest} - /> - } - {requestError && - setRequestError(undefined)} - /> - } - - )} - - -
- )} - -export default SignUpPageStep; diff --git a/src/pages/PrivacyPolicy/index.scss b/src/pages/PrivacyPolicy/index.scss index 3de8dd74..ad437a2c 100644 --- a/src/pages/PrivacyPolicy/index.scss +++ b/src/pages/PrivacyPolicy/index.scss @@ -1,6 +1,6 @@ #welcome-section { .privacy-page { - ion-content { + .content { --background: white; h3 { font-size: 22px; diff --git a/src/pages/SignIn/SignInEmail/index.scss b/src/pages/SignIn/index.scss similarity index 74% rename from src/pages/SignIn/SignInEmail/index.scss rename to src/pages/SignIn/index.scss index 5320698a..d2b51c95 100644 --- a/src/pages/SignIn/SignInEmail/index.scss +++ b/src/pages/SignIn/index.scss @@ -1,10 +1,10 @@ .welcome-section { .sign-up-page { - ion-router-link { + a { display: block; font-weight: normal; align-self: flex-start; - color: var(--ion-color-secondary); + color: var(--color-secondary); margin-bottom: 6.25vw; padding-left: 1.875vw; } diff --git a/src/pages/SignIn/SignInEmail/index.test.tsx b/src/pages/SignIn/index.test.tsx similarity index 63% rename from src/pages/SignIn/SignInEmail/index.test.tsx rename to src/pages/SignIn/index.test.tsx index 63131496..55df1623 100644 --- a/src/pages/SignIn/SignInEmail/index.test.tsx +++ b/src/pages/SignIn/index.test.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import SignInEmail from './index'; +import SignIn from './index'; import { shallow } from 'enzyme'; import { MemoryRouter } from 'react-router-dom'; test('renders without crashing', () => { - const baseElement = shallow(); + const baseElement = shallow(); expect(baseElement).toBeDefined(); }); diff --git a/src/pages/SignIn/SignInEmail/index.tsx b/src/pages/SignIn/index.tsx similarity index 90% rename from src/pages/SignIn/SignInEmail/index.tsx rename to src/pages/SignIn/index.tsx index 912945b6..e49abcc2 100644 --- a/src/pages/SignIn/SignInEmail/index.tsx +++ b/src/pages/SignIn/index.tsx @@ -2,14 +2,14 @@ import React, {useState} from 'react'; import { useFormik } from 'formik'; import * as Yup from 'yup'; -import AuthRequests, { LoginReq } from '../../../services/requests/AuthRequests'; -import InputWrapper from '../../../components/InputWrapper'; +import AuthRequests, { LoginReq } from '../../services/requests/AuthRequests'; +import InputWrapper from '../../components/InputWrapper'; import './index.scss'; import {Link, useHistory} from 'react-router-dom'; -interface SignInEmailProps { } +interface SignInProps { } -const SignInEmail: React.FC = ({}) => { +const SignIn: React.FC = ({}) => { const [credentialError, setCredentialError] = useState(false); const history = useHistory(); @@ -100,4 +100,4 @@ const SignInEmail: React.FC = ({}) => { ) } -export default SignInEmail +export default SignIn diff --git a/src/pages/SignUp/index.scss b/src/pages/SignUp/index.scss index a15eb1bd..5ece80d7 100644 --- a/src/pages/SignUp/index.scss +++ b/src/pages/SignUp/index.scss @@ -3,7 +3,7 @@ .email-signup-step { h3 { margin: 54px 6px 30px; - color: var(--ion-color-dark); + color: var(--color-dark); text-align: left; font-weight: normal; } @@ -12,9 +12,9 @@ border-bottom: 3px black solid; margin: 0 6px 16px 6px; --padding-start: 0; - ion-input { - color: var(--ion-color-medium); - --placeholder-color: var(--ion-color-light-shade); + input { + color: var(--color-medium); + --placeholder-color: var(--color-light-shade); --placeholder-font-weight: normal; --placeholder-opacity: 1; font-style: italic; @@ -25,7 +25,7 @@ border: none; margin: 48px 6px 0; --padding-start: 0; - ion-checkbox { + input[type='checkbox'] { vertical-align: top; margin-right: 5vw; padding-left: 0; @@ -33,7 +33,7 @@ .disclaimer { display: inline-block; font-style: italic; - color: var(--ion-color-medium); + color: var(--color-medium); text-align: left; padding-bottom: 32px; margin: 0; diff --git a/src/pages/SignUp/index.tsx b/src/pages/SignUp/index.tsx index ba5d9022..e58b65cd 100644 --- a/src/pages/SignUp/index.tsx +++ b/src/pages/SignUp/index.tsx @@ -1,14 +1,25 @@ -import React, {useRef, useState} from 'react' +import React, {useEffect, useRef, useState} from 'react' import * as Yup from 'yup' import './index.scss' -import SignUpPageStep, {FormInformation} from "../../components/SignUpPageStep"; import {useHistory} from 'react-router-dom'; -import PhoneNumberInput from "../../components/PhoneNumberInput"; +import SignUpContextProvider, { + SignUpContext, + SignUpContextStateConsumer, + SignUpData +} from "../../contexts/signin/SignUpContext"; +import ServerAlert from "../../components/ServerAlert"; +import {RequestError} from "../../models/request-error"; +import AuthRequests from "../../services/requests/AuthRequests"; +import {useFormik} from "formik"; -const SignUp: React.FC = () => { - const [form, setForm] = useState(undefined); - const history = useHistory(); +interface SignUpContentProps { + setValidationError: (error: string) => void, + signUpContext: SignUpContextStateConsumer, + onSubmit: (formData: SignUpData) => void, +} + +const SignUpContent: React.FC = ({signUpContext, onSubmit, setValidationError}) => { const emailInputRef = useRef(null) const firstNameInputRef = useRef(null) const lastNameInputRef = useRef(null) @@ -20,6 +31,13 @@ const SignUp: React.FC = () => { password: Yup.string().trim().required('Password is required'), }) + const inputs = [ + firstNameInputRef?.current, + lastNameInputRef?.current, + emailInputRef?.current, + passwordInputRef?.current, + ].filter(i => i)as HTMLInputElement[] + const validateForm = (submission: any) : string|undefined => { if (submission.errors.email && submission.touched.email) { return submission.errors.email @@ -30,78 +48,181 @@ const SignUp: React.FC = () => { else if (submission.errors.last_name && submission.touched.last_name) { return submission.errors.last_name; } + else if (submission.errors.password && submission.touched.password) { + return submission.errors.password; + } return undefined } - const formInformation: FormInformation = { - onValidate: validateForm, + const form: any = useFormik({ + initialValues: signUpContext.data, + initialErrors: {first_name: signUpContext.data.first_name}, validationSchema: SignUpSchema, - setForm, - } + onSubmit: onSubmit, + }); + + useEffect(() => { + if (!form.isValid) { + + const currentError = validateForm(form); + + setValidationError(currentError ? currentError : ''); + } + }, [form.isSubmitting, form.isValid]) - const onNextStep = () => { - history.push('/welcome/sign-up/date-of-birth'); + const enterPressed = (event: any) => { + if (event.keyCode === 13 && !form.isSubmitting) { + form.handleSubmit(); + } } + useEffect(() => { + if (inputs) { + inputs.forEach((i: any) => { + i.removeEventListener('keydown', enterPressed); + i.addEventListener('keydown', enterPressed) + }) + } + }, [inputs]) + return ( - i)as HTMLInputElement[]} - > - {form && +
+
-
- - -
-
+ + +
form.handleSubmit() : () => onSubmit(signUpContext.data)} + className={form && form.isValid ? 'valid' : ''} + > + Submit +
+
+
+ ) +} + +const SignUp: React.FC = () => { + + const [validationError, setValidationError] = useState('') + const [requestError, setRequestError] = useState(undefined); + const history = useHistory(); + + const onSubmit = (formData: SignUpData, setData: (data: SignUpData) => void) => { + setData(formData); + if ( formData.phone && !formData.email ) { + duplicatePhoneCheck(formData.phone) + } else if ( formData.email ) { + duplicateEmailCheck(formData.email).then(() => { + setValidationError(''); + processSignUp(formData); + }) + } + } + + const duplicatePhoneCheck = async (phone: string) => { + try { + await AuthRequests.signUp({phone: phone} as SignUpData) + } catch (error: any) { + if ( error.data.errors.phone ) { + setValidationError('Phone number is already in use') + } + } + } + + const duplicateEmailCheck = async (email: string) => { + try { + await AuthRequests.signUp({email: email} as SignUpData) + } catch (error: any) { + if (error.data.errors.email) { + setValidationError('Email number is already in use') + return Promise.reject(); + } + return Promise.resolve(); + } + + } + + const processSignUp = async (signUpData: SignUpData) => { + + try { + await AuthRequests.signUp(signUpData) + const redirectUrl = localStorage.getItem('login_redirect'); + localStorage.removeItem('login_redirect'); + if (redirectUrl) { + history.push(redirectUrl); + } else { + history.push('/home') } -
+ } catch (error) { + setRequestError(error as RequestError) + } + } + + const cancel = () => { + history.goBack(); + } + + return ( +
+
cancel()}>Cancel
+ + + {signUpContext => ( + + onSubmit(data, signUpContext.setData)} + /> + {requestError && + setRequestError(undefined)} + /> + } + + )} + + +
) } diff --git a/src/pages/SignUp/responsive.scss b/src/pages/SignUp/responsive.scss index ee375826..4828acaa 100644 --- a/src/pages/SignUp/responsive.scss +++ b/src/pages/SignUp/responsive.scss @@ -4,14 +4,14 @@ @media screen and (min-width: 514px) { .email-box { - ion-input { + input { font-size: 36px; } } } .disclaimer-checkbox { @media screen and (min-width: 480px) { - ion-checkbox { + input[type="checkbox"]{ margin-right: 24px; } } diff --git a/src/pages/TermsOfUse/index.scss b/src/pages/TermsOfUse/index.scss index 0c39a173..5b314d60 100644 --- a/src/pages/TermsOfUse/index.scss +++ b/src/pages/TermsOfUse/index.scss @@ -1,6 +1,6 @@ #welcome-section { .terms-page { - ion-content { + .content { --background: white; h3 { font-size: 22px;