Skip to content
This repository was archived by the owner on May 1, 2018. It is now read-only.

Commit 19e8fcd

Browse files
committed
add AvatarSelector; close #5
1 parent 2cbb18b commit 19e8fcd

File tree

7 files changed

+231
-56
lines changed

7 files changed

+231
-56
lines changed

.env.development

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
NODE_ENV="development"
2+
REACT_APP_APP_URL="http://localhost:5000/"
23
REACT_APP_API_PATH="http://localhost:5000/api/"

.env.production

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
NODE_ENV="production"
2+
REACT_APP_APP_URL="/"
23
REACT_APP_API_PATH="/api/"
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as React from 'react';
2+
import Paper from 'material-ui/Paper';
3+
import MobileStepper from 'material-ui/MobileStepper';
4+
import Button from 'material-ui/Button';
5+
import KeyboardArrowLeft from 'material-ui-icons/KeyboardArrowLeft';
6+
import KeyboardArrowRight from 'material-ui-icons/KeyboardArrowRight';
7+
8+
interface AvatarSelectorProps {
9+
onPickAvatar: (avatar: string) => void;
10+
}
11+
interface AvatarSelectorState {
12+
ActiveStep: number;
13+
Avatar: number;
14+
}
15+
16+
class AvatarSelector extends React.PureComponent<AvatarSelectorProps, AvatarSelectorState> {
17+
private imagesPerPage = 20;
18+
private imagesPages = 5;
19+
20+
constructor(props: AvatarSelectorProps) {
21+
super(props);
22+
this.handleStep = this.handleStep.bind(this);
23+
this.handleClick = this.handleClick.bind(this);
24+
this.state = {
25+
ActiveStep: 0,
26+
Avatar: 0
27+
};
28+
}
29+
30+
handleClick(e: React.MouseEvent<HTMLImageElement>) {
31+
this.setState({Avatar: +e.currentTarget.alt});
32+
this.props.onPickAvatar(e.currentTarget.alt + '.jpg');
33+
}
34+
35+
handleStep(e: React.MouseEvent<HTMLFormElement>) {
36+
if (e.currentTarget.name === 'next') {
37+
this.setState((prevState) => ({ActiveStep: prevState.ActiveStep + 1}));
38+
} else {
39+
this.setState((prevState) => ({ActiveStep: prevState.ActiveStep - 1}));
40+
}
41+
this.setState((prevState) => {
42+
const Avatar = prevState.ActiveStep * this.imagesPerPage;
43+
this.props.onPickAvatar(Avatar + '.jpg');
44+
return {Avatar};
45+
});
46+
}
47+
48+
render() {
49+
const { ActiveStep, Avatar } = this.state;
50+
const backButton = (
51+
<Button size="small" onClick={this.handleStep} disabled={this.state.ActiveStep === 0} name="back">
52+
<KeyboardArrowLeft />
53+
Back
54+
</Button>
55+
);
56+
const nextButton = (
57+
<Button size="small" onClick={this.handleStep} disabled={ActiveStep === this.imagesPages - 1} name="next">
58+
Next
59+
<KeyboardArrowRight />
60+
</Button>
61+
);
62+
const imgStyle = {borderRadius: '7px', border: '3px solid #00f'};
63+
const imgPath = `${process.env.REACT_APP_APP_URL}images/avatars/`;
64+
65+
return (
66+
<React.Fragment>
67+
<Paper style={{textAlign: 'center', padding: '10px 0 5px', cursor: 'pointer'}}>
68+
{
69+
Array.from(Array(20).keys())
70+
.map(i => (
71+
<img
72+
alt={(ActiveStep * this.imagesPerPage + i).toString()}
73+
onClick={this.handleClick}
74+
key={ActiveStep * this.imagesPerPage + i}
75+
src={`${imgPath}${ActiveStep * this.imagesPerPage + i}.jpg`}
76+
height="50"
77+
width="50"
78+
style={ActiveStep * this.imagesPerPage + i === Avatar ? imgStyle : {}}
79+
/>))
80+
}
81+
</Paper>
82+
<MobileStepper
83+
variant="dots"
84+
steps={this.imagesPages}
85+
position="static"
86+
activeStep={ActiveStep}
87+
nextButton={nextButton}
88+
backButton={backButton}
89+
/>
90+
</React.Fragment>
91+
);
92+
}
93+
}
94+
95+
export default AvatarSelector;

src/components/Shared/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
export * from './Helper';
2+
3+
import AvatarSelector from './AvatarSelector';
4+
export { AvatarSelector };

src/components/forms/AuthForm.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class AuthForm extends React.PureComponent<AuthFormProps, AuthFormState> {
8181
onChange={this.handleChange}
8282
/>
8383
<br />
84+
<br />
8485
<TextField
8586
error={!!PasswordError}
8687
helperText={PasswordError ? PasswordError : 'Enter your Password'}

src/components/forms/SignUpForm.tsx

Lines changed: 129 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ import Button from 'material-ui/Button';
33
import Divider from 'material-ui/Divider';
44
import TextField from 'material-ui/TextField';
55
import Typography from 'material-ui/Typography';
6+
import MobileStepper from 'material-ui/MobileStepper';
67
import Card, { CardActions, CardContent } from 'material-ui/Card';
8+
import KeyboardArrowLeft from 'material-ui-icons/KeyboardArrowLeft';
9+
import KeyboardArrowRight from 'material-ui-icons/KeyboardArrowRight';
710
import withStyles, { WithStyles, StyleRulesCallback } from 'material-ui/styles/withStyles';
811

12+
import { AvatarSelector } from '../';
13+
914
export class SignUpData {
15+
Avatar = '0.jpg';
1016
Login: string;
1117
Password: string;
1218
RepeatPassword: string;
@@ -20,6 +26,7 @@ interface SignUpProps {
2026
type SignUpFormProps = SignUpProps & WithStyles<'root' | 'Card' | 'Typography'>;
2127

2228
interface SignUpFormState {
29+
ActiveStep: number;
2330
LoginError: string;
2431
PasswordError: string;
2532
RepeatPasswordError: string;
@@ -32,6 +39,7 @@ const styles: StyleRulesCallback<'root'> = () => ({
3239
});
3340

3441
class SignUpForm extends React.PureComponent<SignUpFormProps, SignUpFormState> {
42+
private readonly stepsCount = 2;
3543
private signUpData = new SignUpData();
3644
private get valid() {
3745
return !this.state.LoginError && this.signUpData.Login
@@ -43,13 +51,16 @@ class SignUpForm extends React.PureComponent<SignUpFormProps, SignUpFormState> {
4351
super(props);
4452

4553
this.state = {
54+
ActiveStep: 0,
4655
LoginError: '',
4756
PasswordError: '',
4857
RepeatPasswordError: ''
4958
};
5059

51-
this.handleSubmit = this.handleSubmit.bind(this);
5260
this.handleChange = this.handleChange.bind(this);
61+
this.handleStep = this.handleStep.bind(this);
62+
this.handleSubmit = this.handleSubmit.bind(this);
63+
this.onPickAvatar = this.onPickAvatar.bind(this);
5364
}
5465

5566
handleChange(event: React.ChangeEvent<HTMLInputElement>) {
@@ -60,78 +71,139 @@ class SignUpForm extends React.PureComponent<SignUpFormProps, SignUpFormState> {
6071
this.validate(name, value);
6172
}
6273

74+
handleStep(e: React.MouseEvent<HTMLFormElement>) {
75+
const initialState = {
76+
LoginError: '',
77+
PasswordError: '',
78+
RepeatPasswordError: ''
79+
};
80+
if (e.currentTarget.name === 'next') {
81+
this.setState((prevState) => ({...initialState, ActiveStep: prevState.ActiveStep + 1}));
82+
} else {
83+
this.setState((prevState) => {
84+
if (prevState.ActiveStep === 1) {
85+
this.signUpData.Login = '';
86+
this.signUpData.Password = '';
87+
this.signUpData.RepeatPassword = '';
88+
}
89+
return {...initialState, ActiveStep: prevState.ActiveStep - 1};
90+
});
91+
}
92+
}
93+
6394
handleSubmit(event: React.FormEvent<HTMLFormElement>) {
6495
event.preventDefault();
6596
const signUpData = {...this.signUpData};
6697
delete signUpData.RepeatPassword;
6798
this.props.onSubmit(signUpData);
6899
}
69100

70-
render() {
101+
getFormBody() {
102+
switch (this.state.ActiveStep) {
103+
case 0: return this.getStep0();
104+
case 1: return this.getStep1();
105+
default: throw 'NullReferenceException';
106+
}
107+
}
108+
109+
getStep0() {
71110
const { LoginError, PasswordError, RepeatPasswordError } = this.state;
111+
72112
return (
73-
<div className={this.props.classes.root}>
74-
<Card className={'SignUpForm ' + this.props.classes.Card}>
113+
<React.Fragment>
114+
<TextField
115+
error={!!LoginError}
116+
helperText={LoginError ? LoginError : 'Enter your Login'}
117+
label="Login"
118+
name="Login"
119+
fullWidth={true}
120+
onChange={this.handleChange}
121+
/>
122+
<br />
123+
<br />
124+
<TextField
125+
error={!!PasswordError}
126+
helperText={PasswordError ? PasswordError : 'Enter your Password'}
127+
label="Password"
128+
name="Password"
129+
type="password"
130+
fullWidth={true}
131+
onChange={this.handleChange}
132+
/>
133+
<br />
134+
<br />
135+
<TextField
136+
error={!!RepeatPasswordError}
137+
helperText={RepeatPasswordError ? RepeatPasswordError : 'Repeat your Password'}
138+
label="Repeat Password"
139+
name="RepeatPassword"
140+
type="password"
141+
fullWidth={true}
142+
onChange={this.handleChange}
143+
/>
144+
</React.Fragment>
145+
);
146+
}
147+
148+
getStep1() {
149+
return (
150+
<React.Fragment>
151+
<AvatarSelector onPickAvatar={this.onPickAvatar} />
152+
<Button variant={'raised'} type="submit" color="primary" fullWidth={true} disabled={!this.valid}>
153+
Register
154+
</Button>
155+
</React.Fragment>
156+
);
157+
}
158+
159+
onPickAvatar(avatar: string) {
160+
this.signUpData.Avatar = avatar;
161+
}
162+
163+
render() {
164+
const { ActiveStep } = this.state;
165+
const { classes, errors } = this.props;
166+
const backButton = (
167+
<Button size="small" onClick={this.handleStep} disabled={this.state.ActiveStep === 0} name="back">
168+
<KeyboardArrowLeft />
169+
Back
170+
</Button>
171+
);
172+
const nextButton = (
173+
<Button size="small" onClick={this.handleStep} disabled={ActiveStep === this.stepsCount - 1} name="next">
174+
Next
175+
<KeyboardArrowRight />
176+
</Button>
177+
);
178+
179+
return (
180+
<div className={classes.root}>
181+
<Card className={'SignUpForm ' + classes.Card}>
75182
<CardContent>
76-
<Typography component="h2" variant="headline" className={this.props.classes.Typography}>
183+
<Typography component="h2" variant="headline" className={classes.Typography}>
77184
Sign Up
78185
</Typography>
79-
{this.props.errors && this.props.errors
186+
<Typography className={classes.Typography}>
187+
Step {ActiveStep + 1} of {this.stepsCount}
188+
</Typography>
189+
<Divider />
190+
{errors && errors
80191
.map((error: string) => <p key={error}>{error}</p>)}
81192
<form onSubmit={this.handleSubmit}>
82-
<TextField
83-
error={!!LoginError}
84-
helperText={LoginError ? LoginError : 'Enter your Login'}
85-
label="Login"
86-
name="Login"
87-
fullWidth={true}
88-
onChange={this.handleChange}
89-
/>
90-
<br />
91-
<br />
92-
<TextField
93-
error={!!PasswordError}
94-
helperText={PasswordError ? PasswordError : 'Enter your Password'}
95-
label="Password"
96-
name="Password"
97-
type="password"
98-
fullWidth={true}
99-
onChange={this.handleChange}
193+
<MobileStepper
194+
variant="text"
195+
steps={this.stepsCount}
196+
position="static"
197+
activeStep={ActiveStep}
198+
nextButton={(ActiveStep + 1 < this.stepsCount) ? nextButton : <React.Fragment />}
199+
backButton={backButton}
100200
/>
101-
<br />
102-
<br />
103-
<TextField
104-
error={!!RepeatPasswordError}
105-
helperText={RepeatPasswordError ? RepeatPasswordError : 'Repeat your Password'}
106-
label="Repeat Password"
107-
name="RepeatPassword"
108-
type="password"
109-
fullWidth={true}
110-
onChange={this.handleChange}
111-
/>
112-
<br />
113-
<br />
114-
<Button
115-
variant={'raised'}
116-
type="submit"
117-
color="primary"
118-
fullWidth={true}
119-
disabled={!this.valid}
120-
>
121-
Register
122-
</Button>
201+
{this.getFormBody()}
123202
</form>
124-
<br />
125-
<Divider />
126203
</CardContent>
127204
<CardActions>
128205
<a href="/">
129-
<Button
130-
variant={'raised'}
131-
color="default"
132-
>
133-
Sign In
134-
</Button>
206+
<Button variant={'raised'} color="default">Sign In</Button>
135207
</a>
136208
</CardActions>
137209
</Card>
@@ -161,6 +233,8 @@ class SignUpForm extends React.PureComponent<SignUpFormProps, SignUpFormState> {
161233
PasswordError = 'Password must be a minimum of 8 characters';
162234
} else if (value.length > 249) {
163235
PasswordError = 'Password is too long';
236+
} else if (value !== this.signUpData.RepeatPassword) {
237+
PasswordError = 'These passwords don\'t match';
164238
}
165239
newState = {PasswordError};
166240
break;
@@ -172,6 +246,7 @@ class SignUpForm extends React.PureComponent<SignUpFormProps, SignUpFormState> {
172246
}
173247

174248
if (this.state.RepeatPasswordError === RepeatPasswordError) {
249+
newState = {PasswordError: ''};
175250
this.forceUpdate();
176251
} else {
177252
newState = {RepeatPasswordError};

src/services/http/AccountService.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ const checkToken = (token: string): Promise<number> => ApiService.patch(controll
1818
}
1919
});
2020

21-
const register = (body: SignUpData): Promise<ApiResponse<UserToken>> => ApiService
22-
.put(controller, {...body, Avatar: '0.jpg'});
21+
const register = (body: SignUpData): Promise<ApiResponse<UserToken>> => ApiService.put(controller, body);
2322

2423
const AccountService = { auth, checkToken, register };
2524
export { AccountService };

0 commit comments

Comments
 (0)