From efb0954baad3b35403060275a4f48b90a4c2705b Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Fri, 19 Mar 2021 11:41:09 -0400 Subject: [PATCH 01/25] SignUp Page Co-authored-by: Meneguini Co-authored-by: DukeManh --- src/web/next.config.js | 10 +- src/web/package.json | 5 +- src/web/src/components/BannerButtons.tsx | 18 +- .../SignUp/FormFields/CheckBoxInput.tsx | 48 ++++ .../SignUp/FormFields/TextInput.tsx | 49 ++++ .../components/SignUp/FormFields/index.tsx | 4 + .../src/components/SignUp/Forms/BasicInfo.tsx | 163 +++++++++++ .../components/SignUp/Forms/GitHubAccount.tsx | 172 +++++++++++ .../src/components/SignUp/Forms/Overview.tsx | 104 +++++++ .../src/components/SignUp/Forms/RSSFeeds.tsx | 264 +++++++++++++++++ .../src/components/SignUp/Forms/Review.tsx | 140 +++++++++ src/web/src/components/SignUp/Forms/index.tsx | 6 + .../components/SignUp/Schema/FormModel.tsx | 54 ++++ .../components/SignUp/Schema/FormSchema.tsx | 68 +++++ src/web/src/config.ts | 2 + src/web/src/interfaces/index.ts | 16 ++ src/web/src/pages/signup.tsx | 271 ++++++++++++++++++ 17 files changed, 1384 insertions(+), 10 deletions(-) create mode 100644 src/web/src/components/SignUp/FormFields/CheckBoxInput.tsx create mode 100644 src/web/src/components/SignUp/FormFields/TextInput.tsx create mode 100644 src/web/src/components/SignUp/FormFields/index.tsx create mode 100644 src/web/src/components/SignUp/Forms/BasicInfo.tsx create mode 100644 src/web/src/components/SignUp/Forms/GitHubAccount.tsx create mode 100644 src/web/src/components/SignUp/Forms/Overview.tsx create mode 100644 src/web/src/components/SignUp/Forms/RSSFeeds.tsx create mode 100644 src/web/src/components/SignUp/Forms/Review.tsx create mode 100644 src/web/src/components/SignUp/Forms/index.tsx create mode 100644 src/web/src/components/SignUp/Schema/FormModel.tsx create mode 100644 src/web/src/components/SignUp/Schema/FormSchema.tsx create mode 100644 src/web/src/pages/signup.tsx diff --git a/src/web/next.config.js b/src/web/next.config.js index 89fd915bf8..1f08fe8aca 100644 --- a/src/web/next.config.js +++ b/src/web/next.config.js @@ -13,7 +13,15 @@ const dotenv = require('dotenv'); const loadApiUrlFromEnv = (envFile) => dotenv.config({ path: envFile }); // ENV Variables we need to forward to next by prefix with NEXT_PUBLIC_* -const envVarsToForward = ['WEB_URL', 'API_URL', 'IMAGE_URL', 'POSTS_URL', 'AUTH_URL', 'SEARCH_URL']; +const envVarsToForward = [ + 'WEB_URL', + 'API_URL', + 'IMAGE_URL', + 'POSTS_URL', + 'AUTH_URL', + 'SEARCH_URL', + 'FEED_DISCOVERY_URL', +]; // Copy an existing ENV Var so it's visible to next: API_URL -> NEXT_PUBLIC_API_URL const forwardToNext = (envVar) => { diff --git a/src/web/package.json b/src/web/package.json index 49d3cf209c..d45cfd9dc9 100644 --- a/src/web/package.json +++ b/src/web/package.json @@ -15,7 +15,9 @@ "@mdx-js/loader": "^1.6.22", "@next/mdx": "^10.1.3", "@types/smoothscroll-polyfill": "^0.3.1", + "@types/yup": "^0.29.11", "dotenv": "^8.2.0", + "formik": "^2.2.6", "highlight.js": "10.7.2", "jwt-decode": "^3.1.2", "nanoid": "^3.1.22", @@ -25,7 +27,8 @@ "react-use": "^17.2.1", "smoothscroll-polyfill": "^0.4.4", "swr": "^0.5.5", - "valid-url": "^1.0.9" + "valid-url": "^1.0.9", + "yup": "^0.32.9" }, "devDependencies": { "@testing-library/react": "^11.2.6", diff --git a/src/web/src/components/BannerButtons.tsx b/src/web/src/components/BannerButtons.tsx index 2c98114b01..660eba5dd1 100644 --- a/src/web/src/components/BannerButtons.tsx +++ b/src/web/src/components/BannerButtons.tsx @@ -73,14 +73,16 @@ const LandingButtons = () => { > Sign in - + + + )} diff --git a/src/web/src/components/SignUp/FormFields/CheckBoxInput.tsx b/src/web/src/components/SignUp/FormFields/CheckBoxInput.tsx new file mode 100644 index 0000000000..c049fea730 --- /dev/null +++ b/src/web/src/components/SignUp/FormFields/CheckBoxInput.tsx @@ -0,0 +1,48 @@ +import { createStyles, makeStyles } from '@material-ui/core'; +import { useField } from 'formik'; +import FormControl from '@material-ui/core/FormControl'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Checkbox from '@material-ui/core/Checkbox'; + +const useStyles = makeStyles(() => + createStyles({ + formInputLabel: { + fontSize: '1.4em', + color: 'black', + }, + formControlLabel: { + fontSize: '1em', + color: '#474747', + }, + }) +); + +type CheckboxProps = { + name: string; + label: string; + checked: boolean; +}; + +const CheckBoxInput = (props: CheckboxProps) => { + const classes = useStyles(); + + const { label, name, checked, ...rest } = props; + const [field, meta] = useField(props); + + return ( + + + } + label={{label}} + name={name} + /> + + {meta.error && meta.touched ? meta.error : ''} + + ); +}; + +export default CheckBoxInput; diff --git a/src/web/src/components/SignUp/FormFields/TextInput.tsx b/src/web/src/components/SignUp/FormFields/TextInput.tsx new file mode 100644 index 0000000000..191be17a3b --- /dev/null +++ b/src/web/src/components/SignUp/FormFields/TextInput.tsx @@ -0,0 +1,49 @@ +import { useField, FieldHookConfig } from 'formik'; +import TextField, { TextFieldProps } from '@material-ui/core/TextField'; +import { createStyles, makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles(() => + createStyles({ + formInput: { + fontSize: '1.1em', + color: 'black', + }, + formInputLabel: { + fontSize: '1.2em', + color: 'black', + }, + }) +); + +const TextInput = (props: TextFieldProps & FieldHookConfig) => { + const classes = useStyles(); + + const { helperText, error, ...rest } = props; + const [field, meta] = useField(props); + + const renderHelperText = () => + error || (meta.touched && meta.error) ? meta.error || helperText : ''; + + return ( + + ); +}; + +export default TextInput; diff --git a/src/web/src/components/SignUp/FormFields/index.tsx b/src/web/src/components/SignUp/FormFields/index.tsx new file mode 100644 index 0000000000..fb223458ff --- /dev/null +++ b/src/web/src/components/SignUp/FormFields/index.tsx @@ -0,0 +1,4 @@ +import TextInput from './TextInput'; +import CheckBoxInput from './CheckBoxInput'; + +export { TextInput, CheckBoxInput }; diff --git a/src/web/src/components/SignUp/Forms/BasicInfo.tsx b/src/web/src/components/SignUp/Forms/BasicInfo.tsx new file mode 100644 index 0000000000..b6b377092b --- /dev/null +++ b/src/web/src/components/SignUp/Forms/BasicInfo.tsx @@ -0,0 +1,163 @@ +import { ReactNode } from 'react'; +import { createStyles, makeStyles } from '@material-ui/core'; +import { connect } from 'formik'; + +import { SignUpForm } from '../../../interfaces'; +import useAuth from '../../../hooks/use-auth'; +import formModels from '../Schema/FormModel'; +import { TextInput } from '../FormFields'; + +const { firstName, lastName, displayName, email } = formModels; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + padding: '0', + margin: '0', + width: '100%', + position: 'relative', + minHeight: '100%', + }, + container: { + display: 'grid', + gridTemplateColumns: '1fr', + justifyItems: 'center', + textAlign: 'center', + alignItems: 'center', + width: '100%', + position: 'absolute', + minHeight: '100%', + [theme.breakpoints.down(600)]: { + width: '95%', + marginLeft: '2.5%', + }, + }, + helloMessage: { + fontSize: '0.8em', + }, + userInfo: { + color: '#292929', + margin: '0', + padding: '.5%', + width: '90%', + height: '80%', + fontSize: '0.8em', + display: 'grid', + gridTemplateColumns: '1fr 1fr', + justifyContent: 'center', + alignItems: 'center', + border: '1px solid #C5EB98', + background: 'rgba(197, 235, 152, 0.2)', + borderRadius: '5px', + [theme.breakpoints.down(600)]: { + gridTemplateColumns: '1fr', + }, + '& span': { + color: '#525252', + }, + }, + userInfoLabel: { + gridColumnStart: '1', + gridColumnEnd: '3', + [theme.breakpoints.down(600)]: { + gridColumnStart: '1', + gridColumnEnd: '2', + }, + }, + displayNameTitle: { + fontSize: '0.85em', + }, + button: { + fontSize: '0.8em', + height: '35px', + width: '50%', + background: '#121D59', + color: '#A0D1FB', + marginLeft: '5%', + '&:hover': { + color: 'black', + border: '1px solid #121D59', + }, + }, + displayNameInfo: { + textAlign: 'start', + gridColumnStart: '1', + gridColumnEnd: '3', + fontSize: '1em', + }, + inputContainer: { + display: 'grid', + alignItems: 'center', + justifyItems: 'center', + width: '90%', + gridTemplateColumns: '80% 20%', + '& .MuiFormHelperText-root': { + fontSize: '0.9em', + color: 'black', + }, + '& .MuiFormLabel-root': { + color: 'black', + }, + '& .MuiInputBase-input.Mui-disabled': { + marginTop: '16px', + }, + }, + }) +); + +type Props = { + children: ReactNode; +}; + +const InputContainer = ({ children }: Props) => { + const classes = useStyles(); + + return
{children}
; +}; + +const BasicInfo = connect<{}, SignUpForm>((props) => { + const classes = useStyles(); + const { values } = props.formik; + const { user } = useAuth(); + + return ( +
+
+
+

Hello {user?.name || values.displayName}

+
+
+

+ The following information is what we already have: +

+

+ Display name: + {user?.name || values.displayName} +

+

+ Email: + {values.email} +

+
+ + + + + + + + + + + + +
+
+ ); +}); + +export default BasicInfo; diff --git a/src/web/src/components/SignUp/Forms/GitHubAccount.tsx b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx new file mode 100644 index 0000000000..cb0975239f --- /dev/null +++ b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx @@ -0,0 +1,172 @@ +/* eslint-disable camelcase */ +import { useEffect, useState } from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core'; +import { connect } from 'formik'; +import useSWR from 'swr'; + +import { SignUpForm } from '../../../interfaces'; +import formModels from '../Schema/FormModel'; +import { TextInput, CheckBoxInput } from '../FormFields'; +import PostAvatar from '../../Posts/PostAvatar'; + +const { githubUsername, github: githubModel, githubOwnership } = formModels; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + padding: '0', + margin: '0', + width: '100%', + position: 'relative', + minHeight: '100%', + }, + container: { + display: 'grid', + gridTemplateAreas: '1fr', + textAlign: 'center', + justifyItems: 'center', + alignItems: 'start', + width: '100%', + position: 'absolute', + minHeight: '100%', + [theme.breakpoints.down(600)]: { + width: '90%', + marginLeft: '5%', + }, + }, + titlePage: { + fontSize: '1.5em', + }, + subtitlePage: { + fontSize: '1.1em', + lineHeight: '1.8em', + }, + infoContainer: { + display: 'grid', + gridTemplateColumns: '65% 35%', + gridGap: '1%', + textAlign: 'center', + justifyItems: 'center', + alignItems: 'center', + width: '90%', + [theme.breakpoints.down(600)]: { + gridTemplateColumns: '1fr', + }, + }, + inputsContainer: { + width: '100%', + display: 'grid', + gridTemplateColumns: '100%', + '& .MuiFormHelperText-root': { + fontSize: '0.9em', + color: 'black', + }, + '& .MuiFormLabel-root': { + color: 'black', + }, + }, + avatarPreview: { + textAlign: 'center', + justifyItems: 'center', + alignItems: 'center', + justifySelf: 'end', + padding: '6%', + borderRadius: '5px', + [theme.breakpoints.down(600)]: { + justifySelf: 'center', + padding: '3%', + marginTop: '5%', + }, + }, + username: { + fontSize: '1.1em', + }, + }) +); + +const gitHubApiUrl = 'https://api.github.com/users'; + +const GitHubAccount = connect<{}, SignUpForm>((props) => { + const classes = useStyles(); + const { values, setFieldValue } = props.formik; + const [username, setUsername] = useState(values.githubUsername); + const [inputTimeout, setInputTimeout] = useState(setTimeout(() => {}, 0)); + + const { data: github, error } = useSWR( + values.githubUsername ? `${gitHubApiUrl}/${values.githubUsername}` : null, + async (u) => { + try { + const response = await fetch(u); + if (!response.ok) { + throw new Error(response.statusText); + } + return response.json(); + } catch (err) { + throw err; + } + } + ); + + const handleInputChange = (e: React.ChangeEvent) => { + setUsername(e.target.value); + clearTimeout(inputTimeout); + + // Update githubUsername 1000ms after input change + setInputTimeout( + setTimeout(() => { + setFieldValue('githubUsername', e.target.value); + }, 1000) + ); + }; + + useEffect(() => { + if (error) { + setFieldValue('github', {}, true); + } + + if (github) { + setFieldValue( + 'github', + { + username: github.login, + avatarUrl: github.avatar_url, + }, + true + ); + } + }, [github, error, setFieldValue]); + + return ( +
+
+

GitHub Account

+

Enter Github username and verify your profile

+
+
+ +
+ {!error && github && ( +
+ +

{github.login}

+
+ )} +
+ +
+
+ ); +}); + +export default GitHubAccount; diff --git a/src/web/src/components/SignUp/Forms/Overview.tsx b/src/web/src/components/SignUp/Forms/Overview.tsx new file mode 100644 index 0000000000..25b130858a --- /dev/null +++ b/src/web/src/components/SignUp/Forms/Overview.tsx @@ -0,0 +1,104 @@ +import { createStyles, makeStyles, Theme } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + padding: '0', + margin: '0', + width: '100%', + position: 'relative', + minHeight: '100%', + }, + container: { + display: 'grid', + gridTemplateColumns: '1fr', + justifyItems: 'center', + textAlign: 'center', + alignItems: 'center', + width: '100%', + position: 'absolute', + minHeight: '100%', + [theme.breakpoints.down(600)]: { + width: '90%', + marginLeft: '5%', + }, + }, + welcomeMessage: { + fontSize: '0.8em', + }, + telescopeInfo: { + fontSize: '0.8em', + lineHeight: '2.5em', + }, + helpText: { + fontSize: '0.8em', + lineHeight: '2.5em', + }, + helpButtons: { + display: 'flex', + justifyContent: 'center', + width: '100%', + }, + button: { + padding: '0 0.5em', + background: '#121D59', + color: '#A0D1FB', + fontSize: '0.9em', + margin: '0 0.5em 0em 1em', + '&:hover': { + color: 'black', + borderColor: '#121D59', + }, + height: '30px', + }, + text: { + fontSize: '1.04em', + alignSelf: 'end', + lineHeight: '2.5em', + }, + helpStartText: { + color: '#474747', + }, + }) +); + +const Overview = () => { + const classes = useStyles(); + + return ( +
+
+
+

Welcome

+
+
+

+ Telescope requires a number of pieces of user information, for example your Seneca + email, a GitHub account, a Blog, and a user display name. In the following steps we will + gather this information and create your account. +

+
+
+

If you need help to create a GitHub account and a blog page please check:

+
+
+ + +
+
+

Click Next to complete your Telescope account

+

+ * After clicking Next you will be prompted to login to your Seneca account{' '} +

+
+
+
+ ); +}; + +export default Overview; diff --git a/src/web/src/components/SignUp/Forms/RSSFeeds.tsx b/src/web/src/components/SignUp/Forms/RSSFeeds.tsx new file mode 100644 index 0000000000..75741738f2 --- /dev/null +++ b/src/web/src/components/SignUp/Forms/RSSFeeds.tsx @@ -0,0 +1,264 @@ +import { useState, useEffect } from 'react'; +import { Button, createStyles, makeStyles, Theme } from '@material-ui/core'; +import { connect } from 'formik'; +import FormControl from '@material-ui/core/FormControl'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Checkbox from '@material-ui/core/Checkbox'; + +import { feedDiscoveryServiceUrl } from '../../../config'; +import useAuth from '../../../hooks/use-auth'; +import { SignUpForm } from '../../../interfaces'; +import { TextInput, CheckBoxInput } from '../FormFields'; +import formModels from '../Schema/FormModel'; + +const { blogUrl, blogOwnership } = formModels; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + padding: '0', + margin: '0', + position: 'relative', + width: '100%', + minHeight: '100%', + }, + container: { + display: 'grid', + gridTemplateAreas: '1fr', + textAlign: 'center', + justifyItems: 'center', + alignItems: 'center', + position: 'absolute', + minHeight: '100%', + width: '100%', + [theme.breakpoints.down(600)]: { + width: '95%', + marginLeft: '2.5%', + }, + }, + blogPageTitle: { + fontSize: '1.5em', + }, + helpText: { + fontSize: '1.1em', + lineHeight: '1.8em', + }, + infoContainer: { + display: 'grid', + textAlign: 'center', + gridGap: '10%', + justifyItems: 'center', + alignItems: 'center', + width: '90%', + }, + inputsContainer: { + width: '100%', + display: 'grid', + gridTemplateColumns: '75% 25%', + '& .MuiFormHelperText-root': { + fontSize: '0.9em', + color: 'black', + }, + '& .MuiFormLabel-root': { + color: 'black', + }, + [theme.breakpoints.down(600)]: { + gridTemplateColumns: '80% 20%', + }, + }, + helpMessage: { + fontSize: '.9em', + color: 'black', + }, + button: { + height: '35px', + width: '50%', + alignSelf: 'center', + fontSize: '0.8em', + marginLeft: '5%', + background: '#121D59', + color: '#A0D1FB', + '&:hover': { + color: 'black', + border: '1px solid #121D59', + }, + '&.Mui-disabled': { + backgroundColor: 'inherit', + }, + }, + RssButtonContainer: { + width: '90%', + display: 'grid', + }, + infoRSSContainer: { + minHeight: '120px', + maxHeight: '120px', + width: '100%', + overflowY: 'auto', + }, + noBlogMessage: { + fontSize: '1em', + color: '#474747', + marginTop: '40px', + }, + text: { + fontSize: '0.9em', + alignSelf: 'end', + color: '#474747', + }, + RssButtonWrapper: { + width: '100%', + }, + RssButton: { + width: '101%', + borderRadius: '0', + background: '#121D59', + color: '#A0D1FB', + '&:hover': { + color: 'black', + border: '1px solid #121D59', + }, + }, + agreeMessage: { + [theme.breakpoints.down(600)]: { + alignSelf: 'end', + }, + }, + formControlLabel: { + fontSize: '.9em', + height: '10px', + color: '#474747', + }, + }) +); + +const RSSFeeds = connect<{}, SignUpForm>((props) => { + const classes = useStyles(); + const { values, errors, setFieldValue } = props.formik; + const { token } = useAuth(); + + const [feedUrls, setFeedUrls] = useState>([]); + const [blogUrlError, setBlogUrlError] = useState(''); + const [validating, setValidating] = useState(false); + + // A controller to cancel undesired requests + const [controller, setController] = useState(new AbortController()); + + const validateBlog = async () => { + if (!errors.blogUrl && feedDiscoveryServiceUrl) { + try { + setValidating(true); + const abortController = new AbortController(); + setController(abortController); + const signal = abortController.signal; + const response = await fetch(feedDiscoveryServiceUrl, { + signal, + method: 'post', + headers: { + Authorization: `bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + blogUrl: values.blogUrl, + }), + }); + console.log(values.blogUrl); + if (!response.ok) { + throw new Error(response.statusText); + } + const res = await response.json(); + + setBlogUrlError(''); + setFeedUrls(res.feedUrls); + setValidating(false); + } catch (err) { + console.error(err, 'Unable to discover feeds'); + setBlogUrlError(`Unable to find RSS link at ${values.blogUrl}`); + setFeedUrls([]); + setValidating(false); + } + } else { + setFieldValue('feeds', [], true); + } + }; + + const handleCheck = (url: string) => { + const selectedFeeds = values.feeds.includes(url) + ? values.feeds.filter((val: string) => val !== url) + : [...values.feeds, url]; + + setFieldValue('feeds', selectedFeeds, true); + }; + + useEffect(() => { + if (errors.blogUrl) { + validateBlog(); + } + + // Abort feed-discovery on component unmounting + return () => { + controller.abort(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+
+

Blog and RSS

+

+ Enter your blog URL and select the RSS you want to use in Telescope ecosystem. +

+
+
+ + +
+
+
+ {feedUrls.length ? ( + + + {feedUrls.map((url) => ( + handleCheck(url)} + /> + } + label={

{url}

} + /> + ))} +
+ + {errors.feeds || ''} + +
+ ) : ( +

Please validate your blog URL

+ )} +
+
+
+ +
+
+ ); +}); + +export default RSSFeeds; diff --git a/src/web/src/components/SignUp/Forms/Review.tsx b/src/web/src/components/SignUp/Forms/Review.tsx new file mode 100644 index 0000000000..e3703bda82 --- /dev/null +++ b/src/web/src/components/SignUp/Forms/Review.tsx @@ -0,0 +1,140 @@ +import { createStyles, makeStyles, Theme } from '@material-ui/core'; +import { connect } from 'formik'; +import PostAvatar from '../../Posts/PostAvatar'; + +import { SignUpForm } from '../../../interfaces'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + padding: '0', + margin: '0', + width: '100%', + position: 'relative', + minHeight: '100%', + }, + container: { + display: 'grid', + gridTemplateRows: '10% auto 10%', + textAlign: 'center', + justifyItems: 'center', + alignItems: 'center', + width: '100%', + position: 'absolute', + minHeight: '100%', + [theme.breakpoints.down(600)]: { + width: '90%', + marginLeft: '5%', + }, + }, + titlePage: { + fontSize: '1.5em', + }, + contentContainer: { + width: '90%', + display: 'grid', + gridTemplateColumns: 'auto auto', + gridTemplateRows: 'auto auto', + gridGap: '5%', + alignSelf: 'start', + [theme.breakpoints.down(600)]: { + gridTemplateColumns: '1fr', + height: '100%', + alignItems: 'center', + }, + }, + avatar: { + height: '110px', + display: 'grid', + gridTemplateColumns: '1fr', + textAlign: 'center', + justifyItems: 'center', + alignItems: 'center', + padding: '6%', + fontSize: '0.7em', + [theme.breakpoints.down(600)]: { + height: '85px', + padding: '0', + }, + }, + gitHubInfo: { + marginTop: '8%', + [theme.breakpoints.down(600)]: { + marginTop: '0', + textAlign: 'start', + }, + }, + senecaBlogInfo: { + textAlign: 'start', + }, + blogUrl: { + textAlign: 'start', + }, + titleRss: { + textAlign: 'start', + }, + blogRss: { + textAlign: 'start', + padding: '1%', + minHeight: '60px', + maxHeight: '60px', + overflowY: 'auto', + [theme.breakpoints.down(600)]: { + width: '90%', + }, + }, + text: { + fontSize: '0.9em', + alignSelf: 'end', + color: '#474747', + }, + }) +); + +const Review = connect<{}, SignUpForm>((props) => { + const classes = useStyles(); + + const { feeds, displayName, firstName, lastName, email, github, blogUrl } = props.formik.values; + + return ( +
+
+

Review your Information

+
+
+ +

+ Display Name: +

{displayName}

+ +
+
+

From seneca:

+

Full Name: {displayName || `${firstName} ${lastName}`}

+

Email : {email}

+

Blog URL:

+

{blogUrl}

+
+
+
+

GitHub Account:

+

{github.username}

+
+
+
+

Blog RSS:

+
+
+ {feeds.map((rss) => ( +

{rss}

+ ))} +
+
+
+
+
+
+ ); +}); + +export default Review; diff --git a/src/web/src/components/SignUp/Forms/index.tsx b/src/web/src/components/SignUp/Forms/index.tsx new file mode 100644 index 0000000000..a914de41ae --- /dev/null +++ b/src/web/src/components/SignUp/Forms/index.tsx @@ -0,0 +1,6 @@ +import BasicInfo from './BasicInfo'; +import GitHubAccount from './GitHubAccount'; +import Overview from './Overview'; +import Review from './Review'; + +export { BasicInfo, GitHubAccount, Overview, Review }; diff --git a/src/web/src/components/SignUp/Schema/FormModel.tsx b/src/web/src/components/SignUp/Schema/FormModel.tsx new file mode 100644 index 0000000000..5bb637630f --- /dev/null +++ b/src/web/src/components/SignUp/Schema/FormModel.tsx @@ -0,0 +1,54 @@ +export default { + displayName: { + name: 'displayName', + label: 'Display Name', + invalidErrorMsg: 'Make sure display name contains 2-16 characters', + }, + firstName: { + name: 'firstName', + label: 'First name', + requiredErrorMsg: 'First name is required', + invalidErrorMsg: 'Make sure first name contains 2-16 characters', + }, + lastName: { + name: 'lastName', + label: 'Last name', + requiredErrorMsg: 'Last name is required', + invalidErrorMsg: 'Make sure last name contains 2-16 characters', + }, + email: { + name: 'email', + label: 'Email', + }, + github: { + name: 'github', + label: 'Github Data`', + invalidErrorMsg: 'Invalid GitHub profile', + }, + githubUsername: { + name: 'githubUsername', + label: 'Github username', + requiredErrorMsg: 'Github account is required', + }, + githubOwnership: { + name: 'githubOwnership', + label: 'I declare I’m the owner and the maintainer of this GitHub account', + invalidErrorMsg: 'You must be the owner of this account', + }, + blogUrl: { + name: 'blogUrl', + label: 'Blog URl', + requiredErrorMsg: 'Blog Url is required', + invalidErrorMsg: 'Invalid URL', + }, + feeds: { + name: 'feeds', + label: 'RSS Feeds', + requiredErrorMsg: 'Please select at least one URL', + }, + blogOwnership: { + name: 'blogOwnership', + label: 'I declare I’m the owner and the maintainer of this blog account', + invalidErrorMsg: 'You must be the owner of this account', + }, +}; diff --git a/src/web/src/components/SignUp/Schema/FormSchema.tsx b/src/web/src/components/SignUp/Schema/FormSchema.tsx new file mode 100644 index 0000000000..1f8bedc468 --- /dev/null +++ b/src/web/src/components/SignUp/Schema/FormSchema.tsx @@ -0,0 +1,68 @@ +import * as Yup from 'yup'; + +import formModels from './FormModel'; + +const { + firstName, + lastName, + displayName, + githubUsername, + github, + githubOwnership, + feeds, + blogUrl, + blogOwnership, +} = formModels; + +const validateLength = (min: number, max: number) => (val: string | undefined): boolean => + !!val && val.length >= min && val.length <= max; + +const validateCheckBox = (val: boolean | undefined) => !!val; + +// Each signup step has one validation schema +export default [ + // First step has no validation logic + Yup.object().shape({}), + + Yup.object().shape({ + [firstName.name]: Yup.string() + .required(`${firstName.requiredErrorMsg}`) + .test('len', firstName.invalidErrorMsg, validateLength(2, 16)), + [lastName.name]: Yup.string() + .required(`${lastName.requiredErrorMsg}`) + .test('len', lastName.invalidErrorMsg, validateLength(2, 16)), + [displayName.name]: Yup.string().test( + 'len', + displayName.invalidErrorMsg, + validateLength(2, 16) + ), + }), + + Yup.object().shape({ + [githubUsername.name]: Yup.string().required(`${githubUsername.requiredErrorMsg}`), + [github.name]: Yup.object() + .shape({ + username: Yup.string().required(), + avatarUrl: Yup.string().url().required(), + }) + .required(github.invalidErrorMsg), + [githubOwnership.name]: Yup.boolean().test( + 'agreed', + githubOwnership.invalidErrorMsg, + validateCheckBox + ), + }), + + Yup.object().shape({ + [blogUrl.name]: Yup.string().url().required(`${blogUrl.requiredErrorMsg}`), + [feeds.name]: Yup.array().of(Yup.string()).min(1, feeds.requiredErrorMsg), + [blogOwnership.name]: Yup.boolean().test( + 'agreed', + blogOwnership.invalidErrorMsg, + validateCheckBox + ), + }), + + // Reviewing step has no validation logic + Yup.object().shape({}), +]; diff --git a/src/web/src/config.ts b/src/web/src/config.ts index 1a74601b58..665330618a 100644 --- a/src/web/src/config.ts +++ b/src/web/src/config.ts @@ -12,6 +12,7 @@ const imageServiceUrl = process.env.NEXT_PUBLIC_IMAGE_URL; const authServiceUrl = process.env.NEXT_PUBLIC_AUTH_URL; const postsServiceUrl = process.env.NEXT_PUBLIC_POSTS_URL; const searchServiceUrl = process.env.NEXT_PUBLIC_SEARCH_URL; +const feedDiscoveryServiceUrl = process.env.NEXT_PUBLIC_FEED_DISCOVERY_URL; const title = `Telescope`; const description = `A tool for tracking blogs in orbit around Seneca's open source involvement`; @@ -41,4 +42,5 @@ export { imageAlt, postsServiceUrl, searchServiceUrl, + feedDiscoveryServiceUrl, }; diff --git a/src/web/src/interfaces/index.ts b/src/web/src/interfaces/index.ts index f5c7e14657..d611cea2aa 100644 --- a/src/web/src/interfaces/index.ts +++ b/src/web/src/interfaces/index.ts @@ -15,4 +15,20 @@ export type Post = { html: string; }; +export type SignUpForm = { + displayName: string; + firstName: string; + lastName: string; + email: string; + github: { + username: string; + avatarUrl: string; + }; + githubUsername: string; + githubOwnership: boolean; + blogUrl: string; + feeds: Array; + blogOwnership: boolean; +}; + export type ThemeName = 'light' | 'dark'; diff --git a/src/web/src/pages/signup.tsx b/src/web/src/pages/signup.tsx new file mode 100644 index 0000000000..def31d066f --- /dev/null +++ b/src/web/src/pages/signup.tsx @@ -0,0 +1,271 @@ +import { createStyles, makeStyles, Theme } from '@material-ui/core'; +import { useState, useEffect } from 'react'; + +import Button from '@material-ui/core/Button'; +import { Formik, Form, FormikHelpers } from 'formik'; + +import useAuth from '../hooks/use-auth'; +import Overview from '../components/SignUp/Forms/Overview'; +import BasicInfo from '../components/SignUp/Forms/BasicInfo'; +import GitHubAccount from '../components/SignUp/Forms/GitHubAccount'; +import RSSFeeds from '../components/SignUp/Forms/RSSFeeds'; +import Review from '../components/SignUp/Forms/Review'; +import DynamicImage from '../components/DynamicImage'; + +import { SignUpForm } from '../interfaces'; +import formModels from '../components/SignUp/Schema/FormModel'; +import formSchema from '../components/SignUp/Schema/FormSchema'; +import { usersServiceUrl, webUrl } from '../config'; + +const { + firstName, + lastName, + displayName, + githubUsername, + github, + githubOwnership, + blogUrl, + feeds, + email, + blogOwnership, +} = formModels; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + padding: '0', + margin: '0', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + minHeight: '100vh', + width: '100vw', + boxSizing: 'border-box', + position: 'relative', + fontSize: '1.1rem', + }, + imageContainer: { + minHeight: '100vh', + width: '100vw', + position: 'absolute', + top: '0', + bottom: '0', + zIndex: -1, + [theme.breakpoints.down(600)]: { + display: 'none', + }, + }, + signUpContainer: { + margin: '1% 0 1% 0', + display: 'grid', + gridTemplateRows: '10% auto 15%', + gridGap: '2%', + justifyItems: 'center', + fontFamily: 'Spartan', + height: '510px', + width: '510px', + padding: '1%', + borderRadius: '5px', + boxShadow: '2px 4px 4px 1px rgba(0, 0, 0, 0.1)', + background: '#ECF5FE', + '@media (max-height: 500px) and (max-width: 1024px)': { + margin: '0 0 65px 0', + }, + [theme.breakpoints.down(600)]: { + background: 'none', + boxShadow: 'none', + minHeight: '650px', + height: '600px', + position: 'absolute', + top: '0px', + width: '100%', + margin: '0', + padding: '0', + gridTemplateRows: '8% auto 17%', + }, + }, + title: { + color: '#121D59', + fontSize: '22px', + }, + infoContainer: { + width: '100%', + position: 'relative', + }, + buttonsWrapper: { + margin: '0 auto', + width: '50%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + button: { + height: '4rem', + width: '40%', + fontSize: '1.1em', + padding: '0.7em', + margin: '5px', + background: '#E0C05A', + '&:hover': { + color: 'black', + background: '#EBD898', + }, + }, + buttonLogin: { + height: '4rem', + width: '40%', + fontSize: '1.1em', + padding: '0.7em', + margin: '5px', + background: '#FF0000', + color: '#FFF', + '&:hover': { + background: '#FF7070', + }, + }, + text: { + textAlign: 'center', + fontSize: '0.9em', + color: '#474747', + }, + }) +); + +const SignUpPage = () => { + const classes = useStyles(); + const [activeStep, setActiveStep] = useState(0); + const currentSchema = formSchema[activeStep]; + const { user, token, login } = useAuth(); + const [loggedIn, setLoggedIn] = useState(false); + + const handleNext = () => { + setActiveStep(activeStep + 1); + }; + + const handlePrevious = () => { + setActiveStep(activeStep - 1); + }; + + useEffect(() => { + if (user) { + setLoggedIn(true); + } + }, [user]); + + const handleSubmit = async (values: SignUpForm, actions: FormikHelpers) => { + if (activeStep === 4) { + try { + const { firstName, lastName, email, displayName, github, feeds } = values; + const telescopeUser = { + firstName, + lastName, + email, + displayName, + github, + feeds, + }; + const response = await fetch(`${usersServiceUrl}/${user?.id}`, { + method: 'post', + headers: { + Authorization: `bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(telescopeUser), + }); + + if (!response.ok) { + alert('Unable to create account.'); + throw new Error('Unable to post - create account'); + } + login(); + return; + } catch (err) { + console.error(err, 'Unable to Post'); + window.location.href = `${webUrl}`; + } + } else { + handleNext(); + actions.setTouched({}); + actions.setSubmitting(false); + } + }; + + const renderForm = () => { + switch (activeStep) { + case 0: + return ; + case 1: + return ; + case 2: + return ; + case 3: + return ; + case 4: + return ; + default: + return null; + } + }; + + return ( +
+
+ +
+
+

Telescope Account

+ , + [blogOwnership.name]: false, + } as SignUpForm + } + > + {({ isSubmitting }) => ( + <> +
+ {renderForm()} +
+

Click NEXT to continue

+
+
+ {!loggedIn && ( + + )} + {activeStep > 0 && loggedIn && ( + + )} + {activeStep < 5 && loggedIn && ( + + )} +
+
+ + )} +
+
+
+ ); +}; + +export default SignUpPage; From 42242001b061c06cc3ba5b60eaa4e719840e46bd Mon Sep 17 00:00:00 2001 From: Duke Manh <51073515+DukeManh@users.noreply.github.com> Date: Fri, 16 Apr 2021 05:43:48 -0400 Subject: [PATCH 02/25] Change request (#22) --- docker/production.yml | 2 + .../components/SignUp/Forms/GitHubAccount.tsx | 111 ++++++++++-------- .../src/components/SignUp/Forms/RSSFeeds.tsx | 29 +++-- .../components/SignUp/Schema/FormModel.tsx | 3 - .../components/SignUp/Schema/FormSchema.tsx | 23 +--- src/web/src/pages/signup.tsx | 12 +- 6 files changed, 91 insertions(+), 89 deletions(-) diff --git a/docker/production.yml b/docker/production.yml index 539826896a..88ad1bb54d 100644 --- a/docker/production.yml +++ b/docker/production.yml @@ -30,6 +30,7 @@ services: - AUTH_URL=${AUTH_URL} - POSTS_URL=${POSTS_URL} - SEARCH_URL=${SEARCH_URL} + - FEED_DISCOVERY_URL=${FEED_DISCOVERY_URL} container_name: 'telescope' restart: unless-stopped environment: @@ -49,6 +50,7 @@ services: - API_URL - WEB_URL - SEARCH_URL + - FEED_DISCOVERY_URL - LOG_LEVEL - FEED_URL - FEED_URL_INTERVAL_MS diff --git a/src/web/src/components/SignUp/Forms/GitHubAccount.tsx b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx index cb0975239f..5def070d37 100644 --- a/src/web/src/components/SignUp/Forms/GitHubAccount.tsx +++ b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx @@ -1,15 +1,14 @@ -/* eslint-disable camelcase */ -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; +import Button from '@material-ui/core/Button'; import { createStyles, makeStyles, Theme } from '@material-ui/core'; import { connect } from 'formik'; -import useSWR from 'swr'; import { SignUpForm } from '../../../interfaces'; import formModels from '../Schema/FormModel'; import { TextInput, CheckBoxInput } from '../FormFields'; import PostAvatar from '../../Posts/PostAvatar'; -const { githubUsername, github: githubModel, githubOwnership } = formModels; +const { githubUsername, github, githubOwnership } = formModels; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -56,7 +55,7 @@ const useStyles = makeStyles((theme: Theme) => inputsContainer: { width: '100%', display: 'grid', - gridTemplateColumns: '100%', + gridTemplateColumns: '80% 20%', '& .MuiFormHelperText-root': { fontSize: '0.9em', color: 'black', @@ -65,6 +64,22 @@ const useStyles = makeStyles((theme: Theme) => color: 'black', }, }, + button: { + height: '35px', + width: '50%', + alignSelf: 'center', + fontSize: '0.8em', + background: '#121D59', + color: '#A0D1FB', + marginLeft: '5%', + '&:hover': { + color: 'black', + border: '1px solid #121D59', + }, + '&.Mui-disabled': { + backgroundColor: 'inherit', + }, + }, avatarPreview: { textAlign: 'center', justifyItems: 'center', @@ -89,52 +104,51 @@ const gitHubApiUrl = 'https://api.github.com/users'; const GitHubAccount = connect<{}, SignUpForm>((props) => { const classes = useStyles(); const { values, setFieldValue } = props.formik; - const [username, setUsername] = useState(values.githubUsername); - const [inputTimeout, setInputTimeout] = useState(setTimeout(() => {}, 0)); + const [validating, setValidating] = useState(false); + const [error, setError] = useState(''); + const controllerRef = useRef(); - const { data: github, error } = useSWR( - values.githubUsername ? `${gitHubApiUrl}/${values.githubUsername}` : null, - async (u) => { + const validateGit = async () => { + if (values.githubUsername) { + setValidating(true); + if (controllerRef.current) { + controllerRef.current.abort(); + } + controllerRef.current = new AbortController(); try { - const response = await fetch(u); + const response = await fetch(`${gitHubApiUrl}/${values.githubUsername}`, { + signal: controllerRef.current?.signal, + }); if (!response.ok) { throw new Error(response.statusText); } - return response.json(); + const res = await response.json(); + + setFieldValue('github', { + username: res.login, + avatarUrl: res.avatar_url, + }); + setError(''); } catch (err) { - throw err; + console.error(err, 'Unable to get GitHub profile'); + + setError('Unable to get GitHub profile'); + setFieldValue('github', {}, true); + } finally { + setValidating(false); + controllerRef.current = null; } + } else { + setError(''); + setFieldValue('github', {}, true); } - ); - - const handleInputChange = (e: React.ChangeEvent) => { - setUsername(e.target.value); - clearTimeout(inputTimeout); - - // Update githubUsername 1000ms after input change - setInputTimeout( - setTimeout(() => { - setFieldValue('githubUsername', e.target.value); - }, 1000) - ); }; useEffect(() => { - if (error) { - setFieldValue('github', {}, true); - } - - if (github) { - setFieldValue( - 'github', - { - username: github.login, - avatarUrl: github.avatar_url, - }, - true - ); - } - }, [github, error, setFieldValue]); + return () => { + controllerRef.current?.abort(); + }; + }, []); return (
@@ -147,15 +161,20 @@ const GitHubAccount = connect<{}, SignUpForm>((props) => { label={githubUsername.label} name={githubUsername.name} error={!!error} - helperText={!!error && githubModel.invalidErrorMsg} - onChange={handleInputChange} - value={username} + helperText={error || github.invalidErrorMsg} /> +
- {!error && github && ( + {!error && (
- -

{github.login}

+ +

{values.github.username}

)} diff --git a/src/web/src/components/SignUp/Forms/RSSFeeds.tsx b/src/web/src/components/SignUp/Forms/RSSFeeds.tsx index 75741738f2..4374d5b487 100644 --- a/src/web/src/components/SignUp/Forms/RSSFeeds.tsx +++ b/src/web/src/components/SignUp/Forms/RSSFeeds.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { Button, createStyles, makeStyles, Theme } from '@material-ui/core'; import { connect } from 'formik'; import FormControl from '@material-ui/core/FormControl'; @@ -142,19 +142,18 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => { const [feedUrls, setFeedUrls] = useState>([]); const [blogUrlError, setBlogUrlError] = useState(''); const [validating, setValidating] = useState(false); - - // A controller to cancel undesired requests - const [controller, setController] = useState(new AbortController()); + const controllerRef = useRef(); const validateBlog = async () => { - if (!errors.blogUrl && feedDiscoveryServiceUrl) { + if (!errors.blogUrl) { try { setValidating(true); - const abortController = new AbortController(); - setController(abortController); - const signal = abortController.signal; - const response = await fetch(feedDiscoveryServiceUrl, { - signal, + if (controllerRef.current) { + controllerRef.current.abort(); + } + controllerRef.current = new AbortController(); + const response = await fetch(`${feedDiscoveryServiceUrl}`, { + signal: controllerRef.current?.signal, method: 'post', headers: { Authorization: `bearer ${token}`, @@ -164,7 +163,6 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => { blogUrl: values.blogUrl, }), }); - console.log(values.blogUrl); if (!response.ok) { throw new Error(response.statusText); } @@ -172,11 +170,13 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => { setBlogUrlError(''); setFeedUrls(res.feedUrls); - setValidating(false); } catch (err) { console.error(err, 'Unable to discover feeds'); - setBlogUrlError(`Unable to find RSS link at ${values.blogUrl}`); + + setBlogUrlError('Unable to discover feeds'); setFeedUrls([]); + } finally { + controllerRef.current = null; setValidating(false); } } else { @@ -197,9 +197,8 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => { validateBlog(); } - // Abort feed-discovery on component unmounting return () => { - controller.abort(); + controllerRef.current?.abort(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/web/src/components/SignUp/Schema/FormModel.tsx b/src/web/src/components/SignUp/Schema/FormModel.tsx index 5bb637630f..f0a5576c29 100644 --- a/src/web/src/components/SignUp/Schema/FormModel.tsx +++ b/src/web/src/components/SignUp/Schema/FormModel.tsx @@ -2,19 +2,16 @@ export default { displayName: { name: 'displayName', label: 'Display Name', - invalidErrorMsg: 'Make sure display name contains 2-16 characters', }, firstName: { name: 'firstName', label: 'First name', requiredErrorMsg: 'First name is required', - invalidErrorMsg: 'Make sure first name contains 2-16 characters', }, lastName: { name: 'lastName', label: 'Last name', requiredErrorMsg: 'Last name is required', - invalidErrorMsg: 'Make sure last name contains 2-16 characters', }, email: { name: 'email', diff --git a/src/web/src/components/SignUp/Schema/FormSchema.tsx b/src/web/src/components/SignUp/Schema/FormSchema.tsx index 1f8bedc468..446ea34379 100644 --- a/src/web/src/components/SignUp/Schema/FormSchema.tsx +++ b/src/web/src/components/SignUp/Schema/FormSchema.tsx @@ -14,28 +14,15 @@ const { blogOwnership, } = formModels; -const validateLength = (min: number, max: number) => (val: string | undefined): boolean => - !!val && val.length >= min && val.length <= max; - -const validateCheckBox = (val: boolean | undefined) => !!val; - // Each signup step has one validation schema export default [ // First step has no validation logic Yup.object().shape({}), Yup.object().shape({ - [firstName.name]: Yup.string() - .required(`${firstName.requiredErrorMsg}`) - .test('len', firstName.invalidErrorMsg, validateLength(2, 16)), - [lastName.name]: Yup.string() - .required(`${lastName.requiredErrorMsg}`) - .test('len', lastName.invalidErrorMsg, validateLength(2, 16)), - [displayName.name]: Yup.string().test( - 'len', - displayName.invalidErrorMsg, - validateLength(2, 16) - ), + [firstName.name]: Yup.string().required(`${firstName.requiredErrorMsg}`), + [lastName.name]: Yup.string().required(`${lastName.requiredErrorMsg}`), + [displayName.name]: Yup.string(), }), Yup.object().shape({ @@ -49,7 +36,7 @@ export default [ [githubOwnership.name]: Yup.boolean().test( 'agreed', githubOwnership.invalidErrorMsg, - validateCheckBox + (val) => !!val ), }), @@ -59,7 +46,7 @@ export default [ [blogOwnership.name]: Yup.boolean().test( 'agreed', blogOwnership.invalidErrorMsg, - validateCheckBox + (val) => !!val ), }), diff --git a/src/web/src/pages/signup.tsx b/src/web/src/pages/signup.tsx index def31d066f..48e28e56f3 100644 --- a/src/web/src/pages/signup.tsx +++ b/src/web/src/pages/signup.tsx @@ -1,8 +1,7 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core'; import { useState, useEffect } from 'react'; - -import Button from '@material-ui/core/Button'; import { Formik, Form, FormikHelpers } from 'formik'; +import Button from '@material-ui/core/Button'; import useAuth from '../hooks/use-auth'; import Overview from '../components/SignUp/Forms/Overview'; @@ -15,7 +14,7 @@ import DynamicImage from '../components/DynamicImage'; import { SignUpForm } from '../interfaces'; import formModels from '../components/SignUp/Schema/FormModel'; import formSchema from '../components/SignUp/Schema/FormSchema'; -import { usersServiceUrl, webUrl } from '../config'; +import { usersServiceUrl } from '../config'; const { firstName, @@ -136,7 +135,7 @@ const SignUpPage = () => { const [activeStep, setActiveStep] = useState(0); const currentSchema = formSchema[activeStep]; const { user, token, login } = useAuth(); - const [loggedIn, setLoggedIn] = useState(false); + const [loggedIn, setLoggedIn] = useState(!!user); const handleNext = () => { setActiveStep(activeStep + 1); @@ -164,6 +163,7 @@ const SignUpPage = () => { github, feeds, }; + // TODO Update register URL const response = await fetch(`${usersServiceUrl}/${user?.id}`, { method: 'post', headers: { @@ -174,14 +174,12 @@ const SignUpPage = () => { }); if (!response.ok) { - alert('Unable to create account.'); - throw new Error('Unable to post - create account'); + throw new Error(response.statusText); } login(); return; } catch (err) { console.error(err, 'Unable to Post'); - window.location.href = `${webUrl}`; } } else { handleNext(); From 01a04e9c506f46121b1d7176f4a9276128020193 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Fri, 16 Apr 2021 09:47:14 -0400 Subject: [PATCH 03/25] fix buttons and flow --- src/web/src/pages/signup.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/web/src/pages/signup.tsx b/src/web/src/pages/signup.tsx index 48e28e56f3..1faa797347 100644 --- a/src/web/src/pages/signup.tsx +++ b/src/web/src/pages/signup.tsx @@ -142,14 +142,15 @@ const SignUpPage = () => { }; const handlePrevious = () => { - setActiveStep(activeStep - 1); + if (activeStep > 1) setActiveStep(activeStep - 1); }; useEffect(() => { if (user) { - setLoggedIn(true); + setLoggedIn(!!user); + handleNext(); } - }, [user]); + }, []); const handleSubmit = async (values: SignUpForm, actions: FormikHelpers) => { if (activeStep === 4) { @@ -251,7 +252,7 @@ const SignUpPage = () => { Previous )} - {activeStep < 5 && loggedIn && ( + {activeStep > 0 && loggedIn && ( From f02ac590e4450711b6b885b75615418799814b81 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Fri, 16 Apr 2021 09:57:57 -0400 Subject: [PATCH 04/25] Reword overview Reword signup --- src/web/src/components/SignUp/Forms/Overview.tsx | 10 +++------- src/web/src/pages/signup.tsx | 8 +++++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/web/src/components/SignUp/Forms/Overview.tsx b/src/web/src/components/SignUp/Forms/Overview.tsx index 25b130858a..4638d88c15 100644 --- a/src/web/src/components/SignUp/Forms/Overview.tsx +++ b/src/web/src/components/SignUp/Forms/Overview.tsx @@ -57,9 +57,6 @@ const useStyles = makeStyles((theme: Theme) => alignSelf: 'end', lineHeight: '2.5em', }, - helpStartText: { - color: '#474747', - }, }) ); @@ -91,10 +88,9 @@ const Overview = () => {
-

Click Next to complete your Telescope account

-

- * After clicking Next you will be prompted to login to your Seneca account{' '} -

+

+ After creating a blog and a GitHub account you need to login with your Seneca email. +

diff --git a/src/web/src/pages/signup.tsx b/src/web/src/pages/signup.tsx index 1faa797347..298d5f9242 100644 --- a/src/web/src/pages/signup.tsx +++ b/src/web/src/pages/signup.tsx @@ -238,9 +238,11 @@ const SignUpPage = () => { <>
{renderForm()} -
-

Click NEXT to continue

-
+ {!loggedIn && ( +
+

Click LOGIN to start creating your Telescope Account.

+
+ )}
{!loggedIn && (

Telescope Account

+ {
)}
- {!loggedIn && ( + {activeStep === 0 && ( )} - {activeStep > 0 && loggedIn && ( + {activeStep > 1 && loggedIn && ( From a82ad0dd787748acf18f2aac00d3aea86b343349 Mon Sep 17 00:00:00 2001 From: Duke Manh <51073515+DukeManh@users.noreply.github.com> Date: Sat, 17 Apr 2021 06:44:30 -0400 Subject: [PATCH 11/25] Please squash all into 1 commit (#25) --- .../components/SignUp/Forms/GitHubAccount.tsx | 48 ++++++------ .../src/components/SignUp/Forms/RSSFeeds.tsx | 73 +++++++++---------- .../src/components/SignUp/Forms/Review.tsx | 17 ++--- .../components/SignUp/Schema/FormModel.tsx | 3 + .../components/SignUp/Schema/FormSchema.tsx | 2 + src/web/src/interfaces/index.ts | 1 + src/web/src/pages/signup.tsx | 64 ++++++++-------- 7 files changed, 105 insertions(+), 103 deletions(-) diff --git a/src/web/src/components/SignUp/Forms/GitHubAccount.tsx b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx index 5def070d37..48d128a378 100644 --- a/src/web/src/components/SignUp/Forms/GitHubAccount.tsx +++ b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx @@ -109,38 +109,38 @@ const GitHubAccount = connect<{}, SignUpForm>((props) => { const controllerRef = useRef(); const validateGit = async () => { - if (values.githubUsername) { + if (!values.githubUsername) { + setError(''); + setFieldValue('github', {}, true); + return; + } + try { setValidating(true); if (controllerRef.current) { controllerRef.current.abort(); } controllerRef.current = new AbortController(); - try { - const response = await fetch(`${gitHubApiUrl}/${values.githubUsername}`, { - signal: controllerRef.current?.signal, - }); - if (!response.ok) { - throw new Error(response.statusText); - } - const res = await response.json(); - - setFieldValue('github', { - username: res.login, - avatarUrl: res.avatar_url, - }); - setError(''); - } catch (err) { - console.error(err, 'Unable to get GitHub profile'); - - setError('Unable to get GitHub profile'); - setFieldValue('github', {}, true); - } finally { - setValidating(false); - controllerRef.current = null; + const response = await fetch(`${gitHubApiUrl}/${values.githubUsername}`, { + signal: controllerRef.current?.signal, + }); + if (!response.ok) { + throw new Error(response.statusText); } - } else { + const res = await response.json(); + + setFieldValue('github', { + username: res.login, + avatarUrl: res.avatar_url, + }); setError(''); + } catch (err) { + console.error(err, 'Unable to get GitHub profile'); + + setError('Unable to get GitHub profile'); setFieldValue('github', {}, true); + } finally { + setValidating(false); + controllerRef.current = null; } }; diff --git a/src/web/src/components/SignUp/Forms/RSSFeeds.tsx b/src/web/src/components/SignUp/Forms/RSSFeeds.tsx index 4374d5b487..f6600f1b56 100644 --- a/src/web/src/components/SignUp/Forms/RSSFeeds.tsx +++ b/src/web/src/components/SignUp/Forms/RSSFeeds.tsx @@ -139,48 +139,47 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => { const { values, errors, setFieldValue } = props.formik; const { token } = useAuth(); - const [feedUrls, setFeedUrls] = useState>([]); const [blogUrlError, setBlogUrlError] = useState(''); const [validating, setValidating] = useState(false); const controllerRef = useRef(); const validateBlog = async () => { - if (!errors.blogUrl) { - try { - setValidating(true); - if (controllerRef.current) { - controllerRef.current.abort(); - } - controllerRef.current = new AbortController(); - const response = await fetch(`${feedDiscoveryServiceUrl}`, { - signal: controllerRef.current?.signal, - method: 'post', - headers: { - Authorization: `bearer ${token}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - blogUrl: values.blogUrl, - }), - }); - if (!response.ok) { - throw new Error(response.statusText); - } - const res = await response.json(); + if (errors.blogUrl) { + setFieldValue('feeds', [], true); + return; + } + try { + setValidating(true); + if (controllerRef.current) { + controllerRef.current.abort(); + } + controllerRef.current = new AbortController(); + const response = await fetch(`${feedDiscoveryServiceUrl}`, { + signal: controllerRef.current?.signal, + method: 'post', + headers: { + Authorization: `bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + blogUrl: values.blogUrl, + }), + }); + if (!response.ok) { + throw new Error(response.statusText); + } + const res = await response.json(); - setBlogUrlError(''); - setFeedUrls(res.feedUrls); - } catch (err) { - console.error(err, 'Unable to discover feeds'); + setBlogUrlError(''); + setFieldValue('allFeeds', res.feedUrls); + } catch (err) { + console.error(err, 'Unable to discover feeds'); - setBlogUrlError('Unable to discover feeds'); - setFeedUrls([]); - } finally { - controllerRef.current = null; - setValidating(false); - } - } else { - setFieldValue('feeds', [], true); + setBlogUrlError('Unable to discover feeds'); + setFieldValue('allFeeds', []); + } finally { + controllerRef.current = null; + setValidating(false); } }; @@ -224,10 +223,10 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => {
- {feedUrls.length ? ( + {values.allFeeds.length ? ( - {feedUrls.map((url) => ( + {values.allFeeds.map((url) => ( createStyles({ @@ -77,7 +77,6 @@ const useStyles = makeStyles((theme: Theme) => textAlign: 'start', padding: '1%', minHeight: '60px', - maxHeight: '60px', overflowY: 'auto', [theme.breakpoints.down(600)]: { width: '90%', @@ -103,21 +102,19 @@ const Review = connect<{}, SignUpForm>((props) => {
-

- Display Name: -

{displayName}

- +

{displayName}

From seneca:

-

Full Name: {displayName || `${firstName} ${lastName}`}

+

+ Full Name: {firstName} {lastName} +

Email : {email}

-

Blog URL:

-

{blogUrl}

+

Blog URL: {blogUrl}

-

GitHub Account:

+

GitHub Account

{github.username}

diff --git a/src/web/src/components/SignUp/Schema/FormModel.tsx b/src/web/src/components/SignUp/Schema/FormModel.tsx index c587943d69..363933e28a 100644 --- a/src/web/src/components/SignUp/Schema/FormModel.tsx +++ b/src/web/src/components/SignUp/Schema/FormModel.tsx @@ -44,6 +44,9 @@ export default { label: 'RSS Feeds', requiredErrorMsg: 'Please select at least one URL', }, + allFeeds: { + name: 'allFeeds', + }, blogOwnership: { name: 'blogOwnership', label: 'I declare I’m the owner and the maintainer of this blog account', diff --git a/src/web/src/components/SignUp/Schema/FormSchema.tsx b/src/web/src/components/SignUp/Schema/FormSchema.tsx index 2d17b544c0..2b21a690c0 100644 --- a/src/web/src/components/SignUp/Schema/FormSchema.tsx +++ b/src/web/src/components/SignUp/Schema/FormSchema.tsx @@ -10,6 +10,7 @@ const { github, githubOwnership, feeds, + allFeeds, blogUrl, blogOwnership, } = formModels; @@ -43,6 +44,7 @@ export default [ Yup.object().shape({ [blogUrl.name]: Yup.string().url().required(`${blogUrl.requiredErrorMsg}`), [feeds.name]: Yup.array().of(Yup.string()).min(1, feeds.requiredErrorMsg), + [allFeeds.name]: Yup.array().of(Yup.string()), [blogOwnership.name]: Yup.boolean().test( 'agreed', blogOwnership.invalidErrorMsg, diff --git a/src/web/src/interfaces/index.ts b/src/web/src/interfaces/index.ts index d611cea2aa..6e8cfb45d0 100644 --- a/src/web/src/interfaces/index.ts +++ b/src/web/src/interfaces/index.ts @@ -28,6 +28,7 @@ export type SignUpForm = { githubOwnership: boolean; blogUrl: string; feeds: Array; + allFeeds: Array; blogOwnership: boolean; }; diff --git a/src/web/src/pages/signup.tsx b/src/web/src/pages/signup.tsx index 8021ec94a6..fea43df464 100644 --- a/src/web/src/pages/signup.tsx +++ b/src/web/src/pages/signup.tsx @@ -25,6 +25,7 @@ const { githubOwnership, blogUrl, feeds, + allFeeds, email, blogOwnership, } = formModels; @@ -146,48 +147,46 @@ const SignUpPage = () => { }; useEffect(() => { - console.log('useEffect-USER'); if (user) { setLoggedIn(true); - console.log('USER:', user); handleNext(); } }, [user]); const handleSubmit = async (values: SignUpForm, actions: FormikHelpers) => { - if (activeStep === 4) { - try { - const { firstName, lastName, email, displayName, github, feeds } = values; - const telescopeUser = { - firstName, - lastName, - email, - displayName, - github, - feeds, - }; - const response = await fetch(`${authServiceUrl}/register`, { - method: 'post', - headers: { - Authorization: `bearer ${token}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(telescopeUser), - }); - - if (!response.ok) { - throw new Error(response.statusText); - } - const result = await response.json(); - register(result.token); - return; - } catch (err) { - console.error(err, 'Unable to Post'); - } - } else { + if (activeStep < 4) { handleNext(); actions.setTouched({}); actions.setSubmitting(false); + return; + } + try { + const { firstName, lastName, email, displayName, github, feeds } = values; + const telescopeUser = { + firstName, + lastName, + email, + displayName, + github, + feeds, + }; + const response = await fetch(`${authServiceUrl}/register`, { + method: 'post', + headers: { + Authorization: `bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(telescopeUser), + }); + + if (!response.ok) { + throw new Error(response.statusText); + } + const result = await response.json(); + register(result.token); + return; + } catch (err) { + console.error(err, 'Unable to Post'); } }; @@ -234,6 +233,7 @@ const SignUpPage = () => { }, [blogUrl.name]: 'https://', [feeds.name]: [] as Array, + [allFeeds.name]: [] as Array, [blogOwnership.name]: false, } as SignUpForm } From 6a6f2491ecc1876a2dfbfd7a373744f20d4cb8b0 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 10:39:41 -0400 Subject: [PATCH 12/25] PopUp --- src/web/src/components/PopUp.tsx | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/web/src/components/PopUp.tsx diff --git a/src/web/src/components/PopUp.tsx b/src/web/src/components/PopUp.tsx new file mode 100644 index 0000000000..a8797909ec --- /dev/null +++ b/src/web/src/components/PopUp.tsx @@ -0,0 +1,52 @@ +import { MouseEventHandler } from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +type PopUpProps = { + messageTitle: string; + message: string; + agreeAction: MouseEventHandler; + disagreeAction?: MouseEventHandler; + agreeButtonText: string; + disagreeButtonText?: string; +}; + +const PopUp = ({ + messageTitle, + message, + agreeAction, + disagreeAction, + agreeButtonText, + disagreeButtonText, +}: PopUpProps) => { + return ( + <> + + {messageTitle} + + {message} + + + {disagreeAction && ( + + )} + + + + + ); +}; + +export default PopUp; From 5fef63aa130cc7bbeb2e03e7eca6c4f8d96cc55a Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 10:58:22 -0400 Subject: [PATCH 13/25] remove redirect from AuthProvider --- src/web/src/components/AuthProvider.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/web/src/components/AuthProvider.tsx b/src/web/src/components/AuthProvider.tsx index 402a1eaf4a..d58dcaf46d 100644 --- a/src/web/src/components/AuthProvider.tsx +++ b/src/web/src/components/AuthProvider.tsx @@ -126,7 +126,6 @@ const AuthProvider = ({ children }: Props) => { const register = (token: string) => { setToken(token); - router.push('/'); }; return ( From d378e8c4ecab21a62e10da858452279ddd72f359 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 12:52:43 -0400 Subject: [PATCH 14/25] *fix vulnerability on PopUp *spinner *popups messages --- src/web/src/components/PopUp.tsx | 7 +- src/web/src/pages/signup.tsx | 151 +++++++++++++++++++------------ 2 files changed, 101 insertions(+), 57 deletions(-) diff --git a/src/web/src/components/PopUp.tsx b/src/web/src/components/PopUp.tsx index a8797909ec..261d066684 100644 --- a/src/web/src/components/PopUp.tsx +++ b/src/web/src/components/PopUp.tsx @@ -1,4 +1,7 @@ import { MouseEventHandler } from 'react'; + +import { useRouter } from 'next/router'; + import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; import DialogActions from '@material-ui/core/DialogActions'; @@ -23,10 +26,12 @@ const PopUp = ({ agreeButtonText, disagreeButtonText, }: PopUpProps) => { + const router = useRouter(); return ( <> router.push('/')} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > diff --git a/src/web/src/pages/signup.tsx b/src/web/src/pages/signup.tsx index fea43df464..85162fc763 100644 --- a/src/web/src/pages/signup.tsx +++ b/src/web/src/pages/signup.tsx @@ -1,6 +1,7 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core'; import { useState, useEffect } from 'react'; import { Formik, Form, FormikHelpers } from 'formik'; +import { useRouter } from 'next/router'; import Button from '@material-ui/core/Button'; import useAuth from '../hooks/use-auth'; @@ -15,6 +16,8 @@ import { SignUpForm } from '../interfaces'; import formModels from '../components/SignUp/Schema/FormModel'; import formSchema from '../components/SignUp/Schema/FormSchema'; import { authServiceUrl } from '../config'; +import PopUp from '../components/PopUp'; +import Spinner from '../components/Spinner'; const { firstName, @@ -133,10 +136,21 @@ const useStyles = makeStyles((theme: Theme) => const SignUpPage = () => { const classes = useStyles(); - const [activeStep, setActiveStep] = useState(0); + const [activeStep, setActiveStep] = useState(0); const currentSchema = formSchema[activeStep]; const { user, token, login, register } = useAuth(); const [loggedIn, setLoggedIn] = useState(!!user); + const [telescopeAccount, setTelescopeAccount] = useState(undefined); + const [loading, setLoading] = useState(false); + + const router = useRouter(); + + const resetStates = () => { + setActiveStep(0); + setLoggedIn(false); + setTelescopeAccount(undefined); + setLoading(false); + }; const handleNext = () => { setActiveStep(activeStep + 1); @@ -170,6 +184,9 @@ const SignUpPage = () => { github, feeds, }; + + setLoading(true); + const response = await fetch(`${authServiceUrl}/register`, { method: 'post', headers: { @@ -184,6 +201,8 @@ const SignUpPage = () => { } const result = await response.json(); register(result.token); + setTelescopeAccount(true); + handleNext(); return; } catch (err) { console.error(err, 'Unable to Post'); @@ -212,63 +231,83 @@ const SignUpPage = () => {
-
-

Telescope Account

+ {user?.isRegistered && ( + router.push('/')} + agreeButtonText="Ok" + /> + )} + {telescopeAccount && ( + router.push('/')} + agreeButtonText="Ok" + /> + )} + {!loading ? ( +
+

Telescope Account

- , - [allFeeds.name]: [] as Array, - [blogOwnership.name]: false, - } as SignUpForm - } - > - {({ isSubmitting }) => ( - <> - - {renderForm()} - {!loggedIn && ( -
-

Click LOGIN to start creating your Telescope Account.

-
- )} -
- {activeStep === 0 && ( - - )} - {activeStep > 1 && loggedIn && ( - - )} - {activeStep > 0 && loggedIn && ( - + , + [allFeeds.name]: [] as Array, + [blogOwnership.name]: false, + } as SignUpForm + } + > + {({ isSubmitting }) => ( + <> + + {renderForm()} + {!loggedIn && ( +
+

Click LOGIN to start creating your Telescope Account.

+
)} -
- - - )} -
-
+
+ {activeStep === 0 && ( + + )} + {activeStep > 1 && loggedIn && ( + + )} + {activeStep > 0 && loggedIn && ( + + )} +
+ + + )} + +
+ ) : ( + + )}
); }; From 57565247c65c084fb25b0adf0488c38ce6b66fd2 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 13:21:59 -0400 Subject: [PATCH 15/25] sign up working --- src/web/src/pages/signup.tsx | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/web/src/pages/signup.tsx b/src/web/src/pages/signup.tsx index 85162fc763..d60369574a 100644 --- a/src/web/src/pages/signup.tsx +++ b/src/web/src/pages/signup.tsx @@ -19,6 +19,11 @@ import { authServiceUrl } from '../config'; import PopUp from '../components/PopUp'; import Spinner from '../components/Spinner'; +type TelescopeAccountStatus = { + error?: boolean; + created?: boolean; +}; + const { firstName, lastName, @@ -140,18 +145,11 @@ const SignUpPage = () => { const currentSchema = formSchema[activeStep]; const { user, token, login, register } = useAuth(); const [loggedIn, setLoggedIn] = useState(!!user); - const [telescopeAccount, setTelescopeAccount] = useState(undefined); + const [telescopeAccount, setTelescopeAccount] = useState({}); const [loading, setLoading] = useState(false); const router = useRouter(); - const resetStates = () => { - setActiveStep(0); - setLoggedIn(false); - setTelescopeAccount(undefined); - setLoading(false); - }; - const handleNext = () => { setActiveStep(activeStep + 1); }; @@ -197,11 +195,12 @@ const SignUpPage = () => { }); if (!response.ok) { + setTelescopeAccount({ error: true }); throw new Error(response.statusText); } const result = await response.json(); register(result.token); - setTelescopeAccount(true); + setTelescopeAccount({ created: true }); handleNext(); return; } catch (err) { @@ -231,18 +230,26 @@ const SignUpPage = () => {
+ {telescopeAccount.error && ( + router.push('/')} + agreeButtonText="Ok" + /> + )} {user?.isRegistered && ( router.push('/')} agreeButtonText="Ok" /> )} - {telescopeAccount && ( + {telescopeAccount.created && ( router.push('/')} agreeButtonText="Ok" /> From 8ad2e9256071acd77de773337f0f8c8a4df96081 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 16:28:51 -0400 Subject: [PATCH 16/25] Update TelescopeAvatar component: action:? MouseEventHandler --- src/web/src/components/TelescopeAvatar.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/web/src/components/TelescopeAvatar.tsx b/src/web/src/components/TelescopeAvatar.tsx index 60653f0296..0577aad2b1 100644 --- a/src/web/src/components/TelescopeAvatar.tsx +++ b/src/web/src/components/TelescopeAvatar.tsx @@ -1,3 +1,4 @@ +import { MouseEventHandler } from 'react'; import { Avatar } from '@material-ui/core'; type TelescopeAvatarProps = { @@ -7,6 +8,7 @@ type TelescopeAvatarProps = { blog?: string; backgroundColor?: string; fontColor?: string; + action?: MouseEventHandler; }; const getInitials = (name: string) => { @@ -30,6 +32,7 @@ const TelescopeAvatar = ({ blog, backgroundColor, fontColor, + action, }: TelescopeAvatarProps) => { const initials = getInitials(name); const backColor = backgroundColor || '#A0D1FB'; @@ -42,6 +45,7 @@ const TelescopeAvatar = ({ }} > Date: Sat, 17 Apr 2021 16:51:32 -0400 Subject: [PATCH 17/25] Avatar Logout Landing page --- src/web/src/components/BannerButtons.tsx | 20 ++++++++++++++++++-- src/web/src/components/TelescopeAvatar.tsx | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/web/src/components/BannerButtons.tsx b/src/web/src/components/BannerButtons.tsx index 660eba5dd1..4f8fce2524 100644 --- a/src/web/src/components/BannerButtons.tsx +++ b/src/web/src/components/BannerButtons.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles, Tooltip, withStyles, Zoom } from '@material-ui/core'; import Button from '@material-ui/core/Button'; import useAuth from '../hooks/use-auth'; import TelescopeAvatar from './TelescopeAvatar'; @@ -27,6 +27,13 @@ const useStyles = makeStyles((theme) => ({ }, })); +const ButtonTooltip = withStyles({ + tooltip: { + fontSize: '1.5rem', + margin: 0, + }, +})(Tooltip); + const LandingButtons = () => { const classes = useStyles(); const { login, logout, user } = useAuth(); @@ -58,7 +65,16 @@ const LandingButtons = () => { > Sign out - + +
+ logout()} + name={user.name} + img={user.avatarUrl} + size="40px" + /> +
+
) : ( <> diff --git a/src/web/src/components/TelescopeAvatar.tsx b/src/web/src/components/TelescopeAvatar.tsx index 0577aad2b1..3c5bd90645 100644 --- a/src/web/src/components/TelescopeAvatar.tsx +++ b/src/web/src/components/TelescopeAvatar.tsx @@ -49,6 +49,7 @@ const TelescopeAvatar = ({ alt={name} src={img} style={{ + cursor: 'pointer', width: size, height: size, fontSize: `calc(${size} * 0.43)`, From e4c2add698fd7d3981950602d4185be0b15acbc1 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 17:30:10 -0400 Subject: [PATCH 18/25] update banner buttons popUp --- src/web/src/components/BannerButtons.tsx | 28 +++++++++++++----------- src/web/src/components/PopUp.tsx | 18 +++++++++++++-- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/web/src/components/BannerButtons.tsx b/src/web/src/components/BannerButtons.tsx index 4f8fce2524..0e6fbb5744 100644 --- a/src/web/src/components/BannerButtons.tsx +++ b/src/web/src/components/BannerButtons.tsx @@ -1,8 +1,10 @@ import Link from 'next/link'; +import { useRouter } from 'next/router'; import { makeStyles, Tooltip, withStyles, Zoom } from '@material-ui/core'; import Button from '@material-ui/core/Button'; import useAuth from '../hooks/use-auth'; import TelescopeAvatar from './TelescopeAvatar'; +import PopUp from './PopUp'; const useStyles = makeStyles((theme) => ({ buttonsContainer: { @@ -37,9 +39,20 @@ const ButtonTooltip = withStyles({ const LandingButtons = () => { const classes = useStyles(); const { login, logout, user } = useAuth(); + const router = useRouter(); return (
+ {user && !user?.isRegistered && ( + router.push('/signup')} + agreeButtonText="SIGN UP" + disagreeAction={() => logout()} + disagreeButtonText="ABORT" + /> + )} - {user ? ( + {user?.isRegistered ? ( <> - - +
logout()} diff --git a/src/web/src/components/PopUp.tsx b/src/web/src/components/PopUp.tsx index 261d066684..e36a5ad4ca 100644 --- a/src/web/src/components/PopUp.tsx +++ b/src/web/src/components/PopUp.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler } from 'react'; +import { MouseEventHandler, useState } from 'react'; import { useRouter } from 'next/router'; @@ -16,6 +16,7 @@ type PopUpProps = { disagreeAction?: MouseEventHandler; agreeButtonText: string; disagreeButtonText?: string; + cancelButton?: boolean; }; const PopUp = ({ @@ -25,12 +26,20 @@ const PopUp = ({ disagreeAction, agreeButtonText, disagreeButtonText, + cancelButton, }: PopUpProps) => { + const [open, setOpen] = useState(true); + + const handleClose = () => { + setOpen(false); + }; + const router = useRouter(); + return ( <> router.push('/')} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" @@ -45,6 +54,11 @@ const PopUp = ({ {disagreeButtonText} )} + {cancelButton && ( + + )} From fe67e970e890b298ef6ebd0243d7f4231d666795 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 17:56:10 -0400 Subject: [PATCH 19/25] update PopUp logic --- src/web/src/components/PopUp.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/web/src/components/PopUp.tsx b/src/web/src/components/PopUp.tsx index e36a5ad4ca..598db93a84 100644 --- a/src/web/src/components/PopUp.tsx +++ b/src/web/src/components/PopUp.tsx @@ -12,11 +12,12 @@ import DialogTitle from '@material-ui/core/DialogTitle'; type PopUpProps = { messageTitle: string; message: string; - agreeAction: MouseEventHandler; + buttonText?: string; + agreeAction?: MouseEventHandler; disagreeAction?: MouseEventHandler; agreeButtonText: string; disagreeButtonText?: string; - cancelButton?: boolean; + simple?: boolean; }; const PopUp = ({ @@ -26,7 +27,8 @@ const PopUp = ({ disagreeAction, agreeButtonText, disagreeButtonText, - cancelButton, + simple, + buttonText, }: PopUpProps) => { const [open, setOpen] = useState(true); @@ -39,7 +41,7 @@ const PopUp = ({ return ( <> router.push('/')} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" @@ -54,9 +56,9 @@ const PopUp = ({ {disagreeButtonText} )} - {cancelButton && ( + {simple && ( )}
From 412368ed724b8edc999be83d53d39faefde276f4 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sat, 17 Apr 2021 20:05:34 -0400 Subject: [PATCH 24/25] *github const rename *formmodel name to lower case *formModel Url -> URL *Array of steps commented. --- src/web/src/components/SignUp/Forms/GitHubAccount.tsx | 6 +++--- src/web/src/components/SignUp/Schema/FormModel.tsx | 4 ++-- src/web/src/components/SignUp/Schema/FormSchema.tsx | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/web/src/components/SignUp/Forms/GitHubAccount.tsx b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx index 48d128a378..93a55b8cf2 100644 --- a/src/web/src/components/SignUp/Forms/GitHubAccount.tsx +++ b/src/web/src/components/SignUp/Forms/GitHubAccount.tsx @@ -126,11 +126,11 @@ const GitHubAccount = connect<{}, SignUpForm>((props) => { if (!response.ok) { throw new Error(response.statusText); } - const res = await response.json(); + const result = await response.json(); setFieldValue('github', { - username: res.login, - avatarUrl: res.avatar_url, + username: result.login, + avatarUrl: result.avatar_url, }); setError(''); } catch (err) { diff --git a/src/web/src/components/SignUp/Schema/FormModel.tsx b/src/web/src/components/SignUp/Schema/FormModel.tsx index 363933e28a..a574420426 100644 --- a/src/web/src/components/SignUp/Schema/FormModel.tsx +++ b/src/web/src/components/SignUp/Schema/FormModel.tsx @@ -2,7 +2,7 @@ export default { displayName: { name: 'displayName', label: 'Display Name', - requiredErrorMsg: 'Display Name is required', + requiredErrorMsg: 'Display name is required', }, firstName: { name: 'firstName', @@ -36,7 +36,7 @@ export default { blogUrl: { name: 'blogUrl', label: 'Blog URl', - requiredErrorMsg: 'Blog Url is required', + requiredErrorMsg: 'Blog URL is required', invalidErrorMsg: 'Invalid URL', }, feeds: { diff --git a/src/web/src/components/SignUp/Schema/FormSchema.tsx b/src/web/src/components/SignUp/Schema/FormSchema.tsx index 2b21a690c0..fe5c257669 100644 --- a/src/web/src/components/SignUp/Schema/FormSchema.tsx +++ b/src/web/src/components/SignUp/Schema/FormSchema.tsx @@ -17,7 +17,7 @@ const { // Each signup step has one validation schema export default [ - // First step has no validation logic + // First step we receive data from SSO. Yup.object().shape({}), Yup.object().shape({ @@ -26,6 +26,7 @@ export default [ [displayName.name]: Yup.string().required(`${displayName.requiredErrorMsg}`), }), + // Second step we fetch data from GitHub. Yup.object().shape({ [githubUsername.name]: Yup.string().required(`${githubUsername.requiredErrorMsg}`), [github.name]: Yup.object() @@ -41,6 +42,7 @@ export default [ ), }), + // Third step we collect the user blog and the RSSfeeds from it. Yup.object().shape({ [blogUrl.name]: Yup.string().url().required(`${blogUrl.requiredErrorMsg}`), [feeds.name]: Yup.array().of(Yup.string()).min(1, feeds.requiredErrorMsg), @@ -52,6 +54,6 @@ export default [ ), }), - // Reviewing step has no validation logic + // Reviewing step has no validation logic. We just display all data that we collected. Yup.object().shape({}), ]; From ca9d71f92f91be98fff33a912520edc4dc94e311 Mon Sep 17 00:00:00 2001 From: PedroFonsecaDEV Date: Sun, 18 Apr 2021 12:57:52 -0400 Subject: [PATCH 25/25] production.yml and fix button text --- docker/production.yml | 1 + src/web/src/components/BannerButtons.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/production.yml b/docker/production.yml index 88ad1bb54d..c174876f32 100644 --- a/docker/production.yml +++ b/docker/production.yml @@ -48,6 +48,7 @@ services: - PORT - POSTS_URL - API_URL + - AUTH_URL - WEB_URL - SEARCH_URL - FEED_DISCOVERY_URL diff --git a/src/web/src/components/BannerButtons.tsx b/src/web/src/components/BannerButtons.tsx index 53f370f0b1..69cf01fffd 100644 --- a/src/web/src/components/BannerButtons.tsx +++ b/src/web/src/components/BannerButtons.tsx @@ -55,7 +55,7 @@ const LandingButtons = () => { agreeAction={() => router.push('/signup')} agreeButtonText="SIGN UP" disagreeAction={() => logout()} - disagreeButtonText="ABORT" + disagreeButtonText="CANCEL" /> )}