From 1ee2e302e2ad9c34d49f1a329051b61137363e3c Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Wed, 9 Feb 2022 11:44:08 +0530 Subject: [PATCH] Feature(FE): signup page (#642) * chore: icon is updated * feat: signup page design is updated * chore: set get user pref is added * chore: svg is added * feat: signup page is updated * feat: signup page is updated --- frontend/public/signoz-signup.svg | 9 + frontend/public/signoz.svg | 2 +- frontend/src/api/user/getPreference.ts | 24 ++ frontend/src/api/user/getVersion.ts | 24 ++ frontend/src/api/user/setPreference.ts | 26 +++ frontend/src/api/user/signup.ts | 3 +- frontend/src/pages/SignUp/SignUp.tsx | 219 ++++++++++++++++++ frontend/src/pages/SignUp/index.tsx | 171 +++----------- frontend/src/pages/SignUp/styles.ts | 54 +++-- frontend/src/store/actions/app/index.ts | 1 - .../src/store/actions/app/userLoggedIn.ts | 14 -- .../src/types/api/user/getUserPreference.ts | 6 + frontend/src/types/api/user/getVersion.ts | 3 + .../src/types/api/user/setUserPreference.ts | 8 + frontend/src/types/api/user/signup.ts | 1 + 15 files changed, 393 insertions(+), 172 deletions(-) create mode 100644 frontend/public/signoz-signup.svg create mode 100644 frontend/src/api/user/getPreference.ts create mode 100644 frontend/src/api/user/getVersion.ts create mode 100644 frontend/src/api/user/setPreference.ts create mode 100644 frontend/src/pages/SignUp/SignUp.tsx delete mode 100644 frontend/src/store/actions/app/userLoggedIn.ts create mode 100644 frontend/src/types/api/user/getUserPreference.ts create mode 100644 frontend/src/types/api/user/getVersion.ts create mode 100644 frontend/src/types/api/user/setUserPreference.ts diff --git a/frontend/public/signoz-signup.svg b/frontend/public/signoz-signup.svg new file mode 100644 index 0000000000..67c45b1c23 --- /dev/null +++ b/frontend/public/signoz-signup.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/public/signoz.svg b/frontend/public/signoz.svg index 73df99420e..53a3a23754 100644 --- a/frontend/public/signoz.svg +++ b/frontend/public/signoz.svg @@ -2,4 +2,4 @@ - + \ No newline at end of file diff --git a/frontend/src/api/user/getPreference.ts b/frontend/src/api/user/getPreference.ts new file mode 100644 index 0000000000..b284eab47b --- /dev/null +++ b/frontend/src/api/user/getPreference.ts @@ -0,0 +1,24 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/user/getUserPreference'; + +const getPreference = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/userPreferences`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getPreference; diff --git a/frontend/src/api/user/getVersion.ts b/frontend/src/api/user/getVersion.ts new file mode 100644 index 0000000000..a65ede2f0d --- /dev/null +++ b/frontend/src/api/user/getVersion.ts @@ -0,0 +1,24 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/user/getVersion'; + +const getVersion = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/version`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getVersion; diff --git a/frontend/src/api/user/setPreference.ts b/frontend/src/api/user/setPreference.ts new file mode 100644 index 0000000000..de8e309b65 --- /dev/null +++ b/frontend/src/api/user/setPreference.ts @@ -0,0 +1,26 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/user/setUserPreference'; + +const setPreference = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/userPreferences`, { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default setPreference; diff --git a/frontend/src/api/user/signup.ts b/frontend/src/api/user/signup.ts index 9635f70e8f..8778b5c037 100644 --- a/frontend/src/api/user/signup.ts +++ b/frontend/src/api/user/signup.ts @@ -9,8 +9,7 @@ const signup = async ( ): Promise | ErrorResponse> => { try { const response = await axios.post(`/user`, { - email: props.email, - name: props.name, + ...props, }); return { diff --git a/frontend/src/pages/SignUp/SignUp.tsx b/frontend/src/pages/SignUp/SignUp.tsx new file mode 100644 index 0000000000..37253a0aeb --- /dev/null +++ b/frontend/src/pages/SignUp/SignUp.tsx @@ -0,0 +1,219 @@ +import { + Button, + Input, + notification, + Typography, + Switch, + Space, + Card, +} from 'antd'; +import signup from 'api/user/signup'; +import ROUTES from 'constants/routes'; +import history from 'lib/history'; +import React, { useEffect, useState } from 'react'; +import setLocalStorageKey from 'api/browser/localstorage/set'; + +import AppActions from 'types/actions'; +const { Title } = Typography; +import { PayloadProps } from 'types/api/user/getUserPreference'; + +import { + ButtonContainer, + Container, + FormWrapper, + Label, + LeftContainer, + Logo, + MarginTop, +} from './styles'; +import { IS_LOGGED_IN } from 'constants/auth'; +import { useDispatch } from 'react-redux'; +import { Dispatch } from 'redux'; +import setPreference from 'api/user/setPreference'; + +const Signup = ({ version, userpref }: SignupProps): JSX.Element => { + const [loading, setLoading] = useState(false); + + const [firstName, setFirstName] = useState(''); + const [email, setEmail] = useState(''); + const [organizationName, setOrganisationName] = useState(''); + const [hasOptedUpdates, setHasOptedUpdates] = useState( + userpref.hasOptedUpdates, + ); + const [isAnonymous, setisAnonymous] = useState(userpref.isAnonymous); + + const dispatch = useDispatch>(); + + useEffect(() => { + setisAnonymous(userpref.isAnonymous); + setHasOptedUpdates(userpref.hasOptedUpdates); + }, []); + + const setState = ( + value: string, + setFunction: React.Dispatch>, + ) => { + setFunction(value); + }; + + const handleSubmit = (e: React.FormEvent): void => { + (async (): Promise => { + try { + e.preventDefault(); + setLoading(true); + + const userPrefernceResponse = await setPreference({ + isAnonymous, + hasOptedUpdates, + }); + + if (userPrefernceResponse.statusCode === 200) { + const response = await signup({ + email: email, + name: firstName, + organizationName, + }); + + if (response.statusCode === 200) { + setLocalStorageKey(IS_LOGGED_IN, 'yes'); + dispatch({ + type: 'LOGGED_IN', + }); + + history.push(ROUTES.APPLICATION); + } else { + notification.error({ + message: 'Something went wrong', + }); + } + } else { + notification.error({ + message: 'Something went wrong', + }); + } + + setLoading(false); + } catch (error) { + notification.error({ + message: 'Something went wrong', + }); + setLoading(false); + } + })(); + }; + + console.log(userpref); + + const onSwitchHandler = ( + value: boolean, + setFunction: React.Dispatch>, + ) => { + setFunction(value); + }; + + return ( + + + + + SigNoz + + + Monitor your applications. Find what is causing issues. + + + SigNoz {version} + + + + +
+ Create your account +
+ + { + setState(e.target.value, setEmail); + }} + required + id="signupEmail" + /> +
+ +
+ + { + setState(e.target.value, setFirstName); + }} + required + id="signupFirstName" + /> +
+
+ + { + setState(e.target.value, setOrganisationName); + }} + required + id="organizationName" + /> +
+ + + + onSwitchHandler(value, setHasOptedUpdates)} + checked={hasOptedUpdates} + /> + Keep me updated on new SigNoz features + + + + + + onSwitchHandler(value, setisAnonymous)} + checked={isAnonymous} + /> + + Anonymise my usage date. We collect data to measure product usage + + + + + + + +
+
+
+ ); +}; + +interface SignupProps { + version: string; + userpref: PayloadProps; +} + +export default Signup; diff --git a/frontend/src/pages/SignUp/index.tsx b/frontend/src/pages/SignUp/index.tsx index f5ac704dc8..88131c7143 100644 --- a/frontend/src/pages/SignUp/index.tsx +++ b/frontend/src/pages/SignUp/index.tsx @@ -1,148 +1,43 @@ -import { Button, Input, notification, Typography } from 'antd'; -import signup from 'api/user/signup'; -import ROUTES from 'constants/routes'; -import history from 'lib/history'; -import React, { useState } from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { ThunkDispatch } from 'redux-thunk'; -import { UserLoggedIn } from 'store/actions'; -import AppActions from 'types/actions'; +import useFetch from 'hooks/useFetch'; +import React from 'react'; +import SignUpComponent from './SignUp'; +import getVersion from 'api/user/getVersion'; +import { PayloadProps as VersionPayload } from 'types/api/user/getVersion'; +import { PayloadProps as UserPrefPayload } from 'types/api/user/getUserPreference'; -import { - ButtonContainer, - Container, - FormWrapper, - LogoImageContainer, - Title, -} from './styles'; +import Spinner from 'components/Spinner'; +import { Typography } from 'antd'; +import getPreference from 'api/user/getPreference'; -const Signup = ({ loggedIn }: SignupProps): JSX.Element => { - const [notificationsInstance, Element] = notification.useNotification(); +const SignUp = () => { + const versionResponse = useFetch(getVersion); - const [loading, setLoading] = useState(false); + const userPrefResponse = useFetch(getPreference); - const [formState, setFormState] = useState({ - firstName: { value: '' }, - email: { value: '' }, - }); + if (versionResponse.error || userPrefResponse.error) { + return ( + + {versionResponse.errorMessage || + userPrefResponse.errorMessage || + 'Somehthing went wrong'} + + ); + } - const updateForm = ( - name: string, - target: EventTarget & HTMLInputElement, - ): void => { - if (name === 'firstName') { - setFormState({ - ...formState, - firstName: { ...formState.firstName, value: target.value }, - }); - } else if (name === 'email') { - setFormState({ - ...formState, - email: { ...formState.email, value: target.value }, - }); - } - }; + if ( + versionResponse.loading || + versionResponse.payload === undefined || + userPrefResponse.loading || + userPrefResponse.payload === undefined + ) { + return ; + } - const handleSubmit = (e: React.FormEvent): void => { - (async (): Promise => { - try { - e.preventDefault(); - setLoading(true); - const payload = { - first_name: formState.firstName, - email: formState.email, - }; + const version = versionResponse.payload.version; - const response = await signup({ - email: payload.email.value, - name: payload.first_name.value, - }); + const userpref = userPrefResponse.payload; - if (response.statusCode === 200) { - loggedIn(); - history.push(ROUTES.APPLICATION); - } else { - notificationsInstance.error({ - message: 'Something went wrong', - }); - } - setLoading(false); - } catch (error) { - notificationsInstance.error({ - message: 'Something went wrong', - }); - setLoading(false); - } - })(); - }; - - return ( -
- {Element} - - - Create your account - - Monitor your applications. Find what is causing issues. - - - - - - -
-
- - updateForm('email', e.target)} - required - id="signupEmail" - /> -
- -
- - updateForm('firstName', e.target)} - required - id="signupFirstName" - /> -
- - - - -
-
-
- ); + return ; }; -interface DispatchProps { - loggedIn: () => void; -} - -const mapDispatchToProps = ( - dispatch: ThunkDispatch, -): DispatchProps => ({ - loggedIn: bindActionCreators(UserLoggedIn, dispatch), -}); - -type SignupProps = DispatchProps; - -export default connect(null, mapDispatchToProps)(Signup); +export default SignUp; diff --git a/frontend/src/pages/SignUp/styles.ts b/frontend/src/pages/SignUp/styles.ts index e90f440a92..ca9e899165 100644 --- a/frontend/src/pages/SignUp/styles.ts +++ b/frontend/src/pages/SignUp/styles.ts @@ -1,31 +1,53 @@ -import { Space, Typography } from 'antd'; +import { Card, Space } from 'antd'; +import React from 'react'; import styled from 'styled-components'; -export const Container = styled(Space)` +export const Container = styled.div` &&& { - padding-left: 2rem; - margin-top: 3rem; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + + max-width: 1024px; + margin: 0 auto; } `; -export const Title = styled(Typography)` - &&& { - font-size: 1rem; - font-weight: bold; - } -`; - -export const FormWrapper = styled.div` +export const FormWrapper = styled(Card)` display: flex; justify-content: center; + max-width: 432px; + flex: 1; +`; - margin-top: 2rem; +export const Label = styled.label` + margin-bottom: 11px; + margin-top: 19px; + display: inline-block; + font-size: 1rem; + line-height: 24px; +`; + +export const LeftContainer = styled(Space)` + flex: 1; `; export const ButtonContainer = styled.div` - margin-top: 0.5rem; + margin-top: 1.8125rem; + display: flex; + justify-content: center; + align-items: center; `; -export const LogoImageContainer = styled.img` - width: 320px; +interface Props { + marginTop: React.CSSProperties['marginTop']; +} + +export const MarginTop = styled.div` + margin-top: ${({ marginTop }) => marginTop}; +`; + +export const Logo = styled.img` + width: 60px; `; diff --git a/frontend/src/store/actions/app/index.ts b/frontend/src/store/actions/app/index.ts index e6c98da1d9..308027be21 100644 --- a/frontend/src/store/actions/app/index.ts +++ b/frontend/src/store/actions/app/index.ts @@ -1,3 +1,2 @@ export * from './toggleDarkMode'; export * from './toggleSettingsTab'; -export * from './userLoggedIn'; diff --git a/frontend/src/store/actions/app/userLoggedIn.ts b/frontend/src/store/actions/app/userLoggedIn.ts deleted file mode 100644 index 3f03415b72..0000000000 --- a/frontend/src/store/actions/app/userLoggedIn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { IS_LOGGED_IN } from 'constants/auth'; -import { Dispatch } from 'redux'; -import AppActions from 'types/actions'; -import setLocalStorageKey from 'api/browser/localstorage/set'; - -export const UserLoggedIn = (): ((dispatch: Dispatch) => void) => { - return (dispatch: Dispatch): void => { - setLocalStorageKey(IS_LOGGED_IN, 'yes'); - - dispatch({ - type: 'LOGGED_IN', - }); - }; -}; diff --git a/frontend/src/types/api/user/getUserPreference.ts b/frontend/src/types/api/user/getUserPreference.ts new file mode 100644 index 0000000000..fe0a30bf41 --- /dev/null +++ b/frontend/src/types/api/user/getUserPreference.ts @@ -0,0 +1,6 @@ +export interface PayloadProps { + hasOptedUpdates: boolean; + id: number; + isAnonymous: boolean; + uuid: string; +} diff --git a/frontend/src/types/api/user/getVersion.ts b/frontend/src/types/api/user/getVersion.ts new file mode 100644 index 0000000000..78f07363ef --- /dev/null +++ b/frontend/src/types/api/user/getVersion.ts @@ -0,0 +1,3 @@ +export interface PayloadProps { + version: string; +} diff --git a/frontend/src/types/api/user/setUserPreference.ts b/frontend/src/types/api/user/setUserPreference.ts new file mode 100644 index 0000000000..92ebd3d1b1 --- /dev/null +++ b/frontend/src/types/api/user/setUserPreference.ts @@ -0,0 +1,8 @@ +export interface Props { + isAnonymous: boolean; + hasOptedUpdates: boolean; +} + +export interface PayloadProps { + data: string; +} diff --git a/frontend/src/types/api/user/signup.ts b/frontend/src/types/api/user/signup.ts index 00edef563f..8e6feca0d6 100644 --- a/frontend/src/types/api/user/signup.ts +++ b/frontend/src/types/api/user/signup.ts @@ -1,4 +1,5 @@ export interface Props { email: string; name: string; + organizationName: string; }