Commit c3a6262f authored by Florent Chehab's avatar Florent Chehab Committed by Florent Chehab

linting(frontend): major update & prettier

parent b0bd6671
......@@ -7,13 +7,11 @@ stages:
- svg-gen-docu # required to be done before documentation and in separate stages
- documentation
.only-default: &only-default
only:
- master
- merge_requests
check_back:
<<: *only-default
stage: check
......@@ -23,7 +21,7 @@ check_back:
- make setup
script:
- cd backend && ./manage.py check
- cd ../documentation && make extract_django # Try to generate .dot files for the system architecture
- cd ../documentation && make extract_django # Try to generate .dot files for the system architecture
artifacts:
paths:
- documentation/generated/
......@@ -37,7 +35,7 @@ check_back:
check_front:
<<: *only-default
stage: check
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.1
before_script:
- cd frontend && cp -R /usr/src/deps/node_modules .
script:
......@@ -58,7 +56,7 @@ test_back:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432 # We absolutely need this one since a gitlab runner will inject a similar variable; will cause tests to fail.
POSTGRES_PORT: 5432 # We absolutely need this one since a gitlab runner will inject a similar variable; will cause tests to fail.
FIXER_API_TOKEN: 91ed43e97a55f9ed9a501cc005c15e9c
UTC_API_ENDPOINT: http://192.168.122.1:8083/api
services:
......@@ -79,7 +77,7 @@ test_back:
test_frontend:
<<: *only-default
stage: test
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.1
before_script:
- cd frontend && cp -R /usr/src/deps/node_modules .
script:
......@@ -99,7 +97,7 @@ flake8:
eslint:
<<: *only-default
stage: lint
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.1
before_script:
- cd frontend && cp -R /usr/src/deps/node_modules .
script:
......
......@@ -2,7 +2,11 @@
## Next
###### *TBD*
###### _TBD_
- [frontend linting] Config update, big refactoring to conform
- [prettier] added for frontend
- [update] Frontend dependancies and frontend docker image
......@@ -13,15 +17,15 @@
- [bug fix] Broken pagination on previous departure page
## v1.0.1
###### *23 Aug 2019*
###### _23 Aug 2019_
- [feature] Possible to go back to home on frontend crash
- [bug fix] Frontend was crashing when `majors` was `null`
## v1.0.0
###### *25 July 2019*
###### _25 July 2019_
- Opening of the app
......@@ -5,7 +5,7 @@
# - 8000: main app
# - 5000: documentation
version: '3.7'
version: "3.7"
volumes:
# Create some local volume (should be stored in some directory on your computer)
......@@ -18,7 +18,6 @@ networks:
map-nginx:
services:
# Service for the backend app.
backend:
# Get the image from the registry
......@@ -26,11 +25,16 @@ services:
# To use a locally built one, comment above, uncomment bellow.
# build: ./backend
restart: on-failure
volumes: [".:/usr/src/app/", "media_files:/usr/src/app/backend/media"] # "Copy" the repo to the workdir and store media files on volume.
volumes: [".:/usr/src/app/", "media_files:/usr/src/app/backend/media"] # "Copy" the repo to the workdir and store media files on volume.
networks: [backend-nginx, backend-db]
environment:
WAIT_HOSTS: database:5432 # For the 'wait' script, so that we are sure the db is up and running
env_file: [./server/envs/db.env, ./server/envs/django.env, ./server/envs/external_data.env]
WAIT_HOSTS: database:5432 # For the 'wait' script, so that we are sure the db is up and running
env_file:
[
./server/envs/db.env,
./server/envs/django.env,
./server/envs/external_data.env,
]
# Run the django developpement server on image startup.
command: /bin/sh -c "/wait && cd backend && ./entry.sh && ./manage.py runserver 0.0.0.0:8000 --nostatic"
# Required that the `database` and `frontend` service are up and running.
......@@ -59,12 +63,12 @@ services:
networks: [backend-db]
ports: ["5432:5432"]
env_file: [./server/envs/db.env]
volumes: ["postgres_data:/var/lib/postgresql/data/"] # Add a volume to store the DB data.
volumes: ["postgres_data:/var/lib/postgresql/data/"] # Add a volume to store the DB data.
# Service to handle frontend live developpments and building
frontend:
# Get the image from the registry
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v1.1.1
# To use a locally built one, comment above, uncomment bellow.
# build: ./frontend
# On startup, we retrieve the dependencies from the image and start the developpement server
......@@ -85,7 +89,18 @@ services:
image: floawfloaw/light-world-tileserver:2019-05-21--zoom-8
networks: [map-nginx]
volumes: ["./server/map:/data/custom:ro"]
entrypoint: ["node", "/usr/src/app/", "-p", "8080", "--config", "/data/custom/config.json", "--public_url", "http://localhost:8000/map-server/", "--silent"]
entrypoint:
[
"node",
"/usr/src/app/",
"-p",
"8080",
"--config",
"/data/custom/config.json",
"--public_url",
"http://localhost:8000/map-server/",
"--silent",
]
restart: always
# Service to provide a local documentation
......@@ -94,7 +109,7 @@ services:
volumes: ["./documentation:/usr/src/app"]
# Start a simple python folder
command: /bin/sh -c "python -m http.server 5000"
ports: ["5000:5000"] # replicate the server port
ports: ["5000:5000"] # replicate the server port
# service to generate the UML of the backend
gen_doc_uml:
......
module.exports = {
"env": {
"browser": true,
"es6": true,
env: {
browser: true,
es6: true,
"jest/globals": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:import/errors",
"plugin:import/warnings"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
extends: ["airbnb", "prettier"],
globals: {
Atomics: "readonly",
SharedArrayBuffer: "readonly"
},
"plugins": [
"react",
"jest",
"import",
],
"rules": {
"indent": [
"error",
2,
{ "SwitchCase": 1, "ignoredNodes": [ "JSXAttribute", "JSXSpreadAttribute", ] }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"double"
],
"semi": [
"error",
"always"
],
"react/no-unescaped-entities": "off", // that one doesn't improve code readability
"react/jsx-indent-props": [2, "first"],
"react/jsx-indent": [2, 2],
"react/prop-types": "error",
"react/no-deprecated": "error",
parser: "babel-eslint",
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 2018,
sourceType: "module"
},
plugins: ["react", "jest", "import"],
rules: {
"no-warning-comments": [
"error",
{
"terms": ["todo", "fixme", "any other term"],
"location": "anywhere"
terms: ["todo", "fixme"],
location: "anywhere"
}
],
"no-var": "error",
// The warn should be turned to error at some point
"react/jsx-filename-extension": "warn",
"react/require-default-props": "warn",
"react/no-did-update-set-state": "warn",
"import/prefer-default-export": "warn",
// Styling choices bellow...
"react/destructuring-assignment": "off", // might be less readable (and is a pain in the ass to refactor)
"react/no-unescaped-entities": "off", // that one doesn't improve code readability
"react/forbid-prop-types": "off",
"react/jsx-wrap-multilines": "off",
"react/jsx-props-no-spreading": "off",
"react/state-in-constructor": "off",
"react/jsx-one-expression-per-line": "off",
"react/jsx-curly-newline": "off",
"jsx-a11y/accessible-emoji": "off",
"no-underscore-dangle": "off",
"class-methods-use-this": "off",
"arrow-parens": "off",
"no-restricted-globals": "off",
"one-var": "off",
camelcase: "off", // hard to do with python convention
"max-classes-per-file": "off",
"max-len": "off"
}
};
......@@ -22,6 +22,11 @@
"browserslist": [
"defaults"
],
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
},
"dependencies": {
"@date-io/date-fns": "^1.3.9",
"@material-ui/core": "^4.4.0",
......@@ -38,6 +43,7 @@
"lodash": "^4.17.15",
"mapbox-gl": "^1.3.0",
"notistack": "^0.8.9",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-awesome-slider": "^1.0.1",
"react-dom": "^16.9.0",
......@@ -66,6 +72,7 @@
"css-loader": "^3.2.0",
"eslint": "^6.3.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^6.1.0",
"eslint-import-resolver-node": "^0.3.2",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jest": "^22.16.0",
......@@ -73,12 +80,14 @@
"eslint-plugin-react": "^7.14.3",
"eslint-plugin-react-hooks": "^1.7.0",
"file-loader": "^4.2.0",
"husky": "^3.0.5",
"jest": "^24.9.0",
"js-yaml-loader": "^1.2.2",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.12.0",
"postcss-loader": "^3.0.0",
"prop-types": "^15.7.2",
"prettier": "1.18.2",
"pretty-quick": "^1.11.1",
"raw-loader": "^3.1.0",
"react-hot-loader": "^4.12.12",
"react-lorem-component": "^0.13.0",
......
......@@ -2,13 +2,13 @@
*/
import React from "react";
import { connect } from "react-redux";
import { compose } from "recompose";
import { Route, Switch } from "react-router-dom";
import FullScreenDialog from "./FullScreenDialog";
import {connect} from "react-redux";
import CustomComponentForAPI from "../common/CustomComponentForAPI";
import {compose} from "recompose";
import {withErrorBoundary} from "../common/ErrorBoundary";
import {Route, Switch} from "react-router-dom";
import { withErrorBoundary } from "../common/ErrorBoundary";
import getActions from "../../redux/api/getActions";
......@@ -20,9 +20,9 @@ import PageSettings from "../pages/PageThemeSettings";
import PageUser from "../pages/PageUser";
import PageLists from "../pages/PageLists";
import MainAppFrame from "./MainAppFrame";
import {APP_ROUTES} from "../../config/appRoutes";
import { APP_ROUTES } from "../../config/appRoutes";
import PageAboutProject from "../pages/PageAboutProject";
import {PageCgu, PageRgpd} from "../pages/PagesRgpdCgu";
import { PageCgu, PageRgpd } from "../pages/PagesRgpdCgu";
import PageNotFound from "../pages/PageNotFound";
import PageEditPreviousExchanges from "../pages/PageEditExchangeFeedbacks";
import PageMyExchanges from "../pages/PageMyExchanges";
......@@ -30,6 +30,7 @@ import NotifierImportantInformation from "./NotifierImportantInformation";
import FooterImportantInformation from "./FooterImportantInformation";
import PageAboutUnlinkedPartners from "../pages/PageAboutUnlinkedPartners";
import PageLogout from "../pages/PageLogout";
// import PageFiles from "../pages/PageFiles";
/**
......@@ -40,32 +41,54 @@ import PageLogout from "../pages/PageLogout";
class App extends CustomComponentForAPI {
customRender() {
return (
<div style={{display: "flex", flexDirection: "column", justifyItems: "flex-end", minHeight: "100vh"}}>
<div
style={{
display: "flex",
flexDirection: "column",
justifyItems: "flex-end",
minHeight: "100vh"
}}
>
<MainAppFrame>
<FullScreenDialog/>
<NotifierImportantInformation/>
<FullScreenDialog />
<NotifierImportantInformation />
<main>
<Switch>
<Route exact path={APP_ROUTES.base} component={PageHome}/>
<Route path={APP_ROUTES.search} component={PageSearch}/>
<Route path={APP_ROUTES.map} component={PageMap}/>
<Route path={APP_ROUTES.themeSettings} component={PageSettings}/>
<Route path={APP_ROUTES.listsWithParams} component={PageLists}/>
<Route path={APP_ROUTES.universityWithParams} component={PageUniversity}/>
<Route path={APP_ROUTES.myExchanges} component={PageMyExchanges}/>
<Route path={APP_ROUTES.editPreviousExchangeWithParams} component={PageEditPreviousExchanges}/>
<Route path={APP_ROUTES.userWithParams} component={PageUser}/>
{/*<Route path={APP_ROUTES.userFilesWithParams} component={PageFiles}/> WARNING BETA*/}
<Route path={APP_ROUTES.aboutProject} component={PageAboutProject}/>
<Route path={APP_ROUTES.aboutRgpd} component={PageRgpd}/>
<Route path={APP_ROUTES.aboutCgu} component={PageCgu}/>
<Route path={APP_ROUTES.aboutUnlinkedPartners} component={PageAboutUnlinkedPartners}/>
<Route path={APP_ROUTES.logout} component={PageLogout}/>
<Route component={PageNotFound}/>
<Route exact path={APP_ROUTES.base} component={PageHome} />
<Route path={APP_ROUTES.search} component={PageSearch} />
<Route path={APP_ROUTES.map} component={PageMap} />
<Route path={APP_ROUTES.themeSettings} component={PageSettings} />
<Route path={APP_ROUTES.listsWithParams} component={PageLists} />
<Route
path={APP_ROUTES.universityWithParams}
component={PageUniversity}
/>
<Route
path={APP_ROUTES.myExchanges}
component={PageMyExchanges}
/>
<Route
path={APP_ROUTES.editPreviousExchangeWithParams}
component={PageEditPreviousExchanges}
/>
<Route path={APP_ROUTES.userWithParams} component={PageUser} />
{/* <Route path={APP_ROUTES.userFilesWithParams} component={PageFiles}/> WARNING BETA */}
<Route
path={APP_ROUTES.aboutProject}
component={PageAboutProject}
/>
<Route path={APP_ROUTES.aboutRgpd} component={PageRgpd} />
<Route path={APP_ROUTES.aboutCgu} component={PageCgu} />
<Route
path={APP_ROUTES.aboutUnlinkedPartners}
component={PageAboutUnlinkedPartners}
/>
<Route path={APP_ROUTES.logout} component={PageLogout} />
<Route component={PageNotFound} />
</Switch>
</main>
</MainAppFrame>
<FooterImportantInformation/>
<FooterImportantInformation />
</div>
);
}
......@@ -74,30 +97,30 @@ class App extends CustomComponentForAPI {
App.propTypes = {};
// Already load some of the data even if it's not use here.
// /!\ Don't delete it
const mapStateToProps = (state) => {
return {
countries: state.api.countriesAll,
currencies: state.api.currenciesAll,
universities: state.api.universitiesAll,
languages: state.api.languagesAll,
serverModerationStatus: state.api.serverModerationStatusAll,
};
};
// /!\ Don't performDelete it
const mapStateToProps = state => ({
countries: state.api.countriesAll,
currencies: state.api.currenciesAll,
universities: state.api.universitiesAll,
languages: state.api.languagesAll,
serverModerationStatus: state.api.serverModerationStatusAll
});
const mapDispatchToProps = (dispatch) => {
return {
api: {
countries: () => dispatch(getActions("countries").readAll()),
currencies: () => dispatch(getActions("currencies").readAll()),
universities: () => dispatch(getActions("universities").readAll()),
languages: () => dispatch(getActions("languages").readAll()),
serverModerationStatus: () => dispatch(getActions("serverModerationStatus").readAll()), // not needed for server moderation status
},
};
};
const mapDispatchToProps = dispatch => ({
api: {
countries: () => dispatch(getActions("countries").readAll()),
currencies: () => dispatch(getActions("currencies").readAll()),
universities: () => dispatch(getActions("universities").readAll()),
languages: () => dispatch(getActions("languages").readAll()),
serverModerationStatus: () =>
dispatch(getActions("serverModerationStatus").readAll()) // not needed for server moderation status
}
});
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withErrorBoundary(),
connect(
mapStateToProps,
mapDispatchToProps
),
withErrorBoundary()
)(App);
......@@ -2,58 +2,57 @@ import React from "react";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import PropTypes from "prop-types";
import {classNames} from "../../utils/classNames";
import {appBarHeight, siteMaxWidth} from "../../config/sharedStyles";
import {makeStyles} from "@material-ui/styles";
import { makeStyles } from "@material-ui/styles";
import { classNames } from "../../utils/classNames";
import { appBarHeight, siteMaxWidth } from "../../config/sharedStyles";
const useStyle = makeStyles(theme => ({
root: {
flexGrow: 1,
flexGrow: 1
},
appBar: {
width: "100%",
height: appBarHeight(theme),
height: appBarHeight(theme)
},
toolBar: {
width: "100%",
maxWidth: siteMaxWidth(),
display: "flex",
display: "flex"
},
content: {
maxWidth: siteMaxWidth(),
maxWidth: siteMaxWidth()
},
centered: {
margin: "0 auto",
margin: "0 auto"
},
middleBlock: {
flex: 1
},
}
}));
export default function BaseTemplate(props) {
export default function BaseTemplate({ inBetween, toolbarContent, children }) {
const classes = useStyle();
return (
<div className={classes.root}>
<AppBar position="sticky" className={classes.appBar}>
<Toolbar className={classNames(classes.toolBar, classes.centered)}>
{props.toolbarContent}
{toolbarContent}
</Toolbar>
</AppBar>
{props.inBetween}
{inBetween}
<div className={classNames(classes.content, classes.centered)}>
{props.children}
{children}
</div>
</div>
);
}
BaseTemplate.propTypes = {
toolbarContent: PropTypes.node.isRequired,
children: PropTypes.node.isRequired,
inBetween: PropTypes.node.isRequired,
inBetween: PropTypes.node
};
BaseTemplate.defaultProps = {
......
......@@ -3,29 +3,36 @@ import PropTypes from "prop-types";
import Drawer from "@material-ui/core/Drawer";
import List from "@material-ui/core/List";
import Divider from "@material-ui/core/Divider";
import {infoMenuItems, mainMenuHome, mainMenuItems, secondaryMenuItems, settingsMenuItems,} from "./menuItems";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import CustomNavLink from "../common/CustomNavLink";
import SettingsIcon from "@material-ui/icons/Settings";
import InfoIcon from "@material-ui/icons/Info";
import CustomNavLink from "../common/CustomNavLink";
import {
infoMenuItems,
mainMenuHome,
mainMenuItems,
secondaryMenuItems,
settingsMenuItems
} from "./menuItems";
class DrawerMenu extends React.Component {
toListItem(items, inset = false) {
return items.map(({label, route, Icon}, idx) => (
<CustomNavLink key={idx} to={route} onClick={() => this.props.closeDrawer()}>
<ListItem button onClick={() => this.props.closeDrawer()}>
{
Icon !== null ?
<ListItemIcon>
<Icon/>
</ListItemIcon>
:
<></>
}
<ListItemText primary={label} inset={inset}/>
const { closeDrawer } = this.props;
return items.map(({ label, route, Icon }, idx) => (
// eslint-disable-next-line react/no-array-index-key
<CustomNavLink key={idx} to={route} onClick={() => closeDrawer()}>
<ListItem button onClick={() => closeDrawer()}>
{Icon !== null ? (
<ListItemIcon>
<Icon />
</ListItemIcon>
) : (
<></>
)}
<ListItemText primary={label} inset={inset} />
</ListItem>
</CustomNavLink>
));
......@@ -35,33 +42,34 @@ class DrawerMenu extends React.Component {
return (
<ListItem>
<ListItemIcon>
<Icon/>
<Icon />
</ListItemIcon>
<ListItemText primary={label}/>
<ListItemText primary={label} />
</ListItem>
);
}
render() {
const { closeDrawer, open } = this.props;
return (
<div style={{zIndex: 200000}}>
<Drawer open={this.props.open} onClose={() => this.props.closeDrawer()}>
<div style={{ zIndex: 200000 }}>
<Drawer open={open} onClose={() => closeDrawer()}>
<List>{this.toListItem([mainMenuHome])}</List>
<Divider/>