all repos — caroster @ 03b6a54cf07ad4b1011d2387873b2fe91faf99ce

[Octree] Group carpool to your event https://caroster.io

🚨 Add linter and lint code
Tim Izzo tim@octree.ch
Fri, 03 Jul 2020 09:34:52 +0000
commit

03b6a54cf07ad4b1011d2387873b2fe91faf99ce

parent

f527e8380e96162b98e0453baf370e3142c7dddd

M .gitlab-ci.yml.gitlab-ci.yml

@@ -4,24 +4,27 @@ - build

- deploy - analyze -# # CI variables scopées: -# # - NPM_REGISTRY: URL du NPM registry à utiliser (default: https://npm-8ee.hidora.com/) -# Test: -# stage: test -# coverage: /All\sfiles.*?\s+(\d+.\d+)/ -# image: node:current-alpine3.12 -# rules: -# - if: "$CI_PIPELINE_SOURCE =~ merge_request_event" -# - if: '$CI_COMMIT_REF_NAME == "master"' -# script: -# - npm set registry ${NPM_REGISTRY:-https://npm-8ee.hidora.com/} -# - npm ci -# - npm run test -# cache: -# key: "$CI_COMMIT_REF_SLUG" -# paths: -# - node_modules/ -# - ~/.npm +# CI variables scopées: +# - NPM_REGISTRY: URL du NPM registry à utiliser (default: https://npm-8ee.hidora.com/) +Test: + stage: test + coverage: /All\sfiles.*?\s+(\d+.\d+)/ + image: node:current-alpine3.12 + only: + - merge_requests + - master + script: + - npm set registry ${NPM_REGISTRY:-https://npm-8ee.hidora.com/} + - apk add --no-cache python3 + - cd app + - npm i + - npm run lint + # - npm run test + cache: + key: "$CI_COMMIT_REF_SLUG" + paths: + - node_modules/ + - ~/.npm Build: stage: build
A app/.eslintignore

@@ -0,0 +1,3 @@

+build/ +*test.js +src/serviceWorker.js
A app/.eslintrc

@@ -0,0 +1,56 @@

