Re-engineer UI in TypeScript (#89)
* Re-engineer UI in TypeScript * Switch to named imports where possible * Restructure file system layout * Update depencencies * Update README.md * Change explicit colors for better support for dark theme
This commit is contained in:
7
interface/src/ap/APModes.ts
Normal file
7
interface/src/ap/APModes.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { APSettings } from "./types";
|
||||
|
||||
export const AP_MODE_ALWAYS = 0;
|
||||
export const AP_MODE_DISCONNECTED = 1;
|
||||
export const AP_NEVER = 2;
|
||||
|
||||
export const isAPEnabled = ({ provision_mode }: APSettings) => provision_mode === AP_MODE_ALWAYS || provision_mode === AP_MODE_DISCONNECTED;
|
30
interface/src/ap/APSettingsController.tsx
Normal file
30
interface/src/ap/APSettingsController.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { AP_SETTINGS_ENDPOINT } from '../api';
|
||||
import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components';
|
||||
|
||||
import APSettingsForm from './APSettingsForm';
|
||||
import { APSettings } from './types';
|
||||
|
||||
type APSettingsControllerProps = RestControllerProps<APSettings>;
|
||||
|
||||
class APSettingsController extends Component<APSettingsControllerProps> {
|
||||
|
||||
componentDidMount() {
|
||||
this.props.loadData();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SectionContent title="Access Point Settings" titleGutter>
|
||||
<RestFormLoader
|
||||
{...this.props}
|
||||
render={formProps => <APSettingsForm {...formProps} />}
|
||||
/>
|
||||
</SectionContent>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default restController(AP_SETTINGS_ENDPOINT, APSettingsController);
|
71
interface/src/ap/APSettingsForm.tsx
Normal file
71
interface/src/ap/APSettingsForm.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { TextValidator, ValidatorForm, SelectValidator } from 'react-material-ui-form-validator';
|
||||
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import SaveIcon from '@material-ui/icons/Save';
|
||||
|
||||
import {PasswordValidator, RestFormProps, FormActions, FormButton} from '../components';
|
||||
|
||||
import { isAPEnabled, AP_MODE_ALWAYS, AP_MODE_DISCONNECTED, AP_NEVER } from './APModes';
|
||||
import { APSettings } from './types';
|
||||
|
||||
type APSettingsFormProps = RestFormProps<APSettings>;
|
||||
|
||||
class APSettingsForm extends React.Component<APSettingsFormProps> {
|
||||
|
||||
render() {
|
||||
const { data, handleValueChange, saveData, loadData } = this.props;
|
||||
return (
|
||||
<ValidatorForm onSubmit={saveData} ref="APSettingsForm">
|
||||
<SelectValidator name="provision_mode"
|
||||
label="Provide Access Point..."
|
||||
value={data.provision_mode}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('provision_mode')}
|
||||
margin="normal">
|
||||
<MenuItem value={AP_MODE_ALWAYS}>Always</MenuItem>
|
||||
<MenuItem value={AP_MODE_DISCONNECTED}>When WiFi Disconnected</MenuItem>
|
||||
<MenuItem value={AP_NEVER}>Never</MenuItem>
|
||||
</SelectValidator>
|
||||
{
|
||||
isAPEnabled(data) &&
|
||||
<Fragment>
|
||||
<TextValidator
|
||||
validators={['required', 'matchRegexp:^.{1,32}$']}
|
||||
errorMessages={['Access Point SSID is required', 'Access Point SSID must be 32 characters or less']}
|
||||
name="ssid"
|
||||
label="Access Point SSID"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.ssid}
|
||||
onChange={handleValueChange('ssid')}
|
||||
margin="normal"
|
||||
/>
|
||||
<PasswordValidator
|
||||
validators={['required', 'matchRegexp:^.{1,64}$']}
|
||||
errorMessages={['Access Point Password is required', 'Access Point Password must be 64 characters or less']}
|
||||
name="password"
|
||||
label="Access Point Password"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.password}
|
||||
onChange={handleValueChange('password')}
|
||||
margin="normal"
|
||||
/>
|
||||
</Fragment>
|
||||
}
|
||||
<FormActions>
|
||||
<FormButton startIcon={<SaveIcon />} variant="contained" color="primary" type="submit">
|
||||
Save
|
||||
</FormButton>
|
||||
<FormButton variant="contained" color="secondary" onClick={loadData}>
|
||||
Reset
|
||||
</FormButton>
|
||||
</FormActions>
|
||||
</ValidatorForm>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default APSettingsForm;
|
10
interface/src/ap/APStatus.ts
Normal file
10
interface/src/ap/APStatus.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Theme } from "@material-ui/core";
|
||||
import { APStatus } from "./types";
|
||||
|
||||
export const apStatusHighlight = ({ active }: APStatus, theme: Theme) => {
|
||||
return active ? theme.palette.success.main : theme.palette.info.main;
|
||||
}
|
||||
|
||||
export const apStatus = ({ active }: APStatus) => {
|
||||
return active ? "Active" : "Inactive";
|
||||
};
|
29
interface/src/ap/APStatusController.tsx
Normal file
29
interface/src/ap/APStatusController.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components';
|
||||
import { AP_STATUS_ENDPOINT } from '../api';
|
||||
|
||||
import APStatusForm from './APStatusForm';
|
||||
import { APStatus } from './types';
|
||||
|
||||
type APStatusControllerProps = RestControllerProps<APStatus>;
|
||||
|
||||
class APStatusController extends Component<APStatusControllerProps> {
|
||||
|
||||
componentDidMount() {
|
||||
this.props.loadData();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SectionContent title="Access Point Status">
|
||||
<RestFormLoader
|
||||
{...this.props}
|
||||
render={formProps => <APStatusForm {...formProps} />}
|
||||
/>
|
||||
</SectionContent>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default restController(AP_STATUS_ENDPOINT, APStatusController);
|
78
interface/src/ap/APStatusForm.tsx
Normal file
78
interface/src/ap/APStatusForm.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
|
||||
import { WithTheme, withTheme } from '@material-ui/core/styles';
|
||||
import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core';
|
||||
|
||||
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
|
||||
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
|
||||
import ComputerIcon from '@material-ui/icons/Computer';
|
||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
||||
|
||||
import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components';
|
||||
import { apStatusHighlight, apStatus } from './APStatus';
|
||||
import { APStatus } from './types';
|
||||
|
||||
type APStatusFormProps = RestFormProps<APStatus> & WithTheme;
|
||||
|
||||
class APStatusForm extends Component<APStatusFormProps> {
|
||||
|
||||
createListItems() {
|
||||
const { data, theme } = this.props
|
||||
return (
|
||||
<Fragment>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<HighlightAvatar color={apStatusHighlight(data, theme)}>
|
||||
<SettingsInputAntennaIcon />
|
||||
</HighlightAvatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="Status" secondary={apStatus(data)} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>IP</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="IP Address" secondary={data.ip_address} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<DeviceHubIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="MAC Address" secondary={data.mac_address} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<ComputerIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="AP Clients" secondary={data.station_num} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<List>
|
||||
{this.createListItems()}
|
||||
</List>
|
||||
<FormActions>
|
||||
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}>
|
||||
Refresh
|
||||
</FormButton>
|
||||
</FormActions>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withTheme(APStatusForm);
|
38
interface/src/ap/AccessPoint.tsx
Normal file
38
interface/src/ap/AccessPoint.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect, Switch, RouteComponentProps } from 'react-router-dom'
|
||||
|
||||
import { Tabs, Tab } from '@material-ui/core';
|
||||
|
||||
import { AuthenticatedContextProps, withAuthenticatedContext, AuthenticatedRoute } from '../authentication';
|
||||
import { MenuAppBar } from '../components';
|
||||
|
||||
import APSettingsController from './APSettingsController';
|
||||
import APStatusController from './APStatusController';
|
||||
|
||||
type AccessPointProps = AuthenticatedContextProps & RouteComponentProps;
|
||||
|
||||
class AccessPoint extends Component<AccessPointProps> {
|
||||
|
||||
handleTabChange = (event: React.ChangeEvent<{}>, path: string) => {
|
||||
this.props.history.push(path);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { authenticatedContext } = this.props;
|
||||
return (
|
||||
<MenuAppBar sectionTitle="Access Point">
|
||||
<Tabs value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
|
||||
<Tab value="/ap/status" label="Access Point Status" />
|
||||
<Tab value="/ap/settings" label="Access Point Settings" disabled={!authenticatedContext.me.admin} />
|
||||
</Tabs>
|
||||
<Switch>
|
||||
<AuthenticatedRoute exact={true} path="/ap/status" component={APStatusController} />
|
||||
<AuthenticatedRoute exact={true} path="/ap/settings" component={APSettingsController} />
|
||||
<Redirect to="/ap/status" />
|
||||
</Switch>
|
||||
</MenuAppBar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withAuthenticatedContext(AccessPoint);
|
12
interface/src/ap/types.ts
Normal file
12
interface/src/ap/types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export interface APStatus {
|
||||
active: boolean;
|
||||
ip_address: string;
|
||||
mac_address: string;
|
||||
station_num: number;
|
||||
}
|
||||
|
||||
export interface APSettings {
|
||||
provision_mode: number;
|
||||
ssid: string;
|
||||
password: string;
|
||||
}
|
Reference in New Issue
Block a user