update demo-react-login,
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
import { IonCol, IonRouterLink, IonRow } from '@ionic/react';
|
||||||
|
|
||||||
|
interface ActionProps {
|
||||||
|
message: string;
|
||||||
|
text: string;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Action = (props: ActionProps): React.JSX.Element => (
|
||||||
|
<IonRow className="ion-text-center ion-justify-content-center">
|
||||||
|
<IonCol size="12">
|
||||||
|
<p>
|
||||||
|
{props.message}
|
||||||
|
<IonRouterLink className="custom-link" routerLink={props.link}>
|
||||||
|
{' '}
|
||||||
|
{props.text} →
|
||||||
|
</IonRouterLink>
|
||||||
|
</p>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
);
|
@@ -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;
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
import { IonInput, IonLabel } from '@ionic/react';
|
||||||
|
import styles from './CustomField.module.scss';
|
||||||
|
|
||||||
|
interface FieldType {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
input: {
|
||||||
|
props: any;
|
||||||
|
state: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
id: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CustomFieldProps {
|
||||||
|
field: FieldType;
|
||||||
|
errors?: ErrorType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomField = ({ field, errors }: CustomFieldProps): 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 (
|
||||||
|
<div className={styles.field}>
|
||||||
|
<IonLabel className={styles.fieldLabel}>
|
||||||
|
{field.label}
|
||||||
|
{error && <p className="animate__animated animate__bounceIn">{errorMessage}</p>}
|
||||||
|
</IonLabel>
|
||||||
|
<IonInput className={styles.customInput} {...field.input.props} {...field.input.state} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomField;
|
@@ -0,0 +1,13 @@
|
|||||||
|
export const Wave = (): React.JSX.Element => (
|
||||||
|
<svg
|
||||||
|
style={{ marginBottom: '-0.5rem' }}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 1440 320"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#7a506f"
|
||||||
|
fill-opacity="1"
|
||||||
|
d="M0,288L40,277.3C80,267,160,245,240,224C320,203,400,181,480,176C560,171,640,181,720,181.3C800,181,880,171,960,144C1040,117,1120,75,1200,58.7C1280,43,1360,53,1400,58.7L1440,64L1440,320L1400,320C1360,320,1280,320,1200,320C1120,320,1040,320,960,320C880,320,800,320,720,320C640,320,560,320,480,320C400,320,320,320,240,320C160,320,80,320,40,320L0,320Z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
82
03_source/mobile/src/pages/DemoReactLogin/data/fields.tsx
Normal file
82
03_source/mobile/src/pages/DemoReactLogin/data/fields.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
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("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
46
03_source/mobile/src/pages/DemoReactLogin/data/utils.tsx
Normal file
46
03_source/mobile/src/pages/DemoReactLogin/data/utils.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
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;
|
||||||
|
}
|
@@ -1,28 +1,33 @@
|
|||||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
import { IonRouterOutlet, IonTabs } from '@ionic/react';
|
||||||
|
|
||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
import './theme/variables.scss';
|
||||||
import Tab2 from './AppPages/Tab2';
|
|
||||||
|
|
||||||
import './style.scss';
|
import Home from './pages/Home';
|
||||||
|
import Login from './pages/Login';
|
||||||
|
import Signup from './pages/Signup';
|
||||||
|
|
||||||
function DemoReactLogin() {
|
function DemoReactLogin() {
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs>
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-react-login/tab1">
|
<Route exact path="/demo-react-login/home">
|
||||||
<Tab1 />
|
<Home />
|
||||||
</Route>
|
|
||||||
<Route exact path="/demo-react-login/tab2">
|
|
||||||
<Tab2 />
|
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Redirect exact path="/demo-react-login" to="/demo-react-login/tab1" />
|
<Route exact path="/demo-react-login/signup">
|
||||||
|
<Signup />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route exact path="/demo-react-login/login">
|
||||||
|
<Login />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Redirect exact path="/demo-react-login" to="/demo-react-login/home" />
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
|
|
||||||
{/* */}
|
{/*
|
||||||
<IonTabBar slot="bottom">
|
<IonTabBar slot="bottom">
|
||||||
<IonTabButton tab="tab1" href="/demo-react-login/tab1">
|
<IonTabButton tab="tab1" href="/demo-react-login/tab1">
|
||||||
<IonIcon icon={cloudOutline} />
|
<IonIcon icon={cloudOutline} />
|
||||||
@@ -33,6 +38,7 @@ function DemoReactLogin() {
|
|||||||
<IonLabel>Search</IonLabel>
|
<IonLabel>Search</IonLabel>
|
||||||
</IonTabButton>
|
</IonTabButton>
|
||||||
</IonTabBar>
|
</IonTabBar>
|
||||||
|
*/}
|
||||||
</IonTabs>
|
</IonTabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
9
03_source/mobile/src/pages/DemoReactLogin/module.d.ts
vendored
Normal file
9
03_source/mobile/src/pages/DemoReactLogin/module.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
declare module '*.module.css' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.module.scss' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
.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;
|
||||||
|
}
|
59
03_source/mobile/src/pages/DemoReactLogin/pages/Home.tsx
Normal file
59
03_source/mobile/src/pages/DemoReactLogin/pages/Home.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonImg,
|
||||||
|
IonPage,
|
||||||
|
IonRouterLink,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { Action } from '../components/Action';
|
||||||
|
import styles from './Home.module.scss';
|
||||||
|
|
||||||
|
const Home = (): React.JSX.Element => {
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.homePage}>
|
||||||
|
<IonHeader>
|
||||||
|
{/* <IonToolbar className="ion-no-margin ion-no-padding"> */}
|
||||||
|
<IonImg src="/assets/DemoReactLogin/login2.jpeg" />
|
||||||
|
{/* </IonToolbar> */}
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<div className={styles.getStarted}>
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow className={`ion-text-center ion-justify-content-center ${styles.heading}`}>
|
||||||
|
<IonCol size="11" className={styles.headingText}>
|
||||||
|
<IonCardTitle>
|
||||||
|
Join millions of other people discovering their creative side
|
||||||
|
</IonCardTitle>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className={`ion-text-center ion-justify-content-center`}>
|
||||||
|
<IonRouterLink routerLink="/signup" className="custom-link">
|
||||||
|
<IonCol size="11">
|
||||||
|
<IonButton className={`${styles.getStartedButton} custom-button`}>
|
||||||
|
Get started →
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRouterLink>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</div>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid>
|
||||||
|
<Action message="Already got an account?" text="Login" link="/login" />
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
112
03_source/mobile/src/pages/DemoReactLogin/pages/Login.tsx
Normal file
112
03_source/mobile/src/pages/DemoReactLogin/pages/Login.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonPage,
|
||||||
|
IonRouterLink,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import styles from './Login.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 { validateForm } from '../data/utils';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
|
interface FieldType {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
input: {
|
||||||
|
props: any;
|
||||||
|
state: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
id: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Login = (): React.JSX.Element => {
|
||||||
|
const params = useParams();
|
||||||
|
|
||||||
|
const fields: FieldType[] = useLoginFields();
|
||||||
|
const [errors, setErrors] = useState<ErrorType[] | false>(false);
|
||||||
|
|
||||||
|
const login = (): void => {
|
||||||
|
const errors = validateForm(fields);
|
||||||
|
setErrors(errors);
|
||||||
|
|
||||||
|
if (!errors.length) {
|
||||||
|
// Submit your form here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
fields.forEach((field) => field.input.state.reset(''));
|
||||||
|
setErrors(false);
|
||||||
|
};
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.loginPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton icon={arrowBack} text="" className="custom-back" />
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonButton className="custom-button">
|
||||||
|
<IonIcon icon={shapesOutline} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid className="ion-padding">
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12" className={styles.headingText}>
|
||||||
|
<IonCardTitle>Log in</IonCardTitle>
|
||||||
|
<h5>Welcome back, hope you're doing well</h5>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className="ion-margin-top ion-padding-top">
|
||||||
|
<IonCol size="12">
|
||||||
|
{fields.map((field) => {
|
||||||
|
return <CustomField field={field} errors={errors} />;
|
||||||
|
})}
|
||||||
|
|
||||||
|
<IonButton className="custom-button" expand="block" onClick={login}>
|
||||||
|
Login
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid className="ion-no-margin ion-no-padding">
|
||||||
|
<Action message="Don't have an account?" text="Sign up" link="/signup" />
|
||||||
|
<Wave />
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
111
03_source/mobile/src/pages/DemoReactLogin/pages/Signup.tsx
Normal file
111
03_source/mobile/src/pages/DemoReactLogin/pages/Signup.tsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonPage,
|
||||||
|
IonRouterLink,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import styles from './Signup.module.scss';
|
||||||
|
|
||||||
|
import { arrowBack, shapesOutline } from 'ionicons/icons';
|
||||||
|
import CustomField from '../components/CustomField';
|
||||||
|
import { useSignupFields } from '../data/fields';
|
||||||
|
import { Action } from '../components/Action';
|
||||||
|
import { Wave } from '../components/Wave';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { validateForm } from '../data/utils';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
|
interface FieldType {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
input: {
|
||||||
|
props: any;
|
||||||
|
state: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
id: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Signup = (): React.JSX.Element => {
|
||||||
|
const params = useParams();
|
||||||
|
const fields: FieldType[] = useSignupFields();
|
||||||
|
const [errors, setErrors] = useState<ErrorType[] | false>(false);
|
||||||
|
|
||||||
|
const createAccount = (): void => {
|
||||||
|
const errors = validateForm(fields);
|
||||||
|
setErrors(errors);
|
||||||
|
|
||||||
|
if (!errors.length) {
|
||||||
|
// Submit your form here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
fields.forEach((field) => field.input.state.reset(''));
|
||||||
|
setErrors(false);
|
||||||
|
};
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.signupPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton icon={arrowBack} text="" className="custom-back" />
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonButton className="custom-button">
|
||||||
|
<IonIcon icon={shapesOutline} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid className="ion-padding">
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12" className={styles.headingText}>
|
||||||
|
<IonCardTitle>Sign up</IonCardTitle>
|
||||||
|
<h5>Lets get to know each other</h5>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className="ion-margin-top ion-padding-top">
|
||||||
|
<IonCol size="12">
|
||||||
|
{fields.map((field) => {
|
||||||
|
return <CustomField field={field} errors={errors} />;
|
||||||
|
})}
|
||||||
|
|
||||||
|
<IonButton className="custom-button" expand="block" onClick={createAccount}>
|
||||||
|
Create account
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid className="ion-no-margin ion-no-padding">
|
||||||
|
<Action message="Already got an account?" text="Login" link="/login" />
|
||||||
|
<Wave />
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Signup;
|
@@ -0,0 +1,19 @@
|
|||||||
|
import { Store } from "pullstate";
|
||||||
|
|
||||||
|
const AccountStore = new Store({
|
||||||
|
|
||||||
|
logged_in: false,
|
||||||
|
coffee_ids: []
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AccountStore;
|
||||||
|
|
||||||
|
// export const addToCart = (coffeeID) => {
|
||||||
|
|
||||||
|
// CartStore.update(s => { s.coffee_ids = [ ...s.coffee_ids, `${ parseInt(coffeeID) }` ]; });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const removeFromCart = coffeeIndex => {
|
||||||
|
|
||||||
|
// CartStore.update(s => { s.coffee_ids.splice(coffeeIndex, 1) });
|
||||||
|
// }
|
@@ -0,0 +1,13 @@
|
|||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const getState = state => state;
|
||||||
|
|
||||||
|
// General getters
|
||||||
|
// export const getCoffees = createSelector(getState, state => state.coffees);
|
||||||
|
// export const getOffers = createSelector(getState, state => state.offers);
|
||||||
|
// export const getCoffeeSizes = createSelector(getState, state => state.sizes);
|
||||||
|
// export const getCartCoffees = createSelector(getState, state => state.coffee_ids);
|
||||||
|
// export const getFavouriteCoffees = createSelector(getState, state => state.coffee_ids);
|
||||||
|
|
||||||
|
// // More specific getters
|
||||||
|
// export const getCoffee = id => createSelector(getState, state => state.coffees.filter(c => parseInt(c.id) === parseInt(id))[0]);
|
@@ -0,0 +1 @@
|
|||||||
|
export { default as AccountStore } from "./AccountStore";
|
@@ -1,103 +0,0 @@
|
|||||||
#about-page {
|
|
||||||
ion-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
--background: transparent;
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
|
||||||
ion-toolbar ion-button,
|
|
||||||
ion-toolbar ion-menu-button {
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .about-image {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
transition: opacity 500ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .madison {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .austin {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .chicago {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .seattle {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--ion-background-color, #fff);
|
|
||||||
z-index: 2; // display rounded border above header image
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info p {
|
|
||||||
line-height: 130%;
|
|
||||||
|
|
||||||
color: var(--ion-color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-icon {
|
|
||||||
margin-inline-end: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .about-info {
|
|
||||||
--ion-padding: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .about-info h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user