From d27553e7389d91b867ccb4d03f78a2b6d75fbcab Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 18 Jul 2019 19:06:38 +0200 Subject: [PATCH 1/3] basic authentification workflow --- .eslintrc.js | 3 + package.json | 15 ++- src/front/component/App.jsx | 26 +++-- src/front/component/Page.jsx | 27 +++-- src/front/component/Router.jsx | 61 +++++------- src/front/index.css | 4 + src/front/pages/home/home.jsx | 25 +++-- src/front/pages/index.jsx | 71 ------------- src/front/pages/login/github.jsx | 24 +++-- src/front/pages/login/login.jsx | 82 +++++++++++++++ src/front/services/auth.jsx | 30 ++++-- src/index.js | 18 +++- webpack.conf.js | 165 +++++++++++++++++-------------- yarn.lock | 8 +- 14 files changed, 324 insertions(+), 235 deletions(-) delete mode 100644 src/front/pages/index.jsx create mode 100644 src/front/pages/login/login.jsx diff --git a/.eslintrc.js b/.eslintrc.js index b93afd2..ea2e4f3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,5 +32,8 @@ module.exports = { }, }, }, + "env": { + "browser": true + } }; \ No newline at end of file diff --git a/package.json b/package.json index 2105ea5..c98eb34 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "start": "node src/index.js", "build": "webpack --config webpack.conf.js", "watch": "webpack-dev-server --config webpack.conf.js", - "eslint.check": "eslint src/**/*.{js,jsx}", + "eslint.check": "eslint src/**/*.{js,jsx}", "eslint.fix": "eslint src/**/*.{js,jsx} --fix", "prettier.check": "prettier src/**/*.{js,jsx} --list-different", "prettier.fix": "prettier src/**/*.{js,jsx} --write", @@ -40,17 +40,26 @@ "dependencies": { "@material-ui/core": "^4.2.1", "axios": "0.19.0", + "cors": "^2.8.5", "debug": "4.1.1", "dotenv": "8.0.0", "eslint-plugin-import": "^2.18.0", "express": "4.17.1", "glob": "7.1.4", + "js-cookie": "2.2.0", "jsonwebtoken": "8.5.1", + "jwt-decode": "2.2.0", "moment": "2.24.0", "mongoose": "5.6.4", "parse-dashboard": "1.3.3", "parse-server": "https://github.com/brunoMaurice/parse-server/releases/download/3.6.0-bis/parse-server-v3.6.0-bis.tgz", + "prop-types": "^15.7.2", + "react": "16.8.6", + "react-dom": "16.8.6", + "react-router": "^5.0.1", + "react-router-dom": "^5.0.1", "request-promise": "4.2.4", + "rxjs": "^6.5.2", "swagger-ui-express": "4.0.7", "uuid": "3.3.2", "validate.js": "0.13.1", @@ -73,14 +82,10 @@ "eslint-plugin-react": "^7.14.2", "html-webpack-plugin": "3.2.0", "jest": "24.8.0", - "js-cookie": "2.2.0", - "jwt-decode": "2.2.0", "mini-css-extract-plugin": "0.7.0", "node-sass": "4.12.0", "nodemon": "1.19.1", "prettier": "1.18.2", - "react": "16.8.6", - "react-dom": "16.8.6", "sass-loader": "7.1.0", "style-loader": "0.23.1", "url-loader": "2.0.1", diff --git a/src/front/component/App.jsx b/src/front/component/App.jsx index f572402..ed253a3 100644 --- a/src/front/component/App.jsx +++ b/src/front/component/App.jsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { BrowserRouter as Router } from "react-router-dom"; + import Routes from "./Router"; import AppBar from '@material-ui/core/AppBar'; @@ -7,16 +8,28 @@ import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; -import { hasJwt, logout } from '../services/auth'; +import { hasJwt, logout, connectedState } from '../services/auth'; + +class App extends React.PureComponent { -class App extends React.Component { + constructor() { + super(); + this.state = {userConnected : false}; + } + + componentDidMount() { - signout() { - logout(); + connectedState.subscribe((userConnected) => { + console.log(`Connected State: ${userConnected}`); + this.setState({userConnected}); + }) } render() { + const { userConnected } = this.state; + const { history } = this.props; + return (
@@ -24,8 +37,9 @@ class App extends React.Component { Connect -
- {hasJwt() && } +
+ + {userConnected && } diff --git a/src/front/component/Page.jsx b/src/front/component/Page.jsx index 78a4005..4409f8e 100644 --- a/src/front/component/Page.jsx +++ b/src/front/component/Page.jsx @@ -1,13 +1,20 @@ import * as React from 'react'; import { Redirect } from 'react-router'; -import { ROUTES } from './Router'; +import PropTypes from 'prop-types'; // ES6 + import { hasJwt } from '../services/auth'; -class Page extends React.Component { - state = { redirectToReferrer: false }; + +class Page extends React.PureComponent { + + constructor() { + super(); + this.state = { redirectToReferrer: false }; + } async componentDidMount() { - if (!this.props.isPublic && !hasJwt()) { + const { isPublic } = this.props; + if (!isPublic && !hasJwt()) { this.setState({ redirectToReferrer: true }); } } @@ -16,11 +23,19 @@ class Page extends React.Component { const { redirectToReferrer } = this.state; if (redirectToReferrer) { - return ; + return ; } + const { children } = this.props; - return
{this.props.children}
; + return
{ children }
; } } +Page.propTypes = { + isPublic: PropTypes.bool.isRequired, + children: PropTypes.element.isRequired +}; + + + export default Page; diff --git a/src/front/component/Router.jsx b/src/front/component/Router.jsx index 3478516..93af753 100644 --- a/src/front/component/Router.jsx +++ b/src/front/component/Router.jsx @@ -1,50 +1,41 @@ import * as React from 'react'; import { Route } from 'react-router-dom'; +import LoginPage from '../pages/login/login'; +import Github from '../pages/login/github'; +import HomePage from '../pages/home/home'; + export const ROUTES = { HOME: '/', LOGIN_GITHUB: '/login/github', CONNECTED_HOME: '/home' }; -const routesConfig = [ - { - component: '/', - path: '/', - exact: true, - }, - { - component: 'login/github', - path: '/login/github', - exact: true, - }, - { - component: 'home/home', - path: '/home', - exact: true, - }, - -]; - -class Routes extends React.Component { +class Routes extends React.PureComponent { render() { return ( <> - {routesConfig.map((route, i) => { - const path = - route.component.charAt(0) === '/' - ? route.component.substr(1) - : route.component; - const component = require(`./../pages/${path}`).default; - return ( - - ); - })} + + + + + + + + + ); } diff --git a/src/front/index.css b/src/front/index.css index 802c18b..d701104 100644 --- a/src/front/index.css +++ b/src/front/index.css @@ -6,4 +6,8 @@ body { a { text-decoration: none; +} + +.spacer { + flex: 1; } \ No newline at end of file diff --git a/src/front/pages/home/home.jsx b/src/front/pages/home/home.jsx index 434fee7..179e1d7 100644 --- a/src/front/pages/home/home.jsx +++ b/src/front/pages/home/home.jsx @@ -1,9 +1,11 @@ +/* eslint-disable react/forbid-prop-types */ import * as React from 'react'; -import { hasJwt } from '../../services/auth'; -import Page from '../../component/Page'; - import { withRouter } from 'react-router-dom'; import { withStyles } from '@material-ui/core/styles'; +import PropTypes from 'prop-types'; // ES6 + +import { hasJwt } from '../../services/auth'; + const styles = { card: { @@ -20,16 +22,19 @@ const styles = { }; -class ConnectedHome extends React.Component { +class HomePage extends React.PureComponent { render() { - - const { classes } = this.props; return ( - - {hasJwt() &&
Your are authorize to see this
} -
+

Coucou

); } } -export default withRouter(withStyles(styles)(ConnectedHome)); +HomePage.propTypes = { + history: PropTypes.object.isRequired, + classes: PropTypes.object.isRequired, +}; + + + +export default withRouter(withStyles(styles)(HomePage)); diff --git a/src/front/pages/index.jsx b/src/front/pages/index.jsx deleted file mode 100644 index 32373d5..0000000 --- a/src/front/pages/index.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import * as React from 'react'; -import { hasJwt } from '../services/auth'; -import Page from '../component/Page'; - -import { withRouter } from 'react-router-dom'; -import { withStyles } from '@material-ui/core/styles'; -import Card from '@material-ui/core/Card'; -import CardContent from '@material-ui/core/CardContent'; -import CardActions from '@material-ui/core/CardActions'; -import CardMedia from '@material-ui/core/CardMedia'; -import Button from '@material-ui/core/Button'; -import Typography from '@material-ui/core/Typography'; - -const styles = { - card: { - maxWidth: 345, - margin: "0 auto", - "margin-top": "10%", - }, - media: { - height: 140, - }, - buttonContainer: { - "justify-content": "center", - } -}; - - -class Home extends React.Component { - render() { - - if (hasJwt()) { - return this.props.history.push('/home'); - } - - const { classes } = this.props; - return ( - - - - - - - Welcome - - - To start using Connect, please use your GitHub to login. - - - - - - - - - - - ); - } -} - -export default withRouter(withStyles(styles)(Home)); diff --git a/src/front/pages/login/github.jsx b/src/front/pages/login/github.jsx index 9df1341..189e704 100644 --- a/src/front/pages/login/github.jsx +++ b/src/front/pages/login/github.jsx @@ -1,7 +1,8 @@ +/* eslint-disable react/forbid-prop-types */ import * as React from 'react'; +import PropTypes from 'prop-types'; // ES6 + import { setAuthToken } from '../../services/auth'; -import { ROUTES } from '../../component/Router'; -import Page from '../../component/Page'; class Github extends React.Component { constructor(props) { @@ -11,9 +12,10 @@ class Github extends React.Component { } async componentDidMount() { - let params = new URLSearchParams(this.props.location.search); + const { location, history } = this.props; + const params = new URLSearchParams(location.search); - const responses = await fetch(`/api/auth`, { + const responses = await fetch(`${process.env.API_URL}/api/auth`, { headers: { Accept: 'application/json', 'Content-Type': 'application/json', @@ -22,18 +24,24 @@ class Github extends React.Component { body: JSON.stringify({ code: params.get('code') }), }); - if ((await responses.code) !== 200) { - this.props.history.push(ROUTES.HOME); + if (await responses.status !== 200) { + return history.push('/'); } setAuthToken(await responses.text()); - this.props.history.push(ROUTES.HOME); + return history.push('/'); } render() { - return Happy to see you back; + return

Redirection

; } } +Github.propTypes = { + location: PropTypes.object.isRequired, + history: PropTypes.object.isRequired +}; + + export default Github; diff --git a/src/front/pages/login/login.jsx b/src/front/pages/login/login.jsx new file mode 100644 index 0000000..e5c2f4e --- /dev/null +++ b/src/front/pages/login/login.jsx @@ -0,0 +1,82 @@ +/* eslint-disable react/forbid-prop-types */ + +import { withRouter } from 'react-router-dom'; +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import CardActions from '@material-ui/core/CardActions'; +import CardMedia from '@material-ui/core/CardMedia'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; +import PropTypes from 'prop-types'; // ES6 + +import * as React from 'react'; +import { hasJwt } from '../../services/auth'; + + +const styles = { + card: { + maxWidth: 345, + margin: "0 auto", + "margin-top": "10%", + }, + media: { + height: 140, + }, + buttonContainer: { + "justify-content": "center", + } +}; + + +class LoginPage extends React.PureComponent { + componentDidMount() { + if (hasJwt()) { + const { history } = this.props; + history.push('/home'); + } + } + + + render() { + const { classes } = this.props; + return ( + + + + + + Welcome + + + To start using Connect, please use your GitHub to login. + + + + + + + + + + ); + } +} + +LoginPage.propTypes = { + history: PropTypes.object.isRequired, + classes: PropTypes.object.isRequired, +}; + + + +export default withRouter(withStyles(styles)(LoginPage)); diff --git a/src/front/services/auth.jsx b/src/front/services/auth.jsx index 6278fd9..38b8a60 100644 --- a/src/front/services/auth.jsx +++ b/src/front/services/auth.jsx @@ -1,21 +1,14 @@ import * as Cookie from 'js-cookie'; import * as jwtDecode from 'jwt-decode'; +import { BehaviorSubject } from 'rxjs'; const COOKIENAME = 'jwt-connect'; +export const connectedState = new BehaviorSubject(false); export const logout = () => { Cookie.remove(COOKIENAME); }; -export const setAuthToken = token => { - if (!isJwtValid(token)) { - return false; - } - - Cookie.set(COOKIENAME, token); - return true; -}; - const isJwtValid = jwt => { if (!jwt) { return false; @@ -39,9 +32,26 @@ const isJwtValid = jwt => { return now < exp; }; + +export const setAuthToken = token => { + if (!isJwtValid(token)) { + connectedState.next(false); + return false; + } + + Cookie.set(COOKIENAME, token); + connectedState.next(true); + return true; +}; + + export const getJwt = () => { const jwt = Cookie.get(COOKIENAME); - return jwt && isJwtValid(jwt) ? jwt : null; + const valid = jwt && isJwtValid(jwt) ? jwt : null; + connectedState.next(!!valid); + return valid; }; export const hasJwt = () => !!getJwt(); + +hasJwt(); diff --git a/src/index.js b/src/index.js index d297517..44fd6f7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,16 +1,26 @@ -const { APP_PORT } = require('./config'); const express = require('express'); +const path = require('path'); +const cors = require('cors'); + +const api = require('./api'); +const logger = require('./logger'); +const { APP_PORT, FRONT_URL } = require('./config'); const parseApi = require('./middleware/parse'); const parseSandbox = require('./middleware/parseSandbox'); const parseDashboard = require('./middleware/parseDashboard'); const parseSwagger = require('./middleware/parseSwagger'); -const path = require('path'); -const api = require('./api'); -const logger = require('./logger'); const app = express(); + app.use(express.json()); + +const corsOptions = { + origin: FRONT_URL, + optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 +} + +app.use(cors()); app.use(express.urlencoded({ extended: true })); // Serve the Parse API at /parse URL prefix diff --git a/webpack.conf.js b/webpack.conf.js index c54a1be..bf4d100 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -1,4 +1,4 @@ -require('dotenv').config(); +const dotenv = require('dotenv'); const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -23,81 +23,94 @@ const scssLoaders = cssOptions => [ 'sass-loader', ]; -module.exports = { - mode: process.env.NODE_ENV || 'development', - devtool: process.env.NODE_ENV ? false : 'eval-source-map', - entry: ['@babel/polyfill', sources], - output: { - path: dist, - publicPath: '/', - filename: '[name].[contenthash].js', - }, - devServer: { - inline:true, - port: process.env.FRONT_PORT - }, - module: { - rules: [ - { - test: /\.jsx?$/, - use: 'babel-loader', - }, - { - test: [/\.scss$/, /\.css$/], - exclude: /\.module\.scss$/, - use: scssLoaders({}), - }, - { - test: /\.module\.scss$/, - use: scssLoaders({ - modules: true, - localIdentName: '[name]__[local]--[hash:base64:5]', - }), - }, - { - test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/], - loader: require.resolve('url-loader'), - options: { - limit: 10000, - name: 'static/media/[name].[hash:8].[ext]', +module.exports = () => { + const env = dotenv.config().parsed; + + // reduce it to a nice object, the same as before + const envKeys = Object.keys(env).reduce((prev, next) => { + // eslint-disable-next-line no-param-reassign + prev[`process.env.${next}`] = JSON.stringify(env[next]); + return prev; + }, {}); + + return { + mode: process.env.NODE_ENV || 'development', + devtool: process.env.NODE_ENV ? false : 'eval-source-map', + entry: ['@babel/polyfill', sources], + output: { + path: dist, + publicPath: '/', + filename: '[name].[contenthash].js', + }, + devServer: { + inline:true, + port: process.env.FRONT_PORT, + historyApiFallback: true + }, + module: { + rules: [ + { + test: /\.jsx?$/, + use: 'babel-loader', }, - }, - ], - }, - resolve: { - modules: ['src/front', 'node_modules'], - extensions: ['.jsx', '.js', '.json'], - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: EXTRACT_CSS ? '[name].[hash].css' : '[name].css', - chunkFilename: EXTRACT_CSS ? '[id].[hash].css' : '[id].css', - }), - new HtmlWebpackPlugin({ - inject: true, - template: indexTemplate, - publicUrl: process.env.PUBLIC_URL, - }), - new webpack.DefinePlugin({}), - new CopyWebpackPlugin([ - { - from: getPath('src/front/public'), - ignore: 'index.html', - to: dist, - }, - ]), - new webpack.HashedModuleIdsPlugin(), - ], - watchOptions: { - aggregateTimeout: 300, - poll: 1000, - }, - optimization: APPLY_OPTIMIZATIONS - ? { - runtimeChunk: 'single', - splitChunks: { - chunks: 'all', + { + test: [/\.scss$/, /\.css$/], + exclude: /\.module\.scss$/, + use: scssLoaders({}), + }, + { + test: /\.module\.scss$/, + use: scssLoaders({ + modules: true, + localIdentName: '[name]__[local]--[hash:base64:5]', + }), + }, + { + test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/], + loader: require.resolve('url-loader'), + options: { + limit: 10000, + name: 'static/media/[name].[hash:8].[ext]', + }, }, - } - : {}, + ], + }, + resolve: { + modules: ['src/front', 'node_modules'], + extensions: ['.jsx', '.js', '.json'], + }, + plugins: [ + new webpack.DefinePlugin(envKeys), + new MiniCssExtractPlugin({ + filename: EXTRACT_CSS ? '[name].[hash].css' : '[name].css', + chunkFilename: EXTRACT_CSS ? '[id].[hash].css' : '[id].css', + }), + new HtmlWebpackPlugin({ + inject: true, + template: indexTemplate, + publicUrl: process.env.PUBLIC_URL, + }), + new webpack.DefinePlugin({}), + new CopyWebpackPlugin([ + { + from: getPath('src/front/public'), + ignore: 'index.html', + to: dist, + }, + ]), + new webpack.HashedModuleIdsPlugin(), + ], + watchOptions: { + aggregateTimeout: 300, + poll: 1000, + }, + optimization: APPLY_OPTIMIZATIONS + ? { + runtimeChunk: 'single', + splitChunks: { + chunks: 'all', + }, + } + : {}, + } }; diff --git a/yarn.lock b/yarn.lock index 41dde9a..4d7ec7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3057,7 +3057,7 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cors@2.8.5, cors@^2.8.4: +cors@2.8.5, cors@^2.8.4, cors@^2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -8680,7 +8680,7 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== -react-router-dom@5.0.1: +react-router-dom@5.0.1, react-router-dom@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" integrity sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA== @@ -8693,7 +8693,7 @@ react-router-dom@5.0.1: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.0.1: +react-router@5.0.1, react-router@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f" integrity sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg== @@ -9203,7 +9203,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.4.0: +rxjs@^6.4.0, rxjs@^6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== From 4f1f84199b3ddc6bc09da2c222eccdf3f99153ac Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Fri, 19 Jul 2019 12:03:07 +0200 Subject: [PATCH 2/3] feat: refs #110184 #109859 add list of application and details --- src/front/component/Router.jsx | 8 ++ src/front/pages/details/details.jsx | 174 ++++++++++++++++++++++++++++ src/front/pages/home/home.jsx | 95 +++++++++++++-- src/front/services/api.jsx | 43 +++++++ 4 files changed, 308 insertions(+), 12 deletions(-) create mode 100644 src/front/pages/details/details.jsx create mode 100644 src/front/services/api.jsx diff --git a/src/front/component/Router.jsx b/src/front/component/Router.jsx index 93af753..2a94226 100644 --- a/src/front/component/Router.jsx +++ b/src/front/component/Router.jsx @@ -4,6 +4,7 @@ import { Route } from 'react-router-dom'; import LoginPage from '../pages/login/login'; import Github from '../pages/login/github'; import HomePage from '../pages/home/home'; +import DetailsPage from '../pages/details/details'; export const ROUTES = { HOME: '/', @@ -21,6 +22,13 @@ class Routes extends React.PureComponent { component={Github} /> + + + { + this.setState({ + loading: false, + application: res + }); + }); + } + + handleChange(name, event) { + const { application } = this.state; + this.setState({ + application: { + ...application, + [name]: event.target.value + } + }); + } + + render() { + const { classes } = this.props; + const { application, loading } = this.state; + return ( + <> +
+ {loading && } + + {!loading && + ( + <> + this.handleChange('name', event)} + margin="normal" + variant="outlined" + /> + + + this.handleChange('description', event)} + margin="normal" + variant="outlined" + multiline + rows="4" + /> + + this.handleChange('token', event)} + margin="normal" + variant="outlined" + /> + + + this.handleChange('token_sandbox', event)} + margin="normal" + variant="outlined" + /> + + this.handleChange('apple_store_link', event)} + margin="normal" + variant="outlined" + /> + + + this.handleChange('google_market_link', event)} + margin="normal" + variant="outlined" + /> + + + ) + } +
+ + + ); + } +} + +DetailsPage.propTypes = { + classes: PropTypes.object.isRequired, + history: PropTypes.object.isRequired, + match: PropTypes.object.isRequired +}; + + +export default withRouter(withStyles(styles)(DetailsPage)); diff --git a/src/front/pages/home/home.jsx b/src/front/pages/home/home.jsx index 179e1d7..bdc46be 100644 --- a/src/front/pages/home/home.jsx +++ b/src/front/pages/home/home.jsx @@ -1,40 +1,111 @@ +/* eslint-disable no-underscore-dangle */ /* eslint-disable react/forbid-prop-types */ import * as React from 'react'; import { withRouter } from 'react-router-dom'; import { withStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import Divider from '@material-ui/core/Divider'; +import ListItemText from '@material-ui/core/ListItemText'; +import Typography from '@material-ui/core/Typography'; +import CircularProgress from '@material-ui/core/CircularProgress'; + import PropTypes from 'prop-types'; // ES6 -import { hasJwt } from '../../services/auth'; +import { listOfApplications } from '../../services/api'; const styles = { - card: { - maxWidth: 345, + root: { + width: '100%', + maxWidth: 720, margin: "0 auto", - "margin-top": "10%", + display: "flex", + flexWrap: 'wrap', }, - media: { - height: 140, + progress: { + margin: "0 auto", + "margin-top": 140 }, - buttonContainer: { - "justify-content": "center", - } + listContainer: { + width: "100%" + }, + inline: { + display: 'inline', + }, + }; class HomePage extends React.PureComponent { + constructor() { + super(); + this.state = { + loading: true, + developerApplications: [] + } + } + + componentDidMount() { + listOfApplications().then((res) => { + this.setState({ + loading: false, + developerApplications: res + }); + }); + } + + rowClick(application) { + const { history } = this.props; + history.push(`/application/${application._id}`); + } + render() { + const { classes } = this.props; + const { developerApplications, loading } = this.state; return ( -

Coucou

+ <> + + {loading && } + + {!loading && + developerApplications.map(application => ( +
+ this.rowClick(application)}> + + {application.name} + + {` - ${application.updated_at}`} + + + + )} + secondary={application.description} + /> + + +
+ )) + } +
+ + ); } } HomePage.propTypes = { - history: PropTypes.object.isRequired, classes: PropTypes.object.isRequired, + history: PropTypes.object.isRequired, }; - export default withRouter(withStyles(styles)(HomePage)); diff --git a/src/front/services/api.jsx b/src/front/services/api.jsx new file mode 100644 index 0000000..53240da --- /dev/null +++ b/src/front/services/api.jsx @@ -0,0 +1,43 @@ +import { getJwt } from './auth'; + + +export const listOfApplications = async () => { + const jwt = getJwt(); + if (!jwt) { + return []; + } + + const responses = await fetch(`${process.env.API_URL}/api/application`, { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${jwt}` + }, + method: 'GET', + }); + + + return responses.json(); +} + + +export const getApplication = async (appId) => { + const jwt = getJwt(); + if (!jwt) { + return {}; + } + + const responses = await fetch(`${process.env.API_URL}/api/application/${appId}`, { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${jwt}` + }, + method: 'GET', + }); + + + return responses.json(); +} + + From 97c764905d55a082bf5f57398759eb8c32ececed Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Fri, 19 Jul 2019 12:10:49 +0200 Subject: [PATCH 3/3] feat: refs #110184 fix linting --- src/front/component/App.jsx | 7 ++----- src/front/pages/details/details.jsx | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/front/component/App.jsx b/src/front/component/App.jsx index ed253a3..ac741af 100644 --- a/src/front/component/App.jsx +++ b/src/front/component/App.jsx @@ -1,14 +1,14 @@ import * as React from 'react'; import { BrowserRouter as Router } from "react-router-dom"; -import Routes from "./Router"; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; -import { hasJwt, logout, connectedState } from '../services/auth'; +import { logout, connectedState } from '../services/auth'; +import Routes from "./Router"; class App extends React.PureComponent { @@ -19,16 +19,13 @@ class App extends React.PureComponent { } componentDidMount() { - connectedState.subscribe((userConnected) => { - console.log(`Connected State: ${userConnected}`); this.setState({userConnected}); }) } render() { const { userConnected } = this.state; - const { history } = this.props; return (
diff --git a/src/front/pages/details/details.jsx b/src/front/pages/details/details.jsx index b146af8..88dc339 100644 --- a/src/front/pages/details/details.jsx +++ b/src/front/pages/details/details.jsx @@ -36,7 +36,6 @@ class DetailsPage extends React.PureComponent { super(); this.state = { loading: true, - editing: false, application: { name: "Name", description: "Description", @@ -166,7 +165,7 @@ class DetailsPage extends React.PureComponent { DetailsPage.propTypes = { classes: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, + // history: PropTypes.object.isRequired, match: PropTypes.object.isRequired };