Add ESLint to maintain code style. Add pre-commit hook to lint prior.

ESLint is used with the standard react plugin. It detects all kinds of
issues ranging from misspells, indentation, variable-naming, etc.
A pre-commit hook is added to git. Prior commiting, ESlint will run
to validate that everything is OK and the user will have the option
to fix it.

Signed-off-by: Sudhanshu Gautam <me@sudhanshug.com>
This commit is contained in:
Sudhanshu Gautam 2019-07-21 21:27:38 +05:30
parent d30cf925b1
commit 9475f4092a
13 changed files with 1116 additions and 1036 deletions

1
.eslintignore Normal file
View file

@ -0,0 +1 @@
src/serviceWorker.js

34
.eslintrc.yml Normal file
View file

@ -0,0 +1,34 @@
env:
browser: true
es6: true
jest: true
extends:
- 'eslint:recommended'
- 'plugin:react/recommended'
globals:
Atomics: readonly
SharedArrayBuffer: readonly
parser: 'babel-eslint'
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: 2018
sourceType: module
plugins:
- react
settings:
react:
version: 'detect'
rules:
indent:
- error
- 2
linebreak-style:
- error
- unix
quotes:
- error
- single
semi:
- error
- always

View file

@ -24,7 +24,8 @@
"test": "react-scripts test",
"eject": "react-scripts eject",
"predeploy": "yarn run build",
"deploy": "gh-pages -d build"
"deploy": "gh-pages -d build",
"lint": "eslint src/ --ext .js --fix"
},
"eslintConfig": {
"extends": "react-app"
@ -40,5 +41,12 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"babel-eslint": "^10.0.2",
"eslint": "^6.1.0",
"eslint-plugin-react": "^7.14.2",
"prop-types": "latest",
"typescript": "^3.5.3"
}
}

View file

@ -22,21 +22,21 @@ const theme = createMuiTheme({
function App() {
return (
<ThemeProvider theme={theme}>
<Suspense fallback={
<LinearProgress/>
}>
<div className="App">
<Header/>
<Router>
<Switch>
<Route path="" component={Home}/>
<Route default component={NotFound}/>
</Switch>
</Router>
</div>
</Suspense>
</ThemeProvider>
<ThemeProvider theme={theme}>
<Suspense fallback={
<LinearProgress/>
}>
<div className="App">
<Header/>
<Router>
<Switch>
<Route path="" component={Home}/>
<Route default component={NotFound}/>
</Switch>
</Router>
</div>
</Suspense>
</ThemeProvider>
);
}

View file

@ -0,0 +1,50 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from '@material-ui/core';
import React from 'react';
import PropTypes from 'prop-types';
function AlertDialog({open, cancelHandler, acceptHandler, text, title, cancelComponent, acceptComponent}) {
return (
<Dialog
open={open}
onClose={cancelHandler}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle
id="alert-dialog-title">{title}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{text}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={acceptHandler} color="primary">
{acceptComponent}
</Button>
<Button onClick={cancelHandler} color="secondary"
variant="contained" autoFocus>
{cancelComponent}
</Button>
</DialogActions>
</Dialog>
);
}
AlertDialog.propTypes = {
open: PropTypes.boolean,
cancelHandler: PropTypes.func,
acceptHandler: PropTypes.func,
text: PropTypes.string,
title: PropTypes.string,
cancelComponent: PropTypes.elementType,
acceptComponent: PropTypes.elementType,
};
export default AlertDialog;

View file

@ -0,0 +1,54 @@
import {InputAdornment, makeStyles, TextField} from '@material-ui/core';
import {fade} from '@material-ui/core/styles';
import SearchIcon from '@material-ui/icons/Search';
import React from 'react';
import PropTypes from 'prop-types';
const useStylesSearch = makeStyles(theme => ({
root: {
borderColor: '#e2e2e1',
overflow: 'hidden',
margin: 0,
borderRadius: 4,
transition: theme.transitions.create(['border-color', 'box-shadow']),
'&:hover': {
borderColor: fade(theme.palette.primary.main, 0.25),
},
'&$focused': {
backgroundColor: '#fff',
boxShadow: `${fade(theme.palette.primary.main, 0.25)} 0 0 0 2px`,
borderColor: theme.palette.primary.main,
},
},
focused: {},
}));
function SearchTextField(props) {
const classes = useStylesSearch();
return (
<TextField
variant="outlined"
label={
<div className="search-label">
{props.labeltext}
</div>
}
InputProps={
{
classes,
endAdornment: (
<InputAdornment position="start">
<SearchIcon className={classes.label}/>
</InputAdornment>
),
}
} {...props} />
);
}
SearchTextField.propTypes = {
labeltext: PropTypes.string,
};
export default SearchTextField;

View file

@ -0,0 +1,68 @@
import {
IconButton,
makeStyles,
Snackbar,
SnackbarContent,
} from '@material-ui/core';
import ErrorIcon from '@material-ui/icons/Error';
import CloseIcon from '@material-ui/icons/Close';
import React from 'react';
import PropTypes from 'prop-types';
const SnackBarStyles = makeStyles(theme => ({
error: {
backgroundColor: theme.palette.error.dark,
},
message: {
display: 'flex',
alignItems: 'center',
},
icon: {
marginRight: '20px',
fontSize: 20,
},
}));
function ErrorSnackBar({open, closeHandle, errorMessage}) {
const classes = SnackBarStyles();
return (
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={open}
autoHideDuration={6000}
onClose={closeHandle}
ContentProps={{
'aria-describedby': 'message-id',
}}
>
<SnackbarContent
className={classes.error}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<ErrorIcon className={classes.icon}/>
{errorMessage ||
'An unexpected error occurred. Please try again'}
</span>
}
action={[
<IconButton key="close" aria-label="Close" color="inherit"
onClick={closeHandle}>
<CloseIcon/>
</IconButton>,
]}
/>
</Snackbar>
);
}
ErrorSnackBar.propTypes = {
open: PropTypes.boolean,
closeHandle: PropTypes.func,
errorMessage: PropTypes.string,
};
export default ErrorSnackBar;

View file

@ -39,46 +39,46 @@ export default function Header() {
const id = open ? 'simple-popper' : undefined;
return (
<AppBar position="static">
<Toolbar>
<Typography edge="start" variant="h6">{t(
'OpenWrt Firmware Selector Wizard')}</Typography>
<div style={{flexGrow: 1}} />
<Button aria-describedby={id} color="secondary" variant="contained"
onClick={openChangeLanguagePopper} href="#">
{t('Change Language')} &nbsp;
<LanguageIcon/>
</Button>
<Popper
id={id}
open={open}
anchorEl={anchorEl}
transition
disablePortal={true}
>
{({TransitionProps}) => (
<Fade {...TransitionProps} timeout={350}>
<Paper className="language-selector-popper">
<FormControl component="fieldset">
<FormLabel component="legend">Change Language</FormLabel>
<br/>
<RadioGroup
aria-label="Language"
name="language"
value={value}
onChange={changeLanguage}
>
<FormControlLabel value="en" control={<Radio/>}
label={t('English')}/>
<FormControlLabel value="de" control={<Radio/>}
label={t('German')}/>
</RadioGroup>
</FormControl>
</Paper>
</Fade>
)}
</Popper>
</Toolbar>
</AppBar>
<AppBar position="static">
<Toolbar>
<Typography edge="start" variant="h6">{t(
'OpenWrt Firmware Selector Wizard')}</Typography>
<div style={{flexGrow: 1}} />
<Button aria-describedby={id} color="secondary" variant="contained"
onClick={openChangeLanguagePopper} href="#">
{t('Change Language')} &nbsp;
<LanguageIcon/>
</Button>
<Popper
id={id}
open={open}
anchorEl={anchorEl}
transition
disablePortal={true}
>
{({TransitionProps}) => (
<Fade {...TransitionProps} timeout={350}>
<Paper className="language-selector-popper">
<FormControl component="fieldset">
<FormLabel component="legend">Change Language</FormLabel>
<br/>
<RadioGroup
aria-label="Language"
name="language"
value={value}
onChange={changeLanguage}
>
<FormControlLabel value="en" control={<Radio/>}
label={t('English')}/>
<FormControlLabel value="de" control={<Radio/>}
label={t('German')}/>
</RadioGroup>
</FormControl>
</Paper>
</Fade>
)}
</Popper>
</Toolbar>
</AppBar>
);
}

View file

@ -6,16 +6,9 @@ import {
CircularProgress,
ClickAwayListener,
Container,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
FormControl,
Grid,
IconButton,
Input,
InputAdornment,
InputLabel,
List,
ListItem,
@ -24,158 +17,39 @@ import {
OutlinedInput,
Paper,
Select,
Snackbar,
SnackbarContent,
Tab,
Tabs,
TextField,
Tooltip,
Typography,
} from '@material-ui/core';
import {fade, makeStyles} from '@material-ui/core/styles';
import SearchIcon from '@material-ui/icons/Search';
import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
import WarningIcon from '@material-ui/icons/Warning';
import BuildIcon from '@material-ui/icons/Build';
import CloseIcon from '@material-ui/icons/Close';
import ErrorIcon from '@material-ui/icons/Error';
import './home.scss';
import {withTranslation} from 'react-i18next';
import FuzzySet from 'fuzzyset.js';
import DataService from '../../services/data';
import AlertDialog from '../../components/alert-dialog';
import ErrorSnackBar from '../../components/error-snackbar';
import SearchTextField from '../../components/device-search';
import PropTypes from 'prop-types';
const buildStatusCheckInterval = 5000;
const useStylesSearch = makeStyles(theme => ({
root: {
borderColor: '#e2e2e1',
overflow: 'hidden',
margin: 0,
borderRadius: 4,
transition: theme.transitions.create(['border-color', 'box-shadow']),
'&:hover': {
borderColor: fade(theme.palette.primary.main, 0.25),
},
'&$focused': {
backgroundColor: '#fff',
boxShadow: `${fade(theme.palette.primary.main, 0.25)} 0 0 0 2px`,
borderColor: theme.palette.primary.main,
},
},
focused: {},
}));
const SnackBarStyles = makeStyles(theme => ({
error: {
backgroundColor: theme.palette.error.dark,
},
message: {
display: 'flex',
alignItems: 'center',
},
icon: {
marginRight: '20px',
fontSize: 20,
},
}));
function ErrorSnackBar({open, closeHandle, errorMessage}) {
const classes = SnackBarStyles();
return (
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={open}
autoHideDuration={6000}
onClose={closeHandle}
ContentProps={{
'aria-describedby': 'message-id',
}}
>
<SnackbarContent
className={classes.error}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<ErrorIcon className={classes.icon}/>
{errorMessage ||
'An unexpected error occurred. Please try again'}
</span>
}
action={[
<IconButton key="close" aria-label="Close" color="inherit"
onClick={closeHandle}>
<CloseIcon/>
</IconButton>,
]}
/>
</Snackbar>
);
}
function SearchTextField(props) {
const classes = useStylesSearch();
return (
<TextField
variant="outlined"
label={
<div className="search-label">
{props.labeltext}
</div>
}
InputProps={
{
classes,
endAdornment: (
<InputAdornment position="start">
<SearchIcon className={classes.label}/>
</InputAdornment>
),
}
} {...props} />
);
}
function TabContainer({children, dir}) {
return (
<Typography component="div" dir={dir} style={{padding: '20px 0 0'}}>
{children}
</Typography>
<Typography component="div" dir={dir} style={{padding: '20px 0 0'}}>
{children}
</Typography>
);
}
function AlertDialog({open, cancelHandler, acceptHandler, text, title, cancelComponent, acceptComponent}) {
return (
<Dialog
open={open}
onClose={cancelHandler}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle
id="alert-dialog-title">{title}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{text}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={acceptHandler} color="primary">
{acceptComponent}
</Button>
<Button onClick={cancelHandler} color="secondary"
variant="contained" autoFocus>
{cancelComponent}
</Button>
</DialogActions>
</Dialog>
);
}
TabContainer.propTypes = {
children: PropTypes.elementType,
dir: PropTypes.any,
};
class Home extends React.Component {
@ -323,7 +197,7 @@ class Home extends React.Component {
this.basicInterface = val;
};
downloadingImageIndicatorShow = (i) => {
downloadingImageIndicatorShow = () => {
this.setState({
downloading: true,
});
@ -367,7 +241,7 @@ class Home extends React.Component {
}
};
closeConfirmBuildDialog = (v) => {
closeConfirmBuildDialog = () => {
this.confirmingBuild = false;
};
@ -378,36 +252,36 @@ class Home extends React.Component {
displayBuiltImageData = async (buildStatusResponse) => {
console.log(buildStatusResponse);
await this.dataService.getFiles(buildStatusResponse.data.files)
.then((fileListResponse) => {
let builtImages = [];
fileListResponse.forEach((file) => {
const suffix = file.name.substring(file.name.length - 4);
if (suffix === '.bin') {
const type = file.name.split('-').reverse()[0].split('.')[0];
builtImages.push({
url: 'https://chef.libremesh.org' +
.then((fileListResponse) => {
let builtImages = [];
fileListResponse.forEach((file) => {
const suffix = file.name.substring(file.name.length - 4);
if (suffix === '.bin') {
const type = file.name.split('-').reverse()[0].split('.')[0];
builtImages.push({
url: 'https://chef.libremesh.org' +
buildStatusResponse.data.files + file.name,
type,
});
}
});
this.setState({
builtImages,
configChanged: false,
isBuilding: false,
});
type,
});
}
});
this.setState({
builtImages,
configChanged: false,
isBuilding: false,
});
});
clearTimeout(this.checkBuildStatus);
};
buildImageCheck = async (request_hash) => {
const buildStatusResponse = await this.dataService.buildStatusCheck(
request_hash);
request_hash);
if (buildStatusResponse.status === 202) {
this.checkBuildStatus = setTimeout(
() => {
this.buildImageCheck(request_hash);
}, buildStatusCheckInterval,
async () => {
await this.buildImageCheck(request_hash);
}, buildStatusCheckInterval,
);
} else if (buildStatusResponse.status === 200) {
await this.displayBuiltImageData(buildStatusResponse);
@ -423,7 +297,7 @@ class Home extends React.Component {
this.closeConfirmBuildDialog();
const board = this.state.device.id;
const packages = this.state.packages;
const target = this.state.device.target + '/' + this.state.device.subtarget;
const target = this.state.device['target'] + '/' + this.state.device['subtarget'];
const version = this.state.release;
this.setState({
isBuilding: true,
@ -433,9 +307,9 @@ class Home extends React.Component {
if (res.status === 202 && res.data['request_hash'] !== undefined) {
const request_hash = res.data['request_hash'];
this.checkBuildStatus = setTimeout(
async () => {
await this.buildImageCheck(request_hash);
}, buildStatusCheckInterval,
async () => {
await this.buildImageCheck(request_hash);
}, buildStatusCheckInterval,
);
} else if (res.status === 200) {
await this.displayBuiltImageData(res);
@ -456,26 +330,26 @@ class Home extends React.Component {
render() {
const warning432 = this.state.showDeviceData &&
parseInt(
(this.state.device['image_size'] || '').slice(0, -1)) <= 4000 && (
<Paper className="warning-432" elevation={0}>
<Grid
container
direction="row"
justify="center"
alignItems="center"
>
<Grid item>
<WarningIcon className="icon"/>
</Grid>
<Grid item xs>
{this.props.t(
'Devices with ≤4MB flash and/or ≤32MB ram will work but they will be very limited (usually they can\'t install or run additional packages) because they have low RAM and flash space. Consider this when choosing a device to buy, or when deciding to flash OpenWrt on your device because it is listed as supported.')}
</Grid>
</Grid>
</Paper>
);
(this.state.device['image_size'] || '').slice(0, -1)) <= 4000 && (
<Paper className="warning-432" elevation={0}>
<Grid
container
direction="row"
justify="center"
alignItems="center"
>
<Grid item>
<WarningIcon className="icon"/>
</Grid>
<Grid item xs>
{this.props.t(
'Devices with ≤4MB flash and/or ≤32MB ram will work but they will be very limited (usually they can\'t install or run additional packages) because they have low RAM and flash space. Consider this when choosing a device to buy, or when deciding to flash OpenWrt on your device because it is listed as supported.')}
</Grid>
</Grid>
</Paper>
);
const notLoaded = (
<CircularProgress/>
<CircularProgress/>
);
const onLoad = (
<>
@ -484,7 +358,7 @@ class Home extends React.Component {
</Typography>
<Typography>
{this.props.t(
'Please use the input below to download firmware for your device!')}
'Please use the input below to download firmware for your device!')}
</Typography>
<br/>
<ClickAwayListener onClickAway={this.hideSearchResults}>
@ -494,65 +368,65 @@ class Home extends React.Component {
{this.props.t('Version')}
</InputLabel>
<Select
value={this.state.release}
onChange={this.setRelease}
input={<OutlinedInput name="version"
id="version-select" labelWidth={60}/>}
value={this.state.release}
onChange={this.setRelease}
input={<OutlinedInput name="version"
id="version-select" labelWidth={60}/>}
>
{
Object.keys(this.state.distributions['versions'])
.map((version) => (
<MenuItem value={version} key={version}>
<em>{version}</em>
</MenuItem>
),
)
.map((version) => (
<MenuItem value={version} key={version}>
<em>{version}</em>
</MenuItem>
),
)
}
</Select>
</FormControl>
<FormControl className="search-field">
<SearchTextField
id="outlined-adornment-search-devices"
labeltext={this.props.t('Search your device')}
value={this.state.query}
onChange={this.search}
onClick={this.search}
id="outlined-adornment-search-devices"
labeltext={this.props.t('Search your device')}
value={this.state.query}
onChange={this.search}
onClick={this.search}
/>
{
this.state.showSearch && this.state.searchResults.length !==
0 && (
<Paper elevation={4} className="search-results">
<List>
{
this.state.searchResults.map((res, index) => {
return (
<ListItem
key={res}
button
onClick={() => this.selectDevice(res)}
>
<ListItemText primary={
<div>
{res}
</div>
}/>
</ListItem>
);
})
}
</List>
</Paper>
<Paper elevation={4} className="search-results">
<List>
{
this.state.searchResults.map((res) => {
return (
<ListItem
key={res}
button
onClick={() => this.selectDevice(res)}
>
<ListItemText primary={
<div>
{res}
</div>
}/>
</ListItem>
);
})
}
</List>
</Paper>
)
}
{
(this.state.searchResults.length === 0 &&
this.state.showSearch) && (
<Paper elevation={4} className="search-results">
<ListItem>
<ListItemText
primary={this.props.t('No results')}/>
</ListItem>
</Paper>
<Paper elevation={4} className="search-results">
<ListItem>
<ListItemText
primary={this.props.t('No results')}/>
</ListItem>
</Paper>
)
}
</FormControl>
@ -574,128 +448,128 @@ class Home extends React.Component {
<Grid container className="device-info">
<Grid item xs>
<b>{this.props.t(
'Name')}: </b> {this.state.device['title']}({this.state.device['target']}/{this.state.device['subtarget']})
'Name')}: </b> {this.state.device['title']}({this.state.device['target']}/{this.state.device['subtarget']})
</Grid>
<Grid item xs>
<b>{this.props.t(
'Release Version')}: </b> {this.state.release_version_number}
'Release Version')}: </b> {this.state.release_version_number}
</Grid>
</Grid>
<AppBar className="interface-switch-bar" position="relative"
elevation={0}>
elevation={0}>
<Tabs value={this.basicInterface}
onChange={this.changeInterface}>
onChange={this.changeInterface}>
<Tab className="interface-switch"
label={this.props.t('Basic')}/>
label={this.props.t('Basic')}/>
<Tab className="interface-switch"
label={this.props.t('Advanced')}/>
label={this.props.t('Advanced')}/>
</Tabs>
</AppBar>
{
this.basicInterface === 0 ? (
<TabContainer>
{
this.state.device.images.map((image, i) => {
return (
<Button
key={i}
className="download-button"
href={'http://downloads.openwrt.org/snapshots/targets/' +
<TabContainer>
{
this.state.device.images.map((image, i) => {
return (
<Button
key={i}
className="download-button"
href={'http://downloads.openwrt.org/snapshots/targets/' +
this.state.device.target + '/' +
this.state.device.subtarget + '/' +
image.name}
color="primary"
variant="contained"
onClick={() => this.downloadingImageIndicatorShow()}
>
<CloudDownloadIcon
className="download-icon"/>
{image.type}
</Button>
);
})
}
color="primary"
variant="contained"
onClick={() => this.downloadingImageIndicatorShow()}
>
<CloudDownloadIcon
className="download-icon"/>
{image.type}
</Button>
);
})
}
&nbsp;
{
this.state.downloading && (
<CircularProgress size={20}/>
)
}
</TabContainer>
) : (
<TabContainer>
<Paper elevation={0} className="package-list-input">
<div>
{
this.state.packages.map((package_name, i) => {
return (
<Chip className="package"
key={package_name + i}
size="small"
onDelete={() => this.deletePackage(
i)}
label={package_name}
/>
);
})
}
<Tooltip
title={<span>Use comma or new line separated array. <br/>Press enter to apply.</span>}>
<Input
multiline
value={this.state.packageName}
onKeyUp={this.addPackage}
onChange={this.changeAddPackageInput}
placeholder={this.props.t('Add package(s)')}
/>
</Tooltip>
</div>
<br/>
{
this.state.downloading && (
<CircularProgress size={20}/>
this.state.configChanged && !this.state.isBuilding && (
<Button variant="outlined" color="primary"
onClick={this.openConfirmBuildDialog}>
<BuildIcon/>
&nbsp;
{this.props.t('Build')}
</Button>
)
}
</TabContainer>
) : (
<TabContainer>
<Paper elevation={0} className="package-list-input">
<div>
{
this.state.packages.map((package_name, i) => {
return (
<Chip className="package"
key={package_name + i}
size="small"
onDelete={() => this.deletePackage(
i)}
label={package_name}
/>
);
})
}
<Tooltip
title={<span>Use comma or new line separated array. <br/>Press enter to apply.</span>}>
<Input
multiline
value={this.state.packageName}
onKeyUp={this.addPackage}
onChange={this.changeAddPackageInput}
placeholder={this.props.t('Add package(s)')}
/>
</Tooltip>
</div>
<br/>
{
this.state.configChanged && !this.state.isBuilding && (
<Button variant="outlined" color="primary"
onClick={this.openConfirmBuildDialog}>
<BuildIcon/>
&nbsp;
{this.props.t('Build')}
</Button>
)
}
{
this.state.isBuilding && (
<CircularProgress size={20}/>
)
}
{
this.state.builtImages.length > 0 && !this.state.configChanged && (
{
this.state.isBuilding && (
<CircularProgress size={20}/>
)
}
{
this.state.builtImages.length > 0 && !this.state.configChanged && (
<>
{
this.state.builtImages.map((image) => (
<Button
key={image.url}
className="download-button"
href={image.url}
color="primary"
variant="contained"
onClick={() => this.downloadingImageIndicatorShow()}
>
<CloudDownloadIcon
className="download-icon"/>
{image.type}
</Button>
<Button
key={image.url}
className="download-button"
href={image.url}
color="primary"
variant="contained"
onClick={() => this.downloadingImageIndicatorShow()}
>
<CloudDownloadIcon
className="download-icon"/>
{image.type}
</Button>
))
}
&nbsp;
{
this.state.downloading && (
<CircularProgress size={20}/>
<CircularProgress size={20}/>
)
}
</>
)
}
</Paper>
</TabContainer>
)
}
</Paper>
</TabContainer>
)
}
</>
@ -706,23 +580,23 @@ class Home extends React.Component {
return (
<>
<ErrorSnackBar
open={this.state.showUnexpectedErrorBar}
closeHandle={this.closeUnexpectedErrorBar}
open={this.state.showUnexpectedErrorBar}
closeHandle={this.closeUnexpectedErrorBar}
/>
<AlertDialog
cancelHandler={this.closeConfirmBuildDialog}
acceptHandler={this.buildImage}
open={this.confirmingBuild}
text={this.props.t(
'Building image requires computation resources, so we would request you to check if this selection is what you want')}
title={this.props.t(
'Please confirm that you want to perform this action')}
cancelComponent={this.props.t('Cancel')}
acceptComponent={
cancelHandler={this.closeConfirmBuildDialog}
acceptHandler={this.buildImage}
open={this.confirmingBuild}
text={this.props.t(
'Building image requires computation resources, so we would request you to check if this selection is what you want')}
title={this.props.t(
'Please confirm that you want to perform this action')}
cancelComponent={this.props.t('Cancel')}
acceptComponent={
<>
{this.props.t('Build')} &nbsp; <BuildIcon/>
</>
}
}
/>
<Container className="home-container">
<Paper className="home-container-paper">
@ -734,4 +608,8 @@ class Home extends React.Component {
}
}
Home.propTypes = {
t: PropTypes.func
};
export default withTranslation()(Home);

View file

@ -11,15 +11,15 @@ const page404Styles = makeStyles(theme => ({
export default function NotFound() {
var classes = page404Styles();
return (
<Container style={{marginTop: '50px'}}>
<Paper className={classes.root} elevation={3}>
<Typography variant="h5" component="h3">
<Container style={{marginTop: '50px'}}>
<Paper className={classes.root} elevation={3}>
<Typography variant="h5" component="h3">
404 Page Not Found
</Typography>
<Typography component="p">
</Typography>
<Typography component="p">
Please head to the home.
</Typography>
</Paper>
</Container>
</Typography>
</Paper>
</Container>
);
}

View file

@ -14,14 +14,14 @@ const resources = {
};
i18n.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
});
.use(initReactI18next)
.init({
resources,
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
});
export default i18n;

View file

@ -3,17 +3,18 @@ import axios from 'axios';
const base = 'https://cors-anywhere.herokuapp.com/https://mwarning.de/misc/json/bin';
class DataService {
getDevicesData = axios.get(
`${base}/overview.json`)
.then(res => res.data);
`${base}/overview.json`)
.then(res => res.data);
getDeviceData = (device_id) => axios.get(
base + '/targets/' + device_id)
.then(res => res.data);
base + '/targets/' + device_id)
.then(res => res.data);
getDistributions = axios.get(
'https://chef.libremesh.org/api/distributions')
.then(res => res.data);
'https://chef.libremesh.org/api/distributions')
.then(res => res.data);
buildImage = (board, packages, target, version) => {
return axios.post('https://chef.libremesh.org/api/build-request', {
@ -34,7 +35,7 @@ class DataService {
response.isBuilt = res.status === 202 && res.data.files !== undefined;
response.status = res.status;
if (response.isBuilt) {
response = {...response, data: res.data}
response = {...response, data: res.data};
}
});
return response;

1236
yarn.lock

File diff suppressed because it is too large Load diff