+{ + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:jsx-a11y/recommended", + "google" + ], + "parser": "babel-eslint", + "env": { + "es6": true, + "node": true + }, + "parserOptions": { + "sourceType": "module" + }, + "plugins": ["react", "react-hooks", "jsx-a11y"], + "globals": { + "alert": true, + "confirm": true, + "FormData": true, + "XMLHttpRequest": true, + "document": true, + "window": true, + "$": true, + "jQuery": true, + "navigator": true, + "fetch": true + }, + "settings": { + "react": { + "pragma": "React", + "version": "detect" + } + }, + "rules": { + "prefer-const": "error", + "complexity": ["warn", 10], + "max-len": ["warn", 110], + "arrow-parens": "off", + "prefer-destructuring": "error", + "indent": ["error", 2], + "operator-linebreak": "off", + "curly": "off", + "no-extra-boolean-cast": "off", + "quote-props": "off", + "jsx-a11y/no-autofocus": "off", + "react/boolean-prop-naming": "error", + "react/prop-types": "off", + "react/jsx-no-useless-fragment": "error", + "react/jsx-pascal-case": "error", + "react/jsx-max-depth": ["error", {"max": 5}], + "jsx-a11y/click-events-have-key-events": "warn", + "jsx-a11y/no-static-element-interactions": "warn", + "jsx-a11y/no-noninteractive-element-interactions": "warn" + } +}
M app/package-lock.jsonapp/package-lock.json

@@ -5066,6 +5066,12 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="

} } }, + "eslint-config-google": { + "version": "0.14.0", + "resolved": "https://npm-8ee.hidora.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true + }, "eslint-config-react-app": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz",
M app/package.jsonapp/package.json

@@ -28,7 +28,8 @@ "scripts": {

"start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "lint": "eslint ." }, "eslintConfig": { "extends": "react-app"

@@ -44,5 +45,8 @@ "last 1 chrome version",

"last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "eslint-config-google": "^0.14.0" } }
M app/src/components/Map/index.jsapp/src/components/Map/index.js

@@ -1,18 +1,18 @@

-import React from "react"; -import "leaflet/dist/leaflet.css"; -import { Map as LeafletMap, Marker, TileLayer } from "react-leaflet"; -import L from "leaflet"; +import React from 'react'; +import 'leaflet/dist/leaflet.css'; +import {Map as LeafletMap, Marker, TileLayer} from 'react-leaflet'; +import L from 'leaflet'; delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ - iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"), - iconUrl: require("leaflet/dist/images/marker-icon.png"), - shadowUrl: require("leaflet/dist/images/marker-shadow.png"), + iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), + iconUrl: require('leaflet/dist/images/marker-icon.png'), + shadowUrl: require('leaflet/dist/images/marker-shadow.png'), }); -const Map = ({ width = "100%", height = "20rem", position }) => { +const Map = ({width = '100%', height = '20rem', position}) => { return ( - <LeafletMap center={position} zoom={13} style={{ width, height }}> + <LeafletMap center={position} zoom={13} style={{width, height}}> <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
M app/src/components/Paper/index.jsapp/src/components/Paper/index.js

@@ -1,15 +1,15 @@

-import React from "react"; -import PaperMUI from "@material-ui/core/Paper"; -import { makeStyles } from "@material-ui/core/styles"; +import React from 'react'; +import PaperMUI from '@material-ui/core/Paper'; +import {makeStyles} from '@material-ui/core/styles'; -const Paper = ({ className, ...props }) => { +const Paper = ({className, ...props}) => { const classes = useStyles(); return ( - <PaperMUI classes={{ root: classes.root, parent: className }} {...props} /> + <PaperMUI classes={{root: classes.root, parent: className}} {...props} /> ); }; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles(theme => ({ root: { padding: theme.spacing(2), },
M app/src/containers/Car/HeaderEditing.jsapp/src/containers/Car/HeaderEditing.js

@@ -29,7 +29,7 @@ const [meeting, setMeeting] = useState(car?.meeting ?? '');

const [date, setDate] = useState( car?.departure ? moment(car.departure) : moment() ); - const [phone, setPhone] = useState(car?.phone_number ?? ''); + const [phone, setPhone] = useState(car ? car['phone_number'] : ''); const [details, setDetails] = useState(car?.details ?? ''); // Click on ESQ should close the form
M app/src/containers/CarColumns/AddCar.jsapp/src/containers/CarColumns/AddCar.js

@@ -1,25 +1,25 @@

-import React from "react"; -import Button from "@material-ui/core/Button"; -import Container from "@material-ui/core/Container"; -import { makeStyles } from "@material-ui/core"; -import { useTranslation } from "react-i18next"; +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Container from '@material-ui/core/Container'; +import {makeStyles} from '@material-ui/core'; +import {useTranslation} from 'react-i18next'; -const AddCar = ({ toggleNewCar }) => { +const AddCar = ({toggleNewCar}) => { const classes = useStyles(); - const { t } = useTranslation(); + const {t} = useTranslation(); return ( <Container maxWidth="sm" className={classes.container}> <Button variant="contained" onClick={toggleNewCar}> - {t("car.creation.title")} + {t('car.creation.title')} </Button> </Container> ); }; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles(theme => ({ container: { - display: "flex", - justifyContent: "center", + display: 'flex', + justifyContent: 'center', }, }));
M app/src/containers/EventDetails/index.jsapp/src/containers/EventDetails/index.js

@@ -1,32 +1,32 @@

-import React from "react"; -import Typography from "@material-ui/core/Typography"; -import TextField from "../../components/TextField"; -import moment from "moment"; -import { useEvent } from "../../contexts/Event"; -import { useTranslation } from "react-i18next"; -import { makeStyles } from "@material-ui/core"; -import Button from "@material-ui/core/Button"; -import { DatePicker } from "@material-ui/pickers"; -import Map from "../../components/Map"; +import React from 'react'; +import Typography from '@material-ui/core/Typography'; +import TextField from '../../components/TextField'; +import moment from 'moment'; +import {useEvent} from '../../contexts/Event'; +import {useTranslation} from 'react-i18next'; +import {makeStyles} from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import {DatePicker} from '@material-ui/pickers'; +import Map from '../../components/Map'; -const EventDetails = ({ toggleDetails }) => { - const { t } = useTranslation(); +const EventDetails = ({toggleDetails}) => { + const {t} = useTranslation(); const classes = useStyles(); - const { event, isEditing, setEditingEvent, editingEvent } = useEvent(); + const {event, isEditing, setEditingEvent, editingEvent} = useEvent(); if (!event) return null; - const idPrefix = isEditing ? "EditEvent" : "Event"; + const idPrefix = isEditing ? 'EditEvent' : 'Event'; return ( <div> <div className={classes.section}> - <Typography variant="h6">{t("event.fields.starts_on")}</Typography> + <Typography variant="h6">{t('event.fields.starts_on')}</Typography> {isEditing ? ( <DatePicker value={ editingEvent.date ? moment(editingEvent.date) : moment(event.date) } - onChange={(date) => - setEditingEvent({ ...editingEvent, date: date.toISOString() }) + onChange={date => + setEditingEvent({...editingEvent, date: date.toISOString()}) } className={classes.textField} fullWidth

@@ -34,7 +34,7 @@ format="DD.MM.YYYY"

disablePast id={`${idPrefix}Date`} name="date" - TextFieldComponent={(p) => <TextField light {...p} />} + TextFieldComponent={p => <TextField light {...p} />} /> ) : ( <Typography variant="body1" id={`${idPrefix}Date`}>

@@ -43,15 +43,15 @@ </Typography>

)} </div> <div className={classes.section}> - <Typography variant="h6">{t("event.fields.address")}</Typography> + <Typography variant="h6">{t('event.fields.address')}</Typography> {isEditing ? ( <TextField light multiline rows={4} value={editingEvent.address ?? event.address} - onChange={(e) => - setEditingEvent({ ...editingEvent, address: e.target.value }) + onChange={e => + setEditingEvent({...editingEvent, address: e.target.value}) } id={`${idPrefix}Address`} name="address"

@@ -64,7 +64,7 @@ )}

</div> <div className={classes.actions}> <Button onClick={toggleDetails} variant="contained" id={`CarFindBtn`}> - {t("event.actions.find_car")} + {t('event.actions.find_car')} </Button> </div> {event.position && (

@@ -76,13 +76,13 @@ </div>

); }; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles(theme => ({ section: { marginBottom: theme.spacing(2), }, actions: { - display: "flex", - justifyContent: "center", + display: 'flex', + justifyContent: 'center', marginTop: theme.spacing(4), }, map: {
M app/src/containers/EventFab/index.jsapp/src/containers/EventFab/index.js

@@ -16,13 +16,11 @@

const onShare = async () => { if (!event) return null; // If navigator as share capability - if (!!navigator.share) { - const shareData = { + if (!!navigator.share) + return await navigator.share({ title: `Caroster ${event.name}`, url: `${window.location.href}`, - }; - return await navigator.share(shareData); - } + }); // Else copy URL in clipboard else if (!!navigator.clipboard) { await navigator.clipboard.writeText(window.location.href);

@@ -38,7 +36,11 @@ <Fab aria-label="more" onClick={toggleOpen}>

<Icon>add</Icon> </Fab> </div> - <div className={classes.actionContainer} onClick={toggleOpen}> + <div + className={classes.actionContainer} + onClick={toggleOpen} + role="dialog" + > <Fab color="primary" aria-label="share"
M app/src/containers/EventMenu/index.jsapp/src/containers/EventMenu/index.js

@@ -1,19 +1,19 @@

-import React from "react"; -import Menu from "@material-ui/core/Menu"; -import MenuItem from "@material-ui/core/MenuItem"; +import React from 'react'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; -const EventMenu = ({ anchorEl, setAnchorEl, actions = [] }) => { +const EventMenu = ({anchorEl, setAnchorEl, actions = []}) => { return ( <Menu anchorEl={anchorEl} anchorOrigin={{ - vertical: "top", - horizontal: "right", + vertical: 'top', + horizontal: 'right', }} keepMounted transformOrigin={{ - vertical: "top", - horizontal: "right", + vertical: 'top', + horizontal: 'right', }} open={!!anchorEl} onClose={() => setAnchorEl(null)}
M app/src/containers/TosDialog/index.jsapp/src/containers/TosDialog/index.js

@@ -1,26 +1,26 @@

-import React, { useEffect } from "react"; -import Dialog from "@material-ui/core/Dialog"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogContentText from "@material-ui/core/DialogContentText"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import Button from "@material-ui/core/Button"; -import Slide from "@material-ui/core/Slide"; -import { useStrapi } from "strapi-react-context"; -import marked from "marked"; -import { useTranslation } from "react-i18next"; +import React, {useEffect} from 'react'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Button from '@material-ui/core/Button'; +import Slide from '@material-ui/core/Slide'; +import {useStrapi} from 'strapi-react-context'; +import marked from 'marked'; +import {useTranslation} from 'react-i18next'; const Transition = React.forwardRef(function Transition(props, ref) { return <Slide direction="up" ref={ref} {...props} />; }); -const TosDialog = ({ open, toggle }) => { +const TosDialog = ({open, toggle}) => { const strapi = useStrapi(); - const { t } = useTranslation(); - const page = strapi.stores?.pages?.find(({ type }) => type === "tos"); + const {t} = useTranslation(); + const page = strapi.stores?.pages?.find(({type}) => type === 'tos'); useEffect(() => { - strapi.services.pages.find({ type: "tos" }); + strapi.services.pages.find({type: 'tos'}); }, [strapi.services.pages]); return (

@@ -35,12 +35,12 @@ <DialogTitle>{page?.name}</DialogTitle>

<DialogContent> {page && ( <DialogContentText - dangerouslySetInnerHTML={{ __html: marked(page.content) }} + dangerouslySetInnerHTML={{__html: marked(page.content)}} /> )} </DialogContent> <DialogActions> - <Button onClick={toggle}>{t("generic.close")}</Button> + <Button onClick={toggle}>{t('generic.close')}</Button> </DialogActions> </Dialog> );
M app/src/containers/WaitingList/index.jsapp/src/containers/WaitingList/index.js

@@ -100,7 +100,7 @@ <RemoveDialog

text={ <Trans i18nKey="passenger.actions.remove_alert" - values={{name: passengers[removing]}} + values={{name: passengers ? passengers[removing] : null}} components={{italic: <i />, bold: <strong />}} /> }
M app/src/contexts/Event.jsapp/src/contexts/Event.js

@@ -4,33 +4,33 @@ useContext,

useEffect, useMemo, useState, -} from "react"; -import { useStrapi } from "strapi-react-context"; +} from 'react'; +import {useStrapi} from 'strapi-react-context'; const EventContext = createContext(); export default EventContext; export const useEvent = () => useContext(EventContext); -export const EventProvider = ({ match, children }) => { +export const EventProvider = ({match, children}) => { const strapi = useStrapi(); - const { eventId } = match.params; + const {eventId} = match.params; const [isEditing, setIsEditing] = useState(false); const [editingEvent, setEditingEvent] = useState({}); // Fetch event data if not already done useEffect(() => { - if (!strapi.stores.events?.find(({ id }) => eventId === id)) + if (!strapi.stores.events?.find(({id}) => eventId === id)) strapi.services.events.findOne(eventId); }, [eventId, strapi.stores.events, strapi.services.events]); // Fetch event cars on load useEffect(() => { - strapi.services.cars.find({ event: eventId }); + strapi.services.cars.find({event: eventId}); }, [eventId]); // eslint-disable-line react-hooks/exhaustive-deps // Retrieve event data const event = useMemo( - () => strapi.stores.events?.find((e) => e.id === eventId), + () => strapi.stores.events?.find(e => e.id === eventId), [eventId, strapi.stores.events] );
M app/src/contexts/Toast.jsapp/src/contexts/Toast.js

@@ -1,15 +1,15 @@

-import React, { createContext, useState, useContext } from "react"; -import Snackbar from "@material-ui/core/Snackbar"; +import React, {createContext, useState, useContext} from 'react'; +import Snackbar from '@material-ui/core/Snackbar'; const ToastContext = createContext(); export default ToastContext; export const useToast = () => useContext(ToastContext); -export const ToastProvider = ({ children }) => { +export const ToastProvider = ({children}) => { const [toasts, setToasts] = useState([]); - const addToast = (newToast) => setToasts([...toasts, newToast]); + const addToast = newToast => setToasts([...toasts, newToast]); const clearToasts = () => setToasts([]);

@@ -19,12 +19,12 @@ setToasts(tsts);

}; return ( - <ToastContext.Provider value={{ toasts, addToast, clearToasts }}> + <ToastContext.Provider value={{toasts, addToast, clearToasts}}> {children} <Snackbar anchorOrigin={{ - vertical: "bottom", - horizontal: "right", + vertical: 'bottom', + horizontal: 'right', }} autoHideDuration={6000} open={toasts && toasts.length > 0}
M app/src/hooks/useDebounce.jsapp/src/hooks/useDebounce.js

@@ -1,6 +1,12 @@

-import { useState, useEffect } from "react"; +import {useState, useEffect} from 'react'; // https://github.com/xnimorz/use-debounce +/** + * + * @param {*} value Value to debounce + * @param {number} delay Debounce time + * @return {*} + */ function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value);
M app/src/layouts/Centered.jsapp/src/layouts/Centered.js

@@ -1,18 +1,18 @@

-import React from "react"; -import Container from "@material-ui/core/Container"; -import DefaultLayout from "./Default"; -import { makeStyles } from "@material-ui/core/styles"; +import React from 'react'; +import Container from '@material-ui/core/Container'; +import DefaultLayout from './Default'; +import {makeStyles} from '@material-ui/core/styles'; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles(theme => ({ layout: { - display: "flex", - alignItems: "center", - justifyContent: "center", - minHeight: "100vh", + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + minHeight: '100vh', }, })); -const CenteredLayout = ({ children }) => { +const CenteredLayout = ({children}) => { const classes = useStyles(); return (
M app/src/layouts/Default.jsapp/src/layouts/Default.js

@@ -1,6 +1,6 @@

-import React from "react"; +import React from 'react'; -const DefaultLayout = ({ children }) => { +const DefaultLayout = ({children}) => { return <div>{children}</div>; };
M app/src/pages/Event.jsapp/src/pages/Event.js

@@ -155,8 +155,9 @@ color: 'white',

}, })); -export default props => ( +const EventWithContext = props => ( <EventProvider {...props}> <Event {...props} /> </EventProvider> ); +export default EventWithContext;
M app/src/pages/NotFound.jsapp/src/pages/NotFound.js

@@ -1,4 +1,4 @@

-import React from "react"; +import React from 'react'; const NotFound = () => { return <div>404 - Not Found</div>;