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"; 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..0765522 100644 --- a/src/views/assets/locales.ts +++ b/src/views/assets/locales.ts @@ -8,6 +8,14 @@ export default new LocalizedStrings({ login_userNameTp: "User name", login_passwordTp: "Password", login_wrongPassTp: "Login failed. Check username and password.", + 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", + 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..6bbddb9 --- /dev/null +++ b/src/views/signupForm.tsx @@ -0,0 +1,161 @@ +import React from "react"; +import { RouteComponentProps as RCP } from "react-router-dom"; +import { AxiosError, AxiosResponse } from "axios"; + +import req from "../axios"; + +import strings from "./assets/locales"; + +import "./styles/signupForm.scss"; + +enum Status { + UNDEFINED, + FAILED, + SUCCESS, + IN_PROCESS, +} +enum Errors { + FORBIDDEN = 403, + USER_EXISTS = 409, +} +interface State { + name: string; + password: string; + status: Status; + 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: "", + password: "", + status: Status.UNDEFINED, + httpCode: 0, + }; + componentDidUpdate = () => { + if (this.state.status === Status.SUCCESS) { + setTimeout(() => { + this.props.history.push("/"); + }, 5000); + } + }; + signup = () => { + this.setState({ status: Status.IN_PROCESS }); + // prettier-ignore + req.post("/signup", { + name: this.state.name, + password: this.state.password, + }) + .then( + () => this.setState({ status: Status.SUCCESS }), + (e: AxiosError) => this.setState({ + status: Status.FAILED, + httpCode: (e.response as AxiosResponse).status + }) + ); + }; + 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 = () => { + 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 ( +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+ + + +
+
+
+ +
+
+
+
+
+ ); + }; +} + +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; +}