diff --git a/002_source/ionic_mobile/public/assets/login2.jpeg b/002_source/ionic_mobile/public/assets/login2.jpeg new file mode 100644 index 0000000..366dcf8 Binary files /dev/null and b/002_source/ionic_mobile/public/assets/login2.jpeg differ diff --git a/002_source/ionic_mobile/src/RouteConfig.tsx b/002_source/ionic_mobile/src/RouteConfig.tsx index 1cee9b3..87df540 100644 --- a/002_source/ionic_mobile/src/RouteConfig.tsx +++ b/002_source/ionic_mobile/src/RouteConfig.tsx @@ -21,6 +21,10 @@ import ConnectivesWordPage from './pages/Favorite/ConnectivesWordPage'; import FavVocabularyPage from './pages/Favorite/Vocabulary'; import FavoriteVocabularyPage from './pages/Favorite/WordPage'; import ConnectivesPage from './pages/Lesson/ConnectivesPage'; +import AuthHome from './pages/auth/Home'; +import { AuthLogin } from './pages/auth/Login'; +import { AuthSignUp } from './pages/auth/SignUp'; + import Lesson from './pages/Lesson/index'; // NOTES: old version using json file @@ -156,6 +160,18 @@ function RouteConfig() { + + + + + + + + + + + + {/* TODO: remove below */} diff --git a/002_source/ionic_mobile/src/components/Action/index.tsx b/002_source/ionic_mobile/src/components/Action/index.tsx new file mode 100644 index 0000000..c2335ac --- /dev/null +++ b/002_source/ionic_mobile/src/components/Action/index.tsx @@ -0,0 +1,21 @@ +import { IonCol, IonRouterLink, IonRow } from '@ionic/react'; + +function Action(props: { message: string; text: string; link: string }): React.JSX.Element { + return ( + <> + + + + {props.message} + + {' '} + {props.text} → + + + + + > + ); +} + +export { Action }; diff --git a/002_source/ionic_mobile/src/components/CustomField/index.tsx b/002_source/ionic_mobile/src/components/CustomField/index.tsx new file mode 100644 index 0000000..579cb5d --- /dev/null +++ b/002_source/ionic_mobile/src/components/CustomField/index.tsx @@ -0,0 +1,40 @@ +import { IonInput, IonLabel } from '@ionic/react'; +import styles from './style.module.scss'; + +function CustomField({ + field, + errors, +}: { + field: { + id: string; + label: string; + required: boolean; + input: { + props: { type: string; placeholder: string }; + state: { + value: string; + reset: (newValue: any) => void; + onIonChange: (e: any) => Promise; + onKeyUp: (e: any) => Promise; + }; + }; + }; + errors: any; +}): React.JSX.Element { + const error = errors && errors.filter((e) => e.id === field.id)[0]; + const errorMessage = error && errors.filter((e) => e.id === field.id)[0].message; + + return ( + <> + + + {field.label} + {error && {errorMessage}} + + + + > + ); +} + +export { CustomField }; diff --git a/002_source/ionic_mobile/src/components/CustomField/style.module.scss b/002_source/ionic_mobile/src/components/CustomField/style.module.scss new file mode 100644 index 0000000..5171b51 --- /dev/null +++ b/002_source/ionic_mobile/src/components/CustomField/style.module.scss @@ -0,0 +1,29 @@ +.field:not(:last-child) { + margin-bottom: 1rem !important; +} + +.field { + ion-label { + padding-left: 0.2rem; + padding-right: 0.5rem; + color: #d3a6c7; + display: flex; + justify-content: space-between; + align-content: center; + align-items: center; + + p { + color: rgb(236, 149, 35); + } + } +} +.customInput { + // --background: #834e76; + --padding-bottom: 1rem; + --padding-top: 1rem; + --padding-start: 1rem; + --padding-end: 1rem; + border-radius: 10px; + margin-top: 0.25rem; + transition: all 0.2s linear; +} diff --git a/002_source/ionic_mobile/src/components/Wave/index.tsx b/002_source/ionic_mobile/src/components/Wave/index.tsx new file mode 100644 index 0000000..e574cca --- /dev/null +++ b/002_source/ionic_mobile/src/components/Wave/index.tsx @@ -0,0 +1,9 @@ +export const Wave = () => ( + + + +); diff --git a/002_source/ionic_mobile/src/data/fields.tsx b/002_source/ionic_mobile/src/data/fields.tsx new file mode 100644 index 0000000..a5a3399 --- /dev/null +++ b/002_source/ionic_mobile/src/data/fields.tsx @@ -0,0 +1,71 @@ +import { useFormInput } from './utils'; + +export const useSignupFields = () => { + return [ + { + id: 'name', + label: 'Name', + required: true, + input: { + props: { + type: 'text', + placeholder: 'Joe Bloggs', + }, + state: useFormInput(''), + }, + }, + { + id: 'email', + label: 'Email', + required: true, + input: { + props: { + type: 'email', + placeholder: 'joe@bloggs.com', + }, + state: useFormInput(''), + }, + }, + { + id: 'password', + label: 'Password', + required: true, + input: { + props: { + type: 'password', + placeholder: '*********', + }, + state: useFormInput(''), + }, + }, + ]; +}; + +export const useLoginFields = () => { + return [ + { + id: 'email', + label: 'Email', + required: true, + input: { + props: { + type: 'email', + placeholder: 'joe@bloggs.com', + }, + state: useFormInput(''), + }, + }, + { + id: 'password', + label: 'Password', + required: true, + input: { + props: { + type: 'password', + placeholder: '*******', + }, + state: useFormInput(''), + }, + }, + ]; +}; diff --git a/002_source/ionic_mobile/src/data/utils.tsx b/002_source/ionic_mobile/src/data/utils.tsx new file mode 100644 index 0000000..0388ed6 --- /dev/null +++ b/002_source/ionic_mobile/src/data/utils.tsx @@ -0,0 +1,38 @@ +import { useState } from 'react'; + +export const useFormInput = (initialValue = '') => { + const [value, setValue] = useState(initialValue); + + const handleChange = async (e) => { + const tempValue = await e.currentTarget.value; + setValue(tempValue); + }; + + return { + value, + reset: (newValue) => setValue(newValue), + onIonChange: handleChange, + onKeyUp: handleChange, + }; +}; + +export const validateForm = (fields) => { + let errors = []; + + fields.forEach((field) => { + if (field.required) { + const fieldValue = field.input.state.value; + + if (fieldValue === '') { + const error = { + id: field.id, + message: `Please check your ${field.id}`, + }; + + errors.push(error); + } + } + }); + + return errors; +}; diff --git a/002_source/ionic_mobile/src/pages/auth/Home/index.tsx b/002_source/ionic_mobile/src/pages/auth/Home/index.tsx new file mode 100644 index 0000000..ef8041b --- /dev/null +++ b/002_source/ionic_mobile/src/pages/auth/Home/index.tsx @@ -0,0 +1,56 @@ +import { + IonButton, + IonCardTitle, + IonCol, + IonContent, + IonFooter, + IonGrid, + IonHeader, + IonImg, + IonPage, + IonRouterLink, + IonRow, + IonToolbar, +} from '@ionic/react'; +// import { Action } from '../components/Action'; +import styles from './style.module.scss'; +import { Action } from '../../../components/Action'; + +const AuthHome = () => { + return ( + + + {/* */} + + {/* */} + + + + + + + Join millions of other people discovering their creative side + + + + + + + Get started → + + + + + + + + + + + + + + ); +}; + +export default AuthHome; diff --git a/002_source/ionic_mobile/src/pages/auth/Home/style.module.scss b/002_source/ionic_mobile/src/pages/auth/Home/style.module.scss new file mode 100644 index 0000000..4d46306 --- /dev/null +++ b/002_source/ionic_mobile/src/pages/auth/Home/style.module.scss @@ -0,0 +1,41 @@ +.homePage { + ion-header { + ion-img { + border-bottom: 3px solid rgb(236, 149, 35); + } + } + + ion-footer { + // background-color: #7c3b6a; + color: white; + } +} + +.getStarted { + height: 100%; + background-color: #ffffff; + background-image: radial-gradient(#b8b8b8 1px, transparent 1px), radial-gradient(#b8b8b8 1px, #ffffff 1px); + background-size: 40px 40px; + background-position: + 0 0, + 20px 20px; + + ion-card-title { + color: black !important; + letter-spacing: -0.08rem; + font-weight: 900 !important; + } +} + +.heading { + margin-top: 7rem; +} + +.getStartedButton { + font-size: 1.2rem; + margin-top: 1rem; +} + +.helloworld { + color: gold; +} diff --git a/002_source/ionic_mobile/src/pages/auth/Login/index.tsx b/002_source/ionic_mobile/src/pages/auth/Login/index.tsx new file mode 100644 index 0000000..d9ed6aa --- /dev/null +++ b/002_source/ionic_mobile/src/pages/auth/Login/index.tsx @@ -0,0 +1,93 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCardTitle, + IonCol, + IonContent, + IonFooter, + IonGrid, + IonHeader, + IonIcon, + IonImg, + IonInput, + IonInputPasswordToggle, + IonPage, + IonRouterLink, + IonRow, + IonText, + IonToolbar, +} from '@ionic/react'; +import styles from './style.module.scss'; + +import { arrowBack, shapesOutline } from 'ionicons/icons'; +import { CustomField } from '../../../components/CustomField'; +import { useLoginFields } from '../../../data/fields'; + +import { Action } from '../../../components/Action'; +import { Wave } from '../../../components/Wave'; +import { useEffect, useState } from 'react'; + +import { useParams } from 'react-router'; + +function AuthLogin(): React.JSX.Element { + const params = useParams(); + const [errors, setErrors] = useState(false); + + const login = () => {}; + + return ( + + + + + + + + + + + + + + + {/* */} + + + + + Log in + Welcome back, hope you're doing well + + + + + + + + Email (Required) + + + + + + + + Login + + + + + + {/* */} + + + + + + + + ); +} + +export { AuthLogin }; diff --git a/002_source/ionic_mobile/src/pages/auth/Login/style.module.scss b/002_source/ionic_mobile/src/pages/auth/Login/style.module.scss new file mode 100644 index 0000000..b66bd24 --- /dev/null +++ b/002_source/ionic_mobile/src/pages/auth/Login/style.module.scss @@ -0,0 +1,17 @@ +.loginPage { + ion-toolbar { + --border-style: none; + --border-color: transparent; + --padding-top: 1rem; + --padding-bottom: 1rem; + --padding-start: 1rem; + --padding-end: 1rem; + } +} + +.headingText { + h5 { + margin-top: 0.2rem; + // color: #d3a6c7; + } +} diff --git a/002_source/ionic_mobile/src/pages/auth/SignUp/index.tsx b/002_source/ionic_mobile/src/pages/auth/SignUp/index.tsx new file mode 100644 index 0000000..082a7b4 --- /dev/null +++ b/002_source/ionic_mobile/src/pages/auth/SignUp/index.tsx @@ -0,0 +1,95 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCardTitle, + IonCol, + IonContent, + IonFooter, + IonGrid, + IonHeader, + IonIcon, + IonImg, + IonInput, + IonInputPasswordToggle, + IonPage, + IonRouterLink, + IonRow, + IonText, + IonToolbar, +} from '@ionic/react'; +import styles from './style.module.scss'; + +import { arrowBack, shapesOutline } from 'ionicons/icons'; +import { CustomField } from '../../../components/CustomField'; +import { useLoginFields, useSignupFields } from '../../../data/fields'; + +import { Action } from '../../../components/Action'; +import { Wave } from '../../../components/Wave'; +import { useEffect, useState } from 'react'; + +import { useParams } from 'react-router'; + +function AuthSignUp(): React.JSX.Element { + const params = useParams(); + const fields = useSignupFields(); + const [errors, setErrors] = useState(false); + + const login = () => {}; + + function createAccount() {} + + return ( + + + + + + + + + + + + + + + {/* */} + + + + + Sign up + Lets get to know each other + + + + + + {fields.map((field, i) => { + return ( + + + + ); + })} + + + Create account + + + + + + {/* */} + + + + + + + + ); +} + +export { AuthSignUp }; diff --git a/002_source/ionic_mobile/src/pages/auth/SignUp/style.module.scss b/002_source/ionic_mobile/src/pages/auth/SignUp/style.module.scss new file mode 100644 index 0000000..14ee87f --- /dev/null +++ b/002_source/ionic_mobile/src/pages/auth/SignUp/style.module.scss @@ -0,0 +1,17 @@ +.signupPage { + ion-toolbar { + --border-style: none; + --border-color: transparent; + --padding-top: 1rem; + --padding-bottom: 1rem; + --padding-start: 1rem; + --padding-end: 1rem; + } +} + +.headingText { + h5 { + margin-top: 0.2rem; + // color: #d3a6c7; + } +}
+ {props.message} + + {' '} + {props.text} → + +
{errorMessage}