Add prettier support

Add prettier and bind it with eslint.
Also add pre-commit hook to prettify before commit

Signed-off-by: Sudhanshu Gautam <me@sudhanshug.com>
This commit is contained in:
Sudhanshu Gautam 2019-09-02 02:30:15 +05:30
parent 3f46d13ccf
commit ce4d2e1436
13 changed files with 776 additions and 468 deletions

View file

@ -1,9 +1,9 @@
import React, {Suspense} from 'react';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.scss';
import {createMuiTheme} from '@material-ui/core/styles';
import {ThemeProvider} from '@material-ui/styles';
import { createMuiTheme } from '@material-ui/core/styles';
import { ThemeProvider } from '@material-ui/styles';
import Header from './components/header.js';
import Home from './containers/home/home';
import NotFound from './containers/not-found/not-found';
@ -24,23 +24,28 @@ const theme = createMuiTheme({
function App() {
return (
<ThemeProvider theme={theme}>
<Suspense fallback={
<LinearProgress/>
}>
<Suspense fallback={<LinearProgress />}>
<div className="App">
<Header/>
<Header />
<Router>
<Switch>
<Route path="" component={Home}/>
<Route default component={NotFound}/>
<Route path="" component={Home} />
<Route default component={NotFound} />
</Switch>
</Router>
<Paper elevation={4} className="report-problem-container">
<span>
If you come across any issue, feel free to report <a href="https://github.com/aparcar/attendedsysupgrade-server/issues">here</a>.
If you come across any issue, feel free to report{' '}
<a href="https://github.com/aparcar/attendedsysupgrade-server/issues">
here
</a>
.
</span>
<span className="report-link">
For contributions, go to <a href="https://github.com/sudhanshu16/openwrt-firmware-selector/">Github</a>
For contributions, go to{' '}
<a href="https://github.com/sudhanshu16/openwrt-firmware-selector/">
Github
</a>
</span>
</Paper>
</div>

View file

@ -9,7 +9,15 @@ import {
import React from 'react';
import PropTypes from 'prop-types';
function AlertDialog({open, cancelHandler, acceptHandler, body, title, cancelComponent, acceptComponent}) {
function AlertDialog({
open,
cancelHandler,
acceptHandler,
body,
title,
cancelComponent,
acceptComponent,
}) {
return (
<Dialog
open={open}
@ -17,28 +25,28 @@ function AlertDialog({open, cancelHandler, acceptHandler, body, title, cancelCom
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle
id="alert-dialog-title">{title}</DialogTitle>
<DialogTitle id="alert-dialog-title">{title}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{body}
</DialogContentText>
</DialogContent>
<DialogActions>
{
acceptHandler && (
<Button onClick={acceptHandler} color="primary">
{acceptComponent}
</Button>
)
}
{
cancelHandler && (
<Button onClick={cancelHandler} color="secondary" variant="contained" autoFocus>
{cancelComponent}
</Button>
)
}
{acceptHandler && (
<Button onClick={acceptHandler} color="primary">
{acceptComponent}
</Button>
)}
{cancelHandler && (
<Button
onClick={cancelHandler}
color="secondary"
variant="contained"
autoFocus
>
{cancelComponent}
</Button>
)}
</DialogActions>
</Dialog>
);

View file

@ -1,5 +1,5 @@
import {InputAdornment, makeStyles, TextField} from '@material-ui/core';
import {fade} from '@material-ui/core/styles';
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';
@ -29,22 +29,18 @@ function SearchTextField(props) {
return (
<TextField
variant="outlined"
label={
<div className="search-label">
{props.labeltext}
</div>
}
label={<div className="search-label">{props.labeltext}</div>}
disabled={props.disabled}
InputProps={
{
classes,
endAdornment: (
<InputAdornment position="start">
<SearchIcon className={classes.label}/>
</InputAdornment>
),
}
} {...props} />
InputProps={{
classes,
endAdornment: (
<InputAdornment position="start">
<SearchIcon className={classes.label} />
</InputAdornment>
),
}}
{...props}
/>
);
}

View file

@ -23,7 +23,7 @@ const SnackBarStyles = makeStyles(theme => ({
},
}));
function ErrorSnackBar({open, closeHandle, errorMessage}) {
function ErrorSnackBar({ open, closeHandle, errorMessage }) {
const classes = SnackBarStyles();
return (
<Snackbar
@ -43,15 +43,18 @@ function ErrorSnackBar({open, closeHandle, errorMessage}) {
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<ErrorIcon className={classes.icon}/>
{errorMessage ||
'An unexpected error occurred. Please try again'}
<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
key="close"
aria-label="Close"
color="inherit"
onClick={closeHandle}
>
<CloseIcon />
</IconButton>,
]}
/>

View file

@ -14,12 +14,11 @@ import {
Toolbar,
Typography,
} from '@material-ui/core';
import {useTranslation} from 'react-i18next';
import { useTranslation } from 'react-i18next';
import i18next from 'i18next';
export default function Header() {
const {t, i18n} = useTranslation();
const { t, i18n } = useTranslation();
const [value, setValue] = React.useState('en');
const [anchorEl, setAnchorEl] = React.useState(null);
@ -41,13 +40,19 @@ export default function Header() {
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="#">
<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/>
<LanguageIcon />
</Button>
<Popper
id={id}
@ -56,22 +61,28 @@ export default function Header() {
transition
disablePortal={true}
>
{({TransitionProps}) => (
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Paper className="language-selector-popper">
<FormControl component="fieldset">
<FormLabel component="legend">Change Language</FormLabel>
<br/>
<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')}/>
<FormControlLabel
value="en"
control={<Radio />}
label={t('English')}
/>
<FormControlLabel
value="de"
control={<Radio />}
label={t('German')}
/>
</RadioGroup>
</FormControl>
</Paper>

View file

@ -23,14 +23,14 @@ import {
Typography,
ExpansionPanel,
ExpansionPanelSummary,
ExpansionPanelDetails
ExpansionPanelDetails,
} from '@material-ui/core';
import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
import WarningIcon from '@material-ui/icons/Warning';
import BuildIcon from '@material-ui/icons/Build';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import './home.scss';
import {withTranslation} from 'react-i18next';
import { withTranslation } from 'react-i18next';
import FuzzySet from 'fuzzyset.js';
import DataService from '../../services/data';
@ -43,9 +43,9 @@ import PropTypes from 'prop-types';
const buildStatusCheckInterval = 5000;
const confirmationPopupOnBuildResquest = false;
function TabContainer({children, dir}) {
function TabContainer({ children, dir }) {
return (
<Typography component="div" dir={dir} style={{padding: '20px 0 0'}}>
<Typography component="div" dir={dir} style={{ padding: '20px 0 0' }}>
{children}
</Typography>
);
@ -58,7 +58,8 @@ TabContainer.propTypes = {
const sleep = m => new Promise(r => setTimeout(r, m));
const asu = 'https://aparcar.stephen304.com';
const asu_download = 'https://aparcar.stephen304.com/download/json-demo/openwrt/';
const asu_download =
'https://aparcar.stephen304.com/download/json-demo/openwrt/';
class Home extends React.Component {
state = {
@ -99,7 +100,9 @@ class Home extends React.Component {
const versionsResponse = await this.dataService.getVersions();
let data = versionsResponse.data.versions;
for (var i = 0; i < data.length; i++) {
const overviewResponse = await this.dataService.getOverview(data[i].path);
const overviewResponse = await this.dataService.getOverview(
data[i].path
);
data[i].devices = overviewResponse.data.devices;
}
this.generateFuzzySet(data[0].devices);
@ -124,9 +127,9 @@ class Home extends React.Component {
});
};
generateFuzzySet = (data) => {
generateFuzzySet = data => {
let deviceNames = [];
Object.keys(data).forEach((deviceName) => {
Object.keys(data).forEach(deviceName => {
deviceNames.push(deviceName);
});
this.setState({
@ -134,7 +137,7 @@ class Home extends React.Component {
});
};
setRelease = (event) => {
setRelease = event => {
this.generateFuzzySet(this.state.data[event.target.value].devices);
this.setState({
selection: {
@ -146,7 +149,7 @@ class Home extends React.Component {
});
};
selectDevice = async (device_name) => {
selectDevice = async device_name => {
const version = this.state.data[this.state.selection.version];
let selection;
try {
@ -177,9 +180,13 @@ class Home extends React.Component {
console.log(err);
return;
}
const noPackageFoundError = {error: 'no-packages-found'};
const noPackageFoundError = { error: 'no-packages-found' };
try {
let devicePackagesResponse = await this.dataService.getDevicePackages(version.name, selection.device.target, selection.device.id);
let devicePackagesResponse = await this.dataService.getDevicePackages(
version.name,
selection.device.target,
selection.device.id
);
if (devicePackagesResponse.data.length === 0) {
throw noPackageFoundError;
}
@ -200,7 +207,7 @@ class Home extends React.Component {
});
};
search = (event) => {
search = event => {
const query = event.target.value;
this.setState({
query,
@ -243,26 +250,26 @@ class Home extends React.Component {
}, 2000);
};
changeAddPackageInput = (event) => {
changeAddPackageInput = event => {
this.setState({
packageName: event.target.value,
});
};
deletePackage = (i) => {
deletePackage = i => {
let packages = this.state.packages;
packages.splice(i, 1);
this.setState({
packages,
configChanged: true
configChanged: true,
});
};
addPackage = (event) => {
addPackage = event => {
if ((event.which || event.keyCode) === 13 && !event.shiftKey) {
let packages = this.state.packages;
const packageArray = this.state.packageName.split(/[,\n]+/);
packageArray.forEach((package_name) => {
packageArray.forEach(package_name => {
package_name = package_name.replace(' ', '');
if (package_name !== '' && packages.indexOf(package_name) === -1) {
packages.push(package_name);
@ -271,7 +278,7 @@ class Home extends React.Component {
this.setState({
packages,
packageName: '',
configChanged: true
configChanged: true,
});
}
};
@ -284,18 +291,20 @@ class Home extends React.Component {
this.confirmingBuild = true;
};
displayBuiltImageData = async (buildStatusResponse) => {
displayBuiltImageData = async buildStatusResponse => {
const manifestResponse = await this.dataService.getDeviceManifest(
buildStatusResponse.data.image_folder + '/' + buildStatusResponse.data.image_prefix + '.manifest'
buildStatusResponse.data.image_folder +
'/' +
buildStatusResponse.data.image_prefix +
'.manifest'
);
const builtDeviceManifest = manifestResponse.data.split('\n');
let builtImages = [];
buildStatusResponse.data.images.forEach((image) => {
buildStatusResponse.data.images.forEach(image => {
builtImages.push({
url: asu +
buildStatusResponse.data.image_folder + '/' + image.name,
url: asu + buildStatusResponse.data.image_folder + '/' + image.name,
type: image.type,
});
});
@ -309,16 +318,21 @@ class Home extends React.Component {
}
};
buildImageCheck = async (request_hash) => {
buildImageCheck = async request_hash => {
try {
if (!this.state.isBuilding) {
return;
}
const buildStatusResponse = await this.dataService.buildStatusCheck(request_hash);
const buildStatusResponse = await this.dataService.buildStatusCheck(
request_hash
);
if (buildStatusResponse.status === 202) {
if (buildStatusResponse.headers['X-Build-Queue-Position'] !== undefined) {
if (
buildStatusResponse.headers['X-Build-Queue-Position'] !== undefined
) {
this.setState({
queuePosition: buildStatusResponse.headers['X-Build-Queue-Position'],
queuePosition:
buildStatusResponse.headers['X-Build-Queue-Position'],
});
}
await sleep(buildStatusCheckInterval);
@ -346,7 +360,13 @@ class Home extends React.Component {
errorDialogMessage: (
<>
{e.response.data.error} <br />
<a href={asu + e.response.data.log} target="_blank" rel="noopener noreferrer">Build logs</a>
<a
href={asu + e.response.data.log}
target="_blank"
rel="noopener noreferrer"
>
Build logs
</a>
</>
),
});
@ -354,11 +374,7 @@ class Home extends React.Component {
this.setState({
isBuilding: false,
openErrorDialog: true,
errorDialogMessage: (
<>
{e.response.data.error}
</>
),
errorDialogMessage: <>{e.response.data.error}</>,
});
} else {
this.setState({
@ -375,13 +391,23 @@ class Home extends React.Component {
const board = this.state.selection.device.id;
const packages = this.state.packages;
const target = this.state.selection.device['target'];
const version = this.state.data[this.state.selection.version].name.toLowerCase();
const version = this.state.data[
this.state.selection.version
].name.toLowerCase();
this.setState({
isBuilding: true,
builtImages: [],
});
let buildResponse = await this.dataService.buildImage(board, packages, target, version);
if (buildResponse.status === 202 && buildResponse.data['request_hash'] !== undefined) {
let buildResponse = await this.dataService.buildImage(
board,
packages,
target,
version
);
if (
buildResponse.status === 202 &&
buildResponse.data['request_hash'] !== undefined
) {
const request_hash = buildResponse.data['request_hash'];
await sleep(buildStatusCheckInterval);
await this.buildImageCheck(request_hash);
@ -398,7 +424,13 @@ class Home extends React.Component {
errorDialogMessage: (
<>
{e.response.data.error} <br />
<a href={asu + e.response.data.log} target="_blank" rel="noopener noreferrer">Build logs</a>
<a
href={asu + e.response.data.log}
target="_blank"
rel="noopener noreferrer"
>
Build logs
</a>
</>
),
});
@ -406,11 +438,7 @@ class Home extends React.Component {
this.setState({
isBuilding: false,
openErrorDialog: true,
errorDialogMessage: (
<>
{e.response.data.error}
</>
),
errorDialogMessage: <>{e.response.data.error}</>,
});
} else {
this.setState({
@ -426,351 +454,372 @@ class Home extends React.Component {
isBuilding: false,
configChanged: true,
});
}
};
closeErrorDialog = () => {
this.setState({
openErrorDialog: false,
});
}
};
render() {
const warning432 = this.state.showDeviceData && this.state.deviceLoaded &&
parseInt(
(this.state.selection.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"/>
const warning432 = this.state.showDeviceData &&
this.state.deviceLoaded &&
parseInt(
(this.state.selection.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('warning432')}
</Grid>
</Grid>
<Grid item xs>
{this.props.t('warning432')}
</Grid>
</Grid>
</Paper>
);
const notLoaded = (
<CircularProgress/>
);
</Paper>
);
const notLoaded = <CircularProgress />;
const onLoad = (
<>
<Typography variant="h5">
{this.props.t('Download OpenWrt firmware for your device!')}
</Typography>
<Typography>
{this.props.t(
'Please use the input below to download firmware for your device!')}
</Typography>
<br/>
<ClickAwayListener onClickAway={this.hideSearchResults}>
<div className="search-container">
<FormControl className="version-select">
<InputLabel htmlFor="version-select" className="version-label">
{this.props.t('Version')}
</InputLabel>
<Select
value={this.state.selection.version}
onChange={this.setRelease}
disabled={this.state.isBuilding}
input={
<OutlinedInput name="version"id="version-select" labelWidth={60}/>
}
<>
<Typography variant="h5">
{this.props.t('Download OpenWrt firmware for your device!')}
</Typography>
<Typography>
{this.props.t(
'Please use the input below to download firmware for your device!'
)}
</Typography>
<br />
<ClickAwayListener onClickAway={this.hideSearchResults}>
<div className="search-container">
<FormControl className="version-select">
<InputLabel htmlFor="version-select" className="version-label">
{this.props.t('Version')}
</InputLabel>
<Select
value={this.state.selection.version}
onChange={this.setRelease}
disabled={this.state.isBuilding}
input={
<OutlinedInput
name="version"
id="version-select"
labelWidth={60}
/>
}
>
{this.state.data.map((version, i) => (
<MenuItem value={i} key={version.revision}>
<em>{version.name}</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}
disabled={this.state.isBuilding}
/>
{this.state.showSearch && this.state.searchResults.length !== 0 && (
<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>
)}
</FormControl>
</div>
</ClickAwayListener>
{this.state.showDeviceData && !this.state.deviceLoaded && (
<>
<br />
{notLoaded}
</>
)}
{this.state.showDeviceData && this.state.deviceLoaded && (
<>
{warning432}
<br />
{this.state.showAdvanced && (
<AppBar
className="interface-switch-bar"
position="relative"
elevation={0}
>
<Tabs
value={this.state.basicInterface}
onChange={this.changeInterface}
>
{
this.state.data.map((version, i) => (
<MenuItem value={i} key={version.revision}>
<em>{version.name}</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}
disabled={this.state.isBuilding}
/>
{
this.state.showSearch && this.state.searchResults.length !==
0 && (
<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>
)
}
</FormControl>
</div>
</ClickAwayListener>
{
this.state.showDeviceData && !this.state.deviceLoaded && (
<>
<br/>
{notLoaded}
</>
)
}
{
this.state.showDeviceData && this.state.deviceLoaded && (
<>
{warning432}
<br/>
{
this.state.showAdvanced && (
<AppBar className="interface-switch-bar" position="relative" elevation={0}>
<Tabs value={this.state.basicInterface}onChange={this.changeInterface}>
<Tab className="interface-switch" label={this.props.t('Basic')} disabled={this.state.isBuilding}/>
<Tab className="interface-switch" label={this.props.t('Advanced')} disabled={this.state.isBuilding}/>
</Tabs>
</AppBar>
)
}
{
this.state.basicInterface === 0 ? (
<TabContainer>
<Grid container className="device-info">
<Grid item xs>
{this.props.t('Model')}: <b> {this.state.selection.device['title']} </b> <br />
{this.props.t('Target')}: {this.state.selection.device['target']} <br />
{this.props.t('Version')}: {this.state.data[this.state.selection.version].name} ({this.state.data[this.state.selection.version].revision})
</Grid>
<Grid item xs>
<b>{this.props.t('Downloads')}: </b>
{
this.state.selection.device.images.map((image) =>
<div key={image.name}>
<Button
className="download-button"
href={asu_download +
this.state.data[this.state.selection.version].path + '/targets/' +
this.state.selection.device.target + '/' +
image.name}
color="primary"
variant="contained"
onClick={() => this.downloadingImageIndicatorShow()}
>
<CloudDownloadIcon
className="download-icon"/>
{image.name.split('-').reverse()[0].split('.')[0]}
</Button>
</div>
)
}
&nbsp;
{
this.state.downloading && (
<CircularProgress size={20}/>
)
}
</Grid>
</Grid>
</TabContainer>
) : (
<TabContainer>
<Paper elevation={0} className="package-list-input">
<div>
{
this.state.packages.map((package_name, i) =>
<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={confirmationPopupOnBuildResquest ? this.openConfirmBuildDialog : this.buildImage}>
<BuildIcon/>
&nbsp;
{this.props.t('Build')}
</Button>
)
<Tab
className="interface-switch"
label={this.props.t('Basic')}
disabled={this.state.isBuilding}
/>
<Tab
className="interface-switch"
label={this.props.t('Advanced')}
disabled={this.state.isBuilding}
/>
</Tabs>
</AppBar>
)}
{this.state.basicInterface === 0 ? (
<TabContainer>
<Grid container className="device-info">
<Grid item xs>
{this.props.t('Model')}:{' '}
<b> {this.state.selection.device['title']} </b> <br />
{this.props.t('Target')}:{' '}
{this.state.selection.device['target']} <br />
{this.props.t('Version')}:{' '}
{this.state.data[this.state.selection.version].name} (
{this.state.data[this.state.selection.version].revision})
</Grid>
<Grid item xs>
<b>{this.props.t('Downloads')}: </b>
{this.state.selection.device.images.map(image => (
<div key={image.name}>
<Button
className="download-button"
href={
asu_download +
this.state.data[this.state.selection.version].path +
'/targets/' +
this.state.selection.device.target +
'/' +
image.name
}
color="primary"
variant="contained"
onClick={() => this.downloadingImageIndicatorShow()}
>
<CloudDownloadIcon className="download-icon" />
{
this.state.isBuilding && (
<>
<Button variant="outlined" size="small"
onClick={this.cancelBuild}>
&nbsp;
{this.props.t('Cancel')}
</Button>
&nbsp;
&nbsp;
<CircularProgress size={20} style={{verticalAlign: 'middle'}}/>
&nbsp;
Building image
&nbsp;
{
this.state.queuePosition !== -1 && (
<span> (Position in queue: {this.state.queuePosition}) </span>
image.name
.split('-')
.reverse()[0]
.split('.')[0]
}
</Button>
</div>
))}
&nbsp;
{this.state.downloading && <CircularProgress size={20} />}
</Grid>
</Grid>
</TabContainer>
) : (
<TabContainer>
<Paper elevation={0} className="package-list-input">
<div>
{this.state.packages.map((package_name, i) => (
<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={
confirmationPopupOnBuildResquest
? this.openConfirmBuildDialog
: this.buildImage
}
>
<BuildIcon />
&nbsp;
{this.props.t('Build')}
</Button>
)}
{this.state.isBuilding && (
<>
<Button
variant="outlined"
size="small"
onClick={this.cancelBuild}
>
&nbsp;
{this.props.t('Cancel')}
</Button>
&nbsp; &nbsp;
<CircularProgress
size={20}
style={{ verticalAlign: 'middle' }}
/>
&nbsp; Building image &nbsp;
{this.state.queuePosition !== -1 && (
<span>
{' '}
(Position in queue: {this.state.queuePosition}){' '}
</span>
)}
...
</>
)}
{this.state.builtImages.length > 0 &&
!this.state.configChanged && (
<Grid container className="device-info">
<Grid item xs>
{this.props.t('Model')}:{' '}
<b> {this.state.selection.device['title']} </b> <br />
{this.props.t('Target')}:{' '}
{this.state.selection.device['target']} <br />
{this.props.t('Version')}:{' '}
{this.state.data[this.state.selection.version].name} (
{
this.state.data[this.state.selection.version]
.revision
}
)
<ExpansionPanel
className="installed-packages"
elevation={0}
>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
id="packages-manifest"
>
<Typography className="installed-packages-title">
Installed Packages (
{this.state.builtDeviceManifest.length})
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<div>
{this.state.builtDeviceManifest.map(
package_name => (
<div key={package_name}>{package_name}</div>
)
)}
</div>
</ExpansionPanelDetails>
</ExpansionPanel>
</Grid>
<Grid item xs>
<b>{this.props.t('Downloads')}: </b>
{this.state.builtImages.map(image => (
<div key={image.url}>
<Button
className="download-button"
href={image.url}
color="primary"
variant="contained"
onClick={() =>
this.downloadingImageIndicatorShow()
}
...
</>
)
}
{
this.state.builtImages.length > 0 && !this.state.configChanged && (
<Grid container className="device-info">
<Grid item xs>
{this.props.t('Model')}: <b> {this.state.selection.device['title']} </b> <br />
{this.props.t('Target')}: {this.state.selection.device['target']} <br />
{this.props.t('Version')}: {this.state.data[this.state.selection.version].name} ({this.state.data[this.state.selection.version].revision})
<ExpansionPanel className="installed-packages" elevation={0}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
id="packages-manifest"
>
<Typography className="installed-packages-title">Installed Packages ({this.state.builtDeviceManifest.length})</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<div>
{
this.state.builtDeviceManifest.map(
(package_name) => <div key={package_name}>
{package_name}
</div>
)
}
</div>
</ExpansionPanelDetails>
</ExpansionPanel>
</Grid>
<Grid item xs>
<b>{this.props.t('Downloads')}: </b>
{
this.state.builtImages.map((image) =>
<div key={image.url}>
<Button
className="download-button"
href={image.url}
color="primary"
variant="contained"
onClick={() => this.downloadingImageIndicatorShow()}
>
<CloudDownloadIcon
className="download-icon"/>
{image.type}
</Button>
</div>
)
}
&nbsp;
{
this.state.downloading && (
<CircularProgress size={20}/>
)
}
</Grid>
</Grid>
)
}
</Paper>
</TabContainer>
)
}
</>
)
}
</>
>
<CloudDownloadIcon className="download-icon" />
{image.type}
</Button>
</div>
))}
&nbsp;
{this.state.downloading && (
<CircularProgress size={20} />
)}
</Grid>
</Grid>
)}
</Paper>
</TabContainer>
)}
</>
)}
</>
);
return (
<>
<ErrorSnackBar
open={this.state.showUnexpectedErrorBar}
closeHandle={this.closeUnexpectedErrorBar}
/>
<AlertDialog
cancelHandler={this.closeConfirmBuildDialog}
acceptHandler={this.buildImage}
open={this.confirmingBuild}
body={
<>
{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/>
</>
}
/>
<AlertDialog
cancelHandler={this.closeErrorDialog}
open={this.state.openErrorDialog}
body={this.state.errorDialogMessage}
title={this.props.t('There is an error with the packages you selected')}
cancelComponent={this.props.t('Dismiss')}
/>
<Container className="home-container">
<Paper className="home-container-paper">
{this.state.devicesLoaded ? onLoad : notLoaded}
</Paper>
</Container>
</>
<>
<ErrorSnackBar
open={this.state.showUnexpectedErrorBar}
closeHandle={this.closeUnexpectedErrorBar}
/>
<AlertDialog
cancelHandler={this.closeConfirmBuildDialog}
acceptHandler={this.buildImage}
open={this.confirmingBuild}
body={
<>
{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 />
</>
}
/>
<AlertDialog
cancelHandler={this.closeErrorDialog}
open={this.state.openErrorDialog}
body={this.state.errorDialogMessage}
title={this.props.t(
'There is an error with the packages you selected'
)}
cancelComponent={this.props.t('Dismiss')}
/>
<Container className="home-container">
<Paper className="home-container-paper">
{this.state.devicesLoaded ? onLoad : notLoaded}
</Paper>
</Container>
</>
);
}
}
Home.propTypes = {
t: PropTypes.func
t: PropTypes.func,
};
export default withTranslation()(Home);

View file

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

View file

@ -1,5 +1,5 @@
import i18n from 'i18next';
import {initReactI18next} from 'react-i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import translationEN from './locales/en.json';
import translationDE from './locales/de.json';
@ -13,7 +13,8 @@ const resources = {
},
};
i18n.use(LanguageDetector)
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,

View file

@ -1,32 +1,44 @@
import axios from 'axios';
const base_downloads = 'https://cors-anywhere.herokuapp.com/https://aparcar.stephen304.com/download/json-demo/openwrt/';
const base_api = 'https://cors-anywhere.herokuapp.com/https://aparcar.stephen304.com/api/';
const base_downloads =
'https://cors-anywhere.herokuapp.com/https://aparcar.stephen304.com/download/json-demo/openwrt/';
const base_api =
'https://cors-anywhere.herokuapp.com/https://aparcar.stephen304.com/api/';
class DataService {
getVersions = () => axios.get(base_downloads + 'versions.json');
getOverview = (path) => axios.get(base_downloads + path + '/overview.json');
getDeviceData = (device_path) => axios.get(base_downloads + device_path);
getOverview = path => axios.get(base_downloads + path + '/overview.json');
getDevicePackages = (version, target, profile) => axios.get(base_api + 'packages_image?distro=openwrt&version=' + version.toLowerCase() + '&target=' + target + '&profile=' + profile.toLowerCase());
getDeviceData = device_path => axios.get(base_downloads + device_path);
buildImage = (board, packages, target, version) => axios.post(base_api + 'build-request', {
profile: board,
board,
defaults: '',
distro: 'openwrt',
packages,
target,
version,
});
getDevicePackages = (version, target, profile) =>
axios.get(
base_api +
'packages_image?distro=openwrt&version=' +
version.toLowerCase() +
'&target=' +
target +
'&profile=' +
profile.toLowerCase()
);
buildStatusCheck = (request_hash) => axios.get(base_api + 'build-request/' + request_hash);
buildImage = (board, packages, target, version) =>
axios.post(base_api + 'build-request', {
profile: board,
board,
defaults: '',
distro: 'openwrt',
packages,
target,
version,
});
getFiles = (files_url) => axios.get('https://chef.libremesh.org' + files_url).then(res => res.data);
buildStatusCheck = request_hash =>
axios.get(base_api + 'build-request/' + request_hash);
getFiles = files_url =>
axios.get('https://chef.libremesh.org' + files_url).then(res => res.data);
}
export default DataService;