From 22141db0f2df963195f0c50c2a4db59e107812c9 Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Sat, 9 Nov 2019 21:44:11 +0500 Subject: [PATCH 1/4] Implement a basic signup form --- src/main.tsx | 6 ++ src/views/assets/locales.ts | 2 + src/views/loginForm.tsx | 32 +++++++-- src/views/signupForm.tsx | 107 +++++++++++++++++++++++++++++++ src/views/styles/signupForm.scss | 5 ++ 5 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 src/views/signupForm.tsx create mode 100644 src/views/styles/signupForm.scss diff --git a/src/main.tsx b/src/main.tsx index b017014..066d600 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,6 +5,7 @@ import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import { Store } from "./typings/store"; import LoginForm from "./views/loginForm"; +import SignupForm from "./views/signupForm"; import MainView from "./views/mainView"; import EditorView from "./views/editorView"; @@ -49,6 +50,11 @@ class App extends React.Component { {!loggedIn && ( + )} diff --git a/src/views/assets/locales.ts b/src/views/assets/locales.ts index 4d684b9..c050040 100644 --- a/src/views/assets/locales.ts +++ b/src/views/assets/locales.ts @@ -8,6 +8,8 @@ export default new LocalizedStrings({ login_userNameTp: "User name", login_passwordTp: "Password", login_wrongPassTp: "Login failed. Check username and password.", + login_signupBtn: "Sign up", + login_goBackBtn: "Go back", main_loggedInMsg: "Logged in as", main_logoutBtn: "Logout", main_searchTp: "Search tasks...", diff --git a/src/views/loginForm.tsx b/src/views/loginForm.tsx index 5461ec2..fb825ae 100644 --- a/src/views/loginForm.tsx +++ b/src/views/loginForm.tsx @@ -1,6 +1,7 @@ import React from "react"; import { ThunkDispatch } from "redux-thunk"; import { connect } from "react-redux"; +import { RouteComponentProps } from "react-router-dom"; import { login } from "../actions/auth"; import { AuthAction } from "../typings/actions"; @@ -20,7 +21,7 @@ interface State { password: string; loginFailed: boolean; } -interface Props { +interface Props extends RouteComponentProps { loginFailed?: boolean; dispatch: ThunkDispatch; } @@ -40,6 +41,7 @@ class LoginForm extends React.PureComponent { this.setState({ password: event.currentTarget.value }); login = () => this.props.dispatch(login(this.state.username, this.state.password)); + toSignup = () => this.props.history.push("/signup"); onKeyPress = (event: React.KeyboardEvent) => { if (event.key === "Enter") this.login(); }; @@ -75,12 +77,28 @@ class LoginForm extends React.PureComponent { > {strings.login_wrongPassTp} - +
+
+
+ +
+
+
+
+ +
+
+
); diff --git a/src/views/signupForm.tsx b/src/views/signupForm.tsx new file mode 100644 index 0000000..259cd1e --- /dev/null +++ b/src/views/signupForm.tsx @@ -0,0 +1,107 @@ +import React from "react"; +import { RouteComponentProps as RCP } from "react-router-dom"; + +import req from "../axios"; + +import strings from "./assets/locales"; + +import "./styles/signupForm.scss"; + +enum Status { + FAILED, + SUCCESS, + UNDEFINED, +} +interface State { + name: string; + password: string; + status: Status; +} + +class SignupForm extends React.PureComponent { + state = { + name: "", + password: "", + status: Status.UNDEFINED, + }; + componentDidUpdate = () => { + if (this.state.status === Status.SUCCESS) { + setTimeout(() => { + this.props.history.push("/"); + }, 5000); + } + }; + signup = () => { + // prettier-ignore + req.post("/signup", { + name: this.state.name, + password: this.state.password, + }) + .then( + () => this.setState({ status: Status.SUCCESS }), + () => this.setState({ status: Status.FAILED }) + ); + }; + goBack = () => this.props.history.push("/"); + updateName = (e: React.FormEvent) => + this.setState({ name: e.currentTarget.value }); + updatePassword = (e: React.FormEvent) => + this.setState({ password: e.currentTarget.value }); + render = () => { + return ( +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ ); + }; +} + +export default SignupForm; diff --git a/src/views/styles/signupForm.scss b/src/views/styles/signupForm.scss new file mode 100644 index 0000000..aa1bfb1 --- /dev/null +++ b/src/views/styles/signupForm.scss @@ -0,0 +1,5 @@ +@import "./common.scss"; + +form.signupForm { + max-width: 320px; +} From 0a342fc2e30465eb741577c20f29779e2481faeb Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Mon, 11 Nov 2019 00:41:32 +0500 Subject: [PATCH 2/4] Display error messages on signup fail --- src/views/assets/locales.ts | 4 ++++ src/views/signupForm.tsx | 31 ++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/views/assets/locales.ts b/src/views/assets/locales.ts index c050040..dc1a4c7 100644 --- a/src/views/assets/locales.ts +++ b/src/views/assets/locales.ts @@ -8,6 +8,10 @@ export default new LocalizedStrings({ login_userNameTp: "User name", login_passwordTp: "Password", login_wrongPassTp: "Login failed. Check username and password.", + signup_msg: "Signup failed", + signup_unknownError: "Unknown error", + signup_disabled: "Signup disabled", + signup_userExists: "User with this name exists", login_signupBtn: "Sign up", login_goBackBtn: "Go back", main_loggedInMsg: "Logged in as", diff --git a/src/views/signupForm.tsx b/src/views/signupForm.tsx index 259cd1e..f688214 100644 --- a/src/views/signupForm.tsx +++ b/src/views/signupForm.tsx @@ -1,5 +1,6 @@ import React from "react"; import { RouteComponentProps as RCP } from "react-router-dom"; +import { AxiosError, AxiosResponse } from "axios"; import req from "../axios"; @@ -12,10 +13,15 @@ enum Status { SUCCESS, UNDEFINED, } +enum Errors { + FORBIDDEN = 403, + USER_EXISTS = 409, +} interface State { name: string; password: string; status: Status; + httpCode: number; } class SignupForm extends React.PureComponent { @@ -23,6 +29,7 @@ class SignupForm extends React.PureComponent { name: "", password: "", status: Status.UNDEFINED, + httpCode: 0, }; componentDidUpdate = () => { if (this.state.status === Status.SUCCESS) { @@ -39,7 +46,10 @@ class SignupForm extends React.PureComponent { }) .then( () => this.setState({ status: Status.SUCCESS }), - () => this.setState({ status: Status.FAILED }) + (e: AxiosError) => this.setState({ + status: Status.FAILED, + httpCode: (e.response as AxiosResponse).status + }) ); }; goBack = () => this.props.history.push("/"); @@ -48,6 +58,15 @@ class SignupForm extends React.PureComponent { updatePassword = (e: React.FormEvent) => this.setState({ password: e.currentTarget.value }); render = () => { + let message: string; + switch (this.state.httpCode) { + case Errors.FORBIDDEN: + message = strings.signup_disabled; + case Errors.USER_EXISTS: + message = strings.signup_userExists; + default: + message = strings.signup_unknownError; + } return (
@@ -87,6 +106,16 @@ class SignupForm extends React.PureComponent { />
+
+ {`${strings.signup_msg}: ${message}`} +
Date: Mon, 11 Nov 2019 00:57:38 +0500 Subject: [PATCH 3/4] Make the signup form a lot more dynamic --- src/views/assets/locales.ts | 4 +++- src/views/signupForm.tsx | 43 +++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/views/assets/locales.ts b/src/views/assets/locales.ts index dc1a4c7..0765522 100644 --- a/src/views/assets/locales.ts +++ b/src/views/assets/locales.ts @@ -8,7 +8,9 @@ export default new LocalizedStrings({ login_userNameTp: "User name", login_passwordTp: "Password", login_wrongPassTp: "Login failed. Check username and password.", - signup_msg: "Signup failed", + signup_processMsg: "Signing up...", + signup_successMsg: "Success. Redirecting to login...", + signup_failMsg: "Signup failed", signup_unknownError: "Unknown error", signup_disabled: "Signup disabled", signup_userExists: "User with this name exists", diff --git a/src/views/signupForm.tsx b/src/views/signupForm.tsx index f688214..6bbddb9 100644 --- a/src/views/signupForm.tsx +++ b/src/views/signupForm.tsx @@ -9,9 +9,10 @@ import strings from "./assets/locales"; import "./styles/signupForm.scss"; enum Status { + UNDEFINED, FAILED, SUCCESS, - UNDEFINED, + IN_PROCESS, } enum Errors { FORBIDDEN = 403, @@ -24,6 +25,22 @@ interface State { httpCode: number; } +interface MsgProps { + message: string; + code: Status; + matchCode: Status; +} +/** + * Used for displaying messages on matching status code. + */ +const Msg: React.FC = (props) => ( + + {props.message} + +); + class SignupForm extends React.PureComponent { state = { name: "", @@ -39,6 +56,7 @@ class SignupForm extends React.PureComponent { } }; signup = () => { + this.setState({ status: Status.IN_PROCESS }); // prettier-ignore req.post("/signup", { name: this.state.name, @@ -107,14 +125,21 @@ class SignupForm extends React.PureComponent {
- {`${strings.signup_msg}: ${message}`} + + +
From 989309a1c986ce690fea043e99f44d86f192d24f Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Mon, 11 Nov 2019 01:02:57 +0500 Subject: [PATCH 4/4] Bump v0.0.5 --- package.json | 2 +- src/const.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 86f9f0f..28d2474 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amber_web", - "version": "0.0.4.1", + "version": "0.0.5", "private": true, "dependencies": { "@types/jest": "24.0.18", diff --git a/src/const.ts b/src/const.ts index 9687d40..61d5683 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1,7 +1,7 @@ export const baseURI: string = process.env.REACT_APP_APIURI || "https://amber.h.tdem.in/api"; -export const appVersion: string = "0.0.4.1"; +export const appVersion: string = "0.0.5"; export const appFullName: string = "Amber Web"; export const appName: string = "amber_web"; export const appAuthor: string = "Timur Demin";