diff --git a/frontend/.storybook/decorators/withPublicTemplate.tsx b/frontend/.storybook/decorators/withPublicTemplate.tsx index 64959717d..65c6f45d5 100644 --- a/frontend/.storybook/decorators/withPublicTemplate.tsx +++ b/frontend/.storybook/decorators/withPublicTemplate.tsx @@ -1,5 +1,6 @@ import React from 'react'; import PublicBase from 'design-library/templates/base/public-base/public-base'; +import SignupSigninBase from 'design-library/templates/base/signup-signin-base/signup-signin-base'; import ExplorerPublicPage from 'design-library/pages/public-pages/explorer-public-page/explorer-public-page/explorer-public-page'; export const withPublicTemplate = (Story: any, context: any) => { @@ -27,4 +28,12 @@ export const withPublicExplorerTemplate = (Story: any, context: any) => { ) -}; \ No newline at end of file +}; + +export const withSignupSigninBaseTemplate = (Story: any, context: any) => { + return ( + + + + ) +}; diff --git a/frontend/.storybook/main.js b/frontend/.storybook/main.js index f5d28d9c9..7583909a3 100644 --- a/frontend/.storybook/main.js +++ b/frontend/.storybook/main.js @@ -18,11 +18,10 @@ module.exports = { webpackFinal: async (config) => { config.resolve.alias = { ...config.resolve.alias, - images: path.resolve(__dirname, "../src/images"), // Add your alias - app: path.resolve(__dirname, "../src"), // Add your alias - "images": path.resolve(__dirname, "../src/images"), // Add your alias - "design-library": path.resolve(__dirname, "../src/components/design-library"), // Add your alias - "@material-ui/core": "@mui/material", // map MUI v4 import to MUI v5 if present + images: path.resolve(__dirname, "../src/images"), + app: path.resolve(__dirname, "../src"), + "images": path.resolve(__dirname, "../src/images"), + "design-library": path.resolve(__dirname, "../src/components/design-library"), }; config.resolve.fallback = { ...config.resolve.fallback, diff --git a/frontend/src/actions/loginActions.js b/frontend/src/actions/loginActions.js index c3b4aa1d7..6d455e9d5 100644 --- a/frontend/src/actions/loginActions.js +++ b/frontend/src/actions/loginActions.js @@ -231,9 +231,9 @@ export const forgotPassword = data => { .post(api.API_URL + '/auth/forgot-password', data) .then(response => { if(response) { - dispatch(addNotification('user.forgot.password.successfull')) + return dispatch(addNotification('user.forgot.password.successfull')) } else { - dispatch(addNotification('user.forgot.password.error')) + return dispatch(addNotification('user.forgot.password.error')) } }) .catch(error => { @@ -286,7 +286,7 @@ const searchUserRequested = () => { } const searchUserSuccess = user => { - return { type: SEARCH_USER_SUCCESS, logged: false, completed: true, user: user } + return { type: SEARCH_USER_SUCCESS, logged: false, completed: true, data: user } } const searchUserError = error => { diff --git a/frontend/src/components/areas/private/components/session/cookie-policy.tsx b/frontend/src/components/areas/private/components/session/cookie-policy.tsx index 3a08e8ad6..9a3dcbd0f 100644 --- a/frontend/src/components/areas/private/components/session/cookie-policy.tsx +++ b/frontend/src/components/areas/private/components/session/cookie-policy.tsx @@ -49,7 +49,7 @@ We may update this Cookie Policy from time to time to reflect changes in our pra {br}{br} Contact us {br}{br} -If you have any questions or concerns about this Cookie Policy or our use of cookies on Gitpay, please contact us at [insert contact information]. +If you have any questions or concerns about this Cookie Policy or our use of cookies on Gitpay, please contact us at contact@gitpay.me. {br} Please note that this Cookie Policy should be read in conjunction with our Privacy Policy, which provides further information on how we collect, use, and disclose your personal data. ` diff --git a/frontend/src/components/areas/public/features/session/components/session.tsx b/frontend/src/components/areas/public/features/session/components/session.tsx new file mode 100644 index 000000000..3f39d8eab --- /dev/null +++ b/frontend/src/components/areas/public/features/session/components/session.tsx @@ -0,0 +1,24 @@ +import { useEffect } from 'react' +import Auth from '../../../../../../modules/auth' +import { useHistory, useParams } from 'react-router-dom' + +const Session = () => { + const history = useHistory() + const { token } = useParams<{ token: string }>() + + useEffect(() => { + const referer = Auth.getReferer() + + Auth.authenticateUser(token) + + if (referer && referer !== '/') { + history.replace(referer) + } else { + history.replace('/profile') + } + }, [token, history]) + + return null +} + +export default Session diff --git a/frontend/src/components/areas/public/features/session/pages/account-activation-page.tsx b/frontend/src/components/areas/public/features/session/pages/account-activation-page.tsx new file mode 100644 index 000000000..f232e6a55 --- /dev/null +++ b/frontend/src/components/areas/public/features/session/pages/account-activation-page.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import AccountActivation from 'design-library/pages/public-pages/session-public-pages/account-activation/account-activation'; + +const AccountActivationPage = ({ + activateAccount, +}) => { + + return ; +} + +export default AccountActivationPage; \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/session/pages/forgot-page.tsx b/frontend/src/components/areas/public/features/session/pages/forgot-page.tsx new file mode 100644 index 000000000..7aa186866 --- /dev/null +++ b/frontend/src/components/areas/public/features/session/pages/forgot-page.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ForgotPasswordPage from 'design-library/pages/public-pages/session-public-pages/forgot-password-page/forgot-password-page'; + +const ForgotPage = ({ + forgotPassword, +}) => { + return ( + + ); +}; +export default ForgotPage; \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/session/pages/reset-page.tsx b/frontend/src/components/areas/public/features/session/pages/reset-page.tsx new file mode 100644 index 000000000..8bf586c8a --- /dev/null +++ b/frontend/src/components/areas/public/features/session/pages/reset-page.tsx @@ -0,0 +1,22 @@ +import React, { useEffect } from "react" +import ResetPasswordPage from "design-library/pages/public-pages/session-public-pages/reset-password-page/reset-password-page" +import { useParams } from "react-router-dom"; + +const ResetPage = ({ user, searchUser, resetPassword }) => { + const { token } = useParams<{ token: string }>() + + useEffect(() => { + if (token) { + searchUser({ recover_password_token: token }) + } + }, [token, searchUser]) + + return ( + + ); +} + +export default ResetPage; \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/session/pages/session-page.tsx b/frontend/src/components/areas/public/features/session/pages/session-page.tsx new file mode 100644 index 000000000..42ed28fbb --- /dev/null +++ b/frontend/src/components/areas/public/features/session/pages/session-page.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { HashRouter, Route, Switch } from 'react-router-dom'; +import SignupSigninPage from 'design-library/templates/base/signup-signin-base/signup-signin-base'; +import Session from '../components/session'; +import AccountActivation from '../../../../../../containers/account-activation' +import LoginPageContainer from '../../../../../../containers/login-page' +import RegisterPageContainer from '../../../../../../containers/register-page' +import ForgotPasswordPageContainer from '../../../../../../containers/forgot-password-page' +import ResetPasswordPageContainer from '../../../../../../containers/reset-password-page' + +const SessionPage = () => { + return ( + + + + + + + + + + + + + + + ); +}; + +export default SessionPage; + diff --git a/frontend/src/components/areas/public/features/session/pages/signin-page.tsx b/frontend/src/components/areas/public/features/session/pages/signin-page.tsx new file mode 100644 index 000000000..d4e77204c --- /dev/null +++ b/frontend/src/components/areas/public/features/session/pages/signin-page.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import SigninPageTemplate from 'design-library/pages/public-pages/session-public-pages/signin-page/signin-page' + +const SigninPage = ({ + addNotification, +}) => { + return ( + + ) +} + +export default SigninPage \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/session/pages/signup-page.tsx b/frontend/src/components/areas/public/features/session/pages/signup-page.tsx new file mode 100644 index 000000000..0625bb5b8 --- /dev/null +++ b/frontend/src/components/areas/public/features/session/pages/signup-page.tsx @@ -0,0 +1,18 @@ +import React from 'react' +import SignupPageTemplate from 'design-library/pages/public-pages/session-public-pages/signup-page/signup-page' + +const SignupPage = ({ + fetchRoles, + registerUser, + roles, +}) => { + return ( + + ) +} + +export default SignupPage \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/welcome/components/BottomSectionDialog.tsx b/frontend/src/components/areas/public/features/welcome/components/BottomSectionDialog.tsx deleted file mode 100644 index 1af73ce3b..000000000 --- a/frontend/src/components/areas/public/features/welcome/components/BottomSectionDialog.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react' -import { - ListItem, - ListItemButton, - Typography, - Dialog, - AppBar, - Toolbar, - IconButton, -} from '@mui/material' - -import { - Close, -} from '@mui/icons-material' - -import { InfoList } from './CommonStyles' -import { AppBar as AppBarStyles, AppBarHeader } from './bottom-section-dialog.styles' - - -const BottomSectionDialog = ({ - classes, - header, - title, - subtitle, - content, -}) => { - - const [ open, setOpen ] = React.useState(false) - - return ( - - - setOpen(true) } - component='div' - style={ { display: 'block', width: '100%' } } - > - {header} - - - setOpen(false) } - > - - - setOpen(false) } - aria-label='Close' - > - - - - {title} - - - - {content} - - - - - ) -} - -export default BottomSectionDialog \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/welcome/components/Info.js b/frontend/src/components/areas/public/features/welcome/components/Info.js deleted file mode 100644 index b0ad7cd41..000000000 --- a/frontend/src/components/areas/public/features/welcome/components/Info.js +++ /dev/null @@ -1,46 +0,0 @@ -import React, { useEffect } from 'react' -import { FormattedMessage } from 'react-intl' -import styled from 'styled-components' - -import { - Chip, - Typography -} from '@mui/material' - -const Content = styled.span` - margin-top: 5px; - padding-bottom: 10px; - color: white; - text-align: left; - display: inline-block; -` - -const Info = ({ info, tasks, bounties, users }) => { - useEffect(() => { - info() - }, [info]) - - const stats = { - tasks: { value: tasks || '0' }, - bounties: { value: '$' + (bounties || '0') }, - users: { value: users || '0' } - } - - return ( - - - } />, - bounties: , - users: } /> } /> - } } /> - - - ) -} - -export default Info diff --git a/frontend/src/components/areas/public/features/welcome/components/OurStack.js b/frontend/src/components/areas/public/features/welcome/components/OurStack.js deleted file mode 100644 index 8b8ce3ee7..000000000 --- a/frontend/src/components/areas/public/features/welcome/components/OurStack.js +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react' -import { FormattedMessage } from 'react-intl' -import styled from 'styled-components' - -import { - Chip, - Typography, -} from '@mui/material' - -import PropTypes from 'prop-types' - -const data = [ - { label: 'React.js', url: 'https://reactjs.org/' }, - { label: 'Material-ui', url: 'https://material-ui.com/' }, - { label: 'Node.js', url: 'https://nodejs.org' }, - { label: 'Postgres', url: 'https://www.postgresql.org/' }, - { label: 'Heroku', url: 'http://heroku.com/' }, - { label: 'Github', url: 'https://github.com' } -] - -export const Technology = styled(Chip)` - && { - margin: 10px; - font-weight: bold; - } -` - -export const Stack = styled.div` - margin-top: 5px; -` - -export const Content = styled.div` - margin-top: 20px; - padding-bottom: 10px; - color: black; - text-align: center; -` - -const handleClick = url => event => { - window.location.href = url -} - -const OurStack = ({ technologies }) => ( - - - - - { technologies.map((technology, index) => - () - ) - } - - - -) - -OurStack.propTypes = { - technologies: PropTypes.array -} - -OurStack.defaultProps = { - technologies: data -} - -export default OurStack diff --git a/frontend/src/components/areas/public/features/welcome/components/bottom-section-dialog.styles.ts b/frontend/src/components/areas/public/features/welcome/components/bottom-section-dialog.styles.ts deleted file mode 100644 index 5fea6cb06..000000000 --- a/frontend/src/components/areas/public/features/welcome/components/bottom-section-dialog.styles.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { styled } from '@mui/material/styles' -import MuiAppBar from '@mui/material/AppBar' -import { Typography } from '@mui/material' - -export const AppBarHeader = styled(Typography)(({ theme }) => ({ - color: theme.palette.primary.main -})) - -export const AppBar = styled(MuiAppBar)({ - height: '100%', - width: '100%' -}) \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/welcome/components/cubes.js b/frontend/src/components/areas/public/features/welcome/components/cubes.js deleted file mode 100644 index 34f5e4291..000000000 --- a/frontend/src/components/areas/public/features/welcome/components/cubes.js +++ /dev/null @@ -1,167 +0,0 @@ -import React, { useEffect } from 'react' -import * as THREE from 'three' - -const cubeX = (x, y, z, edges, scene) => { - const cube = new THREE.LineSegments(edges, - new THREE.LineBasicMaterial({ - color: '#58d7b2', - opacity: Math.random(), - transparent: true, - linewidth: 3, - linecap: 'round', // ignored by WebGLRenderer - linejoin: 'round' // ignored by WebGLRenderer - })) - cube.position.set(x, y, z) - cube.scale.x = 0.01 - cube.scale.y = 0.01 - cube.scale.z = 0.01 - scene.add(cube) // adds the cube to the scene - return cube -} - -const cubeRotateAndScaleUp = (cube) => { - cube.rotation.z += 0.005 - cube.rotation.x += 0.005 - cube.rotation.y += 0.005 // Runs every frame giving it the animation - cube.scale.x += 0.001 - cube.scale.y += 0.001 - cube.scale.z += 0.001 -} - -const cubeRotateAndScaleDown = (cube) => { - cube.material.opacity -= 0.0002 - cube.material.opacity -= 0.0002 -} - -const cubeReset = (cube) => { - cube.material.opacity = 1 - cube.scale.x = 0.01 - cube.scale.y = 0.01 - cube.scale.z = 0.01 -} - -const Cubes = ({ children }) => { - useEffect(() => { - const height = 285 - const width = window.innerWidth - - // 3 must haves - SCENE , CAMERA, RENDERER - - let scene = new THREE.Scene() // Creates a new scene - - let camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000) // Creates a camera and passes (field of view, aspect ratio, near clipping plane, far clipping plane) - camera.position.set(0, 0, 5)// moves the camera back some so we won't be inside of the cube - camera.lookAt(scene.position) // makes the camera always point toward the scene - scene.add(camera) - - let light = new THREE.PointLight('rgb(88,215,178)') - light.position.set(10, 0, 10) - scene.add(light) - - let renderer = new THREE.WebGLRenderer() - renderer.setSize(width, height) // sets size of render to the screen size - renderer.domElement.style.position = 'absolute' - renderer.domElement.style.top = 0 - renderer.domElement.style.zIndex = -1 - renderer.domElement.style.height = '400px' - document.getElementById('cube-placement').appendChild(renderer.domElement) // Renders a canvas tag to the DOM - - const geometry = new THREE.BoxGeometry() - const sphereGeometry = new THREE.SphereGeometry(2, 32, 32, 0, 6.3, 0, 3) - const edges = new THREE.EdgesGeometry(geometry) - const sphereEdges = new THREE.EdgesGeometry(sphereGeometry) - const cubes = [ - [15, 0, 0, 0], - [15, -3, -1, 0], - [18, 1, 0, 0], - [19, -3, -1, 0], - [-19, -3, -1, 0], - [16, -2, 1, 0], - [-15, 0, 0, 0], - [-15, 1, 1, 0], - [-18, -1, 0, 0], - [-16, 2, -1, 0], - [-17, 1, 0, 0], - [-16, -2, -1, 0], - [-16, 2, 0, 0], - [15, 0, 0, 0], - [15, -3, -1, 0], - [18, 1, 0, 0], - [-15, 0, 0, 0], - [-15, 1, 1, 0], - [-18, -1, 0, 0], - [-16, 2, -1, 0], - [-17, 1, 0, 0], - [-16, -2, -1, 0], - [-16, 2, 0, 0], - [15, 0, 0, 0], - [15, -3, -1, 0], - [18, 1, 0, 0], - [16, -2, 1, 0], - [-15, 0, 0, 0], - [-15, 1, 1, 0], - [-18, -1, 0, 0], - [-16, 2, -1, 0], - [-17, 1, 0, 0], - [-16, -2, -1, 0], - [-16, 2, 0, 0], - [15, 0, 0, 0], - [15, -3, -1, 0], - [18, 1, 0, 0], - [16, -2, 1, 0], - [-15, 0, 0, 0], - [-15, 1, 1, 0], - [-18, -1, 0, 0], - [-16, 2, -1, 0], - [-17, 1, 0, 0], - [-16, -2, -1, 0], - [-16, 2, 0, 0], - [15, 0, 0, 0], - [15, -3, -1, 0], - [18, 1, 0, 0], - [16, -2, 1, 0], - [-15, 0, 0, 0], - [-15, 1, 1, 0], - [-18, -1, 0, 0], - [-16, 2, -1, 0], - [-17, 1, 0, 0], - [-16, -2, -1, 0], - [-16, 2, 0, 0] - - ] - let cubesInstances = [] - const edgetTypes = [edges, sphereEdges] - for (let i = 0; i < cubes.length; i++) { - cubesInstances[i] = cubeX(cubes[i][0], cubes[i][1], cubes[i][2], edgetTypes[cubes[i][3]], scene) - } - - // Render loop to display cube - function render () { - const animation = window.requestAnimationFrame(render) // requestAnimationFrame will pause when the user navigates to a new tab - - for (let i = 0; i < cubesInstances.length; i++) { - cubeRotateAndScaleUp(cubesInstances[i]) - cubeRotateAndScaleDown(cubesInstances[i]) - } - - if (cubesInstances[0].material.opacity < 0) { - window.cancelAnimationFrame(animation) - for (let i = 0; i < cubesInstances.length; i++) { - cubeReset(cubesInstances[i]) - } - render() - } - renderer.render(scene, camera) - } - - render() - }, []) - - return ( -
- { children } -
- ) -} - -export default Cubes diff --git a/frontend/src/components/areas/public/features/welcome/legacy/clientlist.js b/frontend/src/components/areas/public/features/welcome/legacy/clientlist.js deleted file mode 100644 index 6b55ec0a7..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/clientlist.js +++ /dev/null @@ -1,80 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' - -import { Card, Grid, Typography } from '@mui/material' -import { styled } from '@mui/material/styles' - -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - MainTitle, - ResponsiveImage -} from '../components/CommonStyles' - -const Layout = styled('div')(({ theme }) => ({ - width: 'auto', - marginBottom: theme.spacing(6), - marginTop: theme.spacing(2), - [theme.breakpoints.up(900 + theme.spacing(3) * 2)]: { - width: '100%' - }, - textAlign: 'center' -})) - -const HeroContent = styled('div')(({ theme }) => ({ - maxWidth: 600, - margin: '0 auto', - padding: `0 0 ${theme.spacing(4)}px`, -})) - -import clientimg1 from 'images/clients/electron-logo.png' -import clientimg2 from 'images/clients/etherpad-logo.png' -import clientimg3 from 'images/clients/sitespeedio-logo.png' - -const clients = [ - { - title: 'Electron', - img: clientimg1, - link: 'https://www.electron.build/' - }, - { - title: 'Etherpad', - img: clientimg2, - link: 'https://etherpad.org/' - }, - { - title: 'Sitespeed.io', - img: clientimg3, - link: 'https://sitespeed.io' - }, -] -class Clientlist extends Component { - render () { - const { } = this.props - - return ( - - - - - - - - - - { clients.map(client => ( - - - - - - )) } - - - ) - } -} - -Clientlist.propTypes = {} - -export default injectIntl(Clientlist) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/consulting.js b/frontend/src/components/areas/public/features/welcome/legacy/consulting.js deleted file mode 100644 index f59445357..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/consulting.js +++ /dev/null @@ -1,127 +0,0 @@ -import React, { useState, useEffect } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Work, - AccountBalanceWallet, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -const Consulting = ({ classes, ...props }) => { - const [open, setOpen] = useState(false) - - const handleClickOpen = () => { - setOpen(true) - } - - const handleClose = () => { - setOpen(false) - } - - useEffect(() => { - // Component did mount functionality here - }, []) - - return ( - - - - {(msg) => ( - - )} - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- ) -} - -Consulting.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(Consulting) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/contact-recruiter-form.js b/frontend/src/components/areas/public/features/welcome/legacy/contact-recruiter-form.js deleted file mode 100644 index 522e943b8..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/contact-recruiter-form.js +++ /dev/null @@ -1,224 +0,0 @@ -import React, { useState } from 'react' -import Avatar from '@mui/material/Avatar' -import Button from '@mui/material/Button' -import CssBaseline from '@mui/material/CssBaseline' -import TextField from '@mui/material/TextField' -import CircularProgress from '@mui/material/CircularProgress' -import Grid from '@mui/material/Grid' -import { MuiTelInput } from 'mui-tel-input' -import { - ContactMailOutlined -} from '@mui/icons-material' -import Typography from '@mui/material/Typography' -import { styled } from '@mui/material/styles' -import Container from '@mui/material/Container' -import CountryList from './country-list' - -function checkEmail (emailAddress) { - let sQtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]' - let sDtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]' - let sAtom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+' - let sQuotedPair = '\\x5c[\\x00-\\x7f]' - let sDomainLiteral = '\\x5b(' + sDtext + '|' + sQuotedPair + ')*\\x5d' - let sQuotedString = '\\x22(' + sQtext + '|' + sQuotedPair + ')*\\x22' - let sDomainRef = sAtom - let sSubDomain = '(' + sDomainRef + '|' + sDomainLiteral + ')' - let sWord = '(' + sAtom + '|' + sQuotedString + ')' - let sDomain = sSubDomain + '(\\x2e' + sSubDomain + ')*' - let sLocalPart = sWord + '(\\x2e' + sWord + ')*' - let sAddrSpec = sLocalPart + '\\x40' + sDomain // complete RFC822 email address spec - let sValidEmail = '^' + sAddrSpec + '$' // as whole string - - let reValidEmail = new RegExp(sValidEmail) - - return reValidEmail.test(emailAddress) -} - -const Wrapper = styled('div')(({ theme }) => ({ - marginTop: theme.spacing(1), - display: 'flex', - flexDirection: 'column', - alignItems: 'center', -})) - -const StyledAvatar = styled(Avatar)(({ theme }) => ({ - margin: theme.spacing(1), - backgroundColor: theme.palette.secondary.main, -})) - -const StyledForm = styled('form')(({ theme }) => ({ - width: '100%', - marginTop: theme.spacing(2), -})) - -const SubmitButton = styled(Button)(({ theme }) => ({ - margin: theme.spacing(3, 0, 2), - textTransform: 'none', - background: '#4A4EDD', - height: 50, - '&:hover': { - background: '#7F83FF', - }, -})) - -export default function ContactRecruiterForm (props) { - const [formData, setFormData] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { contact } = props - - const validate = (event) => { - const name = event.nativeEvent.target.name - const value = event.nativeEvent.target.value - const required = event.nativeEvent.target.hasAttribute('required') - if (required) { - if (name === 'email' && !checkEmail(value)) { - setFormErrors({ ...formErrors, email: 'invalid email' }) - } - else - if (value === '' || value.length < 3) { - setFormErrors({ ...formErrors, [name]: `invalid ${name}` }) - } - else { - setFormErrors({}) - } - } - } - - const onChange = (event) => { - const name = event.nativeEvent.target.name - const value = event.nativeEvent.target.value - setFormData({ ...formData, [name]: value }) - } - - const onSubmit = (event) => { - event.preventDefault() - if (Object.keys(formErrors).length === 0) { - props.contactRecruiters(formData) - } - } - - const onBlur = (event) => { - validate(event) - } - - return ( - - - - - - - - Contact us - - - - - - - - - - - - - - - - Phone: - - - - - - - - - - - - - - - - - - { contact.completed ? ( - Contact us - ) : ( - - ) } - - - - - - ) -} diff --git a/frontend/src/components/areas/public/features/welcome/legacy/country-list.js b/frontend/src/components/areas/public/features/welcome/legacy/country-list.js deleted file mode 100644 index efea37421..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/country-list.js +++ /dev/null @@ -1,36 +0,0 @@ -import React, { Component } from 'react' - -// note that you can also export the source data via CountryRegionData. It's in a deliberately concise format to -// keep file size down -import { CountryDropdown } from 'react-country-region-selector' - -export default class CountryList extends Component { - constructor (props) { - super(props) - this.state = { country: 'Select Country' } - } - - selectCountry (val) { - this.setState({ country: val }) - } - - render () { - const { country } = this.state - return ( -
- this.selectCountry(val) } /> -
- ) - } -} diff --git a/frontend/src/components/areas/public/features/welcome/legacy/home.js b/frontend/src/components/areas/public/features/welcome/legacy/home.js deleted file mode 100644 index 7c507b3c7..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/home.js +++ /dev/null @@ -1,232 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { Grid, Typography, Divider, List, ListItem, ListItemText, ListItemIcon, Button, Container } from '@mui/material' -import { styled } from '@mui/material/styles' - -import { - Work, - Archive, - CardMembership, - BugReport -} from '@mui/icons-material' - -import { injectIntl, FormattedMessage } from 'react-intl' - -import TopBarContainer from '../../../../../../containers/topbar' -import Bottom from 'design-library/organisms/layouts/bottom-bar-layouts/bottom-bar-layout/bottom-bar-layout' -import messages from './messages' -// removed withStyles/theme styles usage - -import freelancerImage from 'images/collections/collection-flat-build.svg' -import citySoftware from 'images/collections/collection-flat-background.svg' -import deal from 'images/collections/collection-flat-works.svg' - -import { - MainTitle, - ResponsiveImage, - Section, - HeroSection, - HeroTitle, - HeroContent, - HeroActions -} from '../components/CommonStyles' - -// legacy styles removed - -class Home extends Component { - constructor (props) { - super(props) - - this.state = { - value: 0 - } - } - - render () { - const { } = this.props - - return ( -
- -
- - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - -
- -
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- - - - - -
- -
- ) - } -} - -Home.propTypes = {} - -export default injectIntl(Home) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/how-it-works-company.js b/frontend/src/components/areas/public/features/welcome/legacy/how-it-works-company.js deleted file mode 100644 index ae79c7fa7..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/how-it-works-company.js +++ /dev/null @@ -1,138 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Work, - AccountBalanceWallet, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -class HowItWorksCompany extends Component { - constructor (props) { - super(props) - this.state = { - open: false - } - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - } - - componentDidMount () { - - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - render () { - const { classes } = this.props - - return ( - - - - { (msg) => ( - - ) } - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- ) - } -} - -HowItWorksCompany.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(HowItWorksCompany) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/how-it-works-people.js b/frontend/src/components/areas/public/features/welcome/legacy/how-it-works-people.js deleted file mode 100644 index 4aab179b3..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/how-it-works-people.js +++ /dev/null @@ -1,141 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Work, - AccountBalanceWallet, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -class HowItWorksPeople extends Component { - constructor (props) { - super(props) - this.state = { - open: false - } - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - render () { - const { classes } = this.props - - return ( - - - - { (msg) => ( - - ) } - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- ) - } -} - -HowItWorksPeople.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(HowItWorksPeople) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/landing-page.js b/frontend/src/components/areas/public/features/welcome/legacy/landing-page.js deleted file mode 100644 index 1ae176c44..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/landing-page.js +++ /dev/null @@ -1,406 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { FormattedMessage } from 'react-intl' -import { - Button, - AppBar, - Toolbar, - Grid, - Typography, - Link -} from '@mui/material' -import peopleImage from 'images/landingPage_People.png' -import logoGrey from 'images/logo-complete-gray.png' -import screenImage from 'images/gitpay-explore-task-screenshot.png' -import profileImage from 'images/avatar-alexandre.png' -import Bottom from 'design-library/organisms/layouts/bottom-bar-layouts/bottom-bar-layout/bottom-bar-layout' -import Clientlist from './clientlist' -import TeamCard from 'design-library/molecules/cards/team-card/team-card' -import ContactRecruiterFormContainer from '../../../../../../containers/contact-recruiter-form' - -import deal from 'images/deal.png' - -import { - MainTitle, - MainList, - ResponsiveImage, - Section -} from '../components/CommonStyles' - -// Landing Page for GitPay -// Total hours worked on this: ~12hours -// Still need to add in functionality for buttons -// Export Styles to separate file to clean up code but left here for you to see for now - -import headhunterTeamMember1 from 'images/teams/headhunter-team-member1.png' - -const recruiterTeam = [ - { - name: 'Alexandre Magno', - description: 'Founder of Gitpay, and senior software engineer, sharing and helping developers to face the challenges of technical recruitment.', - image: headhunterTeamMember1, - linkedinUrl: 'https://www.linkedin.com/in/alexandremagnoteleszimerer/' - } -] - -const styles = (theme) => ({ - signText: { - fontWeight: 400, - marginTop: 0, - marginBottom: 3, - fontSize: '1.25em', - [theme.breakpoints.down('sm')]: { - fontSize: '1em', - }, - }, - buttonSignin: { - textTransform: 'none', - width: 120, - borderRadius: 25, - marginRight: 20, - '&:hover': { - color: 'white', - background: '#7F83FF', - }, - [theme.breakpoints.down('sm')]: { - width: 90, - }, - }, - buttonSignup: { - borderRadius: 25, - width: 130, - background: '#7F83FF', - color: 'white', - textTransform: 'none', - '&:hover': { - background: '#4A4EDD', - }, - [theme.breakpoints.down('sm')]: { - width: 90, - }, - }, - buttonHire: { - textTransform: 'none', - background: '#4A4EDD', - height: 50, - '&:hover': { - background: '#7F83FF', - }, - }, - buttonHireSmall: { - textTransform: 'none', - background: '#4A4EDD', - '&:hover': { - background: '#7F83FF', - }, - }, - buttonWork: { - border: '2px solid', - textTransform: 'none', - color: '#4A4EDD', - borderColor: '#4A4EDD', - marginRight: 20, - width: 150, - height: 50, - '&:hover': { - background: '#7F83FF', - border: '2px solid', - color: 'white', - }, - [theme.breakpoints.down('sm')]: { - marginRight: 0, - }, - }, - root: { - flexGrow: 1, - marginLeft: 50, - marginRight: 70, - marginTop: 50, - fontFamily: 'Arial', - fontWeight: '300', - color: '#2F2D2D', - lineHeight: '1.5em', - [theme.breakpoints.down('sm')]: { - display: 'flex', - margin: 'auto', - }, - }, - grow: { - flexGrow: 1, - }, - logoImage: { - width: 170, - [theme.breakpoints.down('sm')]: { - width: 120, - }, - }, - topBarContainer: { - marginTop: 40, - marginLeft: 70, - marginRight: 70, - }, - textContainer: { - marginTop: 60, - marginLeft: 30, - [theme.breakpoints.down('sm')]: { - marginLeft: 10, - marginRight: 10 - }, - }, - textSize: { - fontSize: '1.5em', - marginTop: 0, - marginBottom: 0, - }, - center: { - textAlign: 'center', - }, - justify: { - textAlign: 'justify', - }, - bottomImage: { - backgroundRepeat: 'no-repeat', - position: 'absolute', - width: '100vw', - [theme.breakpoints.down('sm')]: { - bottom: 0, - } - }, - header: { - fontSize: '2.5em', - fontWeight: 200, - marginTop: 70, - marginBottom: 36, - [theme.breakpoints.down('sm')]: { - lineHeight: 1.5, - fontSize: '1.3em', - } - }, - paragraph: { - fontSize: '1.2em', - lineHeight: 1.5, - [theme.breakpoints.down('sm')]: { - lineHeight: 1.5, - fontSize: '1em', - margin: '0 auto', - width: 270 - } - }, - margin: { - marginTop: 60, - marginBottom: 100, - marginLeft: 30, - [theme.breakpoints.down('sm')]: { - marginLeft: 0, - }, - }, -}) - -function LandingPage (props) { - const { classes } = props - const ref = React.createRef() - - const handleClick = (event) => { - event.preventDefault() - ref.current.scrollIntoView({ - behavior: 'smooth', - block: 'start', - }) - } - - return ( -
-
- - - - - - logo - -
- - - - - -

- -

-

-
- - - - - - -
- - - - - - -
- - - - - - - - - - - -
- -
-
-
- - - -
-
-
- - - - - - - - - - - -
- -
-
-
- - - -
-
-
- - - - - - - - - - - -
- -
-
-
- - - -
-
-
- - - - - -
-
-
- - - - - -
-
- -
- ) -} - -LandingPage.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default LandingPage diff --git a/frontend/src/components/areas/public/features/welcome/legacy/messages.js b/frontend/src/components/areas/public/features/welcome/legacy/messages.js deleted file mode 100644 index 4cb5bf6ee..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/messages.js +++ /dev/null @@ -1,292 +0,0 @@ -import { defineMessages } from 'react-intl' - -export default defineMessages({ - welcomeFreelancersItemOnePrimary: { - id: 'welcome.main.item.one.primary', - defaultMessage: 'Get paid by contributing with Open Source' - }, - welcomeFreelancersItemOneSecondary: { - id: 'welcome.main.item.one.secondary', - defaultMessage: 'Contribute with Open Source projects and receive rewards in bounties' - }, - welcomeFreelancersItemTwoPrimary: { - id: 'welcome.main.item.two.primary', - defaultMessage: 'Work with on-demand projects as a freelancer using a development workflow' - }, - welcomeFreelancersItemTwoSecondary: { - id: 'welcome.main.item.two.secondary', - defaultMessage: 'We use a Git Workflow to facilitate the process of delivery tasks on demand' - }, - welcomeFreelancersItemThreePrimary: { - id: 'welcome.main.item.three.primary', - defaultMessage: 'Contribute with projects, get paid, receive tailored feedbacks and get your code into production' - }, - welcomeFreelancersItemThreeSecondary: { - id: 'welcome.main.item.three.secondary', - defaultMessage: 'We use a very effective agile process to delivery tasks using Git, and we provide support to learn and improve your skills' - }, - welcomeCompaniesItemOnePrimary: { - id: 'welcome.companies.item.one.primary', - defaultMessage: 'Maintainers of Open Source projects can use Gitpay to outsource the development' - }, - welcomeCompaniesItemOneSecondary: { - id: 'welcome.companies.item.one.secondary', - defaultMessage: 'Import issues from Git, add bounties, then pay contributors when the related Pull Request is merged' - }, - welcomeCompaniesItemTwoPrimary: { - id: 'welcome.companies.item.two.primary', - defaultMessage: 'Organizations can use Gitpay to develop tasks on demand, using a development workflow' - }, - welcomeCompaniesItemTwoSecondary: { - id: 'welcome.companies.item.two.secondary', - defaultMessage: 'Pay for on-demand freelancer work using an integrated workflow and get issues from your project solved' - }, - welcomeCompaniesItemThreePrimary: { - id: 'welcome.companies.item.three.primary', - defaultMessage: 'Funding your Open Source project' - }, - welcomeCompaniesItemThreeSecondary: { - id: 'welcome.companies.item.three.secondary', - defaultMessage: 'You can crowdfunding your Open Source projects on Gitpay, and get funding by inviting sponsor to add bounties' - }, - welcomeHowToItemOnePrimary: { - id: 'welcome.howto.item.one.primary', - defaultMessage: 'Import an issue from a Git repository' - }, - welcomeHowToItemOneSecondary: { - id: 'welcome.howto.item.one.secondary', - defaultMessage: 'Import an issue to be solved, then you can set deadlines, add bounties and we will send to our community' - }, - welcomeHowToItemTwoPrimary: { - id: 'welcome.howto.item.two.primary', - defaultMessage: 'Contributors and freelancers can apply to solve your issue' - }, - welcomeHowToItemTwoSecondary: { - id: 'welcome.howto.item.two.secondary', - defaultMessage: 'The users can send offers and the ones who applied can be assigned to complete the work' - }, - welcomeHowToItemThreePrimary: { - id: 'welcome.howto.item.three.primary', - defaultMessage: 'When the solution is ready, you can review, and once approved, you can merge and send the payment to the peer' - }, - welcomeHowToItemThreeSecondary: { - id: 'welcome.howto.item.three.secondary', - defaultMessage: 'An easy way to have your work integrated with development tools approved by the development community' - }, - welcomeHowToItemFourPrimary: { - id: 'welcome.howto.item.four.primary', - defaultMessage: 'Your issue was solved, and the contributor received the payment' - }, - welcomeHowToItemFourSecondary: { - id: 'welcome.howto.item.four.secondary', - defaultMessage: 'We support payments with Credit Card or Paypal and the bounties are transfered directly to your bank account' - }, - consultingItemPrimary: { - id: 'welcome.how.item.primary', - defaultMessage: 'Create an account on Gitpay' - }, - consultingItemSecondary: { - id: 'welcome.how.item.secondary', - defaultMessage: 'When you create an account, you can setup payment methods to receive bounties, setup your preferences and explore tasks. You can receive issues funded according to your preferences' - }, - consultingItemTwoPrimary: { - id: 'welcome.how.item.two.primary', - defaultMessage: 'Solve issues' - }, - consultingItemTwoSecondary: { - id: 'welcome.how.item.two.secondary', - defaultMessage: 'You can apply to solve issues and receive the payment for a bounty, or even apply for learning purposes, to improve your skills' - }, - consultingItemThreePrimary: { - id: 'welcome.how.item.three.primary', - defaultMessage: 'Receive payments with direct transfer or Paypal' - }, - consultingItemThreeSecondary: { - id: 'welcome.how.item.three.secondary', - defaultMessage: 'Once your changes are merged into the code base and the issue was solved, you will receive the payment in your bank account' - }, - communityItemPrimary: { - id: 'welcome.community.item.primary', - defaultMessage: '8% fee to receive payments for the issues solved' - }, - communityItemSecondary: { - id: 'welcome.community.item.secondary', - defaultMessage: 'We accept direct transfer to your bank account registered on Gitpay or using your Paypal account' - }, - communityItemTwoPrimary: { - id: 'welcome.community.item.two.primary', - defaultMessage: 'A place for opportunities' - }, - communityItemTwoSecondary: { - id: 'welcome.community.item.two.secondary', - defaultMessage: 'You may become one of our top talents and there is chances that you can be hired by partner companies' - }, - communityItemThreePrimary: { - id: 'welcome.community.item.three.primary', - defaultMessage: 'A great workflow that helps you to focus in what matter' - }, - communityItemThreeSecondary: { - id: 'welcome.community.item.three.secondary', - defaultMessage: 'We follow git good practices to automate your delivery using git, and now you can work with tools you love and you will be supported with collaboration to solve issues' - }, - companiesHowItWorksItemPrimary: { - id: 'welcome.howitworks.companies.item.primary', - defaultMessage: 'Import git issues to Gitpay' - }, - companiesHowItWorksItemSecondary: { - id: 'welcome.howitworks.companies.item.secondary', - defaultMessage: 'Import issues from your Github or Bitbucket projects using git to be able to add bounties and manage fundings, and set deadlines and workflows for payments' - }, - companiesHowItWorksItemTwoPrimary: { - id: 'welcome.howitworks.companies.item.two.primary', - defaultMessage: 'Assign contributors to solve' - }, - companiesHowItWorksItemTwoSecondary: { - id: 'welcome.howitworks.companies.item.two.secondary', - defaultMessage: 'Contributors can apply for interest and make offers and you can approve their offers to solve your issues' - }, - companiesHowItWorksItemThreePrimary: { - id: 'welcome.howitworks.companies.item.three.primary', - defaultMessage: 'Receive a change proposal using Pull Requests' - }, - companiesHowItWorksItemThreeSecondary: { - id: 'welcome.howitworks.companies.item.three.secondary', - defaultMessage: 'The change request will be send directly to your approval, and once approved, the change is merged and the payment will be send to the peer' - }, - companiesItemPrimary: { - id: 'welcome.which.companies.item.primary', - defaultMessage: 'For any company' - }, - companiesItemSecondary: { - id: 'welcome.which.companies.item.secondary', - defaultMessage: 'The distributed colaboration helps company grows and provide a great solution to have the tasks solved using agile process and colaboration throught development' - }, - companiesItemTwoSecondary: { - id: 'welcome.which.companies.item.two.secondary', - defaultMessage: 'Companies will be able to use Open Source if they want to create colaborative tools that will help other companies and contribute with the OSS ecosystem' - }, - companiesItemThreePrimary: { - id: 'welcome.which.companies.item.three.primary', - defaultMessage: 'We validate your business integration process' - }, - companiesItemThreeSecondary: { - id: 'welcome.which.companies.item.three.secondary', - defaultMessage: 'We will help to fit in agile process to have your tasks concluded in an independent way and according to your business' - }, - companiesItemPrimary1: { - id: 'welcome.companies.item.primary', - defaultMessage: '8% for Open Source projects' - }, - companiesItemSecondary1: { - id: 'welcome.companies.item.secondary', - defaultMessage: '8% fee for Open Source Projects' - }, - companiesItemTwoPrimary1: { - id: 'welcome.companies.item.two.primary.alt', - defaultMessage: '18% for private projects' - }, - companiesItemTwoSecondary1: { - id: 'welcome.companies.item.two.secondary.alt', - defaultMessage: '18% fee for private projects' - }, - companiesItemThreePrimary1: { - id: 'welcome.companies.item.three.primary.alt', - defaultMessage: 'The fees are not refundable' - }, - companiesItemThreeSecondary1: { - id: 'welcome.companies.item.three.secondary.alt', - defaultMessage: 'You can refund your payment, or transfer to another issue, but the fees are not refundable' - }, - workflowItemPrimary: { - id: 'welcome.workflow.item.primary', - defaultMessage: 'Use of personal information' - }, - workflowItemSecondary: { - id: 'welcome.workflow.item.secondary', - defaultMessage: 'The company may use personal information for the following purposes: login and signup for Gitpay service, email notifications when enabled, and statistic data when authorized' - }, - workflowItemTwoPrimary: { - id: 'welcome.workflow.item.two.primary', - defaultMessage: 'Integration with third party' - }, - workflowItemTwoSecondary: { - id: 'welcome.workflow.item.two.secondary', - defaultMessage: 'We never provide access to your data to a third party without your previous consent' - }, - workflowItemThreePrimary: { - id: 'welcome.workflow.item.three.primary', - defaultMessage: 'Use of Cookies' - }, - workflowItemThreeSecondary: { - id: 'welcome.workflow.item.three.secondary', - defaultMessage: 'We use cookies to help to improve your experience with Gitpay, providing a custom experience according with your needs' - }, - termsItemOnePrimary: { - id: 'welcome.terms.item.one.primary', - defaultMessage: 'Terms' - }, - termsItemOneSecondary: { - id: 'welcome.terms.item.one.secondary', - defaultMessage: 'THIS IS A LEGALLY BINDING AGREEMENT between you and Work n Enjoy Inc., former company of Gitpay. By using gitpay.me website ("Site") or any of the Gitpay services and integrations ("Services"), you agree to all the terms and conditions of this Terms of Service ("Agreement"). If you are entering into this Agreement on behalf of a company or other legal entity, you represent that you have the authority to bind such entity to these terms and conditions, in which case the terms "you" or "your" shall refer to such entity. If you do not have such authority, or if you do not agree with these terms and conditions, you must not proceed with the registration process or use our Site or Service.' - }, - welcomeCollabItemOnePrimary: { - id: 'welcome.collab.item.one.primary', - defaultMessage: 'By developers, for developers' - }, - welcomeCollabItemOneSecondary: { - id: 'welcome.collab.item.one.secondary', - defaultMessage: 'We use tools like Git, and integrations with Github and Gitlab to automate the freelancer process' - }, - welcomeCollabItemTwoPrimary: { - id: 'welcome.collab.item.two.primary', - defaultMessage: 'We are fully Open Source' - }, - welcomeCollabItemTwoSecondary: { - id: 'welcome.collab.item.two.secondary', - defaultMessage: 'We are a open source project made by contributors, and we help our community of contributors to shift their careers to the next level' - }, - welcomeCollabItemThreePrimary: { - id: 'welcome.collab.item.three.primary', - defaultMessage: 'Collaborate from anywhere, empowering development communities' - }, - welcomeCollabItemThreeSecondary: { - id: 'welcome.collab.item.three.secondary', - defaultMessage: 'We use collaborative remote tools like Slack and discuss and review code on Github or Bitbucket, and we do payments for bounties and freelancer work worldwide' - }, - topMenu1: { - id: 'welcome.menu.item.one', - defaultMessage: 'Intro' - }, - topMenu2: { - id: 'welcome.menu.item.two', - defaultMessage: 'For contributors' - }, - topMenu3: { - id: 'welcome.menu.item.three', - defaultMessage: 'For organizations' - }, - topMenu4: { - id: 'welcome.menu.item.four', - defaultMessage: 'Community' - }, - topMenu5: { - id: 'welcome.menu.item.five', - defaultMessage: 'How it works' - }, - topMenu6: { - id: 'welcome.menu.item.six', - defaultMessage: 'Fees' - }, - topMenu7: { - id: 'welcome.menu.item.seven', - defaultMessage: 'Integrations' - }, - topMenu8: { - id: 'welcome.menu.item.eight', - defaultMessage: 'Get started' - }, - topMenu9: { - id: 'welcome.menu.item.nine', - defaultMessage: 'Who is using Gitpay' - } -}) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/terms-of-service-company.js b/frontend/src/components/areas/public/features/welcome/legacy/terms-of-service-company.js deleted file mode 100644 index bfbfa478f..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/terms-of-service-company.js +++ /dev/null @@ -1,114 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -class TermsOfServiceCompany extends Component { - constructor (props) { - super(props) - this.state = { - open: false - } - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - } - - componentDidMount () { - - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - render () { - const { classes } = this.props - - return ( - - - - { (msg) => ( - - ) } - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - -
-
-
- ) - } -} - -TermsOfServiceCompany.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(TermsOfServiceCompany) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/terms-of-service-people.js b/frontend/src/components/areas/public/features/welcome/legacy/terms-of-service-people.js deleted file mode 100644 index 475900ec2..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/terms-of-service-people.js +++ /dev/null @@ -1,114 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -class TermsOfServicePeople extends Component { - constructor (props) { - super(props) - this.state = { - open: false - } - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - } - - componentDidMount () { - - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - render () { - const { classes } = this.props - - return ( - - - - { (msg) => ( - - ) } - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - -
-
-
- ) - } -} - -TermsOfServicePeople.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(TermsOfServicePeople) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/welcome.js b/frontend/src/components/areas/public/features/welcome/legacy/welcome.js deleted file mode 100644 index 5e0474aa0..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/welcome.js +++ /dev/null @@ -1,256 +0,0 @@ -import React, { useRef } from 'react' -import PropTypes from 'prop-types' -import { Grid, Typography, List, ListItem, ListItemText, ListItemIcon, Avatar, Container } from '@mui/material' -import { AccountBalanceWallet, Work, Apps, Assignment, GroupWork } from '@mui/icons-material' -import { injectIntl, FormattedMessage } from 'react-intl' -import TopBarContainer from '../../../../../../containers/topbar' -import Bottom from 'design-library/organisms/layouts/bottom-bar-layouts/bottom-bar-layout/bottom-bar-layout' -import messages from './messages' -// removed withStyles/theme styles usage -import freelancerImage from 'images/collections/collection-flat-community.svg' -import companiesImage from 'images/collections/collection-flat-companies.svg' -import teamImage from 'images/welcome-teamwork.png' - -import { - MainTitle, - MainList, - ResponsiveImage, - Section -} from '../components/CommonStyles' - -// legacy styles removed - -const Welcome = (props) => { - const ref = useRef(null) - const [value, setValue] = React.useState(0) - - React.useEffect(() => { - // componentDidMount() equivalent - }, []) - - React.useEffect(() => { - // componentWillUnmount() equivalent - return () => { - // Clean up code - } - }, []) - - const { } = props - - return ( -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- ) -} - -Welcome.propTypes = {} - -export default injectIntl(Welcome) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/which-companies.js b/frontend/src/components/areas/public/features/welcome/legacy/which-companies.js deleted file mode 100644 index 83d69be56..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/which-companies.js +++ /dev/null @@ -1,134 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Work, - AccountBalanceWallet, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -class WhichCompanies extends Component { - constructor (props) { - super(props) - this.state = { - open: false - } - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - render () { - const { classes } = this.props - - return ( - - - - { (msg) => ( - - ) } - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- ) - } -} - -WhichCompanies.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(WhichCompanies) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/who-subscribes.js b/frontend/src/components/areas/public/features/welcome/legacy/who-subscribes.js deleted file mode 100644 index 6aca8d615..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/who-subscribes.js +++ /dev/null @@ -1,114 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -class WhoSubscribes extends Component { - constructor (props) { - super(props) - this.state = { - open: false - } - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - } - - componentDidMount () { - - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - render () { - const { classes } = this.props - - return ( - - - - { (msg) => ( - - ) } - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - -
-
-
- ) - } -} - -WhoSubscribes.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(WhoSubscribes) diff --git a/frontend/src/components/areas/public/features/welcome/legacy/workflow.js b/frontend/src/components/areas/public/features/welcome/legacy/workflow.js deleted file mode 100644 index dab55ecae..000000000 --- a/frontend/src/components/areas/public/features/welcome/legacy/workflow.js +++ /dev/null @@ -1,138 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { injectIntl, FormattedMessage } from 'react-intl' - -import { - List, - ListItem, - ListItemText, - ListItemIcon, - Dialog, - AppBar, - Toolbar, - IconButton, - Typography, - Avatar, -} from '@mui/material' -import { - Apps, - Work, - AccountBalanceWallet, - Close -} from '@mui/icons-material' - -import Transition from 'design-library/atoms/transitions/transition' -import messages from './messages' - -import { InfoList, MainTitle } from '../components/CommonStyles' - -class Workflow extends Component { - constructor (props) { - super(props) - this.state = { - open: false - } - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - } - - componentDidMount () { - - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - render () { - const { classes } = this.props - - return ( - - - - { (msg) => ( - - ) } - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- ) - } -} - -Workflow.propTypes = { - classes: PropTypes.object.isRequired, -} - -export default injectIntl(Workflow) diff --git a/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.stories.tsx b/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.stories.tsx new file mode 100644 index 000000000..12d145443 --- /dev/null +++ b/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.stories.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import SpotCard from './spot-card'; + +const meta: Meta = { + title: 'Design Library/Molecules/Cards/SpotCard', + component: SpotCard +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => , + args: { + // Adjust these props to match SpotCard's API. They are placeholders. + title: 'Spot title', + subtitle: 'Optional subtitle', + children: 'This is a spot card description.' + } as any +}; \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.styles.ts b/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.styles.ts new file mode 100644 index 000000000..e0f2c644d --- /dev/null +++ b/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.styles.ts @@ -0,0 +1,18 @@ +import { Card, CardContent } from '@mui/material'; +import { styled } from '@mui/material/styles'; + +export const StyledCard = styled(Card)(() => ({ + minWidth: 420, + marginTop: 40, + opacity: 0.8, + overflow: 'visible' +})) + +export const StyledCardContent = styled(CardContent)(() => ({ + textAlign: 'center', + position: 'relative' +})) + +export const Content = styled('div')({ + marginTop: 20 +}); \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.tsx b/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.tsx new file mode 100644 index 000000000..decde768f --- /dev/null +++ b/frontend/src/components/design-library/molecules/cards/spot-card/spot-card.tsx @@ -0,0 +1,88 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; +import { Typography, Dialog } from '@mui/material'; +import CookiePolicy from 'design-library/molecules/content/terms/cookie-policy/cookie-policy'; +import PrivacyPolicy from 'design-library/molecules/content/terms/privacy-policy/privacy-policy'; +import TermsOfService from 'design-library/molecules/content/terms/terms-of-service/terms-of-service'; +import { StyledCard, StyledCardContent, Content } from './spot-card.styles'; + +import logo from 'images/logo-complete.png' + +const SpotCard = ({ + title, + description, + children +}) => { + const [openDialog, setOpenDialog] = useState(false) + const [dialogType, setDialogType] = useState(null) + + const handleOpenDialog = (e, type) => { + e.preventDefault() + setDialogType(type) + setOpenDialog(true) + } + + const renderDialog = () => { + if (dialogType === 'cookie') { + return + } + if (dialogType === 'privacy') { + return + } + if (dialogType === 'terms') { + return + } + } + + const closeDialog = () => { + setOpenDialog(false) + setDialogType(null) + } + + return ( + <> + + + + Logo + + + + {title} + + + {description} + +
+ {children} +
+
+
+
+
+ + + handleOpenDialog(e, 'cookie')} style={{ display: 'inline-block', margin: '0 5px' }}> + + + | + handleOpenDialog(e, 'privacy')} style={{ display: 'inline-block', margin: '0 5px' }}> + + + | + handleOpenDialog(e, 'terms')} style={{ display: 'inline-block', margin: '0 5px' }}> + + + + +
+ {renderDialog()} +
+
+
+ + ); +}; + +export default SpotCard; \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/content/privacy-policy/privacy-policy.stories.tsx b/frontend/src/components/design-library/molecules/content/privacy-policy/privacy-policy.stories.tsx deleted file mode 100644 index 854518728..000000000 --- a/frontend/src/components/design-library/molecules/content/privacy-policy/privacy-policy.stories.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import PrivacyPolicy from './privacy-policy'; - -export default { - title: 'Design Library/Molecules/Content/PrivacyPolicy', - component: PrivacyPolicy -}; - -const Template = (args) => ; - -export const Default = Template.bind({}); -Default.args = {}; \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/content/terms-of-service/terms-of-service.stories.tsx b/frontend/src/components/design-library/molecules/content/terms-of-service/terms-of-service.stories.tsx deleted file mode 100644 index 4fd2a25c9..000000000 --- a/frontend/src/components/design-library/molecules/content/terms-of-service/terms-of-service.stories.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import TermsOfService from './terms-of-service'; - -export default { - title: 'Design Library/Molecules/Content/TermsOfService', - component: TermsOfService -}; - -const Template = (args) => ; - -export const Default = Template.bind({}); -Default.args = { - // Add default props here if any -}; \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.stories.tsx b/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.stories.tsx new file mode 100644 index 000000000..15c82f358 --- /dev/null +++ b/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.stories.tsx @@ -0,0 +1,67 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { action } from "@storybook/addon-actions"; +import BaseTerms from "./base-terms"; + +const meta: Meta = { + title: "Design Library/Molecules/Content/Terms/BaseTerms", + component: BaseTerms, + parameters: { + layout: "centered" + } +}; +export default meta; + +type Story = StoryObj; + +const baseArgs = { + title: "Terms of Service", + subtitle: "Please read these terms carefully before using our service.", + updated: "Last updated: Jan 1, 2025", + content: + "By accessing or using the service you agree to be bound by these Terms. If you disagree with any part of the terms then you may not access the service." + // extraStyles defaults to true in the component +}; + +export const Default: Story = { + args: { + ...baseArgs + } +}; + +export const WithActions: Story = { + args: { + ...baseArgs, + onArrowBack: action("onArrowBack"), + onAgreeTerms: action("onAgreeTerms") + } +}; + +export const NoHeader: Story = { + args: { + ...baseArgs, + noHeader: true + } +}; + +export const WithoutExtraStyles: Story = { + args: { + ...baseArgs, + extraStyles: false + } +}; + +export const LongContent: Story = { + args: { + ...baseArgs, + content: + "These terms apply to all visitors, users and others who access or use the service. " + + "You are responsible for safeguarding the password that you use to access the service. " + + "You agree not to disclose your password to any third party. " + + "Accounts may be terminated for violations of these terms. " + + "We may terminate or suspend access to our service immediately, without prior notice or liability, " + + "for any reason whatsoever, including without limitation if you breach the terms. " + + "The service and its original content, features and functionality are and will remain the exclusive " + + "property of the company and its licensors. " + + "We reserve the right, at our sole discretion, to modify or replace these terms at any time." + } +}; diff --git a/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.styles.ts b/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.styles.ts new file mode 100644 index 000000000..fd69dead7 --- /dev/null +++ b/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.styles.ts @@ -0,0 +1,44 @@ +import { styled } from "@mui/material/styles"; +import { Paper, Button } from "@mui/material"; + +type TermsContainerProps = { + extraStyles?: boolean; +}; + +export const TermsContainer = styled("div")(({ extraStyles }) => ({ + ...(extraStyles + ? { + padding: 20, + textAlign: "left", + position: "absolute", + top: 0, + left: 0, + width: "100%", + background: "white" + } + : {}) +})); + +export const Header = styled("div")({ + marginBottom: 10 +}); + +export const ContentArea = styled("div")({ + overflow: "scroll", + height: "calc(100vh - 200px)" +}); + +export const ActionsBar = styled(Paper)({ + position: "absolute", + bottom: 20, + left: 0, + height: 80, + width: "100%", + background: "white" +}); + +export const ActionButton = styled(Button)({ + float: "right", + marginRight: 20, + marginTop: 20 +}); diff --git a/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.tsx b/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.tsx new file mode 100644 index 000000000..de6c0eeb6 --- /dev/null +++ b/frontend/src/components/design-library/molecules/content/terms/base-terms/base-terms.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { Typography } from "@mui/material"; +import { ArrowBack } from "@mui/icons-material" +import { FormattedMessage } from "react-intl"; +import { TermsContainer, Header, ContentArea, ActionsBar, ActionButton } from "./base-terms.styles"; + +type BaseTermsProps = { + title: React.ReactNode | string; + subtitle: React.ReactNode | string; + updated: React.ReactNode | string; + content: React.ReactNode | string; + onArrowBack?: () => void; + onAgreeTerms?: () => void; + noHeader?: boolean; + extraStyles?: boolean; +}; + + +const BaseTerms = ({ + title, + subtitle, + updated, + content, + onArrowBack, + onAgreeTerms, + noHeader, + extraStyles = true +}:BaseTermsProps) => { + + return ( + <> + + { noHeader ? null : ( + <> +
+ { onArrowBack && ( + + + + )} + + {title} + + + {updated} + +
+ + {subtitle} + + + )} + + + {content} + + + { onAgreeTerms && ( + + + + + + )} +
+ + ) +} + +export default BaseTerms; diff --git a/frontend/src/components/design-library/molecules/content/terms/cookie-policy/cookie-policy.stories.tsx b/frontend/src/components/design-library/molecules/content/terms/cookie-policy/cookie-policy.stories.tsx new file mode 100644 index 000000000..77fb09b74 --- /dev/null +++ b/frontend/src/components/design-library/molecules/content/terms/cookie-policy/cookie-policy.stories.tsx @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import CookiePolicy from './cookie-policy'; + +const meta: Meta = { + title: 'Design Library/Molecules/Content/Terms/CookiePolicy', + component: CookiePolicy, + args: { + extraStyles: true + }, + argTypes: { + extraStyles: { control: 'boolean' } + } +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { extraStyles: true } +}; + +export const NoExtraStyles: Story = { + args: { extraStyles: false } +}; diff --git a/frontend/src/components/design-library/molecules/content/terms/cookie-policy/cookie-policy.tsx b/frontend/src/components/design-library/molecules/content/terms/cookie-policy/cookie-policy.tsx new file mode 100644 index 000000000..e424cd898 --- /dev/null +++ b/frontend/src/components/design-library/molecules/content/terms/cookie-policy/cookie-policy.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; +import BaseTerms from "../base-terms/base-terms"; + + +const CookiePolicy = ({ extraStyles = true }) => { + + const content = ` + {br} + Introduction + {br} +Welcome to Gitpay! This Cookie Policy explains how we use cookies, similar tracking technologies, and your choices regarding such technologies when you visit our website. +{br} +{br} +What are cookies? +Cookies are small text files that are stored on your device (computer, tablet, or mobile) when you visit a website. They enable the website to remember your actions and preferences (such as login details, language, font size, and other display preferences) over a period of time, so you don't have to keep re-entering them whenever you come back to the site or browse from one page to another. +{br} +{br} +Types of cookies we use +We use the following types of cookies on Gitpay: +{br} +{br} +a) Essential cookies: +These cookies are necessary for the operation of our website and enable you to navigate the site and use its features. Without these cookies, certain functionalities may be unavailable. +{br} +{br} +b) Analytical cookies: +Analytical cookies help us understand how visitors interact with our website by collecting information anonymously. This data helps us analyze and improve the performance and usability of our website. +{br} +{br} +c) Functional cookies: +Functional cookies allow us to remember your preferences and provide enhanced functionality and personalization. These cookies may be set by us or by third-party providers whose services we have added to our pages. +{br} +{br} +d) Advertising cookies: +Advertising cookies are used to deliver relevant advertisements to you. They track your browsing habits and help us display personalized ads based on your interests. These cookies may be set by us or by third-party advertising networks. +{br}{br} + +Third-party cookies +We may also use cookies from third-party services, such as Google Analytics, to track and analyze website usage. These third-party cookies are subject to the respective third parties' privacy policies. +{br}{br} +Cookie management{br} +You can manage and control cookies in various ways. You can set your browser to refuse all or some cookies, or to alert you when websites set or access cookies. Please note that if you disable or refuse cookies, some parts of our website may become inaccessible or not function properly. +{br}{br} +To manage your cookie preferences, you can modify the settings in your browser. Each browser has different procedures for managing cookies, so please refer to the instructions provided by your browser. +{br}{br} +Updates to this Cookie Policy{br} +We may update this Cookie Policy from time to time to reflect changes in our practices or legal requirements. We will post any revised version on this page with an updated effective date. +{br}{br} +Contact us +{br}{br} +If you have any questions or concerns about this Cookie Policy or our use of cookies on Gitpay, please contact us at contact@gitpay.me. +{br} +Please note that this Cookie Policy should be read in conjunction with our Privacy Policy, which provides further information on how we collect, use, and disclose your personal data. + ` + + return ( + <> + } + subtitle={} + updated={} + content={ }} />} + extraStyles={extraStyles} + /> + + ) +} + +export default CookiePolicy; diff --git a/frontend/src/components/design-library/molecules/content/terms/privacy-policy/privacy-policy.stories.tsx b/frontend/src/components/design-library/molecules/content/terms/privacy-policy/privacy-policy.stories.tsx new file mode 100644 index 000000000..602d8398d --- /dev/null +++ b/frontend/src/components/design-library/molecules/content/terms/privacy-policy/privacy-policy.stories.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import PrivacyPolicy from './privacy-policy'; + +export default { + title: 'Design Library/Molecules/Content/Terms/PrivacyPolicy', + component: PrivacyPolicy +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); +Default.args = {}; + +export const WithoutHeader = Template.bind({}); +WithoutHeader.args = { + noHeader: true +}; + +export const WithAgreeButton = Template.bind({}); +WithAgreeButton.args = { + onAgreeTerms: () => alert('Agreed to Privacy Policy') +}; + +export const WithExtraStyles = Template.bind({}); +WithExtraStyles.args = { + extraStyles: true +}; + +export const WithArrowBack = Template.bind({}); +WithArrowBack.args = { + onArrowBack: () => alert('Arrow back clicked') +}; diff --git a/frontend/src/components/design-library/molecules/content/privacy-policy/privacy-policy.tsx b/frontend/src/components/design-library/molecules/content/terms/privacy-policy/privacy-policy.tsx similarity index 62% rename from frontend/src/components/design-library/molecules/content/privacy-policy/privacy-policy.tsx rename to frontend/src/components/design-library/molecules/content/terms/privacy-policy/privacy-policy.tsx index 57135944c..ecc691c29 100644 --- a/frontend/src/components/design-library/molecules/content/privacy-policy/privacy-policy.tsx +++ b/frontend/src/components/design-library/molecules/content/terms/privacy-policy/privacy-policy.tsx @@ -1,8 +1,6 @@ import React from "react"; -import { Typography } from "@mui/material"; -import { ArrowBack } from "@mui/icons-material" import { FormattedMessage } from "react-intl"; -import { Button, Paper } from "@mui/material"; +import BaseTerms from "../base-terms/base-terms"; type PrivacyPolicyProps = { onArrowBack?: () => void; @@ -47,40 +45,16 @@ If you have any questions or concerns about our privacy policy, please contact u return ( <> -
- { noHeader ? null : ( - <> -
- { onArrowBack && ( - - - - )} - - - - - - -
- - - - - )} -
- - }} /> - -
- { onAgreeTerms && ( - - - - )} -
+ } + subtitle={} + updated={} + content={ }} />} + extraStyles={extraStyles} + onArrowBack={onArrowBack} + onAgreeTerms={onAgreeTerms} + noHeader={noHeader} + /> ) } diff --git a/frontend/src/components/design-library/molecules/content/terms/terms-of-service/terms-of-service.stories.tsx b/frontend/src/components/design-library/molecules/content/terms/terms-of-service/terms-of-service.stories.tsx new file mode 100644 index 000000000..c3a671918 --- /dev/null +++ b/frontend/src/components/design-library/molecules/content/terms/terms-of-service/terms-of-service.stories.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import TermsOfService from './terms-of-service'; + +export default { + title: 'Design Library/Molecules/Content/Terms/TermsOfService', + component: TermsOfService +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + // Add default props here if any +}; + +export const WithArrowBack = Template.bind({}); +WithArrowBack.args = { + onArrowBack: () => alert('Arrow back clicked') +}; + +export const WithAgreeButton = Template.bind({}); +WithAgreeButton.args = { + onAgreeTerms: () => alert('Agreed to Terms of Service') +}; + +export const WithBothActions = Template.bind({}); +WithBothActions.args = { + onArrowBack: () => alert('Arrow back clicked'), + onAgreeTerms: () => alert('Agreed to Terms of Service') +}; + +export const WithoutExtraStyles = Template.bind({}); +WithoutExtraStyles.args = { + extraStyles: false +}; \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/content/terms-of-service/terms-of-service.tsx b/frontend/src/components/design-library/molecules/content/terms/terms-of-service/terms-of-service.tsx similarity index 62% rename from frontend/src/components/design-library/molecules/content/terms-of-service/terms-of-service.tsx rename to frontend/src/components/design-library/molecules/content/terms/terms-of-service/terms-of-service.tsx index 4c610e52d..452480094 100644 --- a/frontend/src/components/design-library/molecules/content/terms-of-service/terms-of-service.tsx +++ b/frontend/src/components/design-library/molecules/content/terms/terms-of-service/terms-of-service.tsx @@ -1,8 +1,6 @@ import React from "react"; -import { Typography } from "@mui/material"; -import { ArrowBack } from "@mui/icons-material" import { FormattedMessage } from "react-intl"; -import { Button, Paper } from "@mui/material"; +import BaseTerms from "../base-terms/base-terms"; type TermsOfServiceProps = { onArrowBack?: () => void; @@ -50,36 +48,15 @@ By using our platform, you agree to these terms and conditions. If you do not ag return ( <> -
-
- { onArrowBack && ( - - - - )} - - - - - - -
- - - -
- - }} /> - -
- { onAgreeTerms && ( - - - - )} -
+ } + subtitle={} + updated={} + content={}} />} + extraStyles={extraStyles} + onArrowBack={onArrowBack} + onAgreeTerms={onAgreeTerms} + /> ) } diff --git a/frontend/src/components/design-library/molecules/dialogs/activate-account-dialog/activate-account-dialog.tsx b/frontend/src/components/design-library/molecules/dialogs/activate-account-dialog/activate-account-dialog.tsx index 14ff5390c..9028dbdfb 100644 --- a/frontend/src/components/design-library/molecules/dialogs/activate-account-dialog/activate-account-dialog.tsx +++ b/frontend/src/components/design-library/molecules/dialogs/activate-account-dialog/activate-account-dialog.tsx @@ -1,9 +1,38 @@ import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'; -import React from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; import Button from '../../../atoms/buttons/button/button'; -const ActivateAccountDialog = ({ open, onResend, completed }) => { +interface ActivateAccountDialogProps { + open: boolean; + onResend: () => void; + completed?: boolean; +} + +const RESEND_COOLDOWN_SECONDS = 60; + +const ActivateAccountDialog: React.FC = ({ open, onResend, completed }) => { + const [cooldown, setCooldown] = useState(0); + + useEffect(() => { + if (cooldown <= 0) return; + const timer = setInterval(() => { + setCooldown(prev => (prev > 1 ? prev - 1 : 0)); + }, 1000); + return () => clearInterval(timer); + }, [cooldown]); + + const handleResend = useCallback(() => { + if (cooldown > 0) return; + onResend?.(); + setCooldown(RESEND_COOLDOWN_SECONDS); + }, [cooldown, onResend]); + + const buttonLabel = + cooldown > 0 + ? + : ; + return ( @@ -28,20 +57,22 @@ const ActivateAccountDialog = ({ open, onResend, completed }) => { - ); -} +}; + export default ActivateAccountDialog; \ No newline at end of file diff --git a/frontend/src/components/areas/public/features/welcome/components/CommonStyles.js b/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/CommonStyles.js similarity index 95% rename from frontend/src/components/areas/public/features/welcome/components/CommonStyles.js rename to frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/CommonStyles.js index 4afad332a..1d1e47c2f 100644 --- a/frontend/src/components/areas/public/features/welcome/components/CommonStyles.js +++ b/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/CommonStyles.js @@ -1,6 +1,6 @@ import styled, { css } from 'styled-components' -import media from '../../../../../../styleguide/media' +import media from '../../../../../styleguide/media' export const MainTitle = styled.div` text-align: center; @@ -55,7 +55,7 @@ export const ShadowImage = styled.img` export const InfoList = styled.div` text-align: left; - margin-left: 10%; + margin-left: 3%; ${media.phone` margin-left: 0; diff --git a/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.styles.ts b/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.styles.ts index 7e15e7219..39532239e 100644 --- a/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.styles.ts +++ b/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.styles.ts @@ -1,104 +1,18 @@ import { styled } from '@mui/material/styles' -import AppBar from '@mui/material/AppBar' +import MuiAppBar from '@mui/material/AppBar' +import { Toolbar, Typography } from '@mui/material' -export const AppBarStyled = styled(AppBar)(({ theme }) => ({ - position: 'relative' +export const TopBarStyled = styled(Toolbar)(({ theme }) => ({ + borderBottom: `1px solid ${theme.palette.divider}`, + marginBottom: theme.spacing(2) })) -export const AppBarHeader = styled('div')(({ theme }) => ({ - marginLeft: theme.spacing(2), - flex: 1 +export const AppBarHeader = styled(Typography)(({ theme }) => ({ + width: '100%', + color: theme.palette.common.white })) -export const MainTitle = styled('div')<{ - left?: boolean; - center?: boolean; -}>(({ theme, left, center }) => ({ - textAlign: 'center', - display: 'block', - paddingBottom: 10, - borderBottom: '5px solid black', - width: '60%', - marginTop: 20, - marginLeft: 'auto', - marginBottom: 20, - marginRight: 'auto', - ...(left ? { marginRight: '18%' } : {}), - ...(center ? { marginRight: '5%', width: '70%' } : {}), - [theme.breakpoints.down('sm')]: { - width: '60%', - margin: '20px auto', - ...(left ? { marginLeft: 'auto' } : {}) - } -})) - -export const MainList = styled('div')(({ theme }) => ({ - textAlign: 'left', - [theme.breakpoints.down('sm')]: { - marginLeft: 0 - } -})) - -export const ResponsiveImage = styled('img')(({ theme }) => ({ - [theme.breakpoints.down('sm')]: { - width: '100%' - } -})) - -export const ShadowImage = styled('img')(({ theme }) => ({ - boxShadow: '1px 1px 3px 2px #ccc', - [theme.breakpoints.down('sm')]: { - maxWidth: '100%' - } -})) - -export const InfoList = styled('div')(({ theme }) => ({ - textAlign: 'left', - marginLeft: '10%', - [theme.breakpoints.down('sm')]: { - marginLeft: 0 - } -})) - -export const MainBanner = styled('div')(({ theme }) => ({ - boxSizing: 'border-box', - marginBottom: '1rem', - backgroundColor: 'black', - backgroundSize: 'cover', - [theme.breakpoints.down('sm')]: { - background: 'none', - backgroundColor: 'black' - } -})) - -export const Section = styled('div')<{ - alternative?: boolean -}>(({ theme, alternative }) => ({ - textAlign: 'center', - padding: '1rem', - ...(alternative ? { backgroundColor: '#f1f0ea' } : {}) -})) - -export const HeroTitle = styled('div')({}) - -export const HeroSection = styled('div')({ - marginTop: 20, - marginBottom: 20 -}) - -export const HeroContent = styled('div')({ - marginTop: 28, - marginBottom: 20 -}) - -export const HeroActions = styled('div')({ - display: 'flex', - justifyContent: 'flex-start', - alignItems: 'center', - padding: 10 -}) - -export const HeaderTypography = styled('div')({ - display: 'block', +export const AppBar = styled(MuiAppBar)({ + height: '100%', width: '100%' -}) +}) \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.tsx b/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.tsx index 3025ae66d..1efbaea88 100644 --- a/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.tsx +++ b/frontend/src/components/design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog.tsx @@ -1,63 +1,62 @@ import React from 'react' import { - ListItemButton, - Typography, Dialog, - Toolbar, IconButton, - Slide, - SlideProps + Slide } from '@mui/material' import { Close } from '@mui/icons-material' -import { InfoList, AppBarStyled, AppBarHeader, HeaderTypography } from './bottom-section-dialog.styles' +import { InfoList } from './CommonStyles' +import { AppBar as AppBarStyles, AppBarHeader, TopBarStyled } from './bottom-section-dialog.styles' +import { TransitionProps } from '@mui/material/transitions' + +const Transition = React.forwardRef(function Transition( + props: TransitionProps & { + children: React.ReactElement; + }, + ref: React.Ref +) { + return ; +}); const BottomSectionDialog = ({ - Transition = React.forwardRef((props, ref) => ( - - )), - header, + open, + onClose, title, - subtitle, content }) => { - const [ open, setOpen ] = React.useState(false) - return ( - - setOpen(!open) } component={HeaderTypography as any}> - {header} - - setOpen(false) } - > - - - setOpen(false) } - aria-label="Close" - > - - - - {title} - - - - {content} - - - - + + + + + + + + {title} + + + + {content} + + + ) } diff --git a/frontend/src/components/design-library/molecules/dialogs/privacy-dialog/privacy-dialog.tsx b/frontend/src/components/design-library/molecules/dialogs/privacy-dialog/privacy-dialog.tsx index 882d5e5b4..75f4a5e71 100644 --- a/frontend/src/components/design-library/molecules/dialogs/privacy-dialog/privacy-dialog.tsx +++ b/frontend/src/components/design-library/molecules/dialogs/privacy-dialog/privacy-dialog.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Dialog, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; -import PrivacyPolicy from "../../content/privacy-policy/privacy-policy"; +import { Dialog, DialogContent } from "@mui/material"; +import PrivacyPolicy from "../../content/terms/privacy-policy/privacy-policy"; const PrivacyDialog = ({ open, @@ -9,11 +9,8 @@ const PrivacyDialog = ({ return ( - Privacy Policy - - - + ); diff --git a/frontend/src/components/design-library/molecules/dialogs/terms-dialog/terms-dialog.tsx b/frontend/src/components/design-library/molecules/dialogs/terms-dialog/terms-dialog.tsx index 9d6f4521f..c1342bafc 100644 --- a/frontend/src/components/design-library/molecules/dialogs/terms-dialog/terms-dialog.tsx +++ b/frontend/src/components/design-library/molecules/dialogs/terms-dialog/terms-dialog.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Dialog, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; -import TermsOfService from "../../content/terms-of-service/terms-of-service"; +import { Dialog, DialogContent } from "@mui/material"; +import TermsOfService from "../../content/terms/terms-of-service/terms-of-service"; const TermsDialog = ({ open, @@ -9,11 +9,8 @@ const TermsDialog = ({ return ( - Terms and Conditions - - - + ); diff --git a/frontend/src/components/design-library/molecules/form-section/login-form/login-form-reset/login-form-reset.tsx b/frontend/src/components/design-library/molecules/form-section/login-form/login-form-reset/login-form-reset.tsx index f9294c124..b9a33a15b 100644 --- a/frontend/src/components/design-library/molecules/form-section/login-form/login-form-reset/login-form-reset.tsx +++ b/frontend/src/components/design-library/molecules/form-section/login-form/login-form-reset/login-form-reset.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react' import { FormattedMessage } from 'react-intl' -import { useParams } from 'react-router-dom' +import { useHistory, useParams } from 'react-router-dom' import { Button, Typography @@ -17,7 +17,7 @@ type LoginFormResetProps = { action?: string onClose?: () => void onSignin?: () => void - onReset?: ({ password, token}:LoginOnResetProps) => void + onReset?: ({ password, token}:LoginOnResetProps) => Promise noCancelButton?: boolean } @@ -27,6 +27,7 @@ type ErrorStateType = { } const LoginFormReset = ({ action, noCancelButton, onClose, onSignin, onReset }:LoginFormResetProps) => { + const history = useHistory() const { token } = useParams<{ token: string }>() @@ -70,6 +71,7 @@ const LoginFormReset = ({ action, noCancelButton, onClose, onSignin, onReset }:L const password = event.target.password.value if(validForm) { await onReset({ password, token }) + history.push('/signin') } } diff --git a/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signin/login-form-signin.tsx b/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signin/login-form-signin.tsx index d36361b4b..be5741c69 100644 --- a/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signin/login-form-signin.tsx +++ b/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signin/login-form-signin.tsx @@ -10,17 +10,21 @@ import ProviderLoginButtons from '../../../../atoms/buttons/provider-login-butto import api from '../../../../../../consts' import { Margins, Center, SpacedButton, StyledTextField } from './login-form-signin.styles' +import { useParams } from 'react-router-dom' type LoginFormSigninProps = { onSubmit?: (event: React.FormEvent) => void action?: string onClose?: () => void onSignup?: () => void - noCancelButton?: boolean, + noCancelButton?: boolean onForgot?: () => void + addNotification?: (message: string) => void } -const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot }:LoginFormSigninProps) => { +const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot, addNotification }: LoginFormSigninProps) => { + const { status } = useParams<{ status: string }>() + const [state, setState] = useState({ username: '', password: '', @@ -45,10 +49,6 @@ const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot setState({ ...state, rememberMe: !state.rememberMe }) } - const handleType = (type) => { - // handle type logic - } - const validateEmail = (email, currentErrors) => { if (email.length < 3) { setState({ @@ -59,7 +59,7 @@ const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot } }) return false - } else if(email.length > 72) { + } else if (email.length > 72) { setState({ ...state, error: { @@ -94,7 +94,7 @@ const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot } }) return false - } else if(password.length > 72) { + } else if (password.length > 72) { setState({ ...state, error: { @@ -117,7 +117,7 @@ const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot } const validEmail = validateEmail(state.username, state.error) const validPassword = validatePassword(state.password, state.error) - if(!validEmail || !validPassword) { + if (!validEmail || !validPassword) { return event && event.preventDefault(); } onSubmit?.(event) @@ -128,6 +128,12 @@ const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot process.env.NODE_ENV === 'test' && setState({ ...state, captchaChecked: true }) }, []) + useEffect(() => { + if (status === 'invalid') { + addNotification && addNotification('user.invalid') + } + }, [status, addNotification]) + const { error } = state return ( @@ -172,7 +178,7 @@ const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot
- @@ -221,7 +227,7 @@ const LoginFormSignin = ({ onSubmit, onClose, onSignup, noCancelButton, onForgot - + ) } diff --git a/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signup/login-form-signup.tsx b/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signup/login-form-signup.tsx index 06292b627..ecfe6a1c1 100644 --- a/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signup/login-form-signup.tsx +++ b/frontend/src/components/design-library/molecules/form-section/login-form/login-form-signup/login-form-signup.tsx @@ -289,148 +289,150 @@ const LoginFormSignup = ({ const { error, password, confirmPassword } = state return ( -
- - - - - - - - - - - - : ''} - name="confirm_password" - onChange={handleChange('confirmPassword')} - onBlur={handleBlur} - onFocus={handleFocus} - fullWidth - type="password" - label="Confirm Password" - variant="outlined" - id="confirmPassword" - defaultValue={state.confirmPassword} - /> - - - - -
-
- {state.agreeTermsCheck - ? - : - } - - - - - - - - - - -
-
- {agreeTermsCheckError && -
- - - -
- } + <> + + + + + + + + + + - {process.env.NODE_ENV === 'production' && ( -
- setState({ ...state, captchaChecked })} + + : ''} + name="confirm_password" + onChange={handleChange('confirmPassword')} + onBlur={handleBlur} + onFocus={handleFocus} + fullWidth + type="password" + label="Confirm Password" + variant="outlined" + id="confirmPassword" + defaultValue={state.confirmPassword} /> + + + + +
+
+ {state.agreeTermsCheck + ? + : + } + + + + + + + + + + +
- )} - {error.captcha && -
- - {error.captcha} - -
- } -
-
- {noCancelButton ? null : ( - - - - )} - - - -
+ {agreeTermsCheckError && +
- + -
-
-
+ } + + {process.env.NODE_ENV === 'production' && ( +
+ setState({ ...state, captchaChecked })} + /> +
+ )} + {error.captcha && +
+ + {error.captcha} + +
+ } +
+
+ {noCancelButton ? null : ( + + + + )} + + + +
+ + + + +
+
+
+ - + ) } diff --git a/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.stories.tsx b/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.stories.tsx new file mode 100644 index 000000000..acc502919 --- /dev/null +++ b/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.stories.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import type { Meta, StoryObj } from '@storybook/react' +import VerticalMenulList from './vertical-menu-list' + +const meta: Meta = { + title: 'Design Library/Molecules/Lists/VerticalMenuList', + component: VerticalMenulList, + parameters: { + layout: 'centered' + } +} +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + title: 'Menu', + items: [ + { label: 'Dashboard', onClick: () => alert('Dashboard clicked') }, + { label: 'Settings', onClick: () => alert('Settings clicked') }, + { label: 'Logout', onClick: () => alert('Logout clicked') } + ] + } +} + +export const DialogType: Story = { + args: { + type: 'dialog', + title: 'Legal', + items: [ + { label: 'Privacy Policy', component:
Privacy Policy Content
}, + { label: 'Terms of Service', component:
Terms of Service Content
}, + { label: 'Cookie Policy', component:
Cookie Policy Content
} + ] + } +} diff --git a/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.styles.ts b/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.styles.ts new file mode 100644 index 000000000..1549ca363 --- /dev/null +++ b/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.styles.ts @@ -0,0 +1,16 @@ +import { styled } from '@mui/material/styles' +import { List, ListItemButton, Typography } from '@mui/material' + +export const TitleStyled = styled(Typography)(({ theme }) => ({ + paddingLeft: theme.spacing(2) +})) + +export const ListStyled = styled(List)(({ theme }) => ({ + marginLeft: theme.spacing(0), + paddingLeft: theme.spacing(0) +})) + +export const ListItemButtonStyled = styled(ListItemButton)(({ theme }) => ({ + marginLeft: theme.spacing(0), + paddingLeft: theme.spacing(2) +})) \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.tsx b/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.tsx new file mode 100644 index 000000000..bd8854a27 --- /dev/null +++ b/frontend/src/components/design-library/molecules/lists/vertical-menu-list/vertical-menu-list.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import { Typography } from '@mui/material' +import { ListStyled, ListItemButtonStyled, TitleStyled } from './vertical-menu-list.styles' +import BottomSectionDialog from 'design-library/molecules/dialogs/bottom-section-dialog/bottom-section-dialog' + +type VerticalMenuListProps = { + type?: 'link' | 'dialog' + title: React.ReactNode | string + items: { + label: React.ReactNode | string + onClick?: () => void + component?: React.ReactNode + }[] +} + +const VerticalMenuList = ({ + type = 'link', + title, + items +}: VerticalMenuListProps) => { + + const [ open, setOpen ] = React.useState(-1) + + return ( + <> + + {title} + + + {items.map((item, index) => ( + + setOpen(index) }} + > + {item.label} + + + ))} + + { type === 'dialog' && open >= 0 && items[open].component && + setOpen(-1) } + /> + } + + ) +} + +export default VerticalMenuList \ No newline at end of file diff --git a/frontend/src/components/design-library/organisms/layouts/bottom-bar-layouts/bottom-bar-layout/bottom-bar-layout.tsx b/frontend/src/components/design-library/organisms/layouts/bottom-bar-layouts/bottom-bar-layout/bottom-bar-layout.tsx index d77159235..14d9a7785 100644 --- a/frontend/src/components/design-library/organisms/layouts/bottom-bar-layouts/bottom-bar-layout/bottom-bar-layout.tsx +++ b/frontend/src/components/design-library/organisms/layouts/bottom-bar-layouts/bottom-bar-layout/bottom-bar-layout.tsx @@ -1,153 +1,82 @@ import React from 'react' import { FormattedMessage } from 'react-intl' +import { useHistory } from 'react-router-dom' import { Grid, - Typography, - List, - ListItemButton + Typography } from '@mui/material' import SubscribeForm from '../../../forms/subscribe-forms/subscribe-form/subscribe-form' import StatsBar from '../../../../molecules/sections/stats-bar/stats-bar' import SlackCard from './SlackCard' import GithubCard from './GithubCard' +import VerticalMenuList from '../../../../molecules/lists/vertical-menu-list/vertical-menu-list' import { Container, BaseFooter, SubscribeFromWrapper, SecBlock, SpacedDivider, LogoImg } from './bottom-bar-layout.styles' - -import BottomSectionDialog from '../../../../../areas/public/features/welcome/components/BottomSectionDialog' -import PrivacyPolicy from '../../../../../areas/private/components/session/privacy-policy' -import TermsOfService from '../../../../../areas/private/components/session/terms-of-service' -import CookiePolicy from '../../../../../areas/private/components/session/cookie-policy' +import PrivacyPolicy from '../../../../molecules/content/terms/privacy-policy/privacy-policy' +import TermsOfService from '../../../../molecules/content/terms/terms-of-service/terms-of-service' +import CookiePolicy from '../../../../molecules/content/terms/cookie-policy/cookie-policy' import logoCompleteGray from 'images/logo-complete-gray.png' import logoWorknEnjoy from 'images/worknenjoy-logo.png' const Bottom = ({ info = { bounties: 0, tasks: 0, users: 0 }, getInfo }) => { + const history = useHistory() const { tasks, bounties, users } = info + const navigateTo = (path: string) => { + history.push(path) + window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) + } + + const mainMenuItems = [ + { label: 'About us', onClick: () => navigateTo('/welcome') }, + { label: 'Pricing', onClick: () => navigateTo('/pricing') }, + { label: 'Team', onClick: () => navigateTo('/team') }, + { label: 'Documentation', onClick: () => window.open('https://docs.gitpay.me/en') }, + { label: 'Explore', onClick: () => navigateTo('/explore/issues') } + ] + + const legalMenuItems = [ + { + label: , + component: + }, + { + label: , + component: + }, + { + label: , + component: + } + ] + return ( - - - - - - - - window.location.assign('/#/welcome')} - > - - - - - window.location.assign('/#/pricing')} - > - - - - - window.location.assign('/#/team')} - > - - - - - window.open('https://docs.gitpay.me/en')} - > - - - - - window.location.assign('/#/tasks/open')} - > - - - - + } + items={mainMenuItems} + /> - - + - - - - - } - /> - - } - /> - - } - /> - + } + items={legalMenuItems} + /> diff --git a/frontend/src/components/design-library/pages/public-pages/pricing-public-page/pricing-public-page.tsx b/frontend/src/components/design-library/pages/public-pages/pricing-public-page/pricing-public-page.tsx index 62d75c623..da92a48df 100644 --- a/frontend/src/components/design-library/pages/public-pages/pricing-public-page/pricing-public-page.tsx +++ b/frontend/src/components/design-library/pages/public-pages/pricing-public-page/pricing-public-page.tsx @@ -3,10 +3,8 @@ import React from 'react' import { Button, Card, CardActions, CardContent, CardHeader, Grid, Typography } from '@mui/material' import { FormattedMessage } from 'react-intl' -import { - MainTitle -} from '../../../../areas/public/features/welcome/components/CommonStyles' import { Layout, HeroContent, CardPricing } from './pricing-public-page.styles' +import HeroTitle from 'design-library/atoms/typography/hero-title/hero-title' interface Tier { title: string @@ -69,67 +67,67 @@ function PricingPublicPage() { return ( - { /* Hero unit */ } - - - - - - - - + + + - { /* End hero unit */ } - - { tiersMaintainers.map(tier => ( - // Enterprise card is full width at sm breakpoint - - - theme.palette.primary.light }} - /> - - - - Fee { tier.price } - - - - - - { tier.description.map((line, i) => ( - - { line } - - )) } - - { tier.link && - - - - } - - - )) } + + + + } + subheader={} + sx={{ backgroundColor: theme => theme.palette.primary.light, textAlign: 'center' }} + /> + + + { tiersMaintainers.map(tier => ( + + + theme.palette.primary.light, textAlign: 'center' }} + /> + + + + Fee { tier.price } + + + + + + { tier.description.map((line, i) => ( + + { line } + + )) } + + { tier.link && + + + + } + + + )) } + + + + - + { tiersContributors.map(tier => ( - // Enterprise card is full width at sm breakpoint theme.palette.primary.light }} + sx={{ backgroundColor: theme => theme.palette.primary.light, textAlign: 'center' }} /> diff --git a/frontend/src/components/areas/public/features/welcome/legacy/github-app.js b/frontend/src/components/design-library/pages/public-pages/session-public-pages/account-activation/account-activation.stories.tsx similarity index 100% rename from frontend/src/components/areas/public/features/welcome/legacy/github-app.js rename to frontend/src/components/design-library/pages/public-pages/session-public-pages/account-activation/account-activation.stories.tsx diff --git a/frontend/src/components/areas/private/features/account/features/account-activation/account-activation.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/account-activation/account-activation.tsx similarity index 78% rename from frontend/src/components/areas/private/features/account/features/account-activation/account-activation.tsx rename to frontend/src/components/design-library/pages/public-pages/session-public-pages/account-activation/account-activation.tsx index 5c0abc7ae..b9936bbfc 100644 --- a/frontend/src/components/areas/private/features/account/features/account-activation/account-activation.tsx +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/account-activation/account-activation.tsx @@ -5,20 +5,24 @@ import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import { FormattedMessage } from 'react-intl'; +import { useHistory, useParams } from 'react-router-dom'; -const AccountActivation = ({ activateAccount, match, history }) => { +const AccountActivation = ({ onActivateAccount }) => { const [open, setOpen] = React.useState(false); const [ activated, setActivated ] = React.useState(false); + const history = useHistory(); + const { token, userId } = useParams<{ token: string; userId: string }>(); + useEffect(() => { - const token = match.params.token; - const userId = match.params.userId; - activateAccount(token, userId).then((data) => { - if (!data.error) { + const activate = async () => { + const activateAccount = await onActivateAccount(token, userId); + if(!activateAccount.error){ setActivated(true); } - }); - setOpen(true); + setOpen(true); + }; + activate(); }, []); return ( diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/forgot-password-page/forgot-password-page.stories.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/forgot-password-page/forgot-password-page.stories.tsx new file mode 100644 index 000000000..0ee1f4e05 --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/forgot-password-page/forgot-password-page.stories.tsx @@ -0,0 +1,26 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { withSignupSigninBaseTemplate } from '../../../../../../../.storybook/decorators/withPublicTemplate'; +import ForgotPasswordPage from './forgot-password-page'; + +const meta: Meta = { + title: 'Design Library/Pages/Public/Session/ForgotPassword', + decorators: [ withSignupSigninBaseTemplate ], + component: ForgotPasswordPage +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithError: Story = { + args: { + error: 'Unable to load session data' + } +}; + +export const AlternateSession: Story = { + args: { + sessionId: 'another-session-id' + } +}; diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/forgot-password-page/forgot-password-page.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/forgot-password-page/forgot-password-page.tsx new file mode 100644 index 000000000..1e0e15966 --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/forgot-password-page/forgot-password-page.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import SpotCard from 'design-library/molecules/cards/spot-card/spot-card'; +import LoginFormForgot from 'design-library/molecules/form-section/login-form/login-form-forgot/login-form-forgot'; +import { useHistory } from 'react-router-dom'; + +const ForgotUserPage = ({ + forgotPassword +}) => { + const history = useHistory() + return ( + } + description={} + > + history.push('/signin')} + onClose={() => history.push('/')} + onSubmit={forgotPassword} + noCancelButton + /> + + ); +}; + +export default ForgotUserPage; diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/reset-password-page/reset-password-page.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/reset-password-page/reset-password-page.tsx new file mode 100644 index 000000000..731a72bac --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/reset-password-page/reset-password-page.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Skeleton } from '@mui/material'; +import SpotCard from 'design-library/molecules/cards/spot-card/spot-card'; +import LoginFormReset from 'design-library/molecules/form-section/login-form/login-form-reset/login-form-reset'; +import { FormattedMessage } from 'react-intl'; +import { useHistory } from 'react-router-dom'; + +const ResetPasswordPage = ({ + user, + resetPassword +}) => { + const history = useHistory() + const { data, completed } = user || {} + + return ( + } + description={ + completed ? +
+ {chunks}, + br:
+ }} + /> +
: + } + > + history.push('/signin')} + onClose={() => history.push('/')} + onReset={resetPassword} + noCancelButton + /> +
+ ); +}; + +export default ResetPasswordPage; diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/reset-password-page/reset-password-public-page.stories.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/reset-password-page/reset-password-public-page.stories.tsx new file mode 100644 index 000000000..2a823b872 --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/reset-password-page/reset-password-public-page.stories.tsx @@ -0,0 +1,45 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { withSignupSigninBaseTemplate } from '../../../../../../../.storybook/decorators/withPublicTemplate'; +import ResetPasswordPage from './reset-password-page'; + +const meta: Meta = { + title: 'Design Library/Pages/Public/Session/ResetPassword', + decorators: [ withSignupSigninBaseTemplate ], + component: ResetPasswordPage +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + user: { + completed: true, + data: { + username: 'john_doe', + email: 'john_doe@example.com' + } + } + } +}; + +export const Loading: Story = { + args: { + user: { + completed: false, + data: {} + } + } +}; + +export const WithError: Story = { + args: { + error: 'Unable to load session data' + } +}; + +export const AlternateSession: Story = { + args: { + sessionId: 'another-session-id' + } +}; diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/signin-page/signin-page.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signin-page/signin-page.tsx new file mode 100644 index 000000000..d6ccccdb0 --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signin-page/signin-page.tsx @@ -0,0 +1,26 @@ +import SpotCard from 'design-library/molecules/cards/spot-card/spot-card'; +import LoginFormSignin from 'design-library/molecules/form-section/login-form/login-form-signin/login-form-signin'; +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { useHistory } from 'react-router-dom'; + +const SigninPage = ({ + addNotification +}) => { + const history = useHistory(); + return ( + } + description={} + > + history.push('/signup')} + onForgot={() => history.push('/forgot')} + noCancelButton + /> + + ); +}; + +export default SigninPage; \ No newline at end of file diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/signin-page/signin-public-page.stories.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signin-page/signin-public-page.stories.tsx new file mode 100644 index 000000000..e241568be --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signin-page/signin-public-page.stories.tsx @@ -0,0 +1,26 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { withSignupSigninBaseTemplate } from '../../../../../../../.storybook/decorators/withPublicTemplate'; +import SigninPage from './signin-page'; + +const meta: Meta = { + title: 'Design Library/Pages/Public/Session/Signin', + decorators: [ withSignupSigninBaseTemplate ], + component: SigninPage +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithError: Story = { + args: { + error: 'Unable to load session data' + } +}; + +export const AlternateSession: Story = { + args: { + sessionId: 'another-session-id' + } +}; \ No newline at end of file diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/signup-page/signup-page.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signup-page/signup-page.tsx new file mode 100644 index 000000000..a56fa8f6a --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signup-page/signup-page.tsx @@ -0,0 +1,29 @@ +import SpotCard from 'design-library/molecules/cards/spot-card/spot-card'; +import LoginFormSignup from 'design-library/molecules/form-section/login-form/login-form-signup/login-form-signup'; +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { useHistory } from 'react-router-dom'; + +const SignupPage = ({ + handleSignup, + roles, + fetchRoles +}) => { + const history = useHistory(); + return ( + } + description={} + > + history.push('/signin')} + onSubmit={handleSignup} + roles={roles} + fetchRoles={fetchRoles} + noCancelButton + /> + + ); +}; + +export default SignupPage; \ No newline at end of file diff --git a/frontend/src/components/design-library/pages/public-pages/session-public-pages/signup-page/signup-public-page.stories.tsx b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signup-page/signup-public-page.stories.tsx new file mode 100644 index 000000000..46ada1f45 --- /dev/null +++ b/frontend/src/components/design-library/pages/public-pages/session-public-pages/signup-page/signup-public-page.stories.tsx @@ -0,0 +1,39 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { withSignupSigninBaseTemplate } from '../../../../../../../.storybook/decorators/withPublicTemplate'; +import SignupPage from './signup-page'; + +const meta: Meta = { + title: 'Design Library/Pages/Public/Session/Signup', + decorators: [ withSignupSigninBaseTemplate ], + component: SignupPage +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + handleSignup: () => {}, + roles: { + completed: true, + data: [ + { name: 'contributor', label: 'Contributor' }, + { name: 'maintainer', label: 'Maintainer' }, + { name: 'sponsor', label: 'Sponsor' } + ] + }, + fetchRoles: () => {} + } +}; + +export const WithError: Story = { + args: { + error: 'Unable to load session data' + } +}; + +export const AlternateSession: Story = { + args: { + sessionId: 'another-session-id' + } +}; diff --git a/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.stories.tsx b/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.stories.tsx new file mode 100644 index 000000000..1db90a6bf --- /dev/null +++ b/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.stories.tsx @@ -0,0 +1,23 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; +import SignupSigninBase from './signup-signin-base'; + +const meta: Meta = { + title: 'Design Library/Templates/Base/SignupSigninBase', + component: SignupSigninBase, + parameters: { + layout: 'fullscreen' + }, + args: { + children:
Default Content
+ } +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + + } +}; \ No newline at end of file diff --git a/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.styles.ts b/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.styles.ts new file mode 100644 index 000000000..437250bfd --- /dev/null +++ b/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.styles.ts @@ -0,0 +1,61 @@ +import { styled, keyframes } from '@mui/material/styles'; + +const driftSlow = keyframes` + 0% { transform: translateX(-20vw); } + 100% { transform: translateX(120vw); } +`; +const driftMed = keyframes` + 0% { transform: translateX(-30vw); } + 100% { transform: translateX(130vw); } +`; +const driftFast = keyframes` + 0% { transform: translateX(-40vw); } + 100% { transform: translateX(140vw); } +`; + +export const GradientBackground = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + position: 'relative', + overflow: 'hidden', + minHeight: '100vh', + backgroundImage: [ + 'radial-gradient(120% 120% at 30% 0%, #eaf3ff 0%, #edf5f2 30%, #e9f1ed 55%, #e3eee8 75%, #d9e7df 100%)', + 'radial-gradient(140% 140% at 90% -10%, rgba(176,209,238,0.25), rgba(176,209,238,0) 70%)' + ].join(', '), + backgroundRepeat: 'no-repeat, no-repeat', + backgroundAttachment: 'fixed, fixed', + backgroundSize: '160% 160%, 140% 140%', + backgroundPosition: '35% 0%, 60% 20%', + '@media (prefers-reduced-motion: reduce)': { + animation: 'none' + } +})); + +export const CloudsLayer = styled('div')({ + position: 'absolute', + inset: 0, + pointerEvents: 'none', + zIndex: 2 +}); + +export const Cloud = styled('div')< + { $duration?: string; $delay?: string; $blur?: number; $scale?: number; $y?: string; $variant?: 'slow' | 'med' | 'fast' } +>(({ $duration = '60s', $delay = '0s', $blur = 0, $scale = 1, $y = '0vh', $variant = 'slow' }) => { + const animation = $variant === 'fast' ? driftFast : $variant === 'med' ? driftMed : driftSlow; + return { + position: 'absolute', + top: $y, + left: '-30vw', + transform: `translateX(-30vw) scale(${$scale})`, + filter: $blur ? `blur(${$blur}px)` : 'none', + animation: `${animation} ${$duration} linear infinite`, + animationDelay: $delay, + '@media (prefers-reduced-motion: reduce)': { + animation: 'none', + transform: `translateX(0) scale(${$scale})` + } + }; +}); diff --git a/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.tsx b/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.tsx new file mode 100644 index 000000000..b6ece24ef --- /dev/null +++ b/frontend/src/components/design-library/templates/base/signup-signin-base/signup-signin-base.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { GradientBackground, CloudsLayer, Cloud } from './signup-signin-base.styles'; + +const CloudSvg = ({ colorA = '#eaf3ff', colorB = '#cfe2fb', colorC = '#a8c9ee', opacity = 1, style = {} }) => ( + + + + + + + + + + + + + + + + + +); + +export default function SignupSigninBase({ children }) { + return ( + + + + + + + + + + + + + + + + + + + + + +
+ {children} +
+
+ ); +} diff --git a/frontend/src/containers/account-activation.ts b/frontend/src/containers/account-activation.ts index cff8ac07e..382aca4b1 100644 --- a/frontend/src/containers/account-activation.ts +++ b/frontend/src/containers/account-activation.ts @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { activateUser } from '../actions/userActions'; -import AccountActivation from '../components/areas/private/features/account/features/account-activation/account-activation'; +import AccountActivation from '../components/areas/public/features/session/pages/account-activation-page'; const mapDispatchToProps = (dispatch) => ({ activateAccount: (token: string, userId: number) => dispatch(activateUser(userId, token)) diff --git a/frontend/src/containers/forgot-password-page.js b/frontend/src/containers/forgot-password-page.js new file mode 100644 index 000000000..082a3d354 --- /dev/null +++ b/frontend/src/containers/forgot-password-page.js @@ -0,0 +1,24 @@ +import { connect } from 'react-redux' +import { addNotification } from '../actions/notificationActions' +import { registerUser, forgotPassword, resetPassword } from '../actions/loginActions' +import { fetchRoles } from '../actions/userRoleActions' +import ForgotPasswordPage from '../components/areas/public/features/session/pages/forgot-page' + +const mapStateToProps = (state, props) => { + return { + user: state.user, + roles: state.roles + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + addNotification: (msg) => dispatch(addNotification(msg)), + fetchRoles: () => dispatch(fetchRoles()), + registerUser: (data) => dispatch(registerUser(data)), + forgotPassword: (data) => dispatch(forgotPassword(data)), + resetPassword: (data) => dispatch(resetPassword(data)) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(ForgotPasswordPage) diff --git a/frontend/src/containers/login-page.js b/frontend/src/containers/login-page.js index fe2dfb31b..7abf56fdd 100644 --- a/frontend/src/containers/login-page.js +++ b/frontend/src/containers/login-page.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux' import { addNotification } from '../actions/notificationActions' import { registerUser, forgotPassword, resetPassword } from '../actions/loginActions' import { fetchRoles } from '../actions/userRoleActions' -import LoginPage from '../components/areas/private/components/session/login-page' +import SigninPage from '../components/areas/public/features/session/pages/signin-page' const mapStateToProps = (state, props) => { return { @@ -21,4 +21,4 @@ const mapDispatchToProps = (dispatch, ownProps) => { } } -export default connect(mapStateToProps, mapDispatchToProps)(LoginPage) +export default connect(mapStateToProps, mapDispatchToProps)(SigninPage) diff --git a/frontend/src/containers/register-page.js b/frontend/src/containers/register-page.js new file mode 100644 index 000000000..440bcdace --- /dev/null +++ b/frontend/src/containers/register-page.js @@ -0,0 +1,24 @@ +import { connect } from 'react-redux' +import { addNotification } from '../actions/notificationActions' +import { registerUser, forgotPassword, resetPassword } from '../actions/loginActions' +import { fetchRoles } from '../actions/userRoleActions' +import SignupPage from '../components/areas/public/features/session/pages/signup-page' + +const mapStateToProps = (state, props) => { + return { + user: state.user, + roles: state.roles + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + addNotification: (msg) => dispatch(addNotification(msg)), + fetchRoles: () => dispatch(fetchRoles()), + registerUser: (data) => dispatch(registerUser(data)), + forgotPassword: (data) => dispatch(forgotPassword(data)), + resetPassword: (data) => dispatch(resetPassword(data)) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(SignupPage) diff --git a/frontend/src/containers/reset-password-page.js b/frontend/src/containers/reset-password-page.js new file mode 100644 index 000000000..b8271c29d --- /dev/null +++ b/frontend/src/containers/reset-password-page.js @@ -0,0 +1,25 @@ +import { connect } from 'react-redux' +import { addNotification } from '../actions/notificationActions' +import { registerUser, forgotPassword, resetPassword, searchUser } from '../actions/loginActions' +import { fetchRoles } from '../actions/userRoleActions' +import ResetPasswordPage from '../components/areas/public/features/session/pages/reset-page' + +const mapStateToProps = (state, props) => { + return { + user: state.loggedIn, + roles: state.roles + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + addNotification: (msg) => dispatch(addNotification(msg)), + fetchRoles: () => dispatch(fetchRoles()), + registerUser: (data) => dispatch(registerUser(data)), + forgotPassword: (data) => dispatch(forgotPassword(data)), + resetPassword: (data) => dispatch(resetPassword(data)), + searchUser: (data) => dispatch(searchUser(data)) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(ResetPasswordPage) diff --git a/frontend/src/images/login_bg_animated.svg b/frontend/src/images/login_bg_animated.svg new file mode 100644 index 000000000..9c4857126 --- /dev/null +++ b/frontend/src/images/login_bg_animated.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/images/trees_left.svg b/frontend/src/images/trees_left.svg new file mode 100644 index 000000000..b147c1e10 --- /dev/null +++ b/frontend/src/images/trees_left.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/main/routes.js b/frontend/src/main/routes.js index 59207c767..2509b89d9 100644 --- a/frontend/src/main/routes.js +++ b/frontend/src/main/routes.js @@ -2,10 +2,9 @@ import React from 'react' import { Route, HashRouter, Switch, Redirect } from 'react-router-dom' import PrivateRoute from '../components/areas/private/components/session/private-route' import PublicPageContainer from '../containers/public-container' -import Session from '../components/areas/private/components/session/session' import ProfileContainer from '../containers/profile' -import AccountActivation from '../containers/account-activation' -import LoginPageContainer from '../containers/login-page' +import SessionPage from '../components/areas/public/features/session/pages/session-page' + import FourOFour from '../components/design-library/pages/public-pages/four-o-four-public-page/four-o-four-public-page' import Stats from '../components/areas/public/features/stats/Stats-main-page' import TaskListUser from '../containers/task-list-user' @@ -14,20 +13,7 @@ import Auth from '../modules/auth' export default props => ( - {/* */} - - {/* Make sure token auth happens before the broad "/" route */} - - - {/* Private area needs to appear before the broad "/" route */} - - - - - - - @@ -41,8 +27,18 @@ export default props => ( : } /> - - {/* Keep this AFTER specific routes so they can match */} + diff --git a/frontend/src/reducers/loginReducer.js b/frontend/src/reducers/loginReducer.js index 1cd5a7399..aa8144264 100644 --- a/frontend/src/reducers/loginReducer.js +++ b/frontend/src/reducers/loginReducer.js @@ -39,7 +39,7 @@ export const loggedIn = (state = { logged: false, data: {}, completed: true, err case LOGOUT_COMPLETED: return { ...state, logged: action.logged, completed: action.completed } case SEARCH_USER_SUCCESS: - return { ...state, user: action.data } + return { ...state, data: action.data } case SEARCH_USER_ERROR: return { ...state, error: action.error } case FETCH_LOGGED_USER_REQUESTED